4、XACRO优化建模:XACRO语法、宏定义与参数化、模块化机器人模型、将XACRO转换为URDF

好,咱们继续。上一章我们用URDF搭了个机器人,但说实话,写起来挺累的。尤其是当你的机器人有6个完全一样的轮子,或者有8个相同的关节时,复制粘贴能让你怀疑人生。这时候,XACRO就来了。

XACRO是什么?说白了,它就是URDF的升级版——一个宏预处理工具。你写更少的代码,做更多的事。我个人习惯,但凡模型超过3个关节,我肯定用XACRO,绝不手写URDF。

核心思想: XACRO = URDF + 宏 + 数学运算 + 参数化。它本身不是一种新语言,而是对URDF的扩展,最终还是要转回URDF才能被Gazebo识别。

4.1 XACRO语法基础

XACRO的语法其实很简单。它基于XML,只是多了几个特殊的标签。你只要记住三个核心标签就够了:

  • <xacro:property> —— 定义常量/参数
  • <xacro:macro> —— 定义宏
  • <xacro:include> —— 包含其他文件

先看一个最简单的例子。假设你有一个轮子,半径是0.1米。在URDF里你得写:

<link name="wheel">
  <visual>
    <geometry>
      <cylinder radius="0.1" length="0.05"/>
    </geometry>
  </visual>
</link>

用XACRO,你可以这样:

<xacro:property name="wheel_radius" value="0.1"/>

<link name="wheel">
  <visual>
    <geometry>
      <cylinder radius="${wheel_radius}" length="0.05"/>
    </geometry>
  </visual>
</link>

看到了吗?${wheel_radius} 这个语法就是引用属性。以后想改轮子大小,改一个地方就行。我在项目中遇到过,客户突然说轮子要加大20%,如果是纯URDF我得改6个地方,用XACRO改一行就完事。

小技巧: XACRO支持数学运算!比如 ${wheel_radius * 2} 或者 ${2 * PI * 0.1}。这在计算关节位置时特别有用。

4.2 宏定义与参数化

宏才是XACRO的真正杀手锏。你想想看,一个四轮机器人,四个轮子结构完全一样,只是位置不同。难道要写四遍?

不,定义一个宏,然后调用四次就行了。

<xacro:macro name="wheel" params="name prefix reflect">
  <link name="${prefix}_${name}">
    <visual>
      <geometry>
        <cylinder radius="0.1" length="0.05"/>
      </geometry>
      <origin xyz="0 0 0" rpy="0 0 0"/>
    </visual>
  </link>

  <joint name="${prefix}_${name}_joint" type="continuous">
    <parent link="base_link"/>
    <child link="${prefix}_${name}"/>
    <origin xyz="${reflect * 0.2} ${reflect * 0.15} 0" rpy="0 0 0"/>
  </joint>
</xacro:macro>

<!-- 调用宏 -->
<xacro:wheel name="front_left" prefix="robot" reflect="1"/>
<xacro:wheel name="front_right" prefix="robot" reflect="-1"/>

这里 params 定义了宏的参数。调用时传入不同的值,就能生成不同的轮子。注意那个 reflect 参数,我用了正负1来控制左右对称。这个技巧是我从一次做六足机器人时学到的,省了我大量重复计算。

注意: 宏的名字不能和URDF里的标签名冲突。比如你不能定义一个名为"link"的宏,那会出大问题。我曾经犯过这个错,排查了半天才发现是命名冲突。

4.3 模块化机器人模型

当你的机器人越来越复杂,把所有东西塞进一个文件就变得很蠢。我的做法是:按功能模块拆分文件。

比如一个带机械臂的移动机器人,我会拆成这样:

  • robot.urdf.xacro —— 主文件,负责组装
  • chassis.urdf.xacro —— 底盘模块
  • arm.urdf.xacro —— 机械臂模块
  • sensors.urdf.xacro —— 传感器模块(激光雷达、摄像头等)

主文件里用 <xacro:include> 把它们组合起来:

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

  <xacro:include filename="$(find my_robot_description)/urdf/chassis.urdf.xacro"/>
  <xacro:include filename="$(find my_robot_description)/urdf/arm.urdf.xacro"/>
  <xacro:include filename="$(find my_robot_description)/urdf/sensors.urdf.xacro"/>

  <!-- 组装各模块 -->
  <xacro:chassis/>
  <xacro:arm parent="base_link" x="0.1" y="0" z="0.2"/>
  <xacro:lidar parent="base_link" z="0.3"/>

</robot>

这样做的好处太多了。团队协作时,每个人负责一个模块,互不干扰。而且想换机械臂?直接换一个 arm.urdf.xacro 文件就行,主文件几乎不用动。

我参与过一个项目,机器人有3种不同配置(标准版、加强版、轻量版)。我们就是靠模块化XACRO,通过不同的主文件组合出3种型号,代码复用率超过80%。

4.4 将XACRO转换为URDF

Gazebo不认识XACRO,它只认URDF。所以最后一步,必须把XACRO转成URDF。ROS里提供了现成的命令行工具:

# 方法一:使用 xacro 命令
xacro robot.urdf.xacro > robot.urdf

# 方法二:在 launch 文件中自动转换
<param name="robot_description" 
       command="$(find xacro)/xacro $(find my_robot_description)/urdf/robot.urdf.xacro"/>

第二种方法更常用。你在launch文件里直接指定xacro文件,ROS启动时会自动调用xacro工具,生成URDF并加载到参数服务器。这样你永远不需要手动维护一个URDF文件。

验证技巧: 转换后可以用 check_urdf robot.urdf 检查语法。我每次转换完都会跑一遍这个命令,能发现很多低级错误,比如缺少父链接之类的。

嗯,这里还要注意一点。如果你的xacro文件里用了 $(find ...) 这种路径,那必须在ROS环境下运行xacro命令。否则它找不到包路径,会报错。

知识体系总览

为了让你更直观地理解XACRO的整个工作流程,我画了张图:

XACRO优化建模知识体系 宏定义模块 wheel_macro.xacro arm_macro.xacro 参数化属性 wheel_radius=0.1 body_length=0.5 模块化文件 chassis.urdf.xacro sensors.urdf.xacro 主文件 robot.urdf.xacro 使用 include 和 macro 调用组装 xacro 命令转换 xacro robot.urdf.xacro > robot.urdf 最终 URDF 文件

这张图把整个流程串起来了。左边是三个输入源,中间是主文件负责组装,然后通过xacro命令转换成最终的URDF。说白了,XACRO就是一个代码生成器,你给它模板和参数,它给你吐出完整的URDF。

总结一下我的经验: 用XACRO建模,前期多花10分钟设计宏和模块,后期能省下10小时。尤其是当你的机器人需要迭代修改时,XACRO的价值会成倍放大。别偷懒,从一开始就用XACRO。

可可精品资料库