From 01940f54e77aa44376d7adaa1bec9844f2e29360 Mon Sep 17 00:00:00 2001 From: arm_dev Date: Wed, 23 Sep 2026 14:48:16 +0800 Subject: [PATCH] =?UTF-8?q?P5c-3=20=E4=BB=BF=E7=9C=9F=E5=90=AF=E5=8A=A8?= =?UTF-8?q?=E5=99=A8=E8=B7=91=E9=80=9A=EF=BC=9AGazebo=20+=20spawn=20+=20?= =?UTF-8?q?=E4=B8=A4=E4=B8=AA=E6=8E=A7=E5=88=B6=E5=99=A8=20active?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 端到端验收(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 里声明了 会把默认系统挤掉 只要 world 声明任何一个 ,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 --- devices/sim/gazebo/manifest.yaml | 50 ++-- devices/sim/gazebo/worlds/assembly.world | 35 +++ scripts/run_sim_gz.sh | 81 +++++ scripts/sim_env.sh | 41 +++ .../arm_sim_gazebo/gazebo_sim_launcher.py | 190 ++++++++++++ .../arm_sim_gazebo/robot_description.py | 236 +++++++++++++++ .../arm_sim_gazebo/urdf_normalize.py | 44 ++- .../launch/gazebo_sim.launch.py | 278 ++++++++++++++++++ src/arm_sim_gazebo/setup.py | 26 +- .../test/test_robot_description.py | 264 +++++++++++++++++ .../test/test_urdf_normalize.py | 45 +++ 11 files changed, 1253 insertions(+), 37 deletions(-) create mode 100755 scripts/run_sim_gz.sh create mode 100644 src/arm_sim_gazebo/arm_sim_gazebo/gazebo_sim_launcher.py create mode 100644 src/arm_sim_gazebo/arm_sim_gazebo/robot_description.py create mode 100644 src/arm_sim_gazebo/launch/gazebo_sim.launch.py create mode 100644 src/arm_sim_gazebo/test/test_robot_description.py diff --git a/devices/sim/gazebo/manifest.yaml b/devices/sim/gazebo/manifest.yaml index cba5e19..d5e9caf 100644 --- a/devices/sim/gazebo/manifest.yaml +++ b/devices/sim/gazebo/manifest.yaml @@ -3,26 +3,37 @@ # # Gazebo 仿真平台清单。 # -# 进度(截至 P5c-2,如实记录,不要凭印象改): +# 进度(截至 P5c-3,如实记录,不要凭印象改): # ✔ 环境 独立 conda 环境 ros_gz_sim(Gazebo Fortress + ROS 2 Humble) # 安装见 scripts/install_sim_env.sh,激活见 scripts/sim_env.sh # ✔ 世界文件 worlds/assembly.world(sdformat 校验通过) # ✔ 机器人描述 urdf/(复用官方 flexiv_description,已通过 sdformat 校验) # ✔ 控制器 config/controllers.yaml(控制器名与真机一致) -# ✔ 描述归一化 arm_sim_gazebo 包的 urdf_normalize(16 项单测) -# ✘ 启动器 driver.executable 声明的 gazebo_sim_launcher 尚未实现 +# ✔ 描述归一化 arm_sim_gazebo 包的 urdf_normalize(37 项单测) +# ✔ 启动器 gazebo_sim_launcher 已实现并端到端跑通 +# 起 Gazebo → 发归一化 URDF → spawn → 拉起两个控制器(active) +# 入口脚本 scripts/run_sim_gz.sh(此前缺失,被 install_sim_env.sh +# 与 build_sim.sh 的提示语引用,照做会报文件不存在) # ✘ 场景模型 models/ 还是空的(工件/通止规模型未做) # ✘ 触觉插件 sensor_plugins/ 还是空的(Orisys 三向力插件未做) -# ✘ 相机出图 D405 已建模,但话题出图未联调验证 +# ✘ 相机出图 D405/D435 传感器已在 Gazebo 里成功注册并发布话题, +# 但还没接 ros_gz_bridge 到 ROS,也还没验证图像内容 # -# 所以:现在**还不能**靠 run_sim.sh 跑起完整仿真,run_sim.sh 走的仍是 -# arm_bringup 的 mock 驱动(假数据)。接线步骤见本目录 README.md。 +# 所以:D405/D435 与触觉还要等 P5c-4 / P5c-5,装配状态机(P6)依赖它们。 # -# 一个重要结论(接线过程中实测得出,值得记住): -# 官方 flexiv_description 直接喂 Gazebo 会被 sdformat 拒掉,有两处不兼容 -# (7 轴本体缺 包裹;夹爪有零质量 link 导致惯性叠加出 NaN)。 -# 两者都只有 Gazebo 会暴露,RViz/MoveIt 完全正常,所以此前从未被发现。 -# 解决方式是在启动时归一化,见 arm_sim_gazebo/urdf_normalize.py 的长注释。 +# 三个实测踩到、且都属于"静默失效"的坑(后续改仿真时务必先看这三条): +# 1. 官方 flexiv_description 直接喂 Gazebo 会被 sdformat 拒掉:7 轴本体缺 +# 包裹;夹爪有零质量 link 导致惯性叠加出 NaN。两者都只有 +# Gazebo 会暴露,RViz/MoveIt 完全正常。已在启动时归一化解决。 +# 2. world 文件里只要声明了**任何一个** ,Gazebo 就不再加载默认的 +# Physics/UserCommands/SceneBroadcaster。少了 UserCommands, +# /world/<名>/create 服务不存在 → 模型根本没进 Gazebo,而 create 仍打印 +# "OK creation of entity"。所以这三个系统必须在 world 里显式声明。 +# 3. gz 的插件搜索路径不含 $CONDA_PREFIX/lib(ros_gz_sim 只拼 LD_LIBRARY_PATH, +# 而 conda 激活不往那里加),于是 gz_ros2_control 报 "couldn't find shared +# library" 而库其实就在那儿。必须在 ros2 launch 之前设 +# GZ_SIM_SYSTEM_PLUGIN_PATH;资源路径同理(否则 mesh 全加载不出来)。 +# 两者都由 scripts/sim_env.sh 负责。详见该文件里的长注释。 # ============================================================================= id: gazebo @@ -34,9 +45,10 @@ vendor: openrobotics version: "Fortress 6.16 (ignition-gazebo6, ros_gz + gz_ros2_control 0.7.15)" description: > - 物理仿真平台。环境已装好、世界与机器人描述已就绪并通过 sdformat 校验; - 启动器、场景模型(工件/通止规)与 Orisys 触觉插件尚未实现, - 因此仿真目前还不能替代 run_sim.sh 的 mock 路径。 + 物理仿真平台。环境、世界、机器人描述、控制器与启动器均已就绪并端到端验证 + (起 Gazebo → spawn → 两个控制器 active → 机械臂在重力下站住)。 + 场景模型(工件/通止规)与 Orisys 触觉插件尚未实现; + 相机传感器已在 Gazebo 内注册并出话题,但还没接到 ROS 侧(P5c-4)。 注意:Orisys 视触觉没有现成 Gazebo 插件,自定义插件做出来之前, 仿真里无法验证通止规装配的 S07 接触与 S08 拧紧力判据。 @@ -78,10 +90,12 @@ driver: executable: gazebo_sim_launcher build: colcon notes: > - 包已建(含经单测的 URDF 归一化),但 gazebo_sim_launcher 本身**尚未实现**, - 因此 setup.py 里暂未注册这个入口点 —— 注册了模块不存在,构建能过、 - 一运行就 ImportError。P5c-3 补上实现后再注册。 - 在它可用之前,run_sim.sh 仍走 arm_bringup 的 mock 驱动路径。 + 已实现并在 setup.py 注册。它只做"准备描述(展开 + 归一化)+ 转发给 + launch/gazebo_sim.launch.py",启动逻辑不在可执行文件里 —— ros2 launch 的 + 日志/退出码/中断处理都更成熟,自己重造一份只会更难调试。 + 入口建议走 scripts/run_sim_gz.sh(它会先 source scripts/sim_env.sh, + 而 Gazebo 的插件/资源搜索路径必须在 ros2 launch 之前设好,见 sim_env.sh)。 + 单独排查描述用:gazebo_sim_launcher --check / --print-urdf。 # --- 配置项声明 ------------------------------------------------------------ # 设计约束:仿真与真机共用同一套 site.yaml 参数(servo.* / hole_detect.stage2.*), diff --git a/devices/sim/gazebo/worlds/assembly.world b/devices/sim/gazebo/worlds/assembly.world index d24133c..e3c4863 100644 --- a/devices/sim/gazebo/worlds/assembly.world +++ b/devices/sim/gazebo/worlds/assembly.world @@ -169,6 +169,41 @@ + + + + +