基于Go2二值化分割的开发过程问题记录
需求背景
项目目标是在 Unitree Go2 机器人的 Jetson 计算平台上运行一个 ROS2 节点(binary_go2_node),实时获取机器人头顶前置摄像头的画面,用 UNet 语义分割模型识别可行驶路面区域,将融合图、二值化图、灰度图三路结果发布为 ROS2 话题,供外部电脑订阅使用。
整个系统的硬件关系如下:
机器人主板(前摄像头)──网线直连──► Jetson(运行 ROS2 节点 + 推理)──ROS2话题──► 外部电脑
深度相机(Intel RealSense)──USB──► Jetson
问题一:cv2.VideoCapture(4) 根本打不开前摄像头
情况描述
原始代码用 cv2.VideoCapture(4) 打开摄像头,对应的是 /dev/video4 这个设备文件。程序能正常启动,摄像头也能读出画面,但画面来源是深度相机(Intel RealSense)的 RGB 输出流,不是机器人头顶的前置摄像头。无论怎么换设备编号(/dev/video2、/dev/video4),都只能访问深度相机相关的流,前摄画面始终无法获取。
根本原因
这是两条完全独立的数据链路,根本不是同一类设备:
cv2.VideoCapture 走的是 V4L2 内核驱动路径:
cv2.VideoCapture(4) → Linux 内核 V4L2 子系 统 → /dev/video4 → 深度相机驱动
深度相机直接通过 USB 连接 Jetson,操作系统会把它注册为 /dev/video2、/dev/video4 等标准视频设备节点,OpenCV 可以像读 USB 摄像头一样直接读取。
前置摄像头走的是 DDS 网络协议路径:
机器人主板(采集+JPEG压缩)→ 以太网 → CycloneDDS → VideoClient.GetImageSample()
Go2 的头顶前摄没有注册任何 V4L2 设备节点,它由机器人内部的摄像头服务程序持续采集、压缩成 JPEG,通过 DDS 发布-订阅协议在网络上广播。上位机必须用 Unitree SDK 提供的 VideoClient 发送 RPC 请求才能拿到图像数据。用 cv2.VideoCapture 在系统里根本找不到这个摄像头,它对 Linux 来说是不存在的。
解决方案
将摄像头获取方式从 cv2.VideoCapture 替换为 VideoClient:
from unitree_sdk2py.core.channel import ChannelFactoryInitialize
from unitree_sdk2py.go2.video.video_client import VideoClient
ChannelFactoryInitialize(0) # 初始化 DDS 通信
client = VideoClient()
client.SetTimeout(3.0)
client.Init()
code, data = client.GetImageSample() # 获取 JPEG 字节流
frame = cv2.imdecode(np.frombuffer(bytes(data), dtype=np.uint8), cv2.IMREAD_COLOR)
问题二:ChannelFactoryInitialize 与 ROS2 的 DDS domain 冲突
情况描述
将 VideoClient 集成进 ROS2 节点后,在 main() 里先调 ChannelFactoryInitialize(0),再调 rclpy.init(),然后创建节点,启动时报错:
[ERROR] [rmw_cyclonedds_cpp]: rmw_create_node: failed to create domain, error Precondition Not Met
RCLError: rcl node's rmw handle is invalid
换了顺序,先 rclpy.init() 再 ChannelFactoryInitialize(0),依然报同样的错。ROS2 节点始终无法创建。
根本原因
ROS2 和 unitree_sdk2py 底层都使用 CycloneDDS 作为通信中间件,两者都需要调用 CycloneDDS 的核心初始化函数:
ROS2 (rmw_cyclonedds_cpp) → dds_create_domain(0, ros2_config)
unitree_sdk2py → dds_create_domain(0, unitree_config)
CycloneDDS 规定:同一个进程内,同一个 domain ID 只能被初始化一次。无论谁先调,后来的那个调用看到 domain 0 已经存在,若配置不完全一致就报 Precondition Not Met 并失败。这个限制与调用顺序无关,是 CycloneDDS 的设计约束,在同一进程内无解。
解决方案
将 VideoClient 移到独立的子进程中运行,两个进程各自有独立的 CycloneDDS 实例,通过 multiprocessing.Queue 传递 JPEG 字节数据:
主进程 子进程
rclpy.init() ChannelFactoryInitialize(0)
ROS2 CycloneDDS (domain 0) Unitree CycloneDDS (domain 0)
ROS2 节点正常运行 GetImageSample() 循环
↑ ↓
└────── mp.Queue (JPEG bytes) ───┘
子进程持续采集帧并放入 Queue,主进程的 ROS2 timer 每次触发时从 Queue 里取最新帧处理,两侧完全隔离,互不干扰。
问题三:cyclonedds.xml 配置文件中的 <SharedMemory> 字段导致 ROS2 崩溃
情况描述
引入子进程方案后,主进程的 ROS2 节点启动时仍然崩溃,报错信息变为:
config: //CycloneDDS/Domain: SharedMemory: unknown element (cyclonedds.xml line 11)
[ERROR] [rmw_cyclonedds_cpp]: rmw_create_node: failed to create domain, error Error
RCLError: rcl node's rmw handle is invalid
这次错误完全和 unitree_sdk2py 无关,是 ROS2 自己的 CycloneDDS 在解析配置文件时崩溃的。