Files
ros_contorl/devices/sim/gazebo/urdf/arm.sim.urdf.xacro
T
arm_dev 25635e21f8 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>
2026-09-23 14:19:39 +08:00

153 lines
8.3 KiB
XML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?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>