Files
ros_contorl/devices/sim/gazebo
arm_dev 01940f54e7 P5c-3 仿真启动器跑通:Gazebo + spawn + 两个控制器 active
端到端验收(headless,本机无显示器):
    ign 加载 Physics/UserCommands/SceneBroadcaster/Sensors + gz_ros2_control
    机械臂 spawn 进 assembly 世界,7 个 sim_joint 全在 /joint_states
    joint_state_broadcaster 与 rizon_arm_controller 双双 active
    world → sim_tcp 可查,且末端位姿 3 秒内不变(重力下站住)
    网格解析报错 0 条

新增:
  scripts/run_sim_gz.sh                  此前**不存在**,却被 install_sim_env.sh
                                         与 build_sim.sh 的提示语引用
  arm_sim_gazebo/robot_description.py    展开 + 归一化,launch 与 CLI 共用一份
  arm_sim_gazebo/gazebo_sim_launcher.py  薄壳:准备描述后转发给 launch 文件
  launch/gazebo_sim.launch.py            起 gz → 发归一化 URDF → spawn → 拉控制器
  test/test_robot_description.py         含"仓库里这份 world 必须过守卫"的测试

接线过程中实测踩到并修掉的三个坑,共同特征是**每一步看起来都成功**:

1) urdf_normalize 在 Python 路径下静默失效(我自己引入的,最严重)
   xacro 的输出格式在不同环境里不一样:真机环境的 CLI 缩进成多行,而仿真环境
   走 xacro.process_file().toxml() 时全挤在一行。归一化的正则要求标签之间必须有
   \n,于是**仿真路径下它什么都没做**(报告"包裹 0 处"),而 CLI 路径下正常 ——
   之前那次"已修好"的验证走的就是 CLI,所以看起来过了。
   后果是 Gazebo 会丢掉整条机械臂,正是这个归一化本来要防的事。
   已改成对空白格式零假设,并加回归测试锁住。

2) world 里声明了 <plugin> 会把默认系统挤掉
   只要 world 声明任何一个 <plugin>,Gazebo 就不再加载 server.config 的
   Physics/UserCommands/SceneBroadcaster。缺 UserCommands →
   /world/<名>/create 服务不存在 → ros_gz_sim create 调服务超时后回退发 topic、
   没人收 → **模型从未进入 Gazebo**,而 create 节点仍打印
   "OK creation of entity",控制器则永远卡在 "waiting for service"。
   已在这三个系统在 assembly.world 里显式声明,并加纯文本守卫
   check_base_plugins(),让"加了插件忘了补基础系统"在启动前就被指出。

3) gz 的插件与资源搜索路径缺 $CONDA_PREFIX/lib
   gz_ros2_control 报 "Failed to load system plugin
   [libgz_ros2_control-system.so] : couldn't find shared library",而 ldd 全齐、
   库就在 $CONDA_PREFIX/lib 下。原因是 gz 的 SystemLoader 先在搜索路径里找文件,
   找不到就直接放弃,不去 dlopen 裸文件名(所以 conda 二进制的 RPATH 兜不住);
   而 ros_gz_sim 只把 LD_LIBRARY_PATH 拼进搜索路径,conda 激活并不往那里加。
   同一原因导致 mesh 的 model:// 也解析不了(视觉报错很吵,但更要紧的是
   **碰撞网格也加载不出来**,会让后续装配的接触判据失效)。
   由于 gz_sim.launch.py 用 `from os import environ` 在生成 launch 描述时就读
   进程环境,这些变量**必须在 ros2 launch 之前**设好,因此放在 sim_env.sh ——
   在 launch 文件里用 SetEnvironmentVariable 补是无效的,会被它自己算的那份
   字典覆盖。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-23 14:48:16 +08:00
..

devices/sim/gazebo/ —— Gazebo 仿真平台

先读这段:当前仿真是假的

现在项目里的「仿真」并不是 Gazebo。 实际情况是:

  • src/arm_description/urdf/arm.gazebo.xacro 是零引用的死文件;
  • arm_bringup/stack.launch.py 的 isaac 分支只有一行日志,没有任何实际动作;
  • run_sim.sh 走的是 sim.launch.py,底层是 mock 驱动(mock_arm_driver.py), 即用假数据模拟机器人状态,不是物理仿真;
  • Gazebo 从未真正接线。

所以这份文档分两部分:第一部分是怎么把 Gazebo 真正跑起来(当前必须做的前置工作), 第二部分才是日常怎么用。不要跳过第一部分。


第一部分:把 Gazebo 真正接起来(前置工作)

要让 Gazebo 跑起来,缺的是这几块,按顺序补齐:

1. URDF 加 Gazebo 标签

现有 arm_description/urdf/arm.gazebo.xacro 是死文件,需要重写。每个 link 必须补:

<!-- 视觉碰撞体 -->
<gazebo reference="${prefix}link1">
  <material>Gazebo/Grey</material>
</gazebo>

并在 arm.ros2_control.xacro 里把 ros2_control 标签的 hardware 插件从 flexiv_hardware/FlexivHardware 换成 gazebo_ros2_control/GazeboSystem。

2. 关节传动与控制器

Gazebo 需要 transmission 标签把关节映射到 ros2_control。控制器配置可以直接复用 arm_control/config/controllers.yaml 里的 rizon_arm_controller,只需把关节名前缀 从真实 SN(如 Rizon4-063989_)换成仿真专用的前缀(如 sim_)。

3. 传感器插件

仿真对象 插件 对应真实设备
腕部相机 libgazebo_ros_camera.so D405
手外相机 libgazebo_ros_camera.so D435
深度 libgazebo_ros_depth_camera.so 对齐深度流
触觉力 需要自己写插件 Orisys

最关键的一条:Orisys 视触觉没有现成的 Gazebo 插件。 3 路切向力 + 1 路法向力 + 深度图 + 力矢量图 这套数据需要自定义 Gazebo 插件来模拟。 在它做出来之前,仿真里无法验证通止规装配的力控部分(S07 接触、S08 拧紧判定)。 建议的自定义插件做法:订阅 Gazebo 的接触传感器数据,按接触深度和相对滑动速度合成 出一组「类触觉」读数,再按真实传感器的量纲(任意单位增量)发布到同样的内部话题上。

4. 工件与通止规模型

需要给工件(含 k0 中心孔、k1/k2 螺纹孔)和通止规建简化网格,并保证:

  • 工件的 k0/k1/k2 三个点在模型坐标系里的位置与实际一致 —— 否则推理出的坐标映射到仿真里会对不上;
  • 螺纹孔要能表达「拧入」的几何阻力,否则 S08 的力反馈无法触发。

第二部分:日常怎么用

放置格式与命名要求

类型 放哪 格式 命名要求
世界文件 worlds/ .world 或 .sdf <场景名>.world,如 assembly.world
机器人模型 urdf/ .urdf / .xacro 必须以 sim_ 为关节前缀,与真实臂区分
工件/夹具网格 models/<模型名>/ <模型名>.sdf + meshes/ 模型名必须与推理用的类别名一致,如 workpiece
传感器插件 sensor_plugins/ .so 源码 + CMakeLists.txt 插件类名以 Orisys 开头
网格文件 models/<模型名>/meshes/ .stl / .dae 单位必须是米。用毫米建模的必须先缩放到米

导入一个新场景

  1. 把 .world 放进 worlds/,网格放进 models/<模型名>/
  2. 改 config/site.yaml 的 sim.world 指向该文件
  3. 改 devices/registry.yaml 的 sim: gazebo(通常已是)
  4. 启动 scripts/run_sim.sh

修改哪个配置才会生效

想改什么 改哪里
用哪个场景 site.yaml → sim.world
仿真是否启用物理 site.yaml → sim.physics_enable
仿真里的悬高/速度 复用真实的那套参数(servo.* / hole_detect.stage2.*),不要另建一套,否则仿真调好的值搬到真机不生效
关节前缀 site.yaml → sim.joint_prefix(默认 sim_)

设计约束:仿真与真机必须共用同一套 site.yaml 参数。任何「仿真专用参数」都是未来 仿真调参搬到真机失效的隐患。只允许物理引擎本身的参数(如重力、步长)放在 sim.* 下。