【ROS】—— 机器人系统仿真 —URDF优化_xacro (十四)

时间:2023-01-14 08:52:39

前言

????本系列将依托赵虚左老师的ROS课程,写下自己的一些心得与笔记。
????课程链接:https://www.bilibili.com/video/BV1Ci4y1L7ZZ
????讲义链接:http://www.autolabor.com.cn/book/ROSTutorials/index.html
???? 文章可能存在疏漏的地方,恳请大家指出。

1. URDF优化_xacro

前面 URDF 文件构建机器人模型的过程中,存在若干问题。

  • 问题1:设计易出错。

  • 问题2:代码复用性问题。

如果在编程语言中,可以通过变量结合函数直接解决上述问题,在 ROS 中,已经给出了类似编程的优化方案,称之为:Xacro

概念
Xacro 是 XML Macros 的缩写,Xacro 是一种 XML 宏语言,是可编程的 XML。

原理
Xacro 可以声明变量,可以通过数学运算求解,使用流程控制控制执行顺序,还可以通过类似函数的实现,封装固定的逻辑,将逻辑中需要的可变的数据以参数的方式暴露出去,从而提高代码复用率以及程序的安全性。

作用
较之于纯粹的 URDF 实现,可以编写更安全、精简、易读性更强的机器人模型文件,且可以提高编写效率。

2. Xacro_快速体验

需求描述:

使用xacro优化上一节案例中驱动轮实现,需要使用变量封装底盘的半径、高度,使用数学公式动态计算底盘的关节点坐标,使用 Xacro 宏封装*重复的代码并调用宏创建两个*(注意: 在此,演示 Xacro 的基本使用,不必要生成合法的 URDF )。

编写 Xacro 文件,以变量的方式封装属性(常量半径、高度、车轮半径…),以函数的方式封装重复实现(车轮的添加)。

<robot name="mycar" xmlns:xacro="http://wiki.ros.org/xacro">
    <!-- 属性封装 -->
    <xacro:property name="wheel_radius" value="0.0325" />
    <xacro:property name="wheel_length" value="0.0015" />
    <xacro:property name="PI" value="3.1415927" />
    <xacro:property name="base_link_length" value="0.08" />
    <xacro:property name="lidi_space" value="0.015" />

    <!-- 宏 -->
    <xacro:macro name="wheel_func" params="wheel_name flag" >
        <link name="${wheel_name}_wheel">
            <visual>
                <geometry>
                    <cylinder radius="${wheel_radius}" length="${wheel_length}" />
                </geometry>

                <origin xyz="0 0 0" rpy="${PI / 2} 0 0" />

                <material name="wheel_color">
                    <color rgba="0 0 0 0.3" />
                </material>
            </visual>
        </link>

        <!-- 3-2.joint -->
        <joint name="${wheel_name}2link" type="continuous">
            <parent link="base_link"  />
            <child link="${wheel_name}_wheel" />
            <!-- 
                x 无偏移
                y 车体半径
                z z= 车体高度 / 2 + 离地间距 - 车轮半径

            -->
            <origin xyz="0 ${0.1 * flag} ${(base_link_length / 2 + lidi_space - wheel_radius) * -1}" rpy="0 0 0" />
            <axis xyz="0 1 0" />
        </joint>

    </xacro:macro>
    <xacro:wheel_func wheel_name="left" flag="1" />
    <xacro:wheel_func wheel_name="right" flag="-1" />
</robot>

执行:rosrun xacro xacro xxx.xacro > xxx.urdf, 会将 xacro 文件解析为 urdf 文件,内容如下:

<?xml version="1.0" ?>
<!-- =================================================================================== -->
<!-- |    This document was autogenerated by xacro from test.xacro                     | -->
<!-- |    EDITING THIS FILE BY HAND IS NOT RECOMMENDED                                 | -->
<!-- =================================================================================== -->
<robot name="mycar">
  <link name="left_wheel">
    <visual>
      <geometry>
        <cylinder length="0.0015" radius="0.0325"/>
      </geometry>
      <origin rpy="1.57079635 0 0" xyz="0 0 0"/>
      <material name="wheel_color">
        <color rgba="0 0 0 0.3"/>
      </material>
    </visual>
  </link>
  <!-- 3-2.joint -->
  <joint name="left2link" type="continuous">
    <parent link="base_link"/>
    <child link="left_wheel"/>
    <!-- 
                x 无偏移
                y 车体半径
                z z= 车体高度 / 2 + 离地间距 - 车轮半径

            -->
    <origin rpy="0 0 0" xyz="0 0.1 -0.0225"/>
    <axis xyz="0 1 0"/>
  </joint>
  <link name="right_wheel">
    <visual>
      <geometry>
        <cylinder length="0.0015" radius="0.0325"/>
      </geometry>
      <origin rpy="1.57079635 0 0" xyz="0 0 0"/>
      <material name="wheel_color">
        <color rgba="0 0 0 0.3"/>
      </material>
    </visual>
  </link>
  <!-- 3-2.joint -->
  <joint name="right2link" type="continuous">
    <parent link="base_link"/>
    <child link="right_wheel"/>
    <!-- 
                x 无偏移
                y 车体半径
                z z= 车体高度 / 2 + 离地间距 - 车轮半径

            -->
    <origin rpy="0 0 0" xyz="0 -0.1 -0.0225"/>
    <axis xyz="0 1 0"/>
  </joint>
</robot>

3. Xacro_语法详解

xacro 提供了可编程接口,类似于计算机语言,包括变量声明调用、函数声明与调用等语法实现。在使用 xacro 生成 urdf 时,根标签robot中必须包含命名空间声明:xmlns:xacro="http://wiki.ros.org/xacro"

3.1 属性与算数运算

用于封装 URDF 中的一些字段,比如: PAI 值,小车的尺寸,*半径 …
格式

    <xacro:property name="wheel_radius" value="0.0325" />
    <xacro:property name="wheel_length" value="0.0015" />
    <xacro:property name="PI" value="3.1415927" />
    <xacro:property name="base_link_length" value="0.08" />
    <xacro:property name="lidi_space" value="0.015" />

调用

         <cylinder radius="${wheel_radius}" length="${wheel_length}" />
         <origin xyz="0 0 0" rpy="${PI / 2} 0 0" />
         <origin xyz="0 ${0.1 * flag} ${(base_link_length / 2 + lidi_space - wheel_radius) * -1}" rpy="0 0 0" />

3.2 宏

类似于函数实现,提高代码复用率,优化代码结构,提高安全性
定义

    <xacro:macro name="wheel_func" params="wheel_name flag" >
    .....
    .....
    </xacro:macro>
    <!-- 宏 -->
    <xacro:macro name="wheel_func" params="wheel_name flag" >
        <link name="${wheel_name}_wheel">
            <visual>
                <geometry>
                    <cylinder radius="${wheel_radius}" length="${wheel_length}" />
                </geometry>

                <origin xyz="0 0 0" rpy="${PI / 2} 0 0" />

                <material name="wheel_color">
                    <color rgba="0 0 0 0.3" />
                </material>
            </visual>
        </link>

        <!-- 3-2.joint -->
        <joint name="${wheel_name}2link" type="continuous">
            <parent link="base_link"  />
            <child link="${wheel_name}_wheel" />
            <!-- 
                x 无偏移
                y 车体半径
                z z= 车体高度 / 2 + 离地间距 - 车轮半径

            -->
            <origin xyz="0 ${0.1 * flag} ${(base_link_length / 2 + lidi_space - wheel_radius) * -1}" rpy="0 0 0" />
            <axis xyz="0 1 0" />
        </joint>

    </xacro:macro>

调用

    <xacro:宏名称 参数1=xxx 参数2=xxx/>
    <xacro:wheel_func wheel_name="left" flag="1" />
    <xacro:wheel_func wheel_name="right" flag="-1" />

3.3 文件包含

机器人由多部件组成,不同部件可能封装为单独的 xacro 文件,最后再将不同的文件集成,组合为完整机器人,可以使用文件包含实现

文件包含

<robot name="xxx" xmlns:xacro="http://wiki.ros.org/xacro">
      <xacro:include filename="my_base.xacro" />
      <xacro:include filename="my_camera.xacro" />
      <xacro:include filename="my_laser.xacro" />
      ....
</robot>

4. Xacro_完整使用流程示例

4.1 编写 Xacro 文件

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

    <xacro:property name="footprint_radius" value="0.001"/>

    <link name = "base_footprint">
        <visual>
            <geometry>
                <sphere radius="${footprint_radius}"/>
            </geometry>
        </visual>
    </link> 

    <xacro:property name="base_length" value="0.08"/>
    <xacro:property name="base_radius" value="0.1"/>
    <xacro:property name="distance_ground" value="0.015"/>
    <xacro:property name="base_joint_z" value="${distance_ground+base_length / 2}"/>

    <link name = "base_link">
        <visual>
            <geometry>
                <cylinder radius = "0.1" length="0.08"/>
            </geometry>
            <origin xyz = "0 0 0" rpy="0 0 0"/>
            <material name="robot_color">
                <color rgba="0.2 0.2 0.2 1"/>
            </material>
        </visual>
    </link> 

    <joint name="baselink2basefootprint" type="fixed">
        <parent link = "base_footprint"/>
        <child link = "base_link" />
        <origin xyz="0 0 ${base_joint_z}" rpy="0 0 0" />
        <axis xyz="0 0 0" />
    </joint>

</robot>

4.2 集成launch文件

方式1:先将 xacro 文件转换出 urdf 文件,然后集成
先将 xacro 文件解析成 urdf 文件:rosrun xacro xacro xxx.xacro > xxx.urdf然后再按照之前的集成方式直接整合 launch 文件

方式2:在 launch 文件中直接加载 xacro(建议使用)

<launch> 
    <param name="robot_description" command="$(find xacro)/xacro $(find  urdf01_rviz)/urdf/xacro/demo06_base_footprint.urdf.xacro" />

    <!--启动rviz -->
    <node pkg = "rviz" type = "rviz" name="rviz" args="-d $(find urdf01_rviz)/config/demo01.rviz"/>
    <!-- 添加关节状态发布节点 -->
    <node pkg="joint_state_publisher" type="joint_state_publisher" name="joint_state_publisher" />
    <!-- 添加机器人状态发布节点 -->
    <node pkg="robot_state_publisher" type="robot_state_publisher" name="robot_state_publisher" />
    <!-- 可选:用于控制关节运动的节点 -->
    <node pkg="joint_state_publisher_gui" type="joint_state_publisher_gui" name="joint_state_publisher_gui" />

</launch>

完整效果
【ROS】—— 机器人系统仿真 —URDF优化_xacro (十四)demo06_base_footprint.urdf.xacro

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

    <xacro:property name="footprint_radius" value="0.001"/>

    <material name="black">
        <color rgba="0 0 0 1"/>
    </material>

    <link name = "base_footprint">
        <visual>
            <geometry>
                <sphere radius="${footprint_radius}"/>
            </geometry>
        </visual>
    </link> 

    <xacro:property name="base_length" value="0.08"/>
    <xacro:property name="base_radius" value="0.1"/>
    <xacro:property name="distance_ground" value="0.015"/>
    <xacro:property name="base_joint_z" value="${distance_ground+base_length / 2}"/>

    <link name = "base_link">
        <visual>
            <geometry>
                <cylinder radius = "0.1" length="0.08"/>
            </geometry>
            <origin xyz = "0 0 0" rpy="0 0 0"/>
            <material name="robot_color">
                <color rgba="0.2 0.2 0.2 1"/>
            </material>
        </visual>
    </link> 

    <joint name="baselink2basefootprint" type="fixed">
        <parent link = "base_footprint"/>
        <child link = "base_link" />
        <origin xyz="0 0 ${base_joint_z}" rpy="0 0 0" />
        <axis xyz="0 0 0" />
    </joint>

    <xacro:property name="PI" value="3.1415927"/>
    <xacro:property name="wheel_radius" value="0.0325"/>
    <xacro:property name="wheel_length" value="0.015"/>
    <xacro:property name="wheel_joint_z" value="${(base_length / 2 + distance_ground - wheel_radius) * -1}"/>

    <xacro:macro name="wheel_func" params="wheel_name flag">

    <link name = "${wheel_name}">
        <visual>
            <geometry>
                <cylinder radius = "${wheel_radius}" length="${wheel_length}"/>
            </geometry>
            <origin xyz = "0 0 0" rpy="${PI / 2} 0 0"/>
            <material name="black"/>
        </visual>
    </link> 

    <joint name="${wheel_name}2baselink" type="continuous">
        <parent link = "base_link"/>
        <child link = "${wheel_name}" />
        <origin xyz="0 ${flag*0.1} ${wheel_joint_z}" />
        <axis xyz="0 1 0" />
    </joint>

    </xacro:macro>

    <xacro:wheel_func wheel_name="left_wheel" flag = "1" />
    <xacro:wheel_func wheel_name="right_wheel" flag = "-1" />

    <xacro:property name="small_wheel_radius" value="0.0075"/>
    <xacro:property name="small_joint_z" value="${(base_length / 2 + distance_ground - small_wheel_radius) * -1}"/>

    <xacro:macro name="small_wheel_func" params="small_wheel_name  flag">
    <link name = "${small_wheel_name}">
        <visual>
            <geometry>
                <sphere radius="${small_wheel_radius}" />
            </geometry>
            <origin xyz = "0 0 0" rpy="0 0 0"/>
            <material name="black"/>
        </visual>
    </link> 

    <joint name="${small_wheel_name}2baselink" type="continuous">
        <parent link = "base_link"/>
        <child link = "${small_wheel_name}" />
        <origin xyz="${(base_radius - small_wheel_radius)*flag} 0  ${small_joint_z}" />
        <axis xyz="1 1 1" />
    </joint>

    </xacro:macro>

    <xacro:small_wheel_func small_wheel_name ="front_small_wheel" flag = "1" />
    <xacro:small_wheel_func small_wheel_name ="back_small_wheel" flag = "-1" />
</robot>

5. Xacro_实操

实现流程:

  • 首先编写摄像头和雷达的 xacro 文件
  • 然后再编写一个组合文件,组合底盘、摄像头与雷达
  • 最后,通过 launch 文件启动 Rviz 并显示模型

摄像头 xacro 文件:

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

    <xacro:property name="camera_length" value="0.01" /> 
    <xacro:property name="camera_width" value="0.025" /> 
    <xacro:property name="camera_height" value="0.025" /> 
    <xacro:property name="joint_camera_x" value="0.08" /> 
    <xacro:property name="joint_camera_y" value="0.0" /> 
    <xacro:property name="joint_camera_z" value="${base_length / 2 + camera_height / 2}" /> 

    <link name="camera">
        <visual>
            <geometry>
                <box size="${camera_length} ${camera_width} ${camera_height}" />
            </geometry>
            <origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0" />
            <material name="black" />
        </visual>
    </link>

    <joint name="camera2base_link" type="fixed">
        <parent link="base_link" />
        <child link="camera" />
        <origin xyz="${joint_camera_x} ${joint_camera_y} ${joint_camera_z}" />
    </joint>
</robot>

雷达 xacro 文件:

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

    <xacro:property name="support_length" value="0.15" /> 
    <xacro:property name="support_radius" value="0.01" /> 
    <xacro:property name="joint_support_x" value="0.0" /> 
    <xacro:property name="joint_support_y" value="0.0" /> 
    <xacro:property name="joint_support_z" value="${base_length / 2 + support_length / 2}" /> 

    <link name="support">
        <visual>
            <geometry>
                <cylinder radius="${support_radius}" length="${support_length}" />
            </geometry>
            <origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0" />
            <material name="red">
                <color rgba="0.8 0.2 0.0 0.8" />
            </material>
        </visual>
    </link>

    <joint name="support2base_link" type="fixed">
        <parent link="base_link" />
        <child link="support" />
        <origin xyz="${joint_support_x} ${joint_support_y} ${joint_support_z}" />
    </joint>


    <xacro:property name="laser_length" value="0.05" /> 
    <xacro:property name="laser_radius" value="0.03" /> 
    <xacro:property name="joint_laser_x" value="0.0" /> 
    <xacro:property name="joint_laser_y" value="0.0" /> 
    <xacro:property name="joint_laser_z" value="${support_length / 2 + laser_length / 2}" /> 


    <link name="laser">
        <visual>
            <geometry>
                <cylinder radius="${laser_radius}" length="${laser_length}" />
            </geometry>
            <origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0" />
            <material name="black" />
        </visual>
    </link>

    <joint name="laser2support" type="fixed">
        <parent link="support" />
        <child link="laser" />
        <origin xyz="${joint_laser_x} ${joint_laser_y} ${joint_laser_z}" />
    </joint>
</robot>

组合底盘摄像头与雷达的 xacro 文件

<robot name="mycar" xmlns:xacro="http://wiki.ros.org/xacro">
    <xacro:include filename="demo06_base_footprint.urdf.xacro" />
    <xacro:include filename="demo07_car_camera.urdf.xacro" />
    <xacro:include filename="demo08_car_lidar.urdf.xacro" />
</robot>

launch 文件

<launch> 
    <param name="robot_description" command="$(find xacro)/xacro $(find  urdf01_rviz)/urdf/xacro/car.urdf.xacro" />
    <!--启动rviz -->
    <node pkg = "rviz" type = "rviz" name="rviz" args="-d $(find urdf01_rviz)/config/demo01.rviz"/>
    <!-- 添加关节状态发布节点 -->
    <node pkg="joint_state_publisher" type="joint_state_publisher" name="joint_state_publisher" />
    <!-- 添加机器人状态发布节点 -->
    <node pkg="robot_state_publisher" type="robot_state_publisher" name="robot_state_publisher" />
    <!-- 可选:用于控制关节运动的节点 -->
    <node pkg="joint_state_publisher_gui" type="joint_state_publisher_gui" name="joint_state_publisher_gui" />
</launch>

【ROS】—— 机器人系统仿真 —URDF优化_xacro (十四)