2D转3D方案

好问题!其实这个项目里 YOLO 仍然只做了 **2D 检测**,所谓"3D"是通过 **YOLO 给的 2D 像素坐标 + 深度图 + 相机内参** 反投影出来的。下面把关键代码拎出来讲清楚。

核心思路:单目深度相机的"伪 3D 检测"

不是 YOLO 输出 3D,而是 YOLO 给 `(u, v)` 像素坐标,深度图给该像素的 `Z`,内参 `K` 用来反解 `X, Y`。三者合起来才是 3D 坐标。

数学公式(针孔相机反投影):

```

X\] \[u

Y = K⁻¹ · v · Z

Z\] \[1

```

三段关键代码

1) 拿到相机内参并求逆

obj_detect.py:48-49

```python

def caminfo_callback(self, msg):

K = np.array(msg.k).reshape(3, 3)

self.depth_instrinsic_inv = np.linalg.inv(K)

```

`CameraInfo.k` 是 3×3 内参矩阵(fx, fy, cx, cy 等),这里直接缓存 `K⁻¹`,后面反投影要用。

2) YOLO 给出 2D 框,取中心像素 (cx, cy)

obj_detect.py:79-86

```python

results = self.model.predict(img0, verbose=False)

res = results0

boxes = res.boxes.xyxy.cpu().numpy() # YOLO 只输出 2D 框 (x1,y1,x2,y2)

...

for (x1, y1, x2, y2), conf, cls_id in zip(boxes, confs, clses):

cx, cy = int((x1 + x2) / 2), int((y1 + y2) / 2) # 框中心像素

```

注意 YOLO 这里输出的就是 2D `xyxy`,**没有任何深度信息**。

3) 用深度图查 Z,用 K⁻¹ 反投影成 3D

obj_detect.py:99-104

```python

深度估计

Z = depcy, cx * 1e-3 # 深度图里查出来的距离,单位 mm → m

if Z <= 0 or np.isnan(Z):

continue

uv1 = np.array(cx, cy, 1.0)

XYZ = self.depth_instrinsic_inv @ uv1 * Z # 反投影成 3D

```

这就是整个"3D 检测"的核心:

  • `dep` 是 obj_detect.py:36订阅的 `/depth_registered/image_rect` 深度图(与彩色图配准过,所以像素一一对应)。

  • `depcy, cx` 是 YOLO 框中心像素对应的深度值(毫米),`* 1e-3` 转米。

  • `K⁻¹ @ u,v,1` 得到的是相机光轴方向上的单位射线(Z=1 时的 X、Y),乘以 `Z` 就还原成真实 3D 坐标。

  • `XYZ0/1/2` 分别就是相机光学坐标系下的 `X, Y, Z`。

随后塞进 `ObjectHypothesisWithPose.pose.pose.position`,发布到 `/detection` 话题,下游 det_tf.py直接拿这个 position 发 TF。

为什么这样做有意义

| 方式 | 是否真 3D 检测 | 说明 |

|------|----------------|------|

| 单纯 YOLO | ❌ | 只有 2D 像素框 |

| YOLO + 深度图 + 内参反投影 | ⚠️ **半 3D** | 有 3D 位置,但没有 3D 朝向/姿态 |

| 真正的 3D 检测网络(PointNet、VoxelNeXt、SMOKE) | ✅ | 直接从点云或图像回归 6DoF 位姿 |

| YOLO + 深度 + ICP/PnP 配准 | ✅ | 有完整 6DoF 位姿 |

这个项目走的是第二种「半 3D」路线------**只要 3D 位置(XYZ)就够抓取了**,因为:

  1. 抓取的物体是方块,对称性高,姿态不重要。

  2. det_tf.py:46里 `t.transform.rotation.w = 1.0`------发布的 TF 姿态是单位四元数,机器人末端姿态由 MoveIt 自己定,不需要物体姿态。

  3. 简单可靠,不需要训练 3D 检测网络,复用成熟的 YOLO 模型即可。

关键依赖:深度图必须与彩色图对齐

obj_detect.py:25默认订阅的是 **`/depth_registered/image_rect`**(注意 "registered" 这个词):

```python

self.declare_parameter("depth_topic", "/depth_registered/image_rect")

```

也就是说,**深度图必须先被配准到彩色相机的视角**,让深度图与彩色图像素一一对应。这正是项目里 register_depth.launch.pyy#L64-L74) 中的 `depth_image_proc::RegisterNode` 做的事:

```python

launch_ros.descriptions.ComposableNode(

package='depth_image_proc',

plugin='depth_image_proc::RegisterNode',

name='register_node',

remappings=[('depth/image_rect', '/depth/image_raw'),

('depth/camera_info', '/depth/camera_info'),

('rgb/camera_info', '/color/camera_info'),

('depth_registered/image_rect', '/depth_registered/image_rect'), # ← obj_detect 订阅这个

('depth_registered/camera_info', '/depth_registered/camera_info')]

),

```

如果深度图没配准:

  • YOLO 在彩色图上找到框中心 `(cx, cy)`。

  • 用这个 `(cx, cy)` 去深度图里查深度 → 查到的可能是另一个物体的深度,**3D 坐标完全错位**。

  • 所以"registered"这步是这个 2D→3D 反投影方案能成立的前提。

整条链路总结

```

彩色图 ──► YOLO ──► 2D 框 (cx, cy) ┐

深度图 ──► RegisterNode ──► 配准深度图 │ depcy, cx → Z

├──┐

CameraInfo (K) ──► K⁻¹ │ │

┘ │

▼

XYZ = K⁻¹ · u,v,1 · Z

│

▼

/detection (ObjectHypothesisWithPose)

│

▼

det_tf.py 发布 TF: cube1/cube2/...

│

▼

MoveIt 抓取

```

所以严格讲,这个项目并没有做"3D 目标检测",而是做了 **"2D 检测 + 深度反投影"的工程化组合**------这是机器人抓取场景里最常用、性价比最高的方案,因为:

  • 不需要标注 6DoF 位姿数据集(成本高);

  • 不需要训练 3D 检测网络(算力高);

  • 只要有深度相机,任何 2D 检测器都能升级成"3D 定位器"。

唯一的前提是:**深度图必须和彩色图配准**,且**深度图在该像素处必须有有效值**(这就是 obj_detect.py:101那行 `if Z <= 0 or np.isnan(Z): continue` 的意义------防止深度图空洞导致 NaN 坐标传给机器人)。

相关推荐
风花一世月2 小时前
[特殊字符] 我把一套分布式消防方案做成了能跑的 3D 沙盘(三架无人机共用一根水带,在线直接玩)
分布式·3d·ai·无人机
大雷神2 小时前
用鸿蒙开发助手推进《3D种植》鸿蒙版与 Flame 库适配
3d·华为·harmonyos
尘中远6 小时前
QWT 3D 模块重构记:旧渲染管线改进介绍及实时瀑布图示例
3d·重构
Jmyd012317 小时前
用 Web VR 引擎搭建 3D 虚拟展厅:从素材上云到多端实时渲染的完整数据链路
前端·3d·vr·三维数字化
Zldaisy3d19 小时前
北京理工大学 l 金属激光增材制造在线监测技术研究进展
数码相机·3d·制造
大圣编蚕19 小时前
OpenGL ES 教程:从零开始掌握 3D 渲染
大数据·elasticsearch·3d
足球中国1 天前
3D STEP文件编程
3d
时空节拍AI数字人1 天前
数字文旅补贴来了,景区申报要注意什么?
大数据·人工智能·百度·3d·ai·架构·aigc
Martina_03211 天前
AI生成3D模型导入后只剩一个大网格?用6步完成微缩场景拆分与优化
人工智能·游戏·数学建模·3d·ai·自然语言处理·aigc