详细讲解 Xacro(XML Macros)和 SDF(Simulation Description Format),这两个在机器人建模与仿真中至关重要的技术。


第一部分:Xacro 详解

1. Xacro 概述

Xacro(XML Macros)是 ROS 提供的 XML 宏语言,用于生成 URDF 文件。它解决了纯 URDF 的代码重复、缺乏参数化等问题,是复杂机器人建模的标准工具。

核心优势:

  • 参数化:使用变量替代硬编码数值
  • 模块化:通过文件包含复用组件
  • 可计算:支持数学表达式
  • 条件逻辑:根据参数生成不同结构
  • 代码复用:宏定义避免重复代码

2. Xacro 语法详解

2.1 属性定义(<xacro:property>

用于定义常量或计算值,可在文档任意位置使用 ${} 引用。

<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="my_robot">

    <!-- 基本数值属性 -->
    <xacro:property name="pi" value="3.14159265359"/>
    <xacro:property name="wheel_radius" value="0.05"/>
    <xacro:property name="wheel_width" value="0.02"/>
    
    <!-- 字符串属性 -->
    <xacro:property name="wheel_material" value="rubber"/>
    
    <!-- 列表/向量属性(通过字符串解析) -->
    <xacro:property name="chassis_size" value="0.5 0.3 0.1"/>
    
    <!-- 属性可在定义后使用 -->
    <link name="wheel">
        <visual>
            <geometry>
                <cylinder radius="${wheel_radius}" length="${wheel_width}"/>
            </geometry>
        </visual>
    </link>
</robot>
2.2 宏定义(<xacro:macro>

定义可复用的模板,支持参数传递。

<!-- 基础宏语法 -->
<xacro:macro name="macro_name" params="param1 param2 param3='default_value'">
    <!-- 宏体,可使用传入的参数 -->
</xacro:macro>

<!-- 调用宏 -->
<xacro:macro_name param1="value1" param2="value2"/>

完整示例:通用轮子宏

<xacro:macro name="wheel" params="prefix side radius width mass xyz rpy">
    <!-- 连杆定义 -->
    <link name="${prefix}_wheel_link">
        <visual>
            <origin xyz="0 0 0" rpy="${pi/2} 0 0"/>
            <geometry>
                <cylinder radius="${radius}" length="${width}"/>
            </geometry>
            <material name="black"/>
        </visual>
        <collision>
            <origin xyz="0 0 0" rpy="${pi/2} 0 0"/>
            <geometry>
                <cylinder radius="${radius}" length="${width}"/>
            </geometry>
        </collision>
        <inertial>
            <origin xyz="0 0 0" rpy="${pi/2} 0 0"/>
            <mass value="${mass}"/>
            <!-- 圆柱体惯性公式:Ixx=Iyy=m(3r²+h²)/12, Izz=mr²/2 -->
            <inertia ixx="${mass*(3*radius*radius + width*width)/12}" 
                     ixy="0" 
                     ixz="0"
                     iyy="${mass*(3*radius*radius + width*width)/12}" 
                     iyz="0"
                     izz="${mass*radius*radius/2}"/>
        </inertial>
    </link>
    
    <!-- 关节定义 -->
    <joint name="${prefix}_wheel_joint" type="continuous">
        <parent link="chassis"/>
        <child link="${prefix}_wheel_link"/>
        <origin xyz="${xyz}" rpy="${rpy}"/>
        <axis xyz="0 0 1"/>
    </joint>
    
    <!-- 传动装置 -->
    <transmission name="${prefix}_wheel_trans">
        <type>transmission_interface/SimpleTransmission</type>
        <joint name="${prefix}_wheel_joint">
            <hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
        </joint>
        <actuator name="${prefix}_wheel_motor">
            <hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
            <mechanicalReduction>1</mechanicalReduction>
        </actuator>
    </transmission>
    
    <!-- Gazebo 属性 -->
    <gazebo reference="${prefix}_wheel_link">
        <mu1>1.0</mu1>
        <mu2>1.0</mu2>
        <material>Gazebo/Black</material>
    </gazebo>
</xacro:macro>

<!-- 调用宏创建四个轮子 -->
<xacro:wheel prefix="front_left" side="left" radius="${wheel_radius}" 
             width="${wheel_width}" mass="0.5" 
             xyz="0.2 0.15 0.05" rpy="0 0 0"/>
             
<xacro:wheel prefix="front_right" side="right" radius="${wheel_radius}" 
             width="${wheel_width}" mass="0.5" 
             xyz="0.2 -0.15 0.05" rpy="0 0 0"/>
             
<xacro:wheel prefix="rear_left" side="left" radius="${wheel_radius}" 
             width="${wheel_width}" mass="0.5" 
             xyz="-0.2 0.15 0.05" rpy="0 0 0"/>
             
<xacro:wheel prefix="rear_right" side="right" radius="${wheel_radius}" 
             width="${wheel_width}" mass="0.5" 
             xyz="-0.2 -0.15 0.05" rpy="0 0 0"/>
2.3 数学表达式

Xacro 支持完整的 Python 数学运算:

<xacro:property name="a" value="2"/>
<xacro:property name="b" value="3"/>

<!-- 基本运算 -->
${a + b}        <!-- 加法: 5 -->
${a - b}        <!-- 减法: -1 -->
${a * b}        <!-- 乘法: 6 -->
${a / b}        <!-- 除法: 0.666... -->
${a // b}       <!-- 整除: 0 -->
${a % b}        <!-- 取模: 2 -->
${a ** b}       <!-- 幂运算: 8 -->

<!-- 三角函数(弧度) -->
${sin(pi/2)}    <!-- 1.0 -->
${cos(0)}       <!-- 1.0 -->
${tan(pi/4)}    <!-- 1.0 -->
${atan2(y, x)}  <!-- 反正切 -->

<!-- 数学函数 -->
${abs(-5)}      <!-- 5 -->
${round(3.7)}   <!-- 4 -->
${floor(3.7)}   <!-- 3 -->
${ceil(3.2)}    <!-- 4 -->
${sqrt(16)}     <!-- 4 -->
${pow(2, 3)}    <!-- 8 -->

<!-- 条件表达式 -->
${a if a > b else b}  <!-- 三元运算符 -->

<!-- 字符串操作 -->
${'prefix_' + link_name}  <!-- 字符串拼接 -->
2.4 条件语句(<xacro:if> / <xacro:unless>

根据条件选择性生成代码:

<xacro:property name="use_lidar" value="true"/>
<xacro:property name="wheel_num" value="4"/>

<!-- if: 条件为真时执行 -->
<xacro:if value="${use_lidar}">
    <xacro:include filename="$(find my_robot)/urdf/lidar.xacro"/>
    <xacro:lidar_sensor parent="chassis" xyz="0 0 0.5"/>
</xacro:if>

<!-- unless: 条件为假时执行(相当于 if not) -->
<xacro:unless value="${wheel_num == 4}">
    <!-- 如果不是四轮,添加支撑轮 -->
    <link name="caster_wheel">
        ...
    </link>
</xacro:unless>

<!-- 复杂条件 -->
<xacro:if value="${wheel_num > 2 and use_lidar}">
    <!-- 多条件判断 -->
</xacro:if>
2.5 文件包含(<xacro:include>

模块化组织代码,提高复用性:

<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="complex_robot">

    <!-- 包含通用材料定义 -->
    <xacro:include filename="$(find my_robot)/urdf/materials.xacro"/>
    
    <!-- 包含通用宏库 -->
    <xacro:include filename="$(find my_robot)/urdf/wheel.xacro"/>
    <xacro:include filename="$(find my_robot)/urdf/arm.xacro"/>
    <xacro:include filename="$(find my_robot)/urdf/sensors.xacro"/>
    
    <!-- 包含参数配置文件 -->
    <xacro:include filename="$(find my_robot)/config/robot_params.yaml"/>
    <!-- 注意:yaml 加载需要 xacro 的特定语法 -->
    
    <!-- 使用包含的宏 -->
    <xacro:wheel prefix="left" .../>
    <xacro:arm dof="6" .../>
    
</robot>

YAML 参数加载(高级):

# config/robot_params.yaml
wheel:
  radius: 0.05
  width: 0.02
  mass: 0.5
  
chassis:
  length: 0.6
  width: 0.4
  height: 0.2
  mass: 5.0
<!-- 在 xacro 中加载 yaml -->
<xacro:property name="yaml_file" value="$(find my_robot)/config/robot_params.yaml"/>
<xacro:property name="props" value="${xacro.load_yaml(yaml_file)}"/>

<!-- 使用 yaml 参数 -->
<xacro:property name="wheel_radius" value="${props['wheel']['radius']}"/>
2.6 块参数(<xacro:insert_block>

传递 XML 块作为参数,实现更灵活的模板:

<!-- 定义接受块参数的宏 -->
<xacro:macro name="sensor_mount" params="name parent *origin">
    <joint name="${name}_joint" type="fixed">
        <xacro:insert_block name="origin"/>
        <parent link="${parent}"/>
        <child link="${name}_link"/>
    </joint>
    <link name="${name}_link">
        <visual>
            <geometry>
                <cylinder radius="0.02" length="0.05"/>
            </geometry>
        </visual>
    </link>
</xacro:macro>

<!-- 调用时传递 origin 块 -->
<xacro:sensor_mount name="camera" parent="chassis">
    <origin xyz="0.3 0 0.1" rpy="0 0 0"/>
</xacro:sensor_mount>
2.7 属性块(<xacro:property> 配合 <xacro:block>
<!-- 定义 XML 块属性 -->
<xacro:property name="default_inertial">
    <inertial>
        <mass value="1"/>
        <inertia ixx="0.01" ixy="0" ixz="0" iyy="0.01" iyz="0" izz="0.01"/>
    </inertial>
</xacro:property>

<!-- 在宏中使用 -->
<xacro:macro name="simple_link" params="name">
    <link name="${name}">
        <xacro:insert_block name="default_inertial"/>
        <visual>
            <geometry>
                <box size="0.1 0.1 0.1"/>
            </geometry>
        </visual>
    </link>
</xacro:macro>

3. Xacro 高级技巧

3.1 递归宏(构建链式结构)
<!-- 递归构建机械臂链 -->
<xacro:macro name="arm_segment" params="prefix parent i total_length">
    <xacro:property name="segment_length" value="${total_length / 3}"/>
    
    <link name="${prefix}_link_${i}">
        <visual>
            <geometry>
                <cylinder radius="0.05" length="${segment_length}"/>
            </geometry>
        </visual>
    </link>
    
    <joint name="${prefix}_joint_${i}" type="revolute">
        <parent link="${parent}"/>
        <child link="${prefix}_link_${i}"/>
        <origin xyz="0 0 ${segment_length}" rpy="0 0 0"/>
        <axis xyz="0 1 0"/>
        <limit lower="-1.57" upper="1.57" effort="10" velocity="1"/>
    </joint>
    
    <!-- 递归调用直到达到指定数量 -->
    <xacro:if value="${i < 3}">
        <xacro:arm_segment prefix="${prefix}" parent="${prefix}_link_${i}" 
                          i="${i+1}" total_length="${total_length}"/>
    </xacro:if>
</xacro:macro>

<!-- 启动递归 -->
<xacro:arm_segment prefix="arm" parent="base_link" i="1" total_length="0.9"/>
3.2 命名空间与作用域
<!-- 局部属性(仅在宏内有效) -->
<xacro:macro name="scoped_example" params="value">
    <xacro:property name="local_var" value="${value * 2}"/>
    <!-- local_var 只在宏定义内可用 -->
    <link name="link_${local_var}"/>
</xacro:macro>

<!-- 全局属性 -->
<xacro:property name="global_var" value="10"/>

4. Xacro 编译与调试

# 基本编译
rosrun xacro xacro robot.xacro > robot.urdf

# 指定参数
rosrun xacro xacro robot.xacro wheel_radius:=0.1 use_lidar:=true > robot.urdf

# 查看展开后的结果(不保存文件)
rosrun xacro xacro robot.xacro

# 调试模式(显示行号)
rosrun xacro xacro --debug robot.xacro

# ROS2 命令
xacro robot.xacro -o robot.urdf
xacro robot.xacro wheel_radius:=0.1 > robot.urdf

第二部分:SDF 详解

1. SDF 概述

SDF(Simulation Description Format)是 Gazebo 仿真器的原生描述格式,比 URDF 更通用、更强大,支持:

  • 完整仿真世界:多机器人、环境、光照、物理属性
  • 闭链机构:并联机器人、四连杆等
  • 丰富传感器:摄像头、激光雷达、IMU、力传感器等
  • 高级物理:软体、流体、电磁等
  • 插件系统:C++ 插件扩展仿真行为

版本演进:

  • SDF 1.4-1.6:Gazebo 经典版
  • SDF 1.7+:Ignition Gazebo(现 Gazebo Sim)

2. SDF 基本结构

<?xml version="1.0"?>
<sdf version="1.9">
    <!-- 可以是 world 或 model -->
    
    <!-- 世界定义 -->
    <world name="default">
        <!-- 物理引擎、场景、光照、模型实例等 -->
    </world>
    
    <!-- 或模型定义 -->
    <model name="robot">
        <!-- 连杆、关节、传感器等 -->
    </model>
</sdf>

3. SDF Model 详解

3.1 基本模型结构
<?xml version="1.0"?>
<sdf version="1.9">
    <model name="mobile_robot" canonical_link="chassis">
        
        <!-- 静态模型(不参与动力学) -->
        <static>false</static>
        
        <!-- 自碰撞开关 -->
        <self_collide>true</self_collide>
        
        <!-- 允许自动禁用(性能优化) -->
        <allow_auto_disable>true</allow_auto_disable>
        
        <!-- 位姿(相对于世界或父模型) -->
        <pose>0 0 0.5 0 0 0</pose>
        
        <!-- ========== 连杆 ========== -->
        <link name="chassis">
            <pose>0 0 0 0 0 0</pose>  <!-- 相对于模型坐标系 -->
            <inertial>
                <pose>0 0 0.1 0 0 0</pose>
                <mass>5.0</mass>
                <inertia>
                    <ixx>0.208</ixx>
                    <ixy>0</ixy>
                    <ixz>0</ixz>
                    <iyy>0.342</iyy>
                    <iyz>0</iyz>
                    <izz>0.458</izz>
                </inertia>
            </inertial>
            
            <!-- 视觉 -->
            <visual name="chassis_visual">
                <pose>0 0 0 0 0 0</pose>
                <geometry>
                    <box>
                        <size>0.6 0.4 0.2</size>
                    </box>
                </geometry>
                <material>
                    <ambient>0.5 0.5 0.5 1</ambient>
                    <diffuse>0.5 0.5 0.5 1</diffuse>
                    <specular>0.1 0.1 0.1 1</specular>
                </material>
            </visual>
            
            <!-- 碰撞 -->
            <collision name="chassis_collision">
                <pose>0 0 0 0 0 0</pose>
                <geometry>
                    <box>
                        <size>0.6 0.4 0.2</size>
                    </box>
                </geometry>
                <!-- 表面属性 -->
                <surface>
                    <friction>
                        <ode>
                            <mu>0.5</mu>
                            <mu2>0.5</mu2>
                            <slip1>0</slip1>
                            <slip2>0</slip2>
                        </ode>
                    </friction>
                    <contact>
                        <ode>
                            <soft_cfm>0</soft_cfm>
                            <soft_erp>0.2</soft_erp>
                            <kp>100000</kp>
                            <kd>1</kd>
                        </ode>
                    </contact>
                </surface>
            </collision>
            
            <!-- 传感器直接附着在连杆上 -->
            <sensor name="imu_sensor" type="imu">
                <always_on>true</always_on>
                <update_rate>100</update_rate>
                <visualize>true</visualize>
                <topic>imu</topic>
                <imu>
                    <angular_velocity>
                        <x>
                            <noise type="gaussian">
                                <mean>0</mean>
                                <stddev>0.01</stddev>
                            </noise>
                        </x>
                        <y>
                            <noise type="gaussian">
                                <mean>0</mean>
                                <stddev>0.01</stddev>
                            </noise>
                        </y>
                        <z>
                            <noise type="gaussian">
                                <mean>0</mean>
                                <stddev>0.01</stddev>
                            </noise>
                        </z>
                    </angular_velocity>
                </imu>
            </sensor>
        </link>
        
        <!-- ========== 关节 ========== -->
        <joint name="left_wheel_joint" type="revolute">
            <parent>chassis</parent>
            <child>left_wheel</child>
            <pose>0.2 0.25 0 0 1.5707 0</pose>  <!-- 关节坐标系 -->
            <axis>
                <xyz>0 0 1</xyz>
                <limit>
                    <lower>-1e16</lower>
                    <upper>1e16</upper>
                    <effort>10</effort>
                    <velocity>10</velocity>
                </limit>
                <dynamics>
                    <damping>0.1</damping>
                    <friction>0.1</friction>
                </dynamics>
            </axis>
            <physics>
                <ode>
                    <cfm_damping>true</cfm_damping>
                    <implicit_spring_damper>true</implicit_spring_damper>
                </ode>
            </physics>
        </joint>
        
        <!-- 插件 -->
        <plugin name="diff_drive" filename="libDiffDrivePlugin.so">
            <left_joint>left_wheel_joint</left_joint>
            <right_joint>right_wheel_joint</right_joint>
            <wheel_separation>0.5</wheel_separation>
            <wheel_radius>0.1</wheel_radius>
            <topic>cmd_vel</topic>
        </plugin>
        
    </model>
</sdf>
3.2 SDF 关节类型
类型 说明 自由度
revolute 旋转关节(有限位) 1
prismatic 滑动关节 1
ball 球关节(3D旋转) 3
universal 万向节(2轴旋转) 2
screw 螺旋关节(旋转+平移耦合) 1
gearbox 齿轮传动 1
fixed 固定连接 0
continuous 连续旋转 1

闭链结构示例(四连杆):

<link name="link1">...</link>
<link name="link2">...</link>
<link name="link3">...</link>
<link name="link4">...</link>

<!-- 开链部分 -->
<joint name="joint1" type="revolute">
    <parent>link1</parent>
    <child>link2</child>
</joint>
<joint name="joint2" type="revolute">
    <parent>link2</parent>
    <child>link3</child>
</joint>

<!-- 闭链:link3 通过 link4 连接回 link1 -->
<joint name="joint3" type="revolute">
    <parent>link3</parent>
    <child>link4</child>
</joint>
<joint name="joint4" type="revolute">
    <parent>link4</parent>
    <child>link1</child>  <!-- 闭环! -->
</joint>

4. SDF World 详解

<?xml version="1.0"?>
<sdf version="1.9">
    <world name="my_world">
        
        <!-- 物理引擎配置 -->
        <physics name="default_physics" default="true" type="ode">
            <max_step_size>0.001</max_step_size>
            <real_time_factor>1</real_time_factor>
            <real_time_update_rate>1000</real_time_update_rate>
            <ode>
                <solver>
                    <type>quick</type>
                    <iters>50</iters>
                    <sor>1.3</sor>
                </solver>
                <constraints>
                    <cfm>0</cfm>
                    <erp>0.2</erp>
                    <contact_max_correcting_vel>100</contact_max_correcting_vel>
                    <contact_surface_layer>0.001</contact_surface_layer>
                </constraints>
            </ode>
        </physics>
        
        <!-- 场景属性 -->
        <scene>
            <ambient>0.4 0.4 0.4 1</ambient>
            <background>0.7 0.7 0.7 1</background>
            <shadows>true</shadows>
            <grid>false</grid>
        </scene>
        
        <!-- 光照 -->
        <light name="sun" type="directional">
            <pose>0 0 10 0 0 0</pose>
            <cast_shadows>true</cast_shadows>
            <diffuse>0.8 0.8 0.8 1</diffuse>
            <specular>0.2 0.2 0.2 1</specular>
            <attenuation>
                <range>1000</range>
                <constant>0.9</constant>
                <linear>0.01</linear>
                <quadratic>0.001</quadratic>
            </attenuation>
            <direction>-0.5 0.1 -0.9</direction>
        </light>
        
        <!-- 地面平面 -->
        <model name="ground_plane">
            <static>true</static>
            <link name="link">
                <collision name="collision">
                    <geometry>
                        <plane>
                            <normal>0 0 1</normal>
                            <size>100 100</size>
                        </plane>
                    </geometry>
                    <surface>
                        <friction>
                            <ode>
                                <mu>100</mu>
                                <mu2>50</mu2>
                            </ode>
                        </friction>
                    </surface>
                </collision>
                <visual name="visual">
                    <geometry>
                        <plane>
                            <normal>0 0 1</normal>
                            <size>100 100</size>
                        </plane>
                    </geometry>
                    <material>
                        <ambient>0.8 0.8 0.8 1</ambient>
                        <diffuse>0.8 0.8 0.8 1</diffuse>
                        <specular>0.8 0.8 0.8 1</specular>
                    </material>
                </visual>
            </link>
        </model>
        
        <!-- 包含外部模型 -->
        <include>
            <uri>model://sun</uri>
        </include>
        
        <include>
            <uri>model://ground_plane</uri>
        </include>
        
        <!-- 实例化自定义模型 -->
        <include>
            <uri>model://my_robot</uri>
            <name>robot1</name>
            <pose>0 0 0.5 0 0 0</pose>
        </include>
        
        <include>
            <uri>model://my_robot</uri>
            <name>robot2</name>
            <pose>2 0 0.5 0 0 0</pose>
        </include>
        
        <!-- 直接内联模型定义 -->
        <model name="box_obstacle">
            <pose>1 1 0.5 0 0 0</pose>
            <static>true</static>
            <link name="link">
                <collision name="collision">
                    <geometry>
                        <box>
                            <size>1 1 1</size>
                        </box>
                    </geometry>
                </collision>
                <visual name="visual">
                    <geometry>
                        <box>
                            <size>1 1 1</size>
                        </box>
                    </geometry>
                    <material>
                        <ambient>1 0 0 1</ambient>
                    </material>
                </visual>
            </link>
        </model>
        
        <!-- 插件 -->
        <plugin name="world_plugin" filename="libWorldPlugin.so"/>
        
    </world>
</sdf>

5. SDF 传感器详解

5.1 摄像头(Camera)
<sensor name="camera" type="camera">
    <pose>0.1 0 0.5 0 0 0</pose>
    <camera>
        <horizontal_fov>1.047</horizontal_fov>  <!-- 60度 -->
        <image>
            <width>640</width>
            <height>480</height>
            <format>R8G8B8</format>
        </image>
        <clip>
            <near>0.1</near>
            <far>100</far>
        </clip>
        <distortion>
            <k1>0.0</k1>
            <k2>0.0</k2>
            <k3>0.0</k3>
            <p1>0.0</p1>
            <p2>0.0</p2>
            <center>0.5 0.5</center>
        </distortion>
        <noise>
            <type>gaussian</type>
            <mean>0.0</mean>
            <stddev>0.007</stddev>
        </noise>
    </camera>
    <always_on>true</always_on>
    <update_rate>30</update_rate>
    <visualize>true</visualize>
    <topic>camera/image</topic>
</sensor>
5.2 激光雷达(GPU Ray / Ray)
<sensor name="lidar" type="gpu_ray">  <!-- 或 ray 用于 CPU 版本 -->
    <pose>0 0 0.5 0 0 0</pose>
    <ray>
        <scan>
            <horizontal>
                <samples>640</samples>
                <resolution>1</resolution>
                <min_angle>-1.396263</min_angle>  <!-- -80度 -->
                <max_angle>1.396263</max_angle>   <!-- +80度 -->
            </horizontal>
            <vertical>
                <samples>16</samples>
                <resolution>1</resolution>
                <min_angle>-0.261799</min_angle>
                <max_angle>0.261799</max_angle>
            </vertical>
        </scan>
        <range>
            <min>0.08</min>
            <max>10.0</max>
            <resolution>0.01</resolution>
        </range>
        <noise>
            <type>gaussian</type>
            <mean>0.0</mean>
            <stddev>0.01</stddev>
        </noise>
    </ray>
    <always_on>true</always_on>
    <update_rate>20</update_rate>
    <visualize>true</visualize>
    <topic>scan</topic>
</sensor>
5.3 深度相机(Depth Camera)
<sensor name="depth_camera" type="depth_camera">
    <camera>
        <horizontal_fov>1.047</horizontal_fov>
        <image>
            <width>640</width>
            <height>480</height>
        </image>
        <clip>
            <near>0.1</near>
            <far>10</far>
        </clip>
    </camera>
    <always_on>true</always_on>
    <update_rate>30</update_rate>
    <visualize>true</visualize>
    <topic>depth_camera</topic>
</sensor>
5.4 力/力矩传感器(Force Torque)
<sensor name="ft_sensor" type="force_torque">
    <pose>0 0 0 0 0 0</pose>
    <force_torque>
        <frame>sensor</frame>  <!-- 或 child/parent -->
        <measure_direction>child_to_parent</measure_direction>
    </force_torque>
    <always_on>true</always_on>
    <update_rate>100</update_rate>
    <visualize>true</visualize>
    <topic>ft_sensor</topic>
</sensor>

6. SDF 插件系统

插件是 SDF 的强大扩展机制,用 C++ 编写:

<!-- 模型插件 -->
<plugin name="model_plugin" filename="libModelPlugin.so">
    <parameter1>value1</parameter1>
    <parameter2>value2</parameter2>
</plugin>

<!-- 世界插件 -->
<world>
    <plugin name="world_plugin" filename="libWorldPlugin.so"/>
</world>

<!-- 传感器插件 -->
<sensor>
    <plugin name="sensor_plugin" filename="libSensorPlugin.so"/>
</sensor>

常用内置插件:

插件 功能 参数
DiffDrivePlugin 差速驱动 左右轮关节、轮距、轮径
SkidSteerDrivePlugin skid-steer 驱动 四个轮关节
LiftDragPlugin 升力/阻力(无人机) 表面面积、空气密度
BuoyancyPlugin 浮力 流体密度、重力
BatteryPlugin 电池仿真 容量、电压、消费功率

7. URDF 与 SDF 转换

# URDF → SDF(Gazebo 经典版)
gz sdf -p robot.urdf > robot.sdf

# 使用 ROS 包路径
gz sdf -p $(rospack find my_robot)/urdf/robot.urdf > robot.sdf

# SDF → URDF(有限支持,闭链会丢失)
# 通常需要手动转换或使用特定工具

转换注意事项:

特性 URDF→SDF SDF→URDF
闭链结构 不支持(报错) 丢失闭环
传动装置 转为 joint 属性 需手动添加
Gazebo 标签 直接转换 保留
传感器 支持 支持
插件 支持 支持
多机器人 包装为 model 仅提取单个 model

8. 完整示例:SDF 移动机器人

<?xml version="1.0"?>
<sdf version="1.9">
    <model name="diff_drive_robot">
        
        <static>false</static>
        <self_collide>true</self_collide>
        
        <!-- 底盘 -->
        <link name="base_link">
            <pose>0 0 0.1 0 0 0</pose>
            <inertial>
                <mass>2.0</mass>
                <inertia>
                    <ixx>0.02167</ixx>
                    <ixy>0</ixy>
                    <ixz>0</ixz>
                    <iyy>0.02167</iyy>
                    <iyz>0</iyz>
                    <izz>0.04</izz>
                </inertia>
            </inertial>
            <visual name="base_visual">
                <geometry>
                    <box>
                        <size>0.4 0.3 0.1</size>
                    </box>
                </geometry>
                <material>
                    <ambient>0.2 0.6 0.8 1</ambient>
                </material>
            </visual>
            <collision name="base_collision">
                <geometry>
                    <box>
                        <size>0.4 0.3 0.1</size>
                    </box>
                </geometry>
            </collision>
        </link>
        
        <!-- 左轮 -->
        <link name="left_wheel">
            <pose>0 0.2 0.05 0 1.5707 0</pose>
            <inertial>
                <mass>0.5</mass>
                <inertia>
                    <ixx>0.00079</ixx>
                    <ixy>0</ixy>
                    <ixz>0</ixz>
                    <iyy>0.00079</iyy>
                    <iyz>0</iyz>
                    <izz>0.00125</izz>
                </inertia>
            </inertial>
            <visual name="left_wheel_visual">
                <geometry>
                    <cylinder>
                        <radius>0.05</radius>
                        <length>0.04</length>
                    </cylinder>
                </geometry>
                <material>
                    <ambient>0.1 0.1 0.1 1</ambient>
                </material>
            </visual>
            <collision name="left_wheel_collision">
                <geometry>
                    <cylinder>
                        <radius>0.05</radius>
                        <length>0.04</length>
                    </cylinder>
                </geometry>
                <surface>
                    <friction>
                        <ode>
                            <mu>1.0</mu>
                            <mu2>1.0</mu2>
                            <slip1>0.01</slip1>
                            <slip2>0.01</slip2>
                        </ode>
                    </friction>
                </surface>
            </collision>
        </link>
        
        <!-- 右轮(类似定义) -->
        <link name="right_wheel">
            <pose>0 -0.2 0.05 0 1.5707 0</pose>
            <!-- 与左轮对称... -->
        </link>
        
        <!-- 万向轮(支撑) -->
        <link name="caster">
            <pose>-0.15 0 0.025 0 0 0</pose>
            <visual name="caster_visual">
                <geometry>
                    <sphere>
                        <radius>0.025</radius>
                    </sphere>
                </geometry>
            </visual>
            <collision name="caster_collision">
                <geometry>
                    <sphere>
                        <radius>0.025</radius>
                    </sphere>
                </geometry>
            </collision>
        </link>
        
        <!-- 左轮关节 -->
        <joint name="left_wheel_joint" type="revolute">
            <parent>base_link</parent>
            <child>left_wheel</child>
            <pose>0 0.2 0.05 0 0 0</pose>
            <axis>
                <xyz>0 0 1</xyz>
                <limit>
                    <lower>-1e16</lower>
                    <upper>1e16</upper>
                </limit>
            </axis>
        </joint>
        
        <!-- 右轮关节 -->
        <joint name="right_wheel_joint" type="revolute">
            <parent>base_link</parent>
            <child>right_wheel</child>
            <pose>0 -0.2 0.05 0 0 0</pose>
            <axis>
                <xyz>0 0 1</xyz>
                <limit>
                    <lower>-1e16</lower>
                    <upper>1e16</upper>
                </limit>
            </axis>
        </joint>
        
        <!-- 万向轮关节 -->
        <joint name="caster_joint" type="ball">
            <parent>base_link</parent>
            <child>caster</child>
            <pose>-0.15 0 0.025 0 0 0</pose>
        </joint>
        
        <!-- 差速驱动插件 -->
        <plugin name="diff_drive" filename="libDiffDrivePlugin.so">
            <left_joint>left_wheel_joint</left_joint>
            <right_joint>right_wheel_joint</right_joint>
            <wheel_separation>0.4</wheel_separation>
            <wheel_radius>0.05</wheel_radius>
            <odom_topic>odom</odom_topic>
            <cmd_vel_topic>cmd_vel</cmd_vel_topic>
            <publish_odom>true</publish_odom>
            <publish_odom_tf>true</publish_odom_tf>
            <update_rate>50</update_rate>
        </plugin>
        
    </model>
</sdf>

9. 总结对比

特性 Xacro SDF
本质 URDF 的宏预处理器 完整的仿真描述格式
输出 URDF/XML SDF/XML
主要用途 ROS 机器人描述 Gazebo 仿真
闭链支持 否(继承 URDF 限制)
多机器人
世界描述
参数化 是(强大) 有限(1.7+ 支持一些)
传感器定义 基础 丰富
物理属性 基础 详细
最佳实践 ROS 控制 + Gazebo 仿真 纯仿真环境、复杂机构

协作流程:

Xacro(参数化建模)→ URDF(ROS 标准)→ SDF(Gazebo 仿真)
         ↓
    直接用于:MoveIt、RViz、ros_control

掌握 Xacro 和 SDF 是进行复杂机器人系统开发与高级仿真的关键技能。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐