مدلسازی ربات با استفاده از Xacro
شما در مقاله مدلسازی سه بعدی ربات در ROS با اصول اولیه مدلسازی ربات در ROS به زبان urdf آشنا شدید. در این آموزش برخی از ترفندها را برای ساده سازی و کاهش میزان کد در یک فایل URDF با استفاده از Xacro را میآموزید.
همانطور که در آموزشهای قبلی به آن اشاره شد توصیف کامل یک ربات نسبتاً پیچیده با استفاده از urdf کار دشوار و خسته کنندهای است. خوشبختانه ROS این امکان را ایجاد کرده است تا شما با استفاده از پکیج xacro برای سادهتر کردن کدهای خود بهره ببرید. به طور کلی به کمک xacro میتوان سه کار مفید زیر را انجام داد:
- تعریف ثابتها
- محاسبات ساده ریاضی
- تعریف ماکرو
در این آموزش، ما این موارد را بررسی میکنیم تا به کاهش حجم کلی فایل URDF کمک کرده و خواندن و نگهداری آن آسانتر شود.
۱-استفاده از Xacro
xacro مخفف xml macro است و همانطور که از نام آن پیداست، xacro یک زبان ماکرو است که میتواند ماکروهای مختلف را درون خودش اجرا کند و نتیجه را به صورت یک فایل urdf ارائه دهد.
استفاده معمولی از آن به صورت زیر است:
[sourcecode language=”plain”]
xacro –inorder model.xacro> model.urdf
[/sourcecode]
درواقع شما با استفاده از قابلیتهای xacro ربات خودتان را مدلسازی میکنید و سپس در انتها با استفاده از دستور فوق آن را به فرمت urdf تبدیل میکنید.
همچنین شما میتوانید به طور خودکار urdf را درون فایل لانچ خودتان از فایل xacro بسازید. اگرچه این کار راحتتر است اما در لحظه اجرا، به زمان بیشتری برای اجرای فایل لانچ لازم دارد، بنابراین توجه داشته باشید که درصورتی به طور مستقیم از فایل xacro در فایل لانچ خود استفاده کنید، راه اندازی فایل launch شما ممکن است کمی بیشتر طول بکشد.
برای نمونه به نحوه فراخوانی یک فایل xacro که در فایل لانچ pr2_description مربوط به ربات pr2 آورده شده است دقت کنید.
<param name="robot_description"
command="xacro --inorder '$(find pr2_description)/robots/pr2.urdf.xacro'" />
نکته دیگری که باید به آن توجه داشته باشید این است که در بالای فایل xacro ، باید یک فضای نام را مشخص کنید تا فایل به درستی تجزیه شود. به عنوان مثال ، این دو خط اول یک فایل معتبر xacro است:
<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="firefighter">
۲-ثابتها
بیایید تا به تعریف یک لینک از یک ربات در فایل urdf آن نگاهی بیاندازیم
<link name="base_link">
<visual>
<geometry>
<cylinder length="0.6" radius="0.2"/>
</geometry>
<material name="blue"/>
</visual>
<collision>
<geometry>
<cylinder length="0.6" radius="0.2"/>
</geometry>
</collision>
</link>
اطلاعات در اینجا کمی زائد است. طول و شعاع استوانه را باید دو بار مشخص کنیم. بدتر اینکه ، اگر بخواهیم آن را تغییر دهیم ، باید در دو مکان مختلف این کار را انجام دهیم. خوشبختانه ، xacro به شما امکان میدهد خصوصیاتی را که به عنوان ثابت عمل میکنند، تعیین کنید و از آنها به صورت پارامتری استفاده کنید. کد بالا را میتوانیم به اینصورت بنویسیم.
<xacro:property name="width" value="0.2" />
<xacro:property name="bodylen" value="0.6" />
<link name="base_link">
<visual>
<geometry>
<cylinder radius="${width}" length="${bodylen}"/>
</geometry>
<material name="blue"/>
</visual>
<collision>
<geometry>
<cylinder radius="${width}" length="${bodylen}"/>
</geometry>
</collision>
</link>
در رابطه با این کد به نکات زیر توجه کنید:
- دو پارامتر در دو سطر اول مشخص و مقداردهی شدهاند. این تعاریف را می توان تقریباً در هر نقطهای از کد ( با فرض استفاده ازXML درست) ، قبل یا بعد از استفاده از آنها قرار داد اما معمولاً آنها را در ابتدای کدها قرار میدهند.
- به جای مشخص کردن شعاع در المان geometry ، از علامت دلار و براکت برای نشان دادن مقدار استفاده می کنیم.
- این کد همان کد نشان داده شده در بالا را تولید می کند.
توجه داشته باشید که مقدار محتویات درون ساختار ${} برای جایگزینی استفاده می شود. این بدان معنی است که می توانید آن را با متن دیگر ترکیب کنید. به مثال زیر توجه کنید.
<xacro:property name="robotname" value="marvin" />
<link name="${robotname}s_leg" />
که منجر به تولید کد زیر میشود:
<link name="marvins_leg" />
۳-عملیات ریاضی
شما می توانید با استفاده از چهار عمل اصلی (+ ، – ، * ، /) ، منفی و پرانتز ، عبارات دلخواه خود را در ساختار ${} ایجاد کنید.
به عنوان مثال:
<cylinder radius="${wheeldiam/2}" length="0.1"/>
<origin xyz="${reflect*(width+.02)} 0 0.25" />
توجه داشته باشید که تمامی عملیات ریاضی که به این روش انجام میدهید از نوع float خواهد بود. به عبارتی این دستور
<link name="0.833333333333"/>
در توزیعهای Jade به بعد علاوه بر موارد فوق میتوان از اپراتورهای بیشتری به ویژه sin و cos نیز استفاده کرد.
۴-ماکرو
در بین ویژگیهای ذکر شده برایxacro ، بزرگترین و مفیدترین آنها قابلیت استفاده از ماکروها است.
(یادآوری: ماکرو مجموعه ای از دستورات هست که یکبار نوشته میشه و بارها مورد استفاده قرار میگیره)
۴-۱-یک ماکرو ساده
بیایید نگاهی به یک ماکرو ساده اما بدون کاربرد بیندازیم.
به کد زیر توجه کنید:
<xacro:macro name="default_origin">
<origin xyz="0 0 0" rpy="0 0 0"/>
</xacro:macro>
<xacro:default_origin />
(استفاده از ماکرو در این کد مزیتی ایجاد نمیکند، زیرا اگر origin مشخص نشده باشد، همیشه مقداری یکسان خواهد داشت.)
خروجی این کد به صورت زیر است:
<origin rpy="0 0 0" xyz="0 0 0"/>
در رابطه با ماکروها نکات زیر را به خاطر بسپارید:
- از لحاظ فنی استفاده از المان name لازم نیست ، اما برای قابل استفاده بودن باید آن را مشخص کنید.
- هر جایی که <xacro:$NAME /> آورده شود، محتویات تگ xacro: macro در آنجا جایگزین می شود.
- توجه داشته باشید که اگرچه دو کد بالا دقیقاً یکسان نیستند ، XML تولید شده توسط آنها معادل یکدیگر است.
- اگر xacro با نام مشخصی یافت نشود ، گسترش نخواهد یافت و خطایی ایجاد نمی کند.
۵-ماکرو پارامتری
شما میتوانید ماکروها را به صورت پارامتری تنظیم کنید تا هر بار متن یکسانی را تولید نکنند. در این حالت اگر با عملیات ریاضی نیز ترکیب شود، بسیار قدرتمندتر میشود. ابتدا ، مثالی از یک ماکرو ساده که در ربات pr2 استفاده شده است را بررسی میکنیم.
<xacro:macro name="default_inertial" params="mass">
<inertial>
<mass value="${mass}" />
<inertia ixx="1.0" ixy="0.0" ixz="0.0"
iyy="1.0" iyz="0.0"
izz="1.0" />
</inertial>
</xacro:macro>
در واقع ماکرو مانند یک تابع با نام default_inertial و ورودی mass است که مقادیر مربوط به تگ inertial را تنظیم میکند و به عبارتی پارامتر mass ، ورودی متغیر این تابع است.
این ماکرو را می توان با کد زیر فراخوانی کرد.
<xacro:default_inertial mass="10"/>
کد بالا در واقع تگ inertial را به ازای جرم ۱۰ کیلوگرم تولید میکند.
پارامترها دقیقاً مانند خواص (properties) عمل می کنند و می توانید از آنها در توصیف رباتتان استفاده کنید
همچنین در ماکرو میتوانیدکل یک بلوک را نیز به عنوان پارامتر معرفی کنید و در ادامه از آن استفاده کنید. به مثال زیر توجه کنید.
<xacro:macro name="blue_shape" params="name *shape">
<link name="${name}">
<visual>
<geometry>
<xacro:insert_block name="shape" />
</geometry>
<material name="blue"/>
</visual>
<collision>
<geometry>
<xacro:insert_block name="shape" />
</geometry>
</collision>
</link>
</xacro:macro>
<xacro:blue_shape name="base_link">
<cylinder radius=".42" length=".01" />
</xacro:blue_shape>
در مثال بالا ماکرو blue_shape دارای دو پارامتر ورودی name و shape است که وظیفه آن ایجاد یک لینک و قرار دادن یک بلوک هندسی درون تگ geometry آن است. فراخوانی این ماکرو در سه خط پایانی و با مقداردهی نام base_link و بلوک cylinder به عنوان ورودیهای آن انجام شده است.
در رابطه با این مثال به نکات زیر توجه کنید:
- برای مشخص کردن یک بلوک به عنوان پارامتر ، قبل از نام پارامتر خود باید یک ستاره (*) وارد کنید.
- بلوک را می توان با استفاده از دستور insert_block وارد کرد.
- به هر تعدادی که بخواهید میتوانید بلوک را در مکانهای مختلف وارد کنید.
۶-کاربرد عملی
۶-۱-استفاده از ماکرو برای تعریف یک ربات
در مدلسازی یک ربات اغلب میخواهید چندین شئ مشابه را در مکان های مختلف ایجاد کنید، زیر غالباً ساختار بعضی از رباتها متقارن است. لذا شما می توانید از یک ماکرو و یک عملیات ریاضی ساده برای کاهش میزان کدها در این حالت، استفاده کنید، به عنوان مثال به کدهای زیر که مربوط به تعریف بازوهای ربات pr2 است توجه کنید.
<xacro:macro name="leg" params="prefix reflect">
<link name="${prefix}_leg">
<visual>
<geometry>
<box size="${leglen} 0.1 0.2"/>
</geometry>
<origin xyz="0 0 -${leglen/2}" rpy="0 ${pi/2} 0"/>
<material name="white"/>
</visual>
<collision>
<geometry>
<box size="${leglen} 0.1 0.2"/>
</geometry>
<origin xyz="0 0 -${leglen/2}" rpy="0 ${pi/2} 0"/>
</collision>
<xacro:default_inertial mass="10"/>
</link>
<joint name="base_to_${prefix}_leg" type="fixed">
<parent link="base_link"/>
<child link="${prefix}_leg"/>
<origin xyz="0 ${reflect*(width+.02)} 0.25" />
</joint>
<!-- A bunch of stuff cut -->
</xacro:macro>
<xacro:leg prefix="right" reflect="1" />
<xacro:leg prefix="left" reflect="-1" />
در رابطه با مثال فوق با استفاده از xacro به ترفندهای زیر توجه داشته باشید.
ترفند ۱: از یک پیشوند نام برای بدست آوردن دو شیء به همین نام استفاده کنید.
ترفند ۲: برای محاسبه مبدأ مفصل (تگ origin در joint) از روابط ریاضی استفاده کنید. در شرایطی که اندازه ربات خود را تغییر می دهید ، تغییر یک ویژگی با مقداری ریاضی برای محاسبه افست مفصل باعث دردسر زیادی می شود.
ترفند ۳: استفاده از یک پارامتر reflect و تنظیم آن بر روی ۱ یا -۱٫ ببینید که چگونه در این کد به سادگی با استفاده از پارامتر reflect بازوها در دو طرف بدن قرار داده است. در واقع در هنگام مقدار دهی به تگ origin در تعریف مفصل موقعیت قرارگیری لینک را تعیین میکند.
در این آموزش مشاهده کردید که با استفاده از قابلیتهای xacro تا چه اندازه میتوان مدلسازی ربات را ساختارمند و ساده سازی کرد.