
*来源: 第二届3D视觉创新应用竞赛 | 参赛者:** 黄伟浩、黎镜林、程锦涛*
*硬件平台:** Astra Pro 深度相机 + Jetson Nano 开发板*
*GitHub:** 3DReconstruction_Offline-products*
一、项目亮点速览 ✨
你是否曾幻想过——🛒 网上购物也能像实体店一样"亲眼"看清商品的每一个细节?
这支团队告诉你:当然可以!他们用 RGBD 相机 + AI 算法,为中小商家量身打造了一套低成本、高精度的商品三维重建 + 实时位姿跟踪系统,让线上购物体验 up up!📈
核心能力表格:
| 核心能力 | 技术实现 |
|---|---|
| 📦 离线三维重建 | Astra Pro 采集 + ICP 点云拼接 + 泊松网格重建 |
| 🎯 实时位姿跟踪 | G2L-Net 深度学习网络 + 6D 姿态估计 |
| 📱 AR 效果叠加 | 三维模型 + 位姿信息 → 个性化增强现实 |
| 💰 低成本方案 | Jetson Nano + Astra Pro,总成本控制在数千元内 |
二、背景与意义 🏪
2.1 市场需求
近年来,AR/VR 技术蓬勃发展,国内外巨头纷纷布局:
- 🕶️ Google Glass、微软 HoloLens、Magic Leap 等 AR 眼镜引爆全球
- 📈 疫情催化线上交易,2020 年以来线上交易额空前巨大
- 💡 中小商家迫切需要一种低成本方式展示商品三维模型
2.2 痛点与机遇
大型商家(如苹果 🍎)有能力购买昂贵的高精度成像设备,但广大中小商家望而却步!
我们的目标是:让每个小商家都能拥有自己的 3D 商品展示系统! 💪
2.3 核心应用场景

三、系统架构 🏗️
系统流程图:

系统组成详解:
| 模块 | 组件 | 功能描述 |
|---|---|---|
| 信息采集端 | Astra Pro 相机 | 采集 RGB 彩色图像和深度信息 |
| 信息采集端 | Jetson Nano | 实时处理点云数据,执行背景分割 |
| PC 处理端 | RTX 3050 Ti | 运行 G2L-Net 深度学习网络 |
| PC 处理端 | 训练模块 | 离线训练目标物体识别模型 |
四、核心技术详解 🔬
4.1 三维重建模块 🏗️
三维重建是本系统的"地基"工程 🎯,主要分为以下步骤:
步骤 1:点云采集与预处理
使用 Astra Pro 相机围绕目标物体旋转采集多帧点云,每帧点云包含:
- 空间坐标 (x, y, z)
- RGB 颜色信息
步骤 2:深度滤波——背景分割
由于每帧点云都包含前景、背景和目标物体,背景和前景会干扰 ICP 拼接精度。需要通过条件滤波器剔除干扰点:

深度滤波代码实现:
// 条件滤波器设置 - 剔除 Z 轴深度范围外的点
pcl::ConditionAnd<pcl::PointXYZ>::Ptr range_cloud(new pcl::ConditionAnd<pcl::PointXYZ>());
// 保留 1.0m ~ 1.2m 范围内的点(目标物体所在深度)
range_cloud->addComparison(
pcl::FieldComparison<pcl::PointXYZ>::ConstPtr(
new pcl::FieldComparison<pcl::PointXYZ>("z", pcl::ComparisonOps::GT, 1.0)
)
);
range_cloud->addComparison(
pcl::FieldComparison<pcl::PointXYZ>::ConstPtr(
new pcl::FieldComparison<pcl::PointXYZ>("z", pcl::ComparisonOps::LT, 1.2)
)
);
pcl::ConditionalRemoval<pcl::PointXYZ> condream;
condream.setCondition(range_cloud);
condream.setInputCloud(cloud);
condream.setKeepOrganized(true);
condream.filter(*cloud_filtered); // 输出:只保留目标物体点云
> 💡 核心思想: 通过设置 Z 轴深度阈值,将目标物体(1.0~1.2m)与前景(小于1.0m)、背景(大于1.2m)分离!
步骤 3:ICP 点云拼接
经过滤波后的多帧点云需要拼接成完整模型,这里采用经典的 ICP(Iterative Closest Point)算法:

ICP 算法核心流程:
// 初始化 ICP 迭代对象
pcl::IterativeClosestPoint<PointT, PointT> icp;
icp.setMaximumIterations(iterations); // 设置最大迭代次数
// 遍历拼接所有点云帧
for (int i = 1; i <= pcd_num; i++) {
// 读取当前帧点云
filename = "data/zheng/" + std::to_string(i) + "rm.ply";
pcl::io::loadPLYFile(filename, *cloud_tr);
// 设置源点云(待变换)和目标点云(参考)
icp.setInputSource(cloud_tr); // 待对齐的点云
icp.setInputTarget(cloud_icp); // 参考点云
// 执行 ICP 迭代对齐
icp.align(*cloud_icp);
// 获取变换矩阵 R 和 T
transformation_matrix = icp.getFinalTransformation().cast<double>();
if (icp.hasConverged()) {
std::cout << "Fitness Score: " << icp.getFitnessScore() << std::endl;
print4x4Matrix(transformation_matrix);
}
}
// 保存拼接结果
pcl::io::savePLYFileASCII("result.ply", *cloud_in);
ICP 算法原理:
| 步骤 | 说明 |
|---|---|
| 1️⃣ 最近邻搜索 | 对源点云中每个点,在目标点云中找最近邻点 |
| 2️⃣ 误差计算 | 计算所有最近邻点对之间的距离误差 |
| 3️⃣ SVD 分解 | 求解最优旋转矩阵 R 和平移向量 T |
| 4️⃣ 点云变换 | 用 R 和 T 变换源点云 |
| 5️⃣ 迭代 | 重复 1-4,直至误差收敛 |
变换矩阵结构:
| R00 R01 R02 Tx |
| R10 R11 R12 Ty |
| R20 R21 R22 Tz |
| 0 0 0 1 |
步骤 4:K-Means 聚类分割
在更精细的分割任务中,使用 K-Means 聚类 将点云分成 K 个类别:

K-Means 核心实现:
// K-Means 类定义
class KMeans {
public:
int m_k;
std::vector<st_point> mv_pntcloud; // 待聚类点云
std::vector<std::vector<st_point>> m_grp_pntcloud; // K 个类别
std::vector<st_pointxyz> mv_center; // K 个中心点
// 设置输入点云
bool SetInputCloud(pcl::PointCloud<pcl::PointXYZ>::Ptr pPntCloud);
// 初始化 K 个中心点
bool InitKCenter(st_pointxyz pc_arr[]);
// 执行聚类
bool Cluster();
// 计算两点间欧氏距离
double DistBetweenPoints(st_pointxyz& p1, st_pointxyz& p2);
// 更新类别中心
bool UpdateGroupCenter(std::vector<VecPoint_t>& grp_pntcloud,
std::vector<st_pointxyz>& center);
};
// 聚类核心算法
bool KMeans::Cluster() {
do {
// Step 1: 为每个点分配最近的类别
for (size_t i = 0; i < mv_pntcloud.size(); ++i) {
double min_dist = DBL_MAX;
int best_cluster = 0;
for (size_t j = 0; j < m_k; ++j) {
double dist = DistBetweenPoints(mv_pntcloud[i].pnt, mv_center[j]);
if (dist < min_dist) {
min_dist = dist;
best_cluster = j;
}
}
m_grp_pntcloud[best_cluster].push_back(mv_pntcloud[i]);
}
// Step 2: 记录旧中心位置
std::vector<st_pointxyz> old_center = mv_center;
// Step 3: 更新类别中心
UpdateGroupCenter(m_grp_pntcloud, mv_center);
// Step 4: 检查是否收敛
if (!ExistCenterShift(old_center, mv_center)) {
break; // 中心不再移动,迭代结束
}
// 清空类别,准备下一轮迭代
for (size_t i = 0; i < m_k; ++i) {
m_grp_pntcloud[i].clear();
}
} while (true);
return true;
}
步骤 5:Mesh 泊松重建
使用 MeshLab 进行表面网格重建:
- 计算点云法线方向
- 执行泊松表面重建
- 导出最终的 3D 模型

4.2 物体位姿估计模块 🎯
G2L-Net 算法概述
G2L-Net(Global to Local Network) 是一种实时 6D 目标姿态估计网络,包含三个核心步骤:
| 步骤 | 子模块 | 功能 |
|---|---|---|
| 1️⃣ | 全局定位 | YOLOv3 2D 检测,提取 RGB 目标区域 |
| 2️⃣ | 平移定位 | 局部平移网络,3D 分割 + 平移预测 |
| 3️⃣ | 旋转定位 | 旋转残差估计器,精确旋转估计 |
G2L-Net 网络结构:

关键点 3D-3D 配对求解
ADD 指标计算公式:
ADD = (1/|M|) Σ ||(R·x + T) - (R̃·x + T̃)||
x∈M
其中:R, T 为真实值,R̃, T̃ 为预测值
4.3 ROS 通信与数据同步 🔗
主从机配置
| 角色 | 设备 | 功能 |
|---|---|---|
| 主机 | Jetson Nano | 发布相机数据 topic |
| 从机 | PC | 订阅并处理数据 |
RGB-D 同步订阅
import message_filters
# 创建同步订阅器(ApproximateTime 策略)
rgb_sub = message_filters.Subscriber('/camera/rgb/image_rect_color/compressed', CompressedImage)
depth_sub = message_filters.Subscriber('/camera/depth_registered/hw_registered/image_rect_raw/compressedDepth', CompressedImage)
sync = message_filters.ApproximateTimeSynchronizer(
[rgb_sub, depth_sub], # 订阅的 topic 列表
10, # 队列大小
0.1 # 同步时间窗口(秒)
)
sync.registerCallback(multi_callback) # 同步回调函数
def multi_callback(rgb_msg, depth_msg):
# 处理同步的 RGB 和 Depth 数据
pass
动态参数调节
| 参数 | 调整项 | 推荐值 | 说明 |
|---|---|---|---|
| RGB 压缩 | jpeg_quality | 70 | 平衡画质与带宽 |
| Depth 压缩 | png_level | 6~7 | 深度精度与压缩率 |
五、硬件与软件环境 🛠️
5.1 硬件配置
| 设备 | 型号 | 用途 |
|---|---|---|
| 📷 深度相机 | Astra Pro | RGB-D 数据采集 |
| 💻 边缘计算 | Jetson Nano 2G | 实时点云预处理 |
| 🖥️ PC 处理端 | AMD 5600H + RTX 3050 Ti | 深度学习推理 |
| 🪑 转椅 | 可旋转办公椅 | 360° 物体旋转采集 |
5.2 软件环境
| 模块 | 软件/库 | 版本要求 |
|---|---|---|
| 边缘端系统 | Ubuntu 18.04 | – |
| 机器人操作系统 | ROS Melodic | – |
| 相机驱动 | libuvc + libuvc_ros | – |
| 点云处理 | PCL | 1.9.1 |
| 图像处理 | OpenCV | 3.4.6 |
| IDE | Visual Studio | 2019 |
| 网格重建 | MeshLab | 最新版 |
| 深度学习 | PyTorch + TensorRT | – |
5.3 环境部署流程
Jetson Nano 环境配置流程
1️⃣ 烧录镜像
└─→ 下载 JetPack 4.x (对应 2G 版本)
└─→ Etcher 烧录至 SD 卡
2️⃣ 安装 Astra Pro 驱动
└─→ 下载 OpenNI-Linux-x64
└─→ 安装 libuvc 组件
3️⃣ 安装 ROS Melodic
└─→ sudo apt install ros-melodic-ros-base
└─→ source /opt/ros/melodic/setup.bash
4️⃣ 安装 Astra 相机包
└─→ sudo apt install ros-melodic-astra-*
5️⃣ 验证相机
└─→ rosrun astra_launch astra.launch
└─→ rviz 查看 /camera/depth/points
六、性能测试与结果分析 📊
6.1 三维重建效果
| 测试物体 | 点云质量 | Mesh 效果 | 细节还原度 |
|---|---|---|---|
| 正面模型 | ⭐⭐⭐⭐⭐ | 优秀 | 高 |
| 背面模型 | ⭐⭐⭐ | 一般 | 低(仅轮廓) |
6.2 位姿估计性能
| 指标 | 数值 |
|---|---|
| 2D 检测耗时 | ~11ms |
| 姿态估计耗时 | ~32ms |
| 总帧率 | ~23 FPS |
6.3 准确率测试
在 33 张测试图片中:
| 结果 | 数量 | 占比 |
|---|---|---|
| ✅ 正确识别 | 26 张 | 78.8% |
| ⚠️ 丢弃(未检测/模糊) | 5 张 | 15.2% |
| ❌ 识别错误 | 2 张 | 6.0% |
> 📌 误差阈值: 旋转向量分量误差小于 π/6 (约 0.523 rad)
七、技术创新与亮点 💡

7.1 轻量化硬件平台
- Astra Pro + Jetson Nano 构建采集-重建-显示一体化设备
- 体积小、重量轻,便携性强
7.2 低成本解决方案
| 方案类型 | 价格范围 |
|---|---|
| 工业级 3D 扫描仪 | 数万 ~ 数十万 |
| 本方案 | 数千元 |
7.3 高精度重建模型
- PCL 点云库 + 泊松表面重建
- 获得精确度较高的三维模型
7.4 离线+实时双模式
| 模式 | 适用场景 |
|---|---|
| 离线重建 | 商品模型制作、存档 |
| 实时跟踪 | AR 效果叠加、位姿监控 |
八、问题与解决方案 🧩
8.1 ROS 压缩数据问题
| 问题 | 原因 | 解决方案 |
|---|---|---|
| Depth 压缩失真 | JPEG 压缩不适合深度图 | 使用 compressDepth 子主题 |
| OpenCV 读取失败 | 编码格式不兼容 | 使用 cv2.CV_LOAD_IMAGE_UNCHANGED |
# 正确的 Depth 解码方式
depth_img_raw = cv2.imdecode(
np.fromstring(raw_data, np.uint8),
cv2.CV_LOAD_IMAGE_UNCHANGED
)
8.2 D2C 对齐不准问题
| 问题 | 原因 | 解决方案 |
|---|---|---|
| RGB 与 Depth 区域不匹配 | Astra Pro 仅在特定距离支持硬件对齐 | 扩大 YOLO 目标检测框范围 |
8.3 已知不足
| 不足项 | 说明 |
|---|---|
| ❌ AR 效果未实现 | 涉及 OpenGL,开发能力有限 |
| ❌ 背面重建效果一般 | 需要优化 ICP 初始值 |
九、总结与展望 🚀
核心贡献
– 🎯 低成本解决方案:让中小商家也能享受 3D 商品展示的便利
– 🔧 完整技术闭环:采集 → 重建 → 训练 → 跟踪 → AR
– 📈 实时性能:G2L-Net 实现 23 FPS 的位姿跟踪
未来方向
| 方向 | 目标 |
|---|---|
| 🎨 AR 效果增强 | 结合 OpenGL 实现更丰富的 AR 交互 |
| 📱 移动端部署 | 优化模型,实现手机端实时推理 |
| 🏭 工业分拣 | 对接机械臂,实现自动分拣 |
| 🤖 自训练模型 | 开发低成本数据集制作工具 |
🌟 写在最后: 这支团队用朴素的硬件做出了不简单的成果!从三维重建到实时位姿跟踪,从 ROS 通信到深度学习,每个模块都体现了扎实的技术功底。如果你也有商品 3D 展示的需求,不妨试试这套方案,说不定会有惊喜哦!🎉