xacro(URDF用マクロ)¶
概要¶
xacro (XML macro)は、XMLのためのマクロ言語です。xacroを用いることで、XMLを短く、読みやすく書くことができます。Choreonoidで利用可能なロボットモデル記法 URDF (Unified Robot Description Format) はXMLを用いています。したがって、xacroはロボットモデルの記述の簡略化に貢献します。
最新版のChoreonoidではxacroをバンドルしているため、ROS環境の有無を問わず、xacroの機能を利用することが可能です(現在はLinux版でのみ対応)。
なお、xacroファイルでは、robotタグに xmlns:xacro="http://www.ros.org/wiki/xacro" と属性を記述する必要があります。
以下では、 xacroの公式ドキュメント に基づいて、xacroの機能を簡単に紹介します。
変数¶
propertyマクロは、XMLドキュメント内で、変数の役割を果たします.propertyマクロの使い方は2つあります。
1つ目は、数や文字列を変数として扱う使い方です。まずxacro:propertyタグで、変数名(name属性)と値(value属性)によって変数を定義します。そして、使いたい場所で、dollared-bracesと呼ばれる記法により、${変数名} として、その変数を利用します。
次の例では、円筒状の幾何形状を、radiusとlengthという2つの変数を介して指定しています。
例:
<xacro:property name="radius" value="2.1"/>
<xacro:property name="length" value="4.5"/>
<geometry type="cylinder" radius="${radius}" length="${length}"/>
2つ目は、propertyブロックと呼ばれる方法です。propertyブロックでは、valueを指定する代わりに、xacro:propertyタグで囲った部分を変数のように扱うことができます。ただし、これを使う際には、xacro:insert_blockタグを使わなければなりません。
次の例では、xacro:insert_blockタグを用いて、予め定義しておいたoriginタグを適用しています。同じ内容のタグを色々なところで使う場合などで、propertyブロックは有効です。
例:
<xacro:property name="link_origin">
<origin xyz="0.3 0 0" rpy="0 0 0"/>
</xacro:property>
<link name="sample_link">
<inertial>
<xacro:insert_block name="link_origin"/>
...
</inertial>
</link>
辞書・リスト・YAMLファイル¶
xacroでは、変数の定義において、Pythonの辞書形式やリストを利用できます。
例(Python記法):
<xacro:property name="props" value="${dict(a=1, b=2, c=3)}"/>
<xacro:property name="props_alt" value="${dict([('1a',1), ('2b',2), ('3c',3)])}"/>
<xacro:property name="numbers" value="${[1,2,3,4]}"/>
また、YAMLファイルを読み込んで、YAMLファイルから直接変数を定義できます。このとき、YAMLファイル内の変数は、全て辞書内に格納されます。
例(YAMLファイルの読み込み):
<xacro:property name="yaml_file" value="$(find package)/config/props.yaml" />
<xacro:property name="props" value="${load_yaml(yaml_file)}"/>
YAMLファイル:
a: 1
b: 2
c: 3
辞書内の変数の利用例:
<xacro:property name="a" value="${props['a']}"/>
数式¶
xacroでは、XML内で計算を行わせることも可能です。dollared-braces (${})の中で書かれた四則演算や比較、Pythonのmathモジュールで定義された定数(円周率πなど)および関数(三角関係など)を利用できます。
例:
<xacro:property name="R" value="2"/>
<xacro:property name="alpha" value="${sin(30/180*pi)}"/>
<limit lower="${radians(-90)}" upper="${radians(90)}" effort="0" velocity="${radians(75)}"/>
条件分岐¶
xacroでは、条件分岐が使えます。条件分岐のために、xacro:ifタグとxacro:unlessタグが用意されています。
xacro:ifタグは、自身のvalue属性が1もしくはtrueとなるときだけ、タグで囲んだ部分(ブロック内部)を有効にします。逆にxacro:unlessタグは、自身のvalue属性が0もしくはfalseとなるときだけ、タグで囲んだ部分(ブロック内部)を有効にします。どちらのタグも、value属性が1/0とtrue/falseのどちらでもない値を取ったときには、エラーを返すことに注意が必要です。
value属性の中で数学マクロを使うことで、複雑な条件での分岐を行わせることもできます。
例:
<xacro:property name="var" value="useit"/>
<xacro:if value="${var == 'useit'}"/>
<xacro:if value="${var.startswith('use') and var.endswith('it')}"/>
<xacro:property name="allowed" value="${[1,2,3]}"/>
<xacro:if value="${1 in allowed}"/>
ROSコマンド¶
xacroではdollared-parentheses ($())の中で、ROS (Robot Operating System) で使われているコマンドの一部を利用できます。
Choreonoidで利用可能なコマンドは以下の通りです。これらはいずれもROS環境の有無に関わらず利用可能です。
envコマンド¶
$(env ENVIRONMENT_VARIABLE) とすることで 環境変数 ENVIRONMENT_VARIABLE の値を取得します。環境変数が存在しなかった場合、エラーを返します。
optenvコマンド¶
$(optenv ENVIRONMENT_VARIABLE) とすることで、環境変数 ENVIRONMENT_VARIABLE の値を取得します。環境変数が存在しなかった場合、空文字列を返します。
また $(optenv ENVIRONMENT_VARIABLE default_value) とすれば、環境変数が存在しなかった場合、デフォルト値として default_value を返します。
findコマンド¶
$(find pkg) とすると、環境変数 ROS_PACKAGE_PATH の中で末尾が pkg となるパスを探索します。もしそのようなパスが存在しない場合は、コマンドはエラーを返します。
ROS環境では、環境変数 ROS_PACKAGE_PATH が設定され、インストールされているパッケージを参照できます。一方でROS環境がない場合でも、環境変数 ROS_PACKAGE_PATH を設定することで、このコマンドを利用できます。
argコマンド¶
$(arg arg1) とすることで、xacro:argタグで与えられた引数 arg1 を利用できます。
例:
<xacro:arg name="link_name" default="default_link"/>
<link name="$(link_name)">
evalコマンド¶
$(eval <expression>) とすることで、通常のdollared-braces (${})では扱えない複雑な表現 expression を評価できます。
例(コマンドの併用と文字列の連結):
<xacro:property name="paths" value="$(eval env('PATH') + ':' + find('pkg')">
マクロ¶
xacroの最も強力な機能はマクロです.マクロはxacro:macroタグを用いて定義します。name属性でマクロの名前を、params属性でマクロのパラメータ(関数の引数に相当)を指定します。パラメータが複数あるときは、空白を挟んで並べます。
各パラメータは標準で文字列を取りますが、タグやブロック(あるタグによって囲まれた複数のタグ)をパラメータとすることも可能です。パラメータ名の前に*(アスタリスク)を1つ付けることでXMLのタグを、アスタリスクを2つ付けることでXMLブロックを、パラメータとして与えることができるようになります。
タグやブロックをパラメータはとして与えるときには、パラメータ名と与えるタグ名・ブロック名を対応付ける必要はありません。ただし、複数のタグやブロックをパラメータとして与える場合、パラメータの順番と記述した順番が対応することになりますので、注意が必要です。以下の例では、2つのブロック b0 と abc が、それぞれ block0 と block1 に対応して展開されています。
例:
<robot name="sample" xmlns:xacro="http://www.ros.org/wiki/xacro">
<xacro:macro name="sample_macro" params="prefix *tag **block0 **block1">
<link name="${prefix}_link">
<inertial>
<xacro:insert_block name="tag"/>
<inertia ixx="1.0" ixy="0.0" ixz="0.0" iyy="1.0" iyz="0.0" izz="1.0"/>
</inertial>
<xacro:insert_block name="block0"/>
<xacro:insert_block name="block1"/>
</link>
</xacro:macro>
<xacro:sample_macro prefix="sample">
<mass value="1.0"/>
<b0>
<collision>
<geometry>
<box size="1.0 1.0 1.0"/>
</geometry>
</collision>
<!-- memo -->
</b0>
<abc>
<visual>
<geometry>
<box size="0.5 0.5 0.5"/>
</geometry>
<material>
<color rgba="1.0 0.0 0.0 1.0"/>
</material>
</visual>
</abc>
</xacro:sample_macro>
</robot>
xacroの出力結果:
<robot name="sample">
<link name="sample_link">
<inertial>
<mass value="1.0"/>
<inertia ixx="1.0" ixy="0.0" ixz="0.0" iyy="1.0" iyz="0.0" izz="1.0"/>
</inertial>
<collision>
<geometry>
<box size="1.0 1.0 1.0"/>
</geometry>
</collision>
<!-- memo -->
<visual>
<geometry>
<box size="0.5 0.5 0.5"/>
</geometry>
<material>
<color rgba="1.0 0.0 0.0 1.0"/>
</material>
</visual>
</link>
</robot>
マクロ内のマクロ¶
マクロはその内部に他のマクロを持つことができます。ただし、内部のマクロは事前に定義されている必要があります。
例:
<robot name="sample" xmlns:xacro="http://www.ros.org/wiki/xacro">
<link name="a">
<xacro:macro name="mass" params="value">
<mass value="${value}"/>
</xacro:macro>
<xacro:macro name="inertial">
<inertial>
<xacro:mass value="1.0"/>
<inertia ixx="1.0" ixy="0.0" ixz="0.0" iyy="1.0" iyz="0.0" izz="1.0"/>
</inertial>
</xacro:macro>
<xacro:inertial/>
</link>
</robot>
xacroの出力結果:
<robot name="sample">
<link name="a">
<inertial>
<mass value="1.0"/>
<inertia ixx="1.0" ixy="0.0" ixz="0.0" iyy="1.0" iyz="0.0" izz="1.0"/>
</inertial>
</link>
</robot>
スコープ¶
変数やマクロのスコープは基本的にマクロの内部となります。つまり、マクロの中で定義された変数やマクロは、原則としてそのマクロの内部でしか利用できません。
外部の変数やマクロをどうしても参照したいときは、それらの変数やマクロを定義するタグに scope="parent" と属性を追加することで、一つ上の(親の)階層までスコープを広げることができます。あるいは、 scope="global" と属性を追加することで、自身のスコープをグローバル、すなわち全体に広げることができます。ただし、スコープを広げるほど名前の管理が複雑になるため、これらのスコープ拡張の利用には注意が必要です。
パラメータのデフォルト値¶
マクロの標準(文字列の)パラメータは、デフォルト値を取ることができます。デフォルト値は、マクロの定義時に、パラメータ名に := に続いて設定します。
また、^ (サーカムフレックス)を用いて、パラメータ名に :=^ と続けることで、同じ名前の外部の変数を読み込むことができます。さらに、 :=^| 1.0 のようにすると、まず外部変数を探し、それが見つからなかった場合に、バーティカルバーに続く値(ここでは1.0)がデフォルト値として使われます。
例:
<robot name="sample" xmlns:xacro="http://www.ros.org/wiki/xacro">
<link name="a">
<xacro:property name="y" value="10.0"/>
<xacro:macro name="inertial" params="x:=1.0 y:=^ z:=^|3.0">
<inertial>
<origin xyz="${x} ${y} ${z}"/>
<mass value="1.0"/>
<inertia ixx="1.0" ixy="0.0" ixz="0.0" iyy="1.0" iyz="0.0" izz="1.0"/>
</inertial>
</xacro:macro>
<xacro:inertial/>
</link>
</robot>
xacroの出力結果:
<robot name="sample">
<link name="a">
<inertial>
<origin xyz="1.0 10.0 3.0"/>
<mass value="1.0"/>
<inertia ixx="1.0" ixy="0.0" ixz="0.0" iyy="1.0" iyz="0.0" izz="1.0"/>
</inertial>
</link>
</robot>
ファイルの読み込み¶
xacro:include タグを利用すると、他のxacroファイルを読み込めます。
例:
<xacro:include filename="$(find package)/other_file.xacro"/>
マクロや変数の名前が衝突しないように、読み込み時に ns 属性で名前空間を付与することも可能です。
例(名前空間の付与):
<xacro:include filename="$(find package)/other_file.xacro" ns="namespace/>
処理順¶
xacroコマンドは、与えられたファイルを上から順に読み込み、逐次的に処理・評価を実施します。
過去の仕様との比較¶
当初、xacroコマンドは、以下の順番でxacroファイルを処理していました。
ファイル読み込み(xacro:includeタグの展開)
変数 (property) およびマクロの定義
マクロの展開
数式やマクロ等の評価
評価が最後の処理となるため、 if や unless による条件分岐は、変数やマクロの定義に影響を与えませんでした。そこで、処理順をこの旧仕様から現在の仕様に変更することにより、以下の利点が実現されました。
ファイルの読み込みや変数、マクロの定義を条件分岐によって実行できる。そのため、必要なファイル読み込み、変数・マクロの定義だけを実行できる。
読み込むファイルの名前を、変数やマクロ等を利用して指定できる。
ファイルの途中で変数の値を変更すると、それより後の部分にだけその変更が反映される。
ローカルなマクロや変数を、他のスコープの同名変数に影響を与えること無く定義できる。