P5c-2:仿真世界 + 机器人描述 + 控制器配置,并解决两处官方描述与 Gazebo 的不兼容

这一步解决的是"官方 Flexiv 描述能不能喂给 Gazebo"的问题。结论是**不能直接用**,
有两处不兼容,且都只有 Gazebo 会暴露 —— RViz/MoveIt 完全正常,所以此前从未发现。

问题一:7 轴本体的质量/惯量是"孤儿标签"
    rizon_arm.xacro 调 get_inertias 时没写 <inertial> 包裹,而那个宏只吐
    <origin>/<mass>/<inertia> 三个碎片。RViz/MoveIt 忽略不认识的子元素所以看着
    没事;sdformat 判定 "link has no <inertial> block",然后把 **link 和它的父
    关节一起丢掉**。症状极具误导性:机械臂凭空消失或只剩夹爪,报错只有一句
    "link[sim_link1] is not modeled in sdf"。

问题二:夹爪有零质量 link,导致 sdformat 惯性叠加出 NaN
    grav_macro.xacro 给六个纯坐标系 dummy link 写了 mass=0、惯量全 0。
    sdformat 对 fixed 关节会把子 link 惯性叠加到父 link,叠加要除以总质量 →
    质量 0 就出 NaN。而报错指向的是**父** link:
        Pose values must be finite. link[sim_left_finger_mount]/inertial/pose
    完全看不出根因在子 link 的质量上。

修法:启动时归一化(不走改上游文件的路,因为上游由 fetch_flexiv.sh 获取、改了会被
覆盖;而且换别的臂时大概率有别的坑,在启动时统一过一道更可持续)
    - 孤儿三件套 → 只重新分组已有元素,一个数字都不改
    - 零质量 → 补 1e-4 kg(与夹爪其他配件同量级,0.1g 对动力学无影响),
               惯量全 0 时补极小对角值避免张量退化
    不发明物理数据,也不掩盖真问题:上游真没给质量时这里什么都不会包。
    16 项单测,含"已有 <inertial> 不能被二次包裹"(早期用全局正则踩过,
    sdformat 报 "Inertial element must have a mass element")与幂等性。

新增/改动
- devices/sim/gazebo/urdf/:arm.sim.urdf.xacro(主)、.ros2_control(gz 硬件)、
  .gazebo(系统插件/材质/接触刚度)、.sensors(D405 + 双指触觉)
- devices/sim/gazebo/worlds/assembly.world:1ms 步长 + 150 次接触迭代(力信号
  要干净才谈得上"切向力突变"判据)、工作台、world 级渲染插件、D435
- devices/sim/gazebo/config/controllers.yaml:控制器名 rizon_arm_controller
  **故意与真机一致** —— 隔离的是关节/TF 层(sim_ 前缀),不是应用层依赖的接口
- src/arm_sim_gazebo/:新包,含 urdf_normalize(库 + CLI)与单测

一处设计纠正:D435 从 URDF 挪进 world。原本挂在 URDF 的 world link 上,结果
整台相机转 SDF 时凭空消失(sdformat 丢弃无质量根 link 时连带丢了整棵子树,
且退出码 0、不报错)。眼在手外的相机物理上就属于场景,放 world 才对;
渲染插件也一并归 world 级,避免"插件在模型里、相机在世界里"的错配。

验证(不是只看退出码)
- 带夹爪全量:sdformat 退出码 0、0 报错、15 link / 15 joint / 26 frame
- 传感器 d405_rgbd + tactile_left/right 均在;控制器插件与参数路径正确
- 世界文件 sdformat 通过
- 包在 ros_gz_sim 环境构建通过,CLI 实跑结果与库调用一致
- 未注册 gazebo_sim_launcher 入口点:模块还没写,注册了构建能过但一运行就
  ImportError,不如老实缺着(manifest 里如实标注为未实现)

注:实测确认 gz_ros2_control 的硬件/接口声明**不在 SDF 里**,插件是从
/robot_description 话题读 URDF 的 <ros2_control> —— 所以启动器必须先起
robot_state_publisher 且发布归一化后的同一份 URDF,否则控制器注册的关节名
会和 Gazebo 里的对不上。这条已写进 P5c-3 的实现要求。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
arm_dev
2026-09-23 14:19:39 +08:00
parent d93dab31c9
commit 25635e21f8
15 changed files with 1506 additions and 12 deletions
@@ -0,0 +1,90 @@
<?xml version="1.0"?>
<!--
=============================================================================
Gazebo 专属标签(<gazebo>)。
URDF 本身是"几何+运动学"的描述,Gazebo 需要额外信息才能正确仿真:
<plugin> 把 ros2_control 拉进 Gazebo 进程(没有它就没有 controller_manager)
<material> 让可视化能区分各个连杆(纯观感,但调试时非常有用)
阻尼/摩擦 纯 URDF 的 <dynamics> 会被 Gazebo 部分忽略,这里显式覆盖
为什么单独一个文件而不是塞进 arm.sim.urdf.xacro:
传感器(相机、触觉)也要往 <gazebo> 里挂东西(见 arm.sim.sensors.xacro)。
分开后"机器人本体"与"外挂设备"互不干扰,加传感器不用动本体文件。
=============================================================================
-->
<robot xmlns:xacro="http://www.ros.org/wiki/xacro">
<xacro:macro name="arm_sim_gazebo" params="prefix controllers_file">
<!-- =====================================================================
ros2_control 的系统插件。
filename 必须是 Gazebo 能在库路径里找到的 .so 名。本机实测该文件为
$CONDA_PREFIX/lib/libgz_ros2_control-system.so,故写 libgz_ros2_control-system.so。
踩坑提示:网上教程常见 libignition-gazebo-ros2-control-system.so
(Gazebo 改名前的老名字)。名字写错时 Gazebo 只打印一行
"Failed to load system plugin",不报具体原因,很容易卡在这里。
换版本后这样确认:
ls $CONDA_PREFIX/lib/libgz_ros2_control*.so
ls $CONDA_PREFIX/lib/libignition-gazebo*ros2-control*.so
parameters 指向控制器配置文件,控制器名与话题名由它决定。
应用层按 rizon_arm_controller/follow_joint_trajectory 发轨迹
(与真机、mock 完全一致),所以 controllers.yaml 里的控制器名必须是
rizon_arm_controller —— 这是三个后端共享的接口契约,不能改。
===================================================================== -->
<gazebo>
<plugin filename="libgz_ros2_control-system.so"
name="gz_ros2_control::GazeboSimROS2ControlPlugin">
<parameters>${controllers_file}</parameters>
</plugin>
</gazebo>
<!-- =====================================================================
连杆外观色。只为在 Gazebo 里一眼看清是哪一节在动。
官方 URDF 的 visual 里带 mesh,但 Gazebo 渲染 mesh 更吃性能,
用纯色覆盖能显著提速,调试运动时也更直观。
===================================================================== -->
<gazebo reference="${prefix}base_link"><material>Gazebo/DarkGrey</material></gazebo>
<gazebo reference="${prefix}link1"><material>Gazebo/Red</material></gazebo>
<gazebo reference="${prefix}link2"><material>Gazebo/Blue</material></gazebo>
<gazebo reference="${prefix}link3"><material>Gazebo/Green</material></gazebo>
<gazebo reference="${prefix}link4"><material>Gazebo/Yellow</material></gazebo>
<gazebo reference="${prefix}link5"><material>Gazebo/Orange</material></gazebo>
<gazebo reference="${prefix}link6"><material>Gazebo/Purple</material></gazebo>
<gazebo reference="${prefix}link7"><material>Gazebo/Grey</material></gazebo>
<!-- =====================================================================
末端两个关键 link 的接触参数。
通止规装配全靠"接触":下压到碰到工件、旋拧时切向力突变。
这些判据在仿真里能不能复现,取决于接触刚度/阻尼是否合理。
默认值(stiffness=1e6)对金属-金属插入偏软,会有明显穿透感,
这里调到 1e7 并给足阻尼,让接触"硬"一点,力信号更干净。
min_depth / max_vel 是 Gazebo 用来抑制穿透弹跳的收敛参数:
调不好会出现"接触瞬间被弹飞"的假象,进而让力判据误触发。
===================================================================== -->
<gazebo reference="${prefix}flange">
<mu1>0.6</mu1>
<mu2>0.6</mu2>
<kp>1e7</kp>
<kd>1e4</kd>
<minDepth>0.001</minDepth>
<maxVel>0.01</maxVel>
<material>Gazebo/Black</material>
</gazebo>
<gazebo reference="${prefix}tcp">
<mu1>0.6</mu1>
<mu2>0.6</mu2>
<kp>1e7</kp>
<kd>1e4</kd>
<minDepth>0.001</minDepth>
<maxVel>0.01</maxVel>
</gazebo>
</xacro:macro>
</robot>
@@ -0,0 +1,63 @@
<?xml version="1.0"?>
<!--
=============================================================================
仿真版 ros2_control 硬件描述。
为什么不复用官方 flexiv_description 的 rizon.ros2_control.xacro:
那个宏只认两种硬件插件 —— 要么 mock_components/GenericSystem,
要么 flexiv_hardware/FlexivHardwareInterface(真机 RDK)。
Gazebo 需要第三种:gz_ros2_control/GazeboSimSystem。官方宏里没有这个分支,
而且改官方文件等于分叉上游描述,上游一升级就冲突。所以仿真自己写一份。
两处必须对齐,否则 controller_manager 会报 "no hardware interface found":
1. 关节名 —— 必须是 sim_joint1..sim_joint7(前缀 sim_,见 naming 约定)
2. command/state interface 名字 —— 必须与 gz_ros2_control 实际提供的接口一致
Gazebo 侧:position/velocity 可读可写,effort 只能读。
我们只下 position 指令(应用层走 JTC 位置轨迹),所以 command 只声明 position。
验证插件名的方法(换 Gazebo/ros2_control 版本后建议重跑一遍确认):
strings $CONDA_PREFIX/lib/libgz_ros2_control-system.so | grep GazeboSimSystem
cat $CONDA_PREFIX/share/gz_ros2_control/gz_hardware_plugins.xml
本机实测(gz_ros2_control 0.7.15):硬件名为 gz_ros2_control/GazeboSimSystem。
=============================================================================
-->
<robot xmlns:xacro="http://www.ros.org/wiki/xacro">
<xacro:macro name="arm_sim_ros2_control" params="prefix joint_count:=7">
<ros2_control name="GazeboSimSystem" type="system">
<hardware>
<!--
Gazebo 的 ros2_control 硬件插件。它由 Gazebo 侧的系统插件
(arm.sim.gazebo.xacro 里声明的 GazeboSimROS2ControlPlugin)加载,
controller_manager 在 Gazebo 进程内被拉起来。
注意:硬件参数(update_rate 等)不写在这里,而是在 Gazebo 的
<plugin> 标签里配置(见 arm.sim.gazebo.xacro)。写在 <hardware> 下
不会生效,这是 gz_ros2_control 与真机 hardware 的一个区别。
-->
<plugin>gz_ros2_control/GazeboSimSystem</plugin>
</hardware>
<!-- 关节 1..7。全部只给 position 指令 + position/velocity 状态。 -->
<xacro:macro name="arm_sim_joint" params="name">
<joint name="${name}">
<command_interface name="position"/>
<state_interface name="position"/>
<state_interface name="velocity"/>
</joint>
</xacro:macro>
<xacro:arm_sim_joint name="${prefix}joint1"/>
<xacro:arm_sim_joint name="${prefix}joint2"/>
<xacro:arm_sim_joint name="${prefix}joint3"/>
<xacro:arm_sim_joint name="${prefix}joint4"/>
<xacro:arm_sim_joint name="${prefix}joint5"/>
<xacro:arm_sim_joint name="${prefix}joint6"/>
<xacro:arm_sim_joint name="${prefix}joint7"/>
</ros2_control>
</xacro:macro>
</robot>
@@ -0,0 +1,189 @@
<?xml version="1.0"?>
<!--
=============================================================================
外挂传感器:眼在手上的相机 + 双指触觉。
分工(按"装在哪"划分,不是按品牌):
D405 眼在手上(eye-in-hand)—— 装在法兰上,跟着手走 → 属于**机器人**,
所以在这个文件里,跟着 URDF 一起展开
D435 眼在手外(eye-to-hand)—— 装在工作台支架上,俯视台面 → 属于**场景**,
所以在 worlds/assembly.world 里(每台相机只出一份定义,不重复)
为什么 D435 不能挂在 URDF 的 world link 上(实测踩过):
挂了之后整台相机在转 SDF 时凭空消失。sdformat 会丢弃无质量的根 link,
连带它整棵子树;而它**退出码 0、不报任何错**,只是 SDF 里再也找不到
d435 的 frame 和 sensor,启动后话题永远没数据。
另一个理由:渲染系统插件必须是 world 级的,相机和插件放一起最不容易错配。
=============================================================================
和真机的对应关系(重要)
=============================================================================
真机上是 RealSense,出 RGB + 深度。仿真用 rgbd_camera 同时给两者,
话题名对齐 ros_realsense,应用层(跟踪、粗定位)才能不加分支地两边通用:
真机 / 仿真: /d405/color/image_raw + /d405/depth/image_rect_raw
换相机型号时只改这里的尺寸/内参,应用层不用动。
============================================================================
插件文件名
============================================================================
Fortress 的传感器系统插件是 libignition-gazebo6-sensors-system.so,
名称 ignition::gazebo::systems::Sensors。实测确认过:
ls $CONDA_PREFIX/lib/libignition-gazebo6-sensors-system.so
换到更新版本(Harmonic+)后名字会变成 libgz-sim*-sensors-system.so,
且插件名去掉 ignition:: 前缀 —— 那时要改这里。
=============================================================================
-->
<robot xmlns:xacro="http://www.ros.org/wiki/xacro">
<xacro:macro name="arm_sim_sensors"
params="
prefix
sensors_file:=''
d405_enable:=true
tactile_enable:=true
d405_x:=0.0
d405_y:=-0.06
d405_z:=0.05
d405_roll:=0.0
d405_pitch:=0.0
d405_yaw:=0.0
image_width:=640
image_height:=480
image_hz:=30
">
<!-- =======================================================================
传感器系统插件放在 **world 文件**里,不放这里
=======================================================================
渲染是所有传感器共用的世界级能力,而且 D435 在 world 里(见下)。
插件与相机分处两地的话,很容易出现"插件在模型里、相机在世界里"
这种错配 —— 症状是话题永远没数据且不报错。
所以统一由 worlds/assembly.world 声明一次,机器人上的 D405
和触觉也由它一并渲染。
======================================================================= -->
<!-- =======================================================================
RealSense D405 —— 眼在手上
=======================================================================
位姿默认值说明(法兰坐标系,z 朝法兰外侧):
z=+0.05 相机略微伸出法兰,避免看到夹爪本体
y=-0.06 偏置到一侧,与真机上的支架安装位置一致
这些值**不是**真机的标定结果,只是仿真里的合理起始位。
仿真里"手眼关系"本来就精确已知(我们直接定义了相机的安装位姿),
所以不需要跑手眼标定 —— 但保留了可调位姿,方便模拟"标定不准"的情况,
验证跟踪算法对误差的容忍度。
======================================================================= -->
<xacro:if value="${d405_enable}">
<link name="${prefix}d405_link"/>
<joint name="${prefix}d405_joint" type="fixed">
<parent link="${prefix}flange"/>
<child link="${prefix}d405_link"/>
<origin xyz="${d405_x} ${d405_y} ${d405_z}"
rpy="${d405_roll} ${d405_pitch} ${d405_yaw}"/>
</joint>
<gazebo reference="${prefix}d405_link">
<sensor name="d405_rgbd" type="rgbd_camera">
<update_rate>${image_hz}</update_rate>
<always_on>true</always_on>
<!-- visualize=false:headless 下没有 GUI,开着只会白耗渲染资源 -->
<visualize>false</visualize>
<camera name="d405">
<!-- 水平视场角。D405 的实际值约 87°(H) × 58°(V),这里取近似 -->
<horizontal_fov>1.5184</horizontal_fov>
<image>
<width>${image_width}</width>
<height>${image_height}</height>
<format>R8G8B8</format>
</image>
<clip>
<!-- 近裁剪面必须小于相机到工件的最近距离,否则工件会被裁掉 -->
<near>0.02</near>
<far>10.0</far>
</clip>
<depth_camera>
<clip><near>0.02</near><far>10.0</far></clip>
</depth_camera>
</camera>
<!-- 话题名对齐真机 ros_realsense 的命名,应用层两边通用 -->
<topic>/d405</topic>
</sensor>
</gazebo>
</xacro:if>
<!-- =======================================================================
RealSense D435 —— 眼在手外:**不在这个文件里**
=======================================================================
它挂过 world link(URDF 里那个空 link),结果整台相机在转换到 SDF 时
凭空消失:sdformat 会丢掉无质量的根 link,连带它整棵子树。
现象极具误导性 —— sdformat 退出码 0、不报任何错,只是 SDF 里
d435 的 frame 和 sensor 都不存在,启动后话题永远没数据。
正确位置是 world 文件(worlds/assembly.world):眼在手外的相机在物理上
就是装在工作台支架上、不属于机械臂。放那里还有个好处 ——
渲染系统插件必须是 world 级的,相机和插件在一起,不会再出现
"插件在模型里、相机在世界里"这种错配。
======================================================================= -->
<!-- =======================================================================
Orisys 视触觉传感器
=======================================================================
真机上是视触觉:一块弹性体 + 内置相机,读出三向力、深度图、力矢量图。
仿真里没法真的模拟弹性体形变成像,采取的务实方案是:
用 Gazebo 内置的 contact sensor 取接触点的三向力,
再由自定义插件(devices/sim/gazebo/sensor_plugins/)把它
按 Orisys 的 TactileStatus 消息格式发布出去,
含切向/法向分解,使 P6 的力判据在仿真里可跑。
这里只挂 contact sensor 与自定义插件的位置占位;
插件的实现与消息映射在 P5c-5 完成。
为什么要挂两个(left/right):
真机是双指各贴一片,tangential_1/2 + normal 三路力分别来自两片,
单指的数据无法复现"旋拧时切向力从哪一侧来"这个判据。
======================================================================= -->
<xacro:if value="${tactile_enable}">
<link name="${prefix}tactile_left_link"/>
<link name="${prefix}tactile_right_link"/>
<joint name="${prefix}tactile_left_joint" type="fixed">
<parent link="${prefix}tcp"/>
<child link="${prefix}tactile_left_link"/>
<origin xyz="0 0.035 0.005" rpy="0 0 0"/>
</joint>
<joint name="${prefix}tactile_right_joint" type="fixed">
<parent link="${prefix}tcp"/>
<child link="${prefix}tactile_right_link"/>
<origin xyz="0 -0.035 0.005" rpy="0 0 0"/>
</joint>
<gazebo reference="${prefix}tactile_left_link">
<sensor name="tactile_left" type="contact">
<always_on>true</always_on>
<update_rate>100</update_rate>
<!-- 话题名与真机 tactile_node 发布的完全一致:
/tactile/left/status (arm_msgs/TactileStatus)
这样 P6 的状态机在仿真/真机之间不用改一行判断。 -->
<contact>
<collision>${prefix}tactile_left_link_collision</collision>
</contact>
<topic>/tactile/left/raw</topic>
</sensor>
</gazebo>
<gazebo reference="${prefix}tactile_right_link">
<sensor name="tactile_right" type="contact">
<always_on>true</always_on>
<update_rate>100</update_rate>
<contact>
<collision>${prefix}tactile_right_link_collision</collision>
</contact>
<topic>/tactile/right/raw</topic>
</sensor>
</gazebo>
</xacro:if>
</xacro:macro>
</robot>
+152
View File
@@ -0,0 +1,152 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
=============================================================================
仿真版机器人描述(Gazebo 用)。
用法(由 arm_sim_gazebo 的 gazebo_sim_launcher 调用,一般不需要手敲):
xacro /abs/path/devices/sim/gazebo/urdf/arm.sim.urdf.xacro \
controllers_file:=/abs/path/devices/sim/gazebo/config/controllers.yaml
注意:本文件在 devices/ 下,而 devices/ **不是 ROS 包**,所以不能用
$(find ...) 找同目录的兄弟文件。这里用 xacro 的相对包含 —— xacro 会把
相对路径按"当前正在处理的文件所在目录"解析,因此同目录文件直接写文件名即可。
(绝对路径也可以,启动器就是这么传 controllers_file 的。)
============================================================================
设计:复用官方描述,只补仿真缺的那部分
============================================================================
连杆几何 / 惯性 / 碰撞体全部来自官方 flexiv_description,一行都不复制:
<xacro:rizon_robot ...> 会用 Rizon4 的四个 YAML 生成
joint_limits.yaml → 关节限位(与真机一致,仿真才有意义)
default_kinematics.yaml → 各关节原点位姿(决定 TCP 能不能够到工件)
inertia_parameters.yaml → 真实质量与惯量张量(Gazebo 物理必需)
visual_parameters.yaml → mesh 视觉 + 凸包碰撞
这一点很关键:如果仿真自己造一套物理参数,那"仿真里调好的力阈值搬到真机
就不成立",整个仿真也就失去价值了。
仿真额外要补的只有三样,都在本文件里显式写清:
1. gz_ros2_control 硬件 + 系统插件(官方宏只认 mock / 真机两种)
2. tcp / tool0 两个应用层帧(官方描述只有 flange)
3. 外挂传感器(相机/触觉,见 arm.sim.sensors.xacro)
============================================================================
关节前缀为什么是 sim_
============================================================================
真实臂前缀是 Rizon4-063989_。仿真若沿用同一前缀,两边同时跑时 TF 和
/joint_states 会互相污染 —— 症状是 RViz 里模型乱跳、控制器收到别人的关节角。
仿真用独立的 sim_ 前缀 → 天然隔离,可以真机仿真同开对比。
注意这不止是"改个名字":控制器名(rizon_arm_controller)**故意保持与真机一致**,
因为那是应用层依赖的接口契约;隔离的是关节/TF 层,不是接口层。
=============================================================================
-->
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="rizon_sim">
<!-- 机型。Rizon4 / Rizon4s / Rizon4M / Rizon4R / Rizon10 / Rizon10s -->
<xacro:arg name="rizon_type" default="Rizon4"/>
<!--
仿真关节前缀。改成空串则与真机同前缀(只在单跑仿真、且不介意与真机串味时用)。
robot_sn 传空串,配合 arm_prefix=sim,官方宏算出的前缀就是 "sim_"。
官方前缀算法:(arm_prefix + '_' + robot_sn + '_') 若两者都非空,
否则 (arm_prefix + robot_sn + '_')。
-->
<xacro:arg name="arm_prefix" default="sim"/>
<xacro:arg name="robot_sn" default=""/>
<!-- 控制器配置文件的绝对路径(由启动器按仓库根动态拼出来传入) -->
<xacro:arg name="controllers_file" default=""/>
<!-- 是否挂夹爪。换夹爪品牌时这里改(或由启动器从 site.yaml 映射过来)。
注意官方描述里夹爪只有 Flexiv-GN01 一种(grav_macro),换别的品牌
需要另写一份描述 —— 见 devices/grippers/ 的说明。 -->
<xacro:arg name="load_gripper" default="true"/>
<!-- 是否挂外挂传感器(相机/触觉)。关掉可做纯运动学验证,跑得更快。 -->
<xacro:arg name="with_sensors" default="true"/>
<!--
传感器参数直接由启动器传入,不在这里读 YAML。
原因:读 YAML 需要绝对路径,而 xacro 无法自己推导仓库根;且"配置→参数"的
映射统一放在启动器里(那是唯一知道 site.yaml 和仓库布局的地方),
调试时看一处就够,不用在 xacro / YAML / Python 三处来回找。
-->
<xacro:arg name="sensors_file" default=""/>
<xacro:property name="ap" value="$(arg arm_prefix)"/>
<xacro:property name="sn" value="$(arg robot_sn)"/>
<xacro:property name="prefix"
value="${(ap + '_' + sn + '_') if ap != '' and sn != '' else (ap + sn + '_') if (ap + sn) != '' else ''}"/>
<!-- =========================================================================
官方描述:7 轴本体 + 真实惯性 + 凸包碰撞
=========================================================================
ros2_control:=false —— 官方宏的 ros2_control 分支只能给 mock 或真机插件,
仿真要 gz_ros2_control,所以这里关掉、下面自己写。
add_world_link / create_base_joint 保持 true:
它们会生成一个空的 world link 和 world→base_link 的固定关节,
把机械臂焊在世界上。这是必要的 —— 不焊住,仿真一开始整条臂就砸到地面。
注:ros_gz_sim create 没有 =static 之类选项(实测确认),
所以"固定基座"只能在描述文件里做。
-->
<xacro:include filename="$(find flexiv_description)/urdf/common/rizon_robot.xacro"/>
<xacro:rizon_robot
rizon_type="$(arg rizon_type)"
robot_sn="$(arg robot_sn)"
arm_prefix="$(arg arm_prefix)"
joint_limits_parameters_file="$(find flexiv_description)/config/$(arg rizon_type)/joint_limits.yaml"
kinematics_parameters_file="$(find flexiv_description)/config/$(arg rizon_type)/default_kinematics.yaml"
inertia_parameters_file="$(find flexiv_description)/config/$(arg rizon_type)/inertia_parameters.yaml"
visual_parameters_file="$(find flexiv_description)/config/$(arg rizon_type)/visual_parameters.yaml"
load_gripper="$(arg load_gripper)"
gripper_name="Flexiv-GN01"
load_mounted_ft_sensor="false"
ros2_control="false"
add_world_link="true"
create_base_joint="true"
/>
<!-- =========================================================================
应用层帧:tcp / tool0
=========================================================================
与真机 arm.urdf.xacro 的定义保持一致(都是 flange 上的零偏移固定帧)。
应用层代码硬编码查 tcp / tool0(见 arm_application 的 TF 查询),
仿真里这两个帧必须存在,否则跟踪和柔顺下压会在查 TF 时失败。
tool0 是 tcp 的别名,历史原因两者都在用,别删任何一个 ——
删了会有一部分节点静默查不到帧。
-->
<joint name="${prefix}tcp_joint" type="fixed">
<parent link="${prefix}flange"/>
<child link="${prefix}tcp"/>
</joint>
<link name="${prefix}tcp"/>
<joint name="${prefix}tool0_joint" type="fixed">
<parent link="${prefix}tcp"/>
<child link="${prefix}tool0"/>
</joint>
<link name="${prefix}tool0"/>
<!-- =========================================================================
ros2_control:gz_ros2_control 硬件(同目录)
========================================================================= -->
<xacro:include filename="arm.sim.ros2_control.xacro"/>
<xacro:arm_sim_ros2_control prefix="${prefix}"/>
<!-- =========================================================================
Gazebo 标签:系统插件 + 材质 + 接触参数(同目录)
========================================================================= -->
<xacro:include filename="arm.sim.gazebo.xacro"/>
<xacro:arm_sim_gazebo prefix="${prefix}"
controllers_file="$(arg controllers_file)"/>
<!-- =========================================================================
外挂传感器(相机 + 触觉,同目录)
========================================================================= -->
<xacro:if value="$(arg with_sensors)">
<xacro:include filename="arm.sim.sensors.xacro"/>
<xacro:arm_sim_sensors prefix="${prefix}"
sensors_file="$(arg sensors_file)"/>
</xacro:if>
</robot>