前视相机的 FPS 看着很漂亮,底盘却总在拐弯后才开始刹车。检查日志才发现,侧视相机偶尔晚到一帧,nvstreammux 为了凑 batch 把前视画面留在队列里。DeepStream 能把解码、预处理、TensorRT 推理、跟踪和元数据串起来,但这些队列也会把机器人看到的世界变旧。
先找出是哪一级在等
在典型 GStreamer 图中,source 负责输入,解码器将压缩视频变成帧,nvstreammux 汇聚多路帧,nvinfer 执行 TensorRT 推理,nvtracker 在帧间保持目标 ID,后续组件再做显示、编码或消息输出。每个组件都可能有自己的队列和缓存策略。
1 | camera / RTSP source |
对机器人来说,最值得盯住的是 nvstreammux 的批大小与等待超时。较大的 batch 可能提高 GPU 利用率,却会让一条已经到达的前视帧等待其他相机;这是视频分析服务可以接受、移动机器人却常常不能接受的取舍。
机器人真正要看的三个时间
至少区分三个指标:
- 吞吐量:每秒处理多少帧,适合评估资源上限。
- 推理延迟:帧进入模型到得到网络输出的时间。
- 感知年龄:控制或规划使用结果时,距原始相机曝光已过去的时间。
例如 30 FPS 的系统可能每帧推理只需 12 ms,但若 pipeline 累积了 8 帧,规划器拿到的其实是 250 ms 前的世界。对移动底盘而言,这比模型快慢更危险。
1 | perception_age = planner_consume_time - camera_capture_stamp |
所有进入 ROS 2 的检测、跟踪和深度结果都应保留原始采集时间戳,而不是只在发布时填 now()。否则日志会把“旧帧很快发布”误判成“新帧很快处理”。
视频管线不能替控制器做决定
ROS 2 bridge 最好发布结构化、有限的语义信息,例如目标类别、2D/3D 位置、置信度、帧号和时间戳。任务层再根据对象年龄、置信度和当前地图决定是否执行动作。控制器不直接依赖视频管线存活:当相机/DeepStream 进程异常时,应获得明确的失效状态,进入低速、暂停或停车策略。
多相机校准也不可省略。相机间的外参、曝光时间和时钟不同步,会让跟踪器把同一目标看成两个目标,或让后续三维融合出现错位。先用单相机跑通时间戳和失效处理,再增加 batch 和更多传感器,排障成本会低很多。
参考:DeepStream SDK Documentation · GStreamer Application Development Manual
证据边界:本文描述的是多相机管线的排查方法,没有给出某块 Jetson、某个 batch 大小或某种相机组合的实测 FPS 和 P99。具体取值应以目标设备的时间戳、队列长度和温度稳定后的记录为准。