From Bits to the Physical World

A Full-Stack Robotics Guide for AI and Software Developers

01The Big Picture

What Is a Robot, Really?The Full-Stack Map of RoboticsWho's Who in the Industry?

02Hardware

SensorsActuatorsCompute Platforms

03Operating System

ROS 2DDS Deep Dive

04Algorithms

SLAMNav2MoveIt 2PerceptionBehavior Trees

05Simulation & Training

Why Simulation?NVIDIA Isaac Sim

06AI Meets Robotics

Reinforcement LearningImitation LearningFoundation Models

07Toolchain

Visualization & DebuggingDev Environment & DevOps

08The Harsh Reality of Deployment

Real-Robot DeploymentReliability EngineeringFleet Management

09Industry Reality

Business ModelsChina vs. GlobalCareer Advice

Part 4: Algorithms · The robot's brain

Chapter 12

感知 - 机器人的“视觉皮层”

上一章我们让机器人手臂学会了规划运动路径 - MoveIt 能算出从 A 姿态到 B 姿态的无碰撞轨迹。但这里有个前提条件被我们悄悄跳过了:MoveIt 需要知道“目标物体在哪、朝哪个方向”。这个信息从哪来?

答案是感知系统。

想象你站在一张堆满杂物的桌子前,有人让你拿起那瓶矿泉水。你的大脑在几百毫秒内完成了一系列操作:从视网膜接收到的数百万像素中识别出矿泉水的形状和品牌标签,判断它在你面前大约 40 厘米处、瓶口朝上、稍微倾斜了 15 度。然后你的手才知道该从哪个角度伸过去、张开多大。这整个从“原始视觉信号”到“可操作的结构化信息”的过程,就是机器人感知系统在做的事。

用更精确的话说:感知的任务是把原始像素和点云变成“那是一瓶矿泉水,在 [x, y, z] 位置,姿态是 [roll, pitch, yaw]”。这个输出直接喂给 MoveIt 做抓取规划,或者喂给 Nav2 做避障。

这不就是 CV 吗?

如果你有 AI/ML 背景,看到“物体检测”、“语义分割”、“深度估计”这些词会觉得很熟悉。没错,机器人感知的底层技术就是计算机视觉(CV)。但机器人上跑 CV 和你在服务器上跑 CV 是两个完全不同的游戏,区别主要在三个地方。

第一,算力约束是真实的。 你的训练集群可能有 8 张 A100,推理服务器至少也是 T4。但机器人上跑的是 Jetson Orin NX,GPU 算力大概相当于半张 RTX 3050 - 而且这块 GPU 还要同时跑 SLAM、导航和其他任务。一个感知模型如果跑不到 15 FPS 以上,机器人的反应就会明显迟钝。所以你不能直接把最新的 SOTA 模型搬上去用,必须做量化、剪枝、或者换一个更轻量的架构。

第二,输出必须是可操作的。 在学术界跑个物体检测模型,输出一张带 bounding box 的图片,发个 Twitter 就完事了。但机器人需要的不是“图片上哪些像素是杯子” - 它需要“杯子的 3D 位置是 [0.45, -0.12, 0.78] 米,姿态是 [0, 0, 30] 度”。这意味着你必须把 2D 检测结果和深度信息融合,再经过坐标变换投影到机器人的工作空间坐标系里。这中间每一步都可能引入误差。

第三,输入是 messy 的。 实验室里做 CV 研究,数据集是精心拍摄的、光照均匀的、角度多样的。真实世界不会这么配合你 - 机器人在移动过程中相机会有运动模糊,仓库里的灯光忽明忽暗,目标物体可能被其他东西遮住一半,而且同一个“杯子”可能今天是白色的、明天换成了透明的。模型在 COCO 数据集上 mAP 90%,到了你的场景可能直接腰斩。

物体检测与识别

物体检测是感知流水线的第一步 - 在图像中找到目标物体并给出位置。这个领域的工具选择基本分成两条路线。

传统路线:YOLO 系列。 如果你已经知道机器人要处理哪些物体(比如仓库场景下的几十种 SKU),YOLO 是最务实的选择。YOLOv8(Ultralytics 出品)在速度和精度之间取得了很好的平衡 - nano 版本在 Jetson Orin NX 上可以跑到 30+ FPS,足以满足实时需求。你用自己的数据集微调一个模型,部署时用 TensorRT 做推理加速,这是目前工业界最成熟的路径。

开放词汇路线:GroundingDINO / OWL-ViT。 如果你不想为每个新物体都训一个模型怎么办?这就是 open-vocabulary detection 要解决的问题。GroundingDINO 可以接受自然语言描述作为输入 - 你告诉它“找到桌上的红色杯子”,它就能定位到,即使训练数据里没有见过这个特定的杯子。这对于需要处理长尾物品的场景(比如家庭服务机器人,面对的物品种类几乎无限)非常有吸引力。代价是推理速度比 YOLO 慢好几倍,在边缘端部署需要更多优化工作。

分割:SAM(Segment Anything Model)。 Meta 的 SAM 把“给定提示分割任意物体”这个能力做到了令人惊讶的泛化程度。在机器人场景里,常见的用法是先用 YOLO 或 GroundingDINO 拿到 bounding box,再用 SAM 得到精确的物体轮廓 mask。这个 mask 配合深度图,可以提取出只属于目标物体的点云 - 这比 bounding box 精确得多,对后续的位姿估计和抓取规划都有帮助。SAM 2 进一步支持了视频追踪,物体检测一次后可以在后续帧中持续跟踪而不需要重复检测。

6DoF 位姿估计 - 不只是“在哪”,还要知道“朝哪”

检测到物体只是第一步。对于抓取任务来说,知道“桌上有个杯子”远远不够 - 机器人还需要知道杯子的精确 3D 位置和朝向。一个杯口朝上的杯子和一个倒扣的杯子,抓取策略完全不同。这就是 6DoF(6 Degrees of Freedom)位姿估计要做的事:输出目标物体的 3D 平移 [x, y, z] 和 3D 旋转 [roll, pitch, yaw]。

传统方法需要物体的 CAD 模型,先在模型上提取特征,再和真实图像中的特征做匹配。这在工业场景下可行(零件的 CAD 图纸本来就有),但在开放场景下不现实 - 你不可能有全世界所有物品的 CAD 模型。

新一代的方法正在解决这个问题。FoundationPose(NVIDIA,2024)只需要目标物体的一张 RGB 参考图或粗糙的 CAD 模型,就能估计它在新视角下的 6DoF 位姿。它结合了 neural implicit representation 和 render-and-compare 策略,在没见过的物体上也能泛化。MegaPose(INRIA)走的是类似的路线,在大规模合成数据上预训练,然后 zero-shot 推理新物体的位姿。

这些模型的实际精度在大多数场景下已经够用(平移误差约 1-2cm,旋转误差约 5-10 度),但对于需要亚毫米精度的精密装配任务还差得远。这时候通常的做法是把模型的输出作为初始估计,再用 ICP(Iterative Closest Point)等经典点云配准算法做精细化。

深度估计与 3D 重建

要得到物体的 3D 位置,你需要深度信息。如果机器人上有 RealSense 这样的深度相机,直接就能拿到每个像素的深度值。但深度相机有它的局限 - 在户外强光下结构光失效,对透明和反光物体深度数据满是噪声。

单目深度估计提供了一种补充方案。Depth Anything(2024,港大 & TikTok)是目前效果最好的单目深度估计模型之一,它能从单张 RGB 图像预测出稠密的相对深度图。注意是“相对深度” - 它能告诉你“A 比 B 离相机更近”,但不能直接给出绝对距离(除非你做了额外的标定或用 V2 版本的 metric depth 模式)。在深度相机失效的场景下,这是一个有价值的 fallback。

点云是机器人感知中的核心数据结构。不管是来自 LiDAR、深度相机还是立体视觉,最终你拿到的都是一堆 3D 点。处理点云的经典深度学习方法是 PointNet / PointNet++ - 它直接以无序点集作为输入,学习每个点的特征和全局特征。虽然发表于 2017 年,但它的设计思路(对置换不变性的处理)影响了后续几乎所有的点云网络。实际工程中,你可能更多用的是体素化(把点云塞进 3D 格子里)配合 3D 卷积,因为体素化后可以复用 2D 卷积的很多优化技术。

场景级 3D 重建是更高层的感知能力。**NeRF(Neural Radiance Fields)**和 3D Gaussian Splatting 能从多张照片重建出一个场景的完整 3D 表示。在机器人领域,这有几个实际用途:离线建立高精度的环境模型用于规划、生成合成训练数据(在重建的场景里随机放置物体来扩充数据集)、以及做场景变化检测。3D Gaussian Splatting 因为渲染速度快(比 NeRF 快几个数量级),在实时性要求高的机器人应用中更受关注。

NVIDIA Isaac ROS Perception - GPU 加速感知流水线

上面提到的每个模型单独都能跑,但把它们串成一个完整的感知流水线 - 图像进来、检测物体、估计深度、计算位姿、转换坐标系、发布到 ROS topic - 这个系统集成工作的复杂度超出很多人的预期。

Isaac ROS 是 NVIDIA 给 Jetson 平台提供的 ROS 2 加速包集合,其中感知相关的几个包特别有用:

  • Isaac ROS DNN Inference - 在 ROS 2 节点里直接调用 TensorRT 加速的模型推理,支持 ONNX 模型导入
  • Isaac ROS Object Detection - 封装好的检测流水线,YOLO 等模型即插即用
  • Isaac ROS Depth Segmentation - 深度图和语义分割的融合
  • Isaac ROS Pose Estimation - 6DoF 位姿估计 pipeline,包括 FoundationPose 的集成

这些包的最大价值不是算法本身有多先进,而是它们处理了那些让人头疼的工程细节:GPU 内存管理、数据格式转换(ROS Image 消息到 CUDA tensor)、多个模型的 pipeline 调度、零拷贝数据传输(避免 CPU-GPU 之间的数据搬运)。对于 Jetson 平台的开发者来说,用 Isaac ROS 比自己从头搭要省很多功夫。

TensorRT 在这里值得单独说两句。它是 NVIDIA 的推理优化引擎,做的事情包括算子融合、精度校准(FP32 → FP16 / INT8)、针对特定 GPU 架构的 kernel 自动调优。同一个 YOLO 模型,PyTorch 直接跑 vs TensorRT 优化后跑,速度差距可以到 3-5 倍。在边缘端,这个差距决定了你的感知模型能不能满足实时性要求。唯一的麻烦是 TensorRT 的模型转换过程经常出幺蛾子 - 有些 PyTorch 算子不支持、动态 shape 处理不好、量化后精度下降严重 - 这是每个做边缘部署的开发者都会踩的坑。

开发者踩坑指南

感知系统是机器人全栈中 failure mode 最多样的模块,因为它直接面对真实世界的混乱。以下是几个最常见也最头疼的问题。

透明物体。 这是一个真正的 hard problem。玻璃杯、塑料瓶、保鲜膜 - 这些东西对深度相机来说几乎是隐形的。结构光穿透透明物体,ToF 信号被折射,深度图上这些区域要么是空洞(no data),要么是完全错误的值(测到了物体后面的桌面距离)。RGB 检测模型倒是能看到透明物体(因为有反射和折射的视觉线索),但没有可靠的深度信息就没法算 3D 位置。目前的实用解法包括:用 Depth Anything 之类的学习方法来补全深度空洞、用多视角拍摄来三角化位置、或者在场景设计上尽量避免让机器人处理透明物品。没有一个方案是完美的。

反光金属表面。 不锈钢水壶、铝制罐子、镀铬的把手 - 这些东西在深度图上会产生多路径反射(multi-path reflection),导致深度值跳变和噪声。尤其是曲面金属,反射的结构光图案会被扭曲,导致计算出的深度完全不可信。一个常见的策略是用中值滤波和统计学滤波去除离群点,但这意味着你丢失了物体表面的细节信息。

遮挡。 现实场景下物体不会乖乖排成一排等你看。货架上的商品可能被其他商品挡住 80%,你只能看到一个角。部分遮挡对检测模型的 confidence 影响很大 - 模型可能检测不到,或者给出一个很不准的 bounding box。位姿估计在遮挡情况下更是灾难性的,因为可见部分可能不包含足够的几何特征来确定朝向。实际工程中的做法通常是:如果检测 confidence 低于阈值,让机器人换个角度重新看一眼(active perception),或者根据已知的货架布局来做推理。

精度 vs 速度的永恒纠结。 用 YOLOv8-nano 可以在 Jetson 上跑到 40 FPS,但 mAP 可能只有 60%。换成 YOLOv8-large,mAP 上到 80%,但帧率掉到 8 FPS。用 GroundingDINO 做开放词汇检测,泛化能力强,但在 Jetson 上可能只有 2-3 FPS。没有一个模型在所有维度上都是最优的。实际的做法通常是分层架构 - 用一个轻量模型做高频的粗检测(“那个方向好像有东西”),只在需要精细操作时(比如准备抓取了)启动更重的模型做精确的位姿估计。这种 coarse-to-fine 策略在计算资源受限时几乎是必须的。

坐标系变换。 这个不像上面几个那么“酷”,但在实际开发中消耗的调试时间可能比所有上面的加在一起还多。感知模型输出的是相机坐标系下的位置,但 MoveIt 需要的是机器人基座坐标系下的位置。中间要经过 camera → camera_link → base_link 的 TF 变换链。如果手眼标定不准(哪怕差了 1 度),物体位置的误差在手臂末端会被放大到好几厘米 - 足以让抓取失败。很多开发者第一次调通整条感知-规划-执行流水线时,最后卡住的都不是什么高深的算法问题,而是某个坐标系的旋转矩阵搞反了。

感知的输出去了哪里

感知系统不是独立存在的,它是整个机器人系统的“眼睛”,为下游的每个模块提供结构化的环境理解:

  • 物体检测 + 位姿估计的结果通过 ROS 2 topic 发给 MoveIt,作为抓取规划的输入
  • 语义分割和深度信息融合后更新 costmap,让 Nav2 知道哪些区域可以通行
  • 检测到的物体 ID 和状态写入行为树的 Blackboard,让任务调度逻辑知道“目标已找到”还是“需要继续搜索”

换句话说,如果感知模块的输出错了或者慢了,整个系统都会出问题。Nav2 会撞上没被识别的障碍物,MoveIt 会往一个错误的位置伸手,行为树会因为拿到了错误的状态信息而进入错误的分支。这就是为什么在机器人系统中,感知模块虽然从代码量上看可能只占 20%,但从调试时间上看常常占到 50% 以上。

← Previous11. MoveIt 2Next→13. Behavior Trees