19f2183c7771e.png

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 围着工件“开会”📷

19f21892a2e5d.png

方案说明中设计了一个约 1x1x1.2M 的测量空间,并在四角布置 Astra Pro,对中心工件进行全方位测量。Astra Pro 支持在约 0.6-8M 范围内完成深度恢复和点云生成,因此适合这类中小尺寸工件的近距离三维采集。

硬件结构还考虑了高度调节:通过调整底部安装高度,可以适应不同尺寸的工件。这个细节很实用,因为工厂里的工件不会乖乖都长成同一个尺寸。算法喜欢标准化,工件偏偏很有个性。

硬件组成 作用
四台 Astra Pro 从四个方向采集深度与点云
固定支架 保证相机与工件位置关系稳定
可调底座 适应不同高度的工件
中央测量空间 放置待检测工件
处理平台 负责点云保存、配准、拼接和检索

4. 系统总体流程:从四片点云到一个身份编号 🧩

19f21937a2880.png

系统可以分成“采集建模”和“入库检索”两条主线:

阶段 输入 输出
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. 测量空间裁剪:把无关背景请出画面 ✂️

19f21a1a5877d.png

原始点云中除了工件,还可能包含支架、地面、背景、无效深度点等。为了让后续计算集中在工件附近,代码使用 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 让长宽高有据可依 📏

19f21a92740b6.png

工件身份识别不仅依赖“形状像不像”,还要先看基础尺寸是否匹配。项目使用 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 特征描述子:把表面几何压缩成可检索特征 🧠

19f21aaefe9eb.png

方案说明中将 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 特征完成入库检索。

如果传统标记方式像给工件贴便签,那么这套系统更像是给工件建立三维档案。便签会掉,油漆会花,二维码会怕热,但几何形状不会轻易“改名换姓”。工件往测量空间里一站,四台相机一看,数据库一查:嗯,编号找到了,可以安心进入下一道工序。🏭

19f21ade3e3ce.png

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注