ROS2: Optimizing URDF model using XACRO

Optimizing URDF model using XACRO

Introduction

This is an introductory chapter, explaining macros we are going to use.

In the URDF model that we are going to create, all wheels share shape and material properties. Links (URDF term for "objects") of the wheels have a lot in common, as well as joints (URDF term for things that connect objects together).

We can move repeating properties to a separate place (macro) to make our code better structured.

A XACRO file is an XML file. As its name implies, xacro is an Xml Macro. The xacro utility program runs all of the macros and outputs the result, producing a single file, in a way similar to what a preprocessor does to the include files and macros. Typical usage looks something like this:

    $ xacro model.xacro > model.urdf

You can also automatically generate the urdf in a launch file.

path_to_urdf = get_package_share_path('pr2_description') / 'robots' / 'pr2.urdf.xacro'
robot_state_publisher_node = launch_ros.actions.Node(
    package='robot_state_publisher',
    executable='robot_state_publisher',
    parameters=[{
        'robot_description': ParameterValue(
            Command(['xacro ', str(path_to_urdf)]), value_type=str
        )
    }]
)
                

At the top of the URDF file, you must specify a namespace in order for the file to parse properly. For example, these are the first two lines of a valid xacro file:

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

Variables and macros

A minimal XACRO file looks like this:

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

Let's define some variables (constants) in XACRO. These are defined as , with a name and value attribute. The variable names need to use underscores instead of hyphens.

                    
<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="bot">
    <xacro:property name="wheel_radius" value="0.04" />
    <xacro:property name="wheel_length" value="0.05" />
    <xacro:property name="wheel_color_name" value="red" />
    <xacro:property name="wheel_color_rgb" value="1 0 0 1" />
</robot>           
 
                

In addition to the variables, we can introduce Macros (using these variables):

                    
<xacro:macro name="wheel" params="wheel_name">
    <link name="${wheel_name}">
    <visual>
        <geometry>
        <cylinder radius="${wheel_radius}" length="${wheel_length}" />
        </geometry>
        <material name="${wheel_color_name}">
        <color rgba="${wheel_color_rgb}"/>
        </material>
    </visual>
    </link> 
</xacro:macro>
                    
                

It is possible to use complex expressions using the four basic operations (+,-,*,/), the unary minus, and parenthesis. Examples:

                    
<cylinder radius="${wheeldiam/2}" length="0.1"/>
<origin xyz="${reflect*(width+.02)} 0 0.25" />
                    
                

Macros are templates: to instantiate them, we use template name and necessary attributes:

                    
<xacro:wheel name="right_wheel_frontside" />
                    
                

Running XACRO from the Command Line

XACRO files can be manually rendered via the command line.

    $ xacro src/fwd_bot/urdf/bot.xacro

Complete URDF example

Here is a sample bot.xacro file:

                    
<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="bot">
    <xacro:property name="wheel_radius" value="0.04" />
    <xacro:property name="wheel_length" value="0.05" />
    <xacro:property name="wheel_color_name" value="red" />
    <xacro:property name="wheel_color_rgb" value="1 0 0 1" />

    <xacro:macro name="wheel" params="name">
        <link name="${name}">
            <visual>
                <geometry>
                    <cylinder radius="${wheel_radius}" length="${wheel_length}" />
                </geometry>
                <material name="${wheel_color_name}">
                    <color rgba="${wheel_color_rgb}"/>
                </material>
            </visual>
        </link> 
    </xacro:macro>  

    <xacro:macro name="box_link" params="name size color color_rgb" >
        <link name="${name}">
            <visual>
                <geometry>
                    <box size="${size}"/>
                </geometry>
                <material name="${color}">
                    <color rgba="${color_rgb}"/>
                </material>
            </visual>
        </link>
    </xacro:macro>

    <xacro:macro name="static_joint" params="name parent child">
        <joint name="${name}" type="continuous">
            <parent link="${parent}" />
            <child link="${child}" />
            <origin xyz="0 0 0" rpy="0 0 0"/>
        </joint>
    </xacro:macro>

    <xacro:macro name="wheel_joint" params="name parent child xyz ">
    <joint name="${name}" type="continuous">
        <parent link="${parent}" />
        <child link="${child}" />
        <origin xyz="${xyz}" rpy="1.570796 0 0"/>
    </joint>
    </xacro:macro>  

    <xacro:box_link name="base_link" size="0 0 0" color="red" color_rgb="1 0 0 1" />
    <xacro:box_link name="torso" size="0.6 0.3 0.1" color="green" color_rgb="0 1 0 1" />    

    <xacro:wheel name="right_wheel_frontside" />
    <xacro:wheel_joint name="base_link_right_wheel_frontside" parent="base_link" child="right_wheel_frontside" xyz="0.2 -0.2 -0.05" />

    <xacro:wheel name="right_wheel_backside" />
    <xacro:wheel_joint name="base_link_right_wheel_backside" parent="base_link" child="right_wheel_backside" xyz="-0.2 -0.2 -0.05" />

    <xacro:wheel name="left_wheel_frontside" /> 
    <xacro:wheel_joint name="base_link_left_wheel_frontside" parent="base_link" child="left_wheel_frontside" xyz="0.2 0.2 -0.05" />

    <xacro:wheel name="left_wheel_backside" /> 
    <xacro:wheel_joint name="base_link_left_wheel_backside" parent="base_link" child="left_wheel_backside" xyz="-0.2 0.2 -0.05" />    
</robot>
                    
                

Additionally, we can store macros in a separate bot.xacro file, and use bot.urdf.xacro as a main file:

bot.xacro

                    
<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro">
    <xacro:property name="wheel_radius" value="0.04" />
    <xacro:property name="wheel_length" value="0.05" />
    <xacro:property name="wheel_color_name" value="red" />
    <xacro:property name="wheel_color_rgb" value="1 0 0 1" />

    <xacro:macro name="wheel" params="name">
        <link name="${name}">
            <visual>
                <geometry>
                    <cylinder radius="${wheel_radius}" length="${wheel_length}" />
                </geometry>
                <material name="${wheel_color_name}">
                    <color rgba="${wheel_color_rgb}"/>
                </material>
            </visual>
        </link> 
    </xacro:macro>  

    <xacro:macro name="box_link" params="name size color color_rgb" >
        <link name="${name}">
            <visual>
                <geometry>
                    <box size="${size}"/>
                </geometry>
                <material name="${color}">
                    <color rgba="${color_rgb}"/>
                </material>
            </visual>
        </link>
    </xacro:macro>

    <xacro:macro name="static_joint" params="name parent child">
        <joint name="${name}" type="continuous">
            <parent link="${parent}" />
            <child link="${child}" />
            <origin xyz="0 0 0" rpy="0 0 0"/>
        </joint>
    </xacro:macro>

    <xacro:macro name="wheel_joint" params="name parent child xyz ">
    <joint name="${name}" type="continuous">
        <parent link="${parent}" />
        <child link="${child}" />
        <origin xyz="${xyz}" rpy="1.570796 0 0"/>
    </joint>
    </xacro:macro>  
</robot>
                    
                

bot.urdf.xacro

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

    <xacro:box_link name="base_link" size="0 0 0" color="red" color_rgb="1 0 0 1" />
    <xacro:box_link name="torso" size="0.6 0.3 0.1" color="green" color_rgb="0 1 0 1" />    
                    
    <xacro:wheel name="right_wheel_frontside" />
    <xacro:wheel_joint name="base_link_right_wheel_frontside" parent="base_link" child="right_wheel_frontside" xyz="0.2 -0.2 -0.05" />
                    
    <xacro:wheel name="right_wheel_backside" />
    <xacro:wheel_joint name="base_link_right_wheel_backside" parent="base_link" child="right_wheel_backside" xyz="-0.2 -0.2 -0.05" />
                    
    <xacro:wheel name="left_wheel_frontside" /> 
    <xacro:wheel_joint name="base_link_left_wheel_frontside" parent="base_link" child="left_wheel_frontside" xyz="0.2 0.2 -0.05" />
                    
    <xacro:wheel name="left_wheel_backside" /> 
    <xacro:wheel_joint name="base_link_left_wheel_backside" parent="base_link" child="left_wheel_backside" xyz="-0.2 0.2 -0.05" />    
</robot>
                    
                

I am going to use the first approach (everything in one file) for now, as our robot is not that complex.

(C) snowcron.com, all rights reserved

Please read the disclaimer