
1. 项目概览:不刻字、不贴码,靠 3D 模型认工件 🧱
热处理工厂每天会接收来自不同厂家的大量工件。它们数量多、品类杂、外观特征有时还“长得像亲戚”,一旦混在一起,工人分拣和追溯就会变成大型找不同现场。传统做法包括人工录入、激光雕刻、油漆笔标记、喷码、纸质二维码或 RFID 等,但这些方式要么效率低,要么会破坏表面,要么难以适应高温、油污、氧化等热处理生产环境。
本项目提出了一种更“数字化”的思路:利用四台 Astra Pro 深度相机从多个视角采集工件点云,完成三维重建、尺寸测量、特征提取和数据库检索,让每个工件拥有自己的 3D 形态档案。也就是说,工件不一定要贴标签,它自己的几何形状就是标签。😎
| 项目条目 | 内容 |
|---|---|
| 来源 | 第二届 3D 视觉创新应用竞赛参赛选手黎学海 |
| 相机型号 | Astra Pro |
| 支持设备 | 项目资料未明确列出;方案使用四台 Astra Pro 与处理平台 |
| 核心任务 | 工件三维重建、入库、检索、身份匹配 |
| 技术关键词 | 点云采集、SAC-IA 配准、拼接、滤波、MLS 平滑、Bounding Box、FPFH |
| 源码链接 | https://github.com/3DCVdeveloper/3DReconstruction_Heat-treatment-detection |
2. 为什么热处理工件需要“身份识别”?🔥
热处理工件的身份管理有几个特别棘手的点:工件可能经历高温、淬火、回火、喷砂、运输和暂存等流程;表面标记可能脱落、模糊、被覆盖,甚至压根不允许刻字;而且工件前后阶段的状态可能有变化,人工靠肉眼记忆,难度不小。
| 传统方式 | 优点 | 问题 |
|---|---|---|
| 人工录入 | 成本低 | 效率低,难以自动追溯 |
| 激光雕刻 | 标识稳定 | 可能破坏工件表面 |
| 油漆笔标记 | 操作简单 | 依赖个人习惯,规范性弱 |
| 二维码纸条 | 信息量大 | 不耐热,不适合复杂工况 |
| RFID | 可自动读取 | 对金属环境和高温流程要求高 |
| 3D 数字模型 | 非接触、无损、可追溯 | 依赖点云质量与算法稳定性 |
本项目的优势在于:它不试图往工件身上“贴点什么”,而是把工件自身的三维几何信息提取出来,形成可检索的数字档案。对智能制造来说,这就像把工件从“散装记忆”推进到“数据库管理”。工件终于不用靠油漆笔证明自己是谁了。🪪
3. 硬件方案:四台 Astra Pro 围着工件“开会”📷

方案说明中设计了一个约 1x1x1.2M 的测量空间,并在四角布置 Astra Pro,对中心工件进行全方位测量。Astra Pro 支持在约 0.6-8M 范围内完成深度恢复和点云生成,因此适合这类中小尺寸工件的近距离三维采集。
硬件结构还考虑了高度调节:通过调整底部安装高度,可以适应不同尺寸的工件。这个细节很实用,因为工厂里的工件不会乖乖都长成同一个尺寸。算法喜欢标准化,工件偏偏很有个性。
| 硬件组成 | 作用 |
|---|---|
| 四台 Astra Pro | 从四个方向采集深度与点云 |
| 固定支架 | 保证相机与工件位置关系稳定 |
| 可调底座 | 适应不同高度的工件 |
| 中央测量空间 | 放置待检测工件 |
| 处理平台 | 负责点云保存、配准、拼接和检索 |
4. 系统总体流程:从四片点云到一个身份编号 🧩

系统可以分成“采集建模”和“入库检索”两条主线:
| 阶段 | 输入 | 输出 |
|---|---|---|
| 1. 多视角采集 | 四台 Astra Pro 点云 | 四组局部点云 |
| 2. 点云配准 | 标准块点云 | 四路相机相对位姿矩阵 |
| 3. 点云拼接 | 四组点云 + R/T 矩阵 | 完整工件点云 |
| 4. 后处理 | 原始拼接点云 | 干净、平滑、裁剪后的点云 |
| 5. 尺寸测量 | 工件点云 | 长、宽、高、体积等几何量 |
| 6. 特征提取 | 点云表面结构 | FPFH、三视图等描述信息 |
| 7. 入库检索 | 尺寸 + 体积 + 特征 | 工件编号、名称、匹配结果 |
代码主流程位于 main.cpp,其核心链路与方案说明基本一致:加载四路点云,应用四个变换矩阵,合并为整体点云,再进行裁剪、去噪、平滑、保存和尺寸计算。
pcl::io::loadPLYFile<pcl::PointXYZ>("cloud\t11.ply", *cloud0);
pcl::io::loadPLYFile<pcl::PointXYZ>("cloud\t22.ply", *cloud1);
pcl::io::loadPLYFile<pcl::PointXYZ>("cloud\t33.ply", *cloud2);
pcl::io::loadPLYFile<pcl::PointXYZ>("cloud\t44.ply", *cloud3);
pcl::transformPointCloud(*cloud0, *cloud0, r0);
pcl::transformPointCloud(*cloud1, *cloud1, r1);
pcl::transformPointCloud(*cloud2, *cloud2, r2);
pcl::transformPointCloud(*cloud3, *cloud3, r3);
*cloudAll = *cloud0 + *cloud1;
*cloudAll = *cloudAll + *cloud2;
*cloudAll = *cloudAll + *cloud3;
5. 点云配准:先把四台相机放进同一个坐标系 🧭
四台相机看到的是同一个工件,但每台相机都有自己的坐标系。如果不配准,四片点云就像四个人分别描述同一件工件:都对,但拼不到一起。点云配准的目标,就是求出每台相机相对于统一坐标系的旋转矩阵 R 和平移向量 T。
方案说明中使用标准块辅助配准,并采用 SAC-IA,也就是采样一致性初始配准。它通过采样候选对应关系、估计刚体变换、评价变换质量来获得初始配准结果。相较于直接精配准,SAC-IA 计算量更小,更适合先获得相机间的大致空间关系。
项目中的 cloud/配准.txt 保存了四个 4×4 变换矩阵,main.cpp 中也直接使用这些矩阵完成坐标变换:
Eigen::Matrix4d r0 = Eigen::Matrix4d::Identity();
r0 << -0.689348, 0.477992, 0.544357, -0.446394,
-0.723581, -0.417930, -0.549332, 0.461347,
0.035074, 0.772567, -0.633964, 0.798094,
0.000000, 0.000000, 0.000000, 1.000000;
pcl::transformPointCloud(*cloud0, *cloud0, r0);
可以把这一步理解成给每个点云块“搬家”:原来它住在相机自己的小坐标系里,现在搬到统一的工件坐标系里,大家终于能坐在同一张桌子上开会了。
6. 测量空间裁剪:把无关背景请出画面 ✂️

原始点云中除了工件,还可能包含支架、地面、背景、无效深度点等。为了让后续计算集中在工件附近,代码使用 PCL 的 PassThrough 滤波器对 x/y/z 三个方向进行范围裁剪。
int cutting(pcl::PointCloud<pcl::PointXYZ>::Ptr& raw,
float x1, float x2,
float y1, float y2,
float z1, float z2)
{
pcl::PassThrough<pcl::PointXYZ> passX;
passX.setInputCloud(raw);
passX.setFilterFieldName("x");
passX.setFilterLimits(x1, x2);
passX.setFilterLimitsNegative(false);
passX.filter(*raw);
pcl::PassThrough<pcl::PointXYZ> passY;
passY.setInputCloud(raw);
passY.setFilterFieldName("y");
passY.setFilterLimits(y1, y2);
passY.filter(*raw);
}
在 main.cpp 中,四路点云先被裁剪到同一测量空间,再拼接成完整点云:
cutting(cloud0, -0.625, 0.598, -0.4, 0.39, 0.75, 1.52);
cutting(cloud1, -0.625, 0.598, -0.4, 0.39, 0.75, 1.52);
cutting(cloud2, -0.625, 0.598, -0.4, 0.39, 0.75, 1.52);
cutting(cloud3, -0.625, 0.598, -0.4, 0.39, 0.75, 1.52);
这一步看似简单,但对工业场景非常关键。背景点越少,后面的尺寸测量和特征提取越不容易被带偏。
7. 离群点去除与平滑:给点云做一次“工业级修面”🧼
多视角点云拼接后,常见问题包括孤立噪点、表面毛刺、局部空洞和轻微错位。项目中使用统计滤波去除离群点,再通过移动最小二乘法平滑点云表面。
| 处理方法 | 代码函数 | 作用 |
|---|---|---|
| 统计离群点去除 | outlinerRemover |
删除密度异常、远离邻域的孤立点 |
| 移动最小二乘 | MovingLeastSquares |
平滑表面,减少采样抖动 |
| 可视化对比 | cshoww |
同屏查看原点云与处理后点云 |
统计滤波使用邻域平均距离来判断异常点:
int outlinerRemover(pcl::PointCloud<pcl::PointXYZ>::Ptr& cloud,
int pointnum,
double threshindex)
{
pcl::StatisticalOutlierRemoval<pcl::PointXYZ> sor;
sor.setInputCloud(cloud);
sor.setMeanK(pointnum);
sor.setStddevMulThresh(threshindex);
sor.filter(*cloud_inliner);
pcl::copyPointCloud<pcl::PointXYZ>(*cloud_inliner, *cloud);
}
移动最小二乘法则根据邻域点拟合局部曲面,降低小范围跳动:
int MovingLeastSquares(pcl::PointCloud<pcl::PointXYZ>::Ptr& cloud, float r)
{
pcl::MovingLeastSquares<pcl::PointXYZ, pcl::PointXYZ> mls;
mls.setInputCloud(cloud);
mls.setPolynomialOrder(2);
mls.setPolynomialFit(false);
mls.setSearchRadius(r);
mls.process(cloud_p);
cloud = cloud_p.makeShared();
return 0;
}
在示例数据中,单个 .ply 文件头部显示约 1,310,720 个顶点;经过拼接、裁剪、去噪和平滑后,生成的 all.pcd 记录了 46,783 个点。数据规模从“满天星”变成“重点对象”,算法终于不用在背景噪声里加班了。
8. 尺寸测量:Bounding Box 让长宽高有据可依 📏

工件身份识别不仅依赖“形状像不像”,还要先看基础尺寸是否匹配。项目使用 PCL 的 MomentOfInertiaEstimation 获取点云的包围盒,并输出长、宽、高。
int boundingBox(pcl::PointCloud<pcl::PointXYZ>::Ptr& cloud,
float length, float wide, float height, int showoff)
{
pcl::MomentOfInertiaEstimation<pcl::PointXYZ> feature_extractor;
feature_extractor.setInputCloud(cloud);
feature_extractor.compute();
pcl::PointXYZ min_point_AABB;
pcl::PointXYZ max_point_AABB;
feature_extractor.getAABB(min_point_AABB, max_point_AABB);
length = max_point_AABB.x - min_point_AABB.x;
wide = max_point_AABB.y - min_point_AABB.y;
height = max_point_AABB.z - min_point_AABB.z;
}
方案说明中提到的是 OBB 方向包围盒:通过 PCA 主元分析获得点云主方向,再在主方向坐标系中计算包围盒。源码当前实现主要使用 AABB 轴对齐包围盒并保留惯性矩/特征值接口,说明该模块已具备尺寸计算基础,后续可进一步完善为更严格的 OBB 测量。
| 包围盒类型 | 特点 | 适用情况 |
|---|---|---|
| AABB | 与坐标轴对齐,计算简单 | 工件姿态固定或已校正 |
| OBB | 随主方向旋转,贴合度更高 | 工件摆放角度变化较大 |
9. 形状分割与几何拟合:圆、平面、圆柱都安排上 ⚙️
源码中还包含多种几何模型拟合与分割函数,包括平面、圆、球、圆柱、边界检测和欧式聚类。这些模块可以辅助识别具有规则结构的工件,例如轴类件、环形件、孔结构件或带圆柱面的零件。
| 文件 | 技术模块 | 作用 |
|---|---|---|
sac_circle.cpp |
RANSAC/LMedS/MSAC 几何拟合 | 平面、圆、球、圆柱模型估计 |
segCylinder.cpp |
基于法向量的 SAC 分割 | 提取圆柱、圆、球等结构 |
Euclidean.cpp |
欧式聚类 | 分离多个点云簇,保留主体工件 |
boundDetection.cpp |
边界检测 | 提取点云轮廓与边界区域 |
planeCalibration.cpp |
平面校正 | 将标定平面法向旋转到统一方向 |
圆柱分割的核心逻辑如下:
pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;
ne.setInputCloud(raw);
ne.setKSearch(120);
ne.compute(*cloud_normals);
pcl::SACSegmentationFromNormals<PointT, pcl::Normal> seg;
seg.setOptimizeCoefficients(true);
seg.setModelType(pcl::SACMODEL_CYLINDER);
seg.setMethodType(pcl::SAC_RANSAC);
seg.setNormalDistanceWeight(0.3);
seg.setMaxIterations(MaxIterations);
seg.setDistanceThreshold(dist);
seg.setRadiusLimits(r_min, r_max);
seg.segment(*inliers_cylinder, *coefficients_cylinder);
这类模块的意义在于:工件身份不只由整体尺寸决定,局部几何结构也很重要。两个工件长宽高相近,但一个有孔、一个有凸台,那身份当然不能混。点云几何特征就是它们的“结构指纹”。
10. FPFH 特征描述子:把表面几何压缩成可检索特征 🧠

方案说明中将 FPFH 作为工件检索的重要特征。FPFH 即快速点特征直方图,用于描述点云局部邻域的几何关系。相比 PFH,它减少了全连接邻域点对计算,速度更快,更适合接近实时的应用场景。
项目中的 fpfh.cpp 保留了 FPFH 计算与可视化代码,不过当前被 #if 0 包裹,说明它更像是实验阶段或预留模块。核心处理思路包括:
pcl::NormalEstimation<pcl::PointXYZ, pcl::PointNormal> ne;
ne.setInputCloud(cloud);
ne.setSearchMethod(tree);
ne.setKSearch(10);
ne.compute(*normals);
pcl::FPFHEstimationOMP<pcl::PointXYZ,
pcl::PointNormal,
pcl::FPFHSignature33> fpfh;
fpfh.setInputCloud(cloud);
fpfh.setInputNormals(normals);
fpfh.setSearchMethod(tree);
fpfh.setRadiusSearch(0.02);
fpfh.compute(*fpfhs);
在检索系统中,FPFH 可以作为尺寸和体积筛选之后的精细匹配依据。简单说,尺寸像是“身高体重”,FPFH 像是“局部纹理和骨架结构”,两者结合,匹配更稳。
11. 工件入库与检索:先粗筛,再精认 🎯
方案说明给出了一个很合理的检索顺序:先用低成本的尺寸与体积缩小候选集合,再用更细致的三视图和特征描述子完成最终匹配。
输入:待检索工件点云 Q,数据库 D
1. 计算 Q 的长、宽、高
2. 在 D 中筛选尺寸落入阈值范围的候选工件
3. 计算 Q 的点云体积,继续缩小候选集合
4. 对候选工件比较三视图与 FPFH 特征描述子
5. 返回最相似的工件编号、名称与匹配分数
| 检索阶段 | 使用特征 | 目的 |
|---|---|---|
| 一级筛选 | 长、宽、高 | 快速排除尺寸差异大的工件 |
| 二级筛选 | 点云体积 | 缩小候选范围 |
| 三级匹配 | 三视图 | 比较整体外形投影 |
| 四级匹配 | FPFH 描述子 | 比较局部几何结构 |
| 输出结果 | 工件编号、名称 | 完成身份识别与追溯 |
这种“由粗到细”的检索流程非常适合工业数据库。不要一上来就让 FPFH 跟全库硬碰硬,先让长宽高把明显不相关的候选请出去,后面的精匹配才会又快又准。
12. 可视化:让点云处理过程看得见 👀
项目中的 Visualization.cpp 封装了点云可视化函数,支持单点云显示、带名称显示和双点云对比。对于点云项目来说,可视化不是装饰,而是调试生命线:配准对不对、裁剪准不准、噪点有没有去掉,都需要肉眼检查。
int cshoww(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud1,
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud2)
{
pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ>
color_write(cloud1, 20, 20, 255);
pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ>
color_red(cloud2, 230, 20, 20);
viewer->addPointCloud<pcl::PointXYZ>(cloud1, color_write, "cloud1");
viewer->addPointCloud<pcl::PointXYZ>(cloud2, color_red, "cloud2");
}
这类对比显示在滤波前后特别有用:蓝色是原始点云,红色是处理结果,哪里被删了、哪里保留了,一眼就能看出。点云算法最怕“我以为处理好了”,可视化能让它少一点自信,多一点证据。
13. 技术亮点总结 ✨
| 技术亮点 | 具体体现 | 价值 |
|---|---|---|
| 四目 3D 采集 | 四角 Astra Pro 覆盖工件 | 减少遮挡,获得更完整点云 |
| 标准块配准 | SAC-IA 求取相机位姿 | 提高配准效率与准确性 |
| 点云拼接 | 4×4 R/T 矩阵统一坐标 | 构建完整工件模型 |
| 点云后处理 | 裁剪、统计滤波、MLS 平滑 | 提升建模质量 |
| 尺寸测量 | AABB/OBB 包围盒思想 | 获得长宽高等基础身份信息 |
| 特征检索 | 三视图 + FPFH | 支持相似工件区分 |
| 无损识别 | 不刻字、不贴码 | 适合热处理复杂工况 |
14. 当前限制与改进方向 🛠️
| 方向 | 当前状态 | 建议 |
|---|---|---|
| CMake 工程 | CMakeList.txt 未覆盖全部源码模块 |
同步加入 boundingbox.cpp、fpfh.cpp 等文件 |
| 包围盒计算 | 代码主要实现 AABB | 完善 PCA-OBB,并输出姿态校正后的尺寸 |
| FPFH 模块 | 代码处于预留/实验状态 | 接入主流程并保存描述子到数据库 |
| 数据库系统 | 文档描述入库检索逻辑 | 增加工件编号、批次、工艺历史等字段 |
| 多相机采集 | 样例使用离线 .ply 文件 |
完善实时采集、同步和自动保存 |
| 配准维护 | 变换矩阵硬编码 | 增加标定流程、矩阵配置文件和误差评估 |
| 工业鲁棒性 | 已验证核心点云流程 | 增加高反光、黑色表面、油污、氧化皮等测试 |
15. 结语:让工件自己“证明自己是谁”🏷️
这个项目的价值不只在于“把工件扫成点云”,而在于把三维重建真正放进了工业身份管理流程:先用四目 Astra Pro 获取全面点云,再通过配准、拼接、滤波、平滑和尺寸测量建立数字模型,最后结合体积、三视图和 FPFH 特征完成入库检索。
如果传统标记方式像给工件贴便签,那么这套系统更像是给工件建立三维档案。便签会掉,油漆会花,二维码会怕热,但几何形状不会轻易“改名换姓”。工件往测量空间里一站,四台相机一看,数据库一查:嗯,编号找到了,可以安心进入下一道工序。🏭
