Gazebo仿真机器人和相机时Gazebo ROS Control 插件偶发性加载失败bug分析

张开发
2026/4/15 4:44:10 15 分钟阅读

分享文章

Gazebo仿真机器人和相机时Gazebo ROS Control 插件偶发性加载失败bug分析
Gazebo ROS Control 插件偶发性加载失败一个隐蔽的竞争条件问题描述在 ROS Noetic Gazebo 仿真环境中为四足机械臂Go1 Z1的 URDF 模型添加深度相机libgazebo_ros_openni_kinect.so后自定义的gazebo_ros_control插件偶发性无法加载。具体表现controller_manager服务不存在controller_spawner报错Controller Spawner couldnt find the expected controller_manager ROS interface.rostopic echo /joint_states无数据添加相机之前一切正常添加之后偶发失败排查过程1. 确认插件卡在哪里查看~/.ros/log/latest/rosout.log发现 gazebo_ros_control 的日志停在了gazebo_ros_control plugin is waiting for model URDF in parameter [robot_description] on the ROS param server.之后没有任何后续日志——initSim从未被调用。但手动检查参数服务器参数确实存在rosparam get /robot_description|head-5# 正常返回 URDF 内容长度约 58KB2. 确认是 deferred thread 卡死gazebo_ros_control插件的Load()函数会创建一个 detached 的deferred_load_thread_在后台线程中执行getURDF()→parseTransmissions()→initSim()→ 创建ControllerManager。这允许其他 Gazebo 插件如 ft_sensor在此期间继续加载。通过检查 gzserver 进程的线程状态cat/proc/gzserver_pid/task/thread_id/wchan# 输出: hrtimer_nanosleep有一个线程持续处于hrtimer_nanosleep状态——这正是getURDF()函数中每 100ms 轮询参数的 sleep 循环。说明这个线程永远找不到参数但参数明明存在。3. 在 ROS Master 日志中找到根本原因关键突破来自~/.ros/log/latest/master.log[rosmaster.master][ERROR] 2026-04-13 22:17:13,397: Traceback (most recent call last): File .../rosmaster/master_api.py, line 435, in searchParam search_key self.param_server.search_param(caller_id, key) File .../rosmaster/paramserver.py, line 116, in search_param raise ValueError(namespace must be global) ValueError: namespace must be global而这个错误的上下文是相机插件正在密集注册参数22:17:13,395: PUB [/camera/rgb/image_raw/compressed] /gazebo 22:17:13,396: SERVICE [/camera/rgb/image_raw/compressed/set_parameters] /gazebo 22:17:13,397: [ERROR] ValueError: namespace must be global -- searchParam 失败! 22:17:13,397: PUB [/camera/rgb/image_raw/compressed/parameter_descriptions] /gazebo 22:17:13,398: PUB [/camera/rgb/image_raw/compressed/parameter_updates] /gazebo根本原因gazebo_ros_control插件的getURDF()函数调用model_nh_.searchParam(robot_description, ...)来查找 URDF 参数。同时深度相机插件libgazebo_ros_openni_kinect.so的 deferredLoadThread在启动时会向 ROS Master密集注册大量参数和 topiccompressedDepth、compressed、theora 等 image_transport 相关的参数。当两个线程同时向 ROS Master 发送 XML-RPC 请求时触发了 ROS Masterparamserver.py中的一个 bugsearch_param函数收到了格式异常的 namespace 参数抛出ValueError: namespace must be global。这导致searchParam返回失败getURDF认为参数不存在继续循环等待由于代码中使用了ROS_INFO_ONCE后续循环不再打印任何日志表面上看起来像是静默卡死偶发性的原因只有当两个线程的 XML-RPC 请求恰好在 ROS Master 中交错时才会触发。如果 gazebo_ros_control 在相机插件的参数注册风暴开始之前就完成了searchParam则一切正常。修复方案将gazebo.xacro中robotParam的值从相对路径改为全局路径加前导/!-- 修复前 --robotParamrobot_description/robotParam!-- 修复后 --robotParam/robot_description/robotParam当参数名以/开头时searchParam不需要沿 namespace 层级向上搜索直接匹配全局参数避免了触发 ROS Master 中的并发 bug。诊断方法速查如果你遇到了类似的 gazebo_ros_control 加载失败问题可以按以下步骤排查# 1. 检查 rosout 日志中是否卡在 waiting for model URDFgrepwaiting for model URDF~/.ros/log/latest/rosout.log# 2. 检查 controller_manager 服务是否存在rosservice list|grepcontroller_manager/load_controller# 3. 关键检查 ROS Master 日志中是否有 searchParam 错误grepnamespace must be global~/.ros/log/latest/master.log# 4. 检查 gzserver 线程状态确认是否有线程卡在 sleep 循环fortidin$(ls/proc/$(pgrep-fgzserver.*ode)/task/);dowchan$(cat/proc/$(pgrep-fgzserver.*ode)/task/$tid/wchan2/dev/null)[$wchanhrtimer_nanosleep]echoThread$tidstuck in nanosleepdone适用环境ROS Noetic (Ubuntu 20.04)Gazebo 11ros-noetic-gazebo-ros-control 2.9.3任何同时使用gazebo_ros_control和libgazebo_ros_openni_kinect.so或其他会密集注册参数的 Gazebo 插件的场景

更多文章