2019-03-25

Using xacro with RobWork

Xacro (which stands for XML macro) is a quite useful tool which is used in ROS to make easier and cleaner robot descriptions. Typically, you would create a .xacro file defining the robot parameters in more human-readable format and then 'compile' it using xacro to .urdf or .gazebo descriptions.

But it works quite well for RobWork workcells too. RobWork XML format suffers from strict syntax same as URDF. Using xacro might make some stuff little easier.

How to use it?
Xacro is available as a part of ROS (http://www.ros.org/). You can install it with the whole system, but it is also probably possible to install just the xacro package with necessary dependencies:

sudo apt install ros-kinetic-xacro


Simply call xacro in command to translate a xacro file into XML:

xacro -i source.xacro | sed s/xmlns.*\"//g > scene.wc.xml

Substitute source.xacro and scene.wc.xml with the desired filenames. The sed bit in the middle is required to remove the xmlns attribute added by xacro automatically, which unfortunately is not accepted by the RobWork parser. Otherwise the file should be good as is.

Simplest use
One of the simplest things you can do is to get rid of magic numbers in the workcell and define the named parameters:

<WorkCell xmlns:xacro="http://www.ros.org/wiki/xacro" name="example_1">
 
 
  <xacro:property name="box_position_x" value="1" />
  <xacro:property name="box_position_y" value="2" />
  <xacro:property name="box_size" value="0.45" />

 
  <Frame name="box" refframe="WORLD">
    <RPY>0 0 0</RPY>
    <Pos>${box_position_x} ${box_position_y} 0</Pos>
    <Property name="ShowFrameAxis">true</Property>
    <Drawable name="box_geo">
      <RPY>0 0 0</RPY>
      <Pos>0 0 ${box_size/2}</Pos>
      <RGB>1 0 0</RGB>
      <Box x="${box_size}" y="${box_size}" z="${box_size}"/>
    </Drawable>
  </Frame>
 
</WorkCell>


The xmlns attribute is required. Notice how we can now easily define the size and position of the cube. Additionally, the drawable is now automatically placed such that the cube rests with the bottom face on the WORLD surface. And this is how it looks like:
example_1.xacro





Macros
Another nice thing is that you can now define macros to be used for some repetitive stuff.  The macros can take parameters and they can be iterated:


<WorkCell xmlns:xacro="http://www.ros.org/wiki/xacro" name="example_2">
 
  <xacro:property name="start_box_width" value="0.15"/>
  <xacro:property name="delta_box_width" value="0.01"/>
 
  <!-- this macro draws a box with id, color (r, g, b) and size at position (x, y) -->
  <xacro:macro name="box" params="id x y r g b size">
    <Frame name="box_${id}" refframe="WORLD">
      <RPY>0 0 0</RPY>
      <Pos>${x} ${y} 0</Pos>
      <Property name="ShowFrameAxis">true</Property>
      <Drawable name="box_geo_${id}">
        <RPY>0 0 0</RPY>
        <Pos>0 0 ${size/2}</Pos>
        <RGB>${r} ${g} ${b}</RGB>
        <Box x="${size}" y="${size}" z="${size}"/>
      </Drawable>
    </Frame>
  </xacro:macro>
 
  <!-- this macro makes a line of boxes along x axis at specified y coordinate -->
  <xacro:macro name="box_line" params="i j y g size">
    <xacro:box id="${i}_${j}" x="${i*0.5-3}" y="${y}" r="${i/11}" g="${g}" b="0" size="${size+i*delta_box_width}"/>
   
    <!-- this is how you loop things (recurrence): -->
    <xacro:if value="${i-1}">
      <xacro:box_line i="${i-1}" j="${j}" y="${y}" g="${g}" size="${size}"/>
    </xacro:if>
  </xacro:macro>
 
  <!-- this macro draws a square of boxes -->
  <xacro:macro name="box_square" params="j">
    <xacro:box_line i="11" j="${j}" y="${j*0.5-3}" g="${j/11}" size="${start_box_width+j*delta_box_width}"/>
   
    <xacro:if value="${j-1}">
      <xacro:box_square j="${j-1}"/>
    </xacro:if>
  </xacro:macro>

 
  <!-- use the macro -->
  <xacro:box_square j="11"/>
 
</WorkCell>


It might be a bit convoluted, but notice how powerful it is. The generated XML WC file has over 1,300 lines... Take a look at the bold part to see how the looping can be achieved.
example_2.xacro

Including things
RobWork XML format has an <Include> tag that lets you include other files, but it's quite limited. For instance, you can't easily include two copies of a device into your workcell. It can be useful, for example, to include a transparent copy of your robot to be used as the movable phantom, while the solid one is used to visualize its actual state.

This is the example_3.xacro file:

<WorkCell xmlns:xacro="http://www.ros.org/wiki/xacro" name="example_3">
 
  <Frame name="robot_1" refframe="WORLD"/>
  <xacro:property name="robotid" value="1"/>
  <xacro:property name="trans" value="1"/> <!-- this robot is solid -->
  <xacro:include filename="my_robot.xacro"/>
 
  <Frame name="robot_2" refframe="WORLD"/>
  <xacro:property name="robotid" value="2"/>
  <xacro:property name="trans" value="0.3"/> <!-- this robot is transparent -->
  <xacro:include filename="my_robot.xacro"/>
 
  <CollisionSetup file="example_3.proxy.xml"/>
 
</WorkCell>


This is the my_robot.xacro file. This file itself could be xacro'ed a bit more:

<dummy xmlns:xacro="http://www.ros.org/wiki/xacro">
<SerialDevice name="my_robot_${robotid}">
  <Frame name="Base"/>
 
  ...
 
  <Joint name="Joint6" type="Revolute">
    <RPY>0 -90 0</RPY>
    <Pos>0 0 0</Pos>
    <PosLimit min="-360" max="360"/>
  </Joint>
 
  <Frame name="TCP"/>
 
  <Drawable name="BaseGeo" refframe="Base">
    <RPY>0 0 0</RPY>
    <Pos>0 0 0.025</Pos>
    <RGB>0.37 0.73 0.93 ${trans}</RGB>
    <Cylinder radius="0.1" z="0.05"/>
  </Drawable>
 
  ...

  <CollisionSetup file="my_robot.proxy.xml"/>
 
  <Q name="Home">-1.074184 0.243 2.207 2.259184 1.571184 0.499</Q>
</SerialDevice>
</dummy>


Important: Note the <dummy> tag. This is required because xacro only includes the contents of the root tag of the included file.


example_3.xacro

The files are available in the GitHub repository: https://github.com/dagothar/robwork-xacro