قبل از هر چیز یک تصویر ذهنی بساز. آنچه در این پیوست یاد میگیری، دقیقاً همین مسیر را طی میکند: یک ربات متحرک واقعی که روی کاغذ و در SolidWorks طراحی شده، قدمبهقدم به زبانی ترجمه میشود که ROS 2 بفهمد.
یک اشتباه رایج این است که تصور کنیم SolidWorks مستقیماً و بهصورت زنده به ROS 2 وصل میشود. اینطور نیست. مسیر واقعی یک خط لوله (Pipeline) چند مرحلهای است: یک اسمبلی مکانیکی در SolidWorks آماده میکنیم، یک افزونه (Exporter) آن را به فایلهای URDF و Mesh تبدیل میکند، و بعد این فایلها را داخل یک پکیج ROS 2 عادی قرار میدهیم — دقیقاً همان چیزی که در فصل ۴ درباره ساختار URDF بدنه ARCHO یاد گرفتی.
هدف این پیوست فقط رساندن مدل مکانیکی ARCHO به یک URDF قابلفهم برای ROS 2 و دیدن سالم آن در RViz است. بعد از سالم شدن مدل در RViz، سراغ Gazebo و ros2_control (فصلهای ۷ و ۸) میرویم — نه زودتر.
SolidWorks یک مترجم مکانیکی است، نه یک درایور زنده. کارش این است که هندسه، جرم و نحوه حرکت قطعات نسبت به هم را یکبار به زبان URDF ترجمه کند. از آن به بعد، ROS 2 دیگر کاری به SolidWorks ندارد.
URDF مخفف Unified Robot Description Format است: یک فایل متنی XML که به ROS میگوید ربات از چه قطعاتی تشکیل شده، هر قطعه به کدام قطعه وصل است، مفصلها کجا هستند، محور چرخش یا حرکت هر مفصل چیست، جرم و مرکز جرم هر قطعه چقدر است، و شکل ظاهری و شکل برخورد فیزیکی قطعات چگونه است.
Link = یک جسم صلب (یک قطعه فیزیکی مستقل)
Joint = اتصال بین دو Link که نحوه حرکت نسبی آنها را مشخص میکند
برای مثال، درخت Linkهای ARCHO — همان ربات دوچرخی که در فصل اول معرفی شد — این شکلی است:
و هر اتصال با یک Joint مستقل تعریف میشود:
base_link
├── left_wheel_joint → left_wheel_link
├── right_wheel_joint → right_wheel_link
├── lidar_joint → lidar_link
└── camera_joint → camera_link
قبل از باز کردن Exporter، یک قدم عقب برو. SolidWorks به تو میگوید «این قطعه شاسی است، این چرخ است، این پیچ است، این موتور است، این گیربکس است، این فنر است...» — یعنی اسمبلی را بر اساس تعداد قطعات میبیند. اما ROS اصلاً نمیپرسد «چند قطعه داری؟». ROS فقط یک سؤال دارد:
کدام چیزها با هم مثل یک جسم واحد حرکت میکنند، و بین این جسمها چه حرکتهایی وجود دارد؟
Link = جسم صلب — Joint = رابطه حرکتی بین دو جسم صلب.
هیچوقت مستقیم نرو داخل URDF Exporter. اول از خودت بپرس: کدام چیز ثابت است؟ کدام چیز میچرخد؟ کدام چیز بالاپایین میرود؟ کدام چیز با چیز دیگری یکپارچه حرکت میکند؟
مثلاً یک ربات ساده با شاسی، موتور، گیربکس، شفت، چرخ و دوربین را در نظر بگیر. فرض کن موتور و پوسته گیربکس به هم پیچ شدهاند، شفت خروجی گیربکس میچرخد، چرخ روی شفت قفل است، و دوربین روی بدنه ثابت است. از نظر ROS اینطور دستهبندی میشود:
شاسی + دوربین → یک Link (یا Linkهای Fixed)
موتور + پوسته گیربکس → یک Link
شفت + چرخ → یک Link
چرا شفت و چرخ یک Link شدند؟ چون شفت نسبت به چرخ حرکت ندارد — هر دو با هم میچرخند.
هر بار شک کردی دو قطعه یک Link باشند یا نه، فقط بپرس: آیا این دو قطعه نسبت به هم حرکت میکنند؟ اگر جواب «نه» بود، احتمالاً میتوانند یک Link باشند.
مثلاً Wheel + Hub + Shaft اگر همه قفل باشند، یک wheel_link میشوند. اما Wheel و Gearbox Housing نسبت به هم حرکت دارند، پس نمیتوانند یک Link باشند.
Joint یک قطعه فیزیکی نیست. wheel_joint، arm_joint، suspension_joint
و camera_joint هیچکدام قطعهای در SolidWorks نیستند؛ Joint فقط میگوید «این Link نسبت به
آن Link چگونه حرکت میکند». مثلاً:
gearbox_link
↓ wheel_joint
wheel_link
یعنی wheel_link نسبت به gearbox_link میچرخد.
| نوع | حرکت | مثال |
|---|---|---|
fixed | هیچ حرکتی ندارد | دوربین پیچشده به شاسی: base_link ↓fixed camera_link |
continuous | دوران نامحدود | چرخ، موتور، شفت دورانی: gearbox_link ↓continuous wheel_link |
revolute | دوران محدود | مفصل بازو بین ۹۰- تا ۹۰+ درجه: arm_link_1 ↓revolute arm_link_2 |
prismatic | حرکت خطی | ریل خطی، جک، بالاسکرو، بالابر: base_link ↓prismatic linear_stage_link |
فنر یک جسم صلب نیست — کوتاه میشود، بلند میشود، تغییر شکل میدهد؛ ولی URDF معمولی بر اساس Rigid Body ساخته شده. پس باید دو چیز را از هم جدا کنی:
اگر میخواهی خود فنر در RViz دیده شود، یک spring_link بساز و Mesh فنر را داخلش بگذار.
اما رفتار فنر (اینکه گیربکس نسبت به شاسی بالاپایین میرود) را یک Joint دیگر تعریف
میکند، مثلاً base_link ↓suspension_joint gearbox_link. مدل ظاهری فنر و حرکتی که فنر
ایجاد میکند، دو چیز جدا هستند.
فرض کن پروژهای داری با بدنه، گیربکس و چرخ، و حرکت واقعی اینطور است: بدنه ↓تعلیق گیربکس ↓دوران چرخ. درخت مفهومی URDF (فقط برای فهمیدن سیستم) اینطور دیده میشود:
base_link
└── suspension_joint
└── gearbox_link
└── wheel_joint
└── wheel_link
داخل SolidWorks URDF Exporter فقط Link میسازی؛ Jointها هرگز Node جداگانه در Tree نیستند. همان سیستم بالا در Exporter اینطور میشود:
❌ اشتباه:
base_link
└── suspension_joint
└── gearbox_link
✅ درست:
base_link
└── gearbox_link
و داخل تنظیمات همان gearbox_link مینویسی: Joint Name: suspension_joint.
فرض کن این Tree را در Exporter ساختی:
base_link
└── gearbox_link
└── wheel_link
وقتی روی gearbox_link کلیک میکنی، مینویسی Link Name: gearbox_link و Joint Name: suspension_joint. Exporter از همین دو خط میفهمد Parent = base_link، Child = gearbox_link، Joint = suspension_joint. همین کار را روی wheel_link با Joint Name: wheel_joint و Joint Type: Continuous تکرار میکنی تا Exporter بفهمد gearbox_link ↓wheel_joint wheel_link.
هر Link باید بداند کدام قطعات SolidWorks متعلق به او هستند. مثلاً gearbox_link ممکن است
شامل Gearbox_Housing.SLDPRT، Motor_Housing.SLDPRT و
Motor_Bracket.SLDPRT باشد — چون این سه قطعه نسبت به هم حرکت ندارند، همه را به همان یک Link
Assign میکنی. برای wheel_link هم ممکن است Wheel.SLDPRT،
Hub.SLDPRT و Output_Shaft.SLDPRT را Assign کنی.
هر URDF باید فقط یک ریشه داشته باشد — معمولاً base_link — یعنی همهچیز در نهایت به آن وصل است:
base_link
├── left_wheel_link
├── right_wheel_link
├── lidar_link
├── camera_link
└── arm_link
اگر camera_link به هیچ چیز وصل نباشد، URDF دو ریشه مستقل خواهد داشت — همان خطای Two root links found که در جدول خطاهای رایج هم دیدیم.
از ابتدا یک قرارداد نامگذاری ثابت بگذار — اجباری نیست ولی فوقالعاده مفید است:
Link → آخرش _link (base_link, left_wheel_link, gearbox_link, camera_link, lidar_link)
Joint → آخرش _joint (left_wheel_joint, camera_joint, suspension_joint)
و هیچ اسم تکراری نداشته باش — نه دو Link به نام یکسان، نه دو Joint به نام یکسان. تنها استثنا خود base_link است که چون Root است، اصلاً Joint ندارد (Joint Name برایش خالی میماند)؛ هر Link دیگری باید حتماً یک Joint Name داشته باشد.
اگر Joint متحرک باشد، باید ROS بداند حول کدام محور حرکت میکند: 1 0 0 برای X،
0 1 0 برای Y، 0 0 1 برای Z. اگر شفت چرخ در راستای Y باشد،
axis = 0 1 0 — اما هیچوقت از روی عادت نگو «چرخ همیشه محور Y است»؛ باید مدل واقعی را نگاه
کنی. همانطور که در بخش سیستم مختصات این پیوست گفتیم، ROS ترجیح میدهد X = Forward،
Y = Left، Z = Up، پس قبل از Export یک ROS_Coordinate_System در
SolidWorks بساز و مبدأ را جایی منطقی مثل مرکز شاسی یا مرکز محور چرخها بگذار.
فرض کن پروژه جدیدی داری با Body، Motor، Gearbox، Output Shaft، Wheel و Camera. حرکت واقعی اینطور است: Body نسبت به Motor+Gearbox ثابت (fixed)، Motor/Gearbox نسبت به Output Shaft+Wheel دوران دارد (rotation)، و Body نسبت به Camera ثابت (fixed) است. پس Linkها اینها میشوند:
base_link
gearbox_link
wheel_link
camera_link
و درخت واقعی داخل SolidWorks Exporter:
base_link
├── gearbox_link
│ └── wheel_link
└── camera_link
با تنظیمات زیر روی هر Child Link:
gearbox_link → Joint Name: gearbox_mount_joint | Joint Type: Fixed
wheel_link → Joint Name: wheel_joint | Joint Type: Continuous
camera_link → Joint Name: camera_joint | Joint Type: Fixed
base_link فقط یک عدد است و Joint ندارد۱. Link = جسم صلب.
۲. Joint = حرکت بین دو Link.
۳. قطعاتی که نسبت به هم حرکت ندارند → یک Link.
۴. داخل SolidWorks Exporter، Tree فقط از Linkها ساخته میشود.
۵. Joint هر Child Link داخل تنظیمات همان Child Link تعریف میشود — نه بهعنوان Node جدا.
و کل جریان فکری هر پروژه جدید باید همین مسیر باشد:
قطعات CAD
↓
کدامها با هم حرکت میکنند؟
↓
Rigid Body Groups
↓
Links
↓
حرکت بین Links
↓
Joints
↓
Conceptual URDF Tree
↓
SolidWorks Tree = Links Only
↓
Assign Components
↓
Configure Joint on Child Link
↓
Preview
↓
Export URDF
↓
ROS 2 validation
این همان منطقی است که در ادامه این پیوست، قدمبهقدم و روی مثال واقعی ARCHO، پیاده میکنیم.
این مهمترین بخش کل کار است. بیشتر مشکلات URDF از خود Exporter نیست؛ از اسمبلی نامرتب SolidWorks میآید.
هر قطعهای که قرار است نسبت به قطعه دیگر حرکت کند، باید یک Link مستقل باشد. قطعاتی که هرگز نسبت به هم حرکت نمیکنند، لازم نیست Link جداگانه داشته باشند.
فرض کن شاسی ARCHO این قطعات را دارد: شاسی، کاور، باتری، برد الکترونیک، براکتها، چرخ چپ، چرخ راست، چرخ هرزگرد (Caster)، LiDAR و دوربین. چون شاسی، کاور، باتری، برد و براکتها هرگز نسبت به هم حرکت نمیکنند، همگی بخشی از یک Link واحد میشوند:
base_link
├── chassis
├── cover
├── battery
├── electronics
└── brackets
اما چرخها و سنسورها باید Link مستقل داشته باشند:
left_wheel_link
right_wheel_link
caster_link
lidar_link
camera_link
ساختار اسمبلی پیشنهادی در SolidWorks تقریباً به این شکل است:
archo_assembly.SLDASM
│
├── base_subassembly.SLDASM
│ ├── chassis.SLDPRT
│ ├── top_cover.SLDPRT
│ ├── battery.SLDPRT
│ ├── motor_mount_left.SLDPRT
│ └── motor_mount_right.SLDPRT
│
├── left_wheel.SLDPRT
├── right_wheel.SLDPRT
├── caster.SLDPRT
├── lidar.SLDPRT
└── camera.SLDPRT
در SolidWorks باید شاسی اصلی ثابت (Fixed) باشد. در FeatureManager روی شاسی راستکلیک کن و بزن:
Right Click → Fix
کنار شاسی باید علامت (f) دیده شود، مثلاً (f) base_chassis. چرخها نباید Fixed باشند — اگر بودند، روی آنها راستکلیک کن و بزن Float. ساختار صحیح اینطور دیده میشود:
(f) base_chassis
(-) left_wheel
(-) right_wheel
(-) caster
Exporter از ساختار اسمبلی، Mateها و انتخابهای تو برای ساخت Jointها استفاده میکند. برای هر چرخ حداقل این دو Mate لازم است:
Mate → ConcentricMate → Coincident یا Mate → Distance
گزینه Lock Rotation نباید فعال باشد، وگرنه چرخ در URDF خروجی نمیتواند بچرخد. برای تست،
چرخ را با ماوس بچرخان — اگر حول محور خودش آزادانه میچرخد، Mate درست تعریف شده است. همین کار را برای
هر دو چرخ (چپ و راست) انجام بده.
ROS از یک قرارداد مختصات استاندارد استفاده میکند: X به جلو، Y به چپ، Z به بالا.
نمای بالا:
+X
جلوی ربات
↑
|
+Y ← [ ARCHO ] → -Y
|
↓
-X
نمای جانبی:
+Z
↑
|
|
└────→ +X
اگر سیستم مختصات مدل SolidWorks تو با این قرارداد یکی نباشد، ARCHO ممکن است در RViz خوابیده دیده شود، وارونه باشد، به سمت اشتباه قرار بگیرد، یا چرخها حول محور اشتباه بچرخند.
برای رفع این مشکل از ریشه، در SolidWorks یک سیستم مختصات اختصاصی بساز:
Insert
→ Reference Geometry
→ Coordinate System
نامش را مثلاً ROS_Coordinate_System بگذار و محورها را اینطور تنظیم کن:
X = Forward
Y = Left
Z = Up
مبدأ را ترجیحاً یکی از این نقاط قرار بده: مرکز هندسی شاسی، وسط محور دو چرخ، روی سطح زمین زیر مرکز ربات، یا مرکز صفحه پایه.
در یک اسمبلی واقعی، معمولاً این کار را نه یکبار، بلکه برای هر Link و هر Joint جداگانه تکرار میکنی — یک Coordinate System برای شاسی، یکی برای هر چرخ، یکی برای هر Caster. نتیجهاش شبیه این میشود: دهها Triad کوچک، هرکدام روی نقطهای که بعداً محل دقیق یک Joint خواهد بود:
برای یک ربات متحرک، ساختار حرفهایتر داشتن دو Link است: base_footprint که روی زمین قرار
دارد، و base_link که در مرکز فیزیکی شاسی است. برای اولین Export همین که فقط
base_link داشته باشی کافی است؛ base_footprint را بعداً دستی به URDF اضافه
میکنیم — همانطور که در فصل ۵ (TF2) درباره درخت قابهای مختصات صحبت کردیم.
URDF فقط یک تصویر سهبعدی نیست؛ برای شبیهسازی فیزیکی به مشخصات دینامیکی هم نیاز دارد. SolidWorks میتواند از Material و هندسه هر قطعه، جرم، مرکز جرم، ممان اینرسی و تانسور اینرسی را محاسبه کند.
برای هر قطعه یا Subassembly، یک Material واقعی تعریف کن:
Right Click on Material
→ Edit Material
مثلاً Aluminum 6061، ABS، Steel یا Rubber — هرکدام که به قطعه واقعی نزدیکتر است. بعد بررسی کن:
Evaluate
→ Mass Properties
باید اعداد منطقی برای Mass، Center of Mass، Principal Axes و Moments of Inertia ببینی. Exporter میتواند این اطلاعات را مستقیماً به تگ <inertial> در URDF منتقل کند.
اگر Material تعریف نشده باشد یا جرم اشتباه محاسبه شود، در Gazebo ممکن است ربات پرتاب شود، چرخها بلرزند، مدل داخل زمین فرو برود، شبیهسازی ناپایدار شود، یا ARCHO با یک تماس کوچک واژگون شود. این دقیقاً همان دسته خطاهایی است که در فصل ۷ (Gazebo) درباره اهمیت Inertia صحبت کردیم.
بعد از اینکه همه Jointها را تعریف کردی، Exporter یک صفحه دیگر هم نشانت میدهد: Configure Link Properties. اینجا دیگر حرف از حرکت نیست؛ حرف از این است که هر Link چقدر سنگین است، جرمش کجا متمرکز شده، و ظاهرش را با چه رنگی ببینیم.
left_wheel — بخش بالا (Inertial) از خود CAD محاسبه میشود، بخش پایین (Visual and Collision Meshes) ظاهر و رفتار فیزیکی Link را تعیین میکنداین صفحه هم دو تکه اصلی دارد؛ بیا هرکدام را جدا باز کنیم.
اگر Material را درست تعریف کرده باشی، Exporter خودش این اعداد را از CAD میکشد بیرون — دیگر لازم نیست حدس بزنی:
| فیلد | یعنی چه |
|---|---|
| Mass (kg) | جرم واقعی Link، محاسبهشده از حجم و Density مادهٔ انتخابی — نه یک عدد فرضی. |
| Inertial Origin (x, y, z) | محل مرکز جرم (Center of Mass) نسبت به قاب Link. لازم نیست دقیقاً روی صفر باشد — مثلاً x=-0.0001, y=-0.025, z=0 کاملاً طبیعی است. |
| Moment of Inertia — ixx, iyy, izz | مقاومت جسم در برابر چرخش حول هر یک از سه محور اصلی، بر حسب kg·m². |
| Moment of Inertia — ixy, ixz, iyz | جملههای برهمکنشی تانسور اینرسی؛ برای اجسام تقریباً متقارن معمولاً عددهای بسیار کوچکی هستند. |
این شش عدد با هم ماتریس اینرسی Link را میسازند و مستقیم به تگ <inertial> در URDF میروند — همان تگی که چند خط بعد در نمونه کامل URDF دوباره میبینیمش.
| فیلد | یعنی چه |
|---|---|
| Origin (m) / Roll-Pitch-Yaw | اگر Mesh دقیقاً روی مبدأ Link نباشد، همینجا جابهجایی و چرخش لازم را وارد میکنی. |
| Mesh Detail — Coarse | مثلث کمتر → فایل کوچکتر، بارگذاری سریعتر، بار محاسباتی کمتر برای RViz و Gazebo. |
| Mesh Detail — Fine | مثلث بیشتر → ظاهر دقیقتر، ولی فایل بزرگتر و سنگینتر. برای بیشتر پروژههای ROS، Fine واقعاً مزیت محسوسی نمیدهد. |
| Color — Red / Green / Blue | رنگ Visual Mesh، هرکدام عددی بین ۰ تا ۱. |
| Color — Alpha | میزان شفافیت: ۱ یعنی کاملاً کدر، ۰ یعنی کاملاً شفاف. |
| Material name | اسمی که میتوان بین چند Link مشترک گذاشت، مثلاً wheel_rubber یا black_plastic، تا رنگ یکبار تعریف و در همهجا استفاده شود. |
این صفحه برای هر Link یکبار Visual و یکبار Collision میسازد — و همان قاعدهای که چند صفحه بعد با جزئیات بیشتر میبینیم همینجا هم برقرار است: Mesh دقیق برای Visual خوب است، اما برای Collision بهتر است از یک Box یا Cylinder ساده استفاده کنی تا Gazebo مجبور نباشد برخورد را با هزاران مثلث کوچک محاسبه کند.
ابزار کلاسیک و شناختهشده، پروژه SolidWorks to URDF Exporter از سازمان ROS است. این ابزار اسمبلی SolidWorks را به URDF، فایلهای Mesh و ساختار اولیه پکیج ROS تبدیل میکند.
بعد از نصب Exporter:
Tools → Add-Ins.SolidWorks to URDF Exporter را پیدا کن.Active Add-ins و Start Up را فعال کن.بعد از این مرحله، دستور Export باید در پایین منوی Tools ظاهر شود: Tools → Export as URDF.
بررسی کن که SolidWorks هنگام نصب بسته بوده باشد، Installer را با Run as administrator
اجرا کرده باشی، نسخه Exporter با نسخه SolidWorks سازگار باشد، Windows فایل DLL را Block نکرده باشد،
و SolidWorks را پس از نصب Restart کرده باشی.
برای نسخههای جدید SolidWorks، ابزار جدیدتری به نام sw2robot نیز معرفی شده که بهصورت
Standalone از COM استفاده میکند، Mateها را تحلیل میکند و یک ویرایشگر مرورگری برای اصلاح محور،
Joint Limit و Collision دارد. این ابزار میتواند در نسخههایی که Add-in کلاسیک مشکل دارد جایگزین
مناسبی باشد. با این حال، مسیر آموزشی این پیوست با Exporter کلاسیک است، چون ساختار URDF حاصل از آن
را باید دستی بشناسی — درست همان چیزی که در فصل ۴ بهصورت خام یاد گرفتی.
اسمبلی اصلی ARCHO را باز کن و برو به Tools → Export as URDF. یک پنجره URDF Exporter باز میشود که سمت چپش یک درخت خالی است — همان جایی که قرار است Linkها یکییکی در آن ظاهر شوند. برای اینکه بدانی مقصد نهایی کجاست، اول یک نمونه واقعی از یک درخت کاملشده را ببین:
base_link در ریشه، دو چرخ محرک، و چهار Caster که هرکدام خودش یک زیر-Link برای چرخ کوچکش دارد — دقیقاً همان چیزی که چند خط بعد قدمبهقدم میسازیمشهر سطر این درخت را که باز کنی، Exporter دو دکمه کوچک به تو نشان میدهد: یکی برای اضافهکردن یک Link فرزند جدید، یکی برای تعریف Component (اینکه کدام قطعات SolidWorks داخل این Link هستند). قدمبهقدم همین کار را برای ARCHO انجام میدهیم.
اولین Link باید قطعه پایه ربات باشد: base_link. برای آن تمام قطعات ثابت شاسی را انتخاب کن — بدنه، کاور، براکتهای ثابت، باتری، کامپیوتر، بردهای الکترونیکی و موتورهایی که بدنه ثابت دارند.
در Exporter، نام Link را دقیق و بدون فاصله وارد کن؛ فقط از حروف کوچک انگلیسی، عدد و خط زیر استفاده کن:
| نام بد | نام خوب |
|---|---|
| Base Link | base_link |
| Main Body | chassis |
| Robot Chassis 1 | base_link |
| شاسی اصلی | base_link |
یک Child Link برای base_link بساز به نام left_wheel_link و مشخص کن:
Parent Link: base_link
Child Link: left_wheel_link
Joint Name: left_wheel_joint
برای نوع Joint دو انتخاب رایج داری:
| نوع Joint | کاربرد |
|---|---|
continuous | چرخی که میتواند بدون محدودیت بچرخد — انتخاب درست برای چرخهای محرک ARCHO |
revolute | مفصلی که فقط در یک بازه میچرخد، مثلاً بازوی ربات بین ۹۰- تا ۹۰+ درجه |
برای چرخ ARCHO، Joint Type: Continuous را انتخاب کن.
در ROS محور Joint با یک بردار سهمؤلفهای مشخص میشود، مثلاً <axis xyz="0 1 0"/>.
سه حالت رایج: 1 0 0 برای چرخش حول X، 0 1 0 برای چرخش حول Y، و
0 0 1 برای چرخش حول Z. برای یک ربات دوچرخ معمولی که جلو در راستای X است، محور شفت چرخها
معمولاً Y است، پس <axis xyz="0 1 0"/>. ممکن است برای چرخ مقابل جهت معکوس لازم شود
(0 -1 0) — اما ابتدا هر دو را با محور هندسی صحیح Export کن؛ جهت فرمان موتور بعداً در
ros2_control هم قابل اصلاح است.
چرخ راست را دقیقاً مثل چرخ چپ تعریف کن:
Parent Link: base_link
Child Link: right_wheel_link
Joint Name: right_wheel_joint
Joint Type: continuous
Axis: 0 1 0
درخت فعلی اینطور دیده میشود:
base_link
├── left_wheel_joint
│ └── left_wheel_link
└── right_wheel_joint
└── right_wheel_link
وقتی روی یک Joint در درخت دوبار کلیک میکنی، Exporter تو را به یک صفحه کاملتر میبرد — همان جایی که واقعاً تعیین میکنی این مفصل دقیقاً کجاست، حول چه محوری حرکت میکند و چقدر قدرت دارد. اولین بار که این صفحه را میبینی کمی شلوغ به نظر میرسد، اما هر بخش آن یک سؤال ساده جواب میدهد:
left_wheel_joint — Parent، Child، Origin، Axis، Limit، Dynamics و Safety Controller همه در یک نگاهبیا این صفحه را مثل یک نقشه بخوانیم، بخش به بخش:
| بخش | یعنی چه |
|---|---|
| Parent Link / Child Link | همان دو سر Joint — مثلاً base_link و left_wheel. اینجا همان رابطهای است که در بخش قبل دربارهاش حرف زدیم. |
| Joint Name / Joint Type | اسم Joint و نوعش (fixed / continuous / revolute / prismatic). |
| Coordinates | اشاره به همان Coordinate Systemی که قبلاً در SolidWorks ساختی — Exporter از روی آن، محل و جهت Joint نسبت به Parent را حساب میکند. |
| Axis | همان Reference Axisی که ساختی — میگوید مفصل حول کدام محور میچرخد یا در کدام راستا سر میخورد. |
| Origin — Position (m) | مختصات x / y / z محل Joint نسبت به Parent، بر حسب متر. |
| Origin — Orientation (rad) | Roll / Pitch / Yaw — چرخش قاب Joint نسبت به Parent، بر حسب رادیان. اگر Coordinate Systemها را از اول درست ساخته باشی، اغلب همینجا صفر میماند. |
| Limit — lower / upper | کمینه و بیشینهی حرکت Joint (برای دورانی بر حسب rad، برای خطی بر حسب m). برای continuous معمولاً خالی میماند. |
| Limit — effort | حداکثر نیرو یا گشتاور مجاز Joint (برای دورانی N·m، برای خطی N) — بهتر است از مشخصات واقعی موتور و گیربکس گرفته شود. |
| Limit — velocity | حداکثر سرعت Joint (rad/s یا m/s). برای چرخ میتوان آن را از رابطه ω = v / r حساب کرد، که در آن v سرعت خطی ربات و r شعاع چرخ است. |
| Dynamics — friction / damping | اصطکاک داخلی و مقاومت وابسته به سرعت Joint. این دو عدد بعداً رفتار Joint را در Gazebo نرم یا خشک میکنند و معمولاً نیاز به Tune شدن دارند. |
| Calibration — rising / falling | مرجع کالیبراسیون Joint؛ در بیشتر رباتهای چرخدار ساده لازم نیست. |
| Safety Controller | محدودیتهای نرم (soft lower/upper limit, k position, k velocity) — بیشتر برای بازوهای رباتیک و مفاصل محدود کاربرد دارد؛ برای چرخ continuous معمولاً خالی میماند. |
| Mimic Other Joint | وقتی حرکت یک Joint دقیقاً تابع Joint دیگری است — مثلاً دو انگشت یک Gripper که همیشه همزمان باز و بسته میشوند. |
اگر Coordinate System و Axis را در SolidWorks درست ساخته باشی، اکثر فیلدهای این صفحه — بهخصوص Origin و Axis — خودشان درست پر میشوند. برعکسش هم درست است: هر غلطی که اینجا میبینی معمولاً نشانه این است که باید برگردی و Coordinate System را در قدم قبلی اصلاح کنی، نه اینکه اعداد این صفحه را دستی وصلهپینه کنی.
اگر LiDAR به بدنه پیچ شده و حرکت نمیکند، دو روش داری. روش اول این است که مدل LiDAR را داخل Mesh شاسی نگه داری — ساده است ولی بعداً TF مستقل برای LiDAR نداری. روش دوم و حرفهایتر، ساختن یک Link مستقل با Fixed Joint است:
Parent: base_link
Child: lidar_link
Joint: lidar_joint
Type: fixed
که در URDF اینطور دیده میشود:
<joint name="lidar_joint" type="fixed">
<parent link="base_link"/>
<child link="lidar_link"/>
<origin xyz="0.25 0 0.35" rpy="0 0 0"/>
</joint>
همین کار را برای دوربین هم انجام بده: camera_link، camera_joint، نوع fixed. درخت نهایی Linkها:
base_link
├── left_wheel_link
├── right_wheel_link
├── caster_link
├── lidar_link
└── camera_link
برای تست اولیه، سادهترین و پایدارترین انتخاب این است که Caster را با یک Fixed Joint وصل کنی (caster_joint، type="fixed"). مدل فیزیکی کامل — با دو حرکت مستقل caster_swivel_joint و caster_wheel_joint — در Gazebo پیچیدهتر و گاهی ناپایدار است، پس آن را برای بعد نگه میداریم.
هر Link معمولاً دو مدل هندسی جدا دارد: Visual که کاربر میبیند، و Collision که موتور فیزیک برای برخورد استفاده میکند. Visual میتواند دقیق و زیبا باشد؛ Collision بهتر است ساده باشد.
برای مثال، Visual شاسی میتواند STL پیچیده باشد:
<visual>
<geometry>
<mesh filename="package://archo_description/meshes/base_link.stl"/>
</geometry>
</visual>
اما Collision بهتر است یک شکل ساده مثل Box باشد:
<collision>
<geometry>
<box size="0.60 0.45 0.20"/>
</geometry>
</collision>
چون باعث میشود شبیهسازی سنگین شود، برخوردها ناپایدار شوند، ربات بلرزد، Gazebo پردازش بیشتری انجام دهد و چرخها به جزئیات کوچک بدنه گیر کنند. قاعده ساده: Visual Mesh دقیق، Collision Mesh ساده.
برای کیفیت Mesh هم همین منطق را رعایت کن؛ کیفیت خیلی بالا حجم فایل را زیاد میکند و RViz و Gazebo را کندتر میکند. برای ARCHO، Visual Mesh با کیفیت متوسط یا خوب و Collision با اشکال ساده یا سادهشده کافی است.
یک مسیر ساده در ویندوز انتخاب کن و از فاصله، حروف فارسی یا کاراکترهای خاص در مسیر پرهیز کن:
| مسیر بد | مسیر خوب |
|---|---|
C:\Users\Pouya\Desktop\ربات جدید من\ | C:\ros_exports\archo_description\ |
بعد Export را اجرا کن. خروجی معمولاً شبیه این است:
archo_description/
├── CMakeLists.txt
├── package.xml
├── launch/
├── meshes/
│ ├── base_link.STL
│ ├── left_wheel_link.STL
│ ├── right_wheel_link.STL
│ ├── lidar_link.STL
│ └── camera_link.STL
├── textures/
└── urdf/
└── archo.urdf
اگر Workspace تو ~/ros2_ws است و خروجی در ویندوز C:\ros_exports\archo_description قرار دارد، در WSL این مسیر برابر است با /mnt/c/ros_exports/archo_description.
لینوکس به حروف بزرگ و کوچک حساس است؛ base_link.STL و base_link.stl دو فایل کاملاً متفاوتاند. بهتر است همه Meshها را lowercase کنی:
سپس داخل URDF نیز نامها را از base_link.STL به base_link.stl اصلاح کن.
ساختار صحیح مسیر همیشه package://package_name/folder/file است، مثلاً
<mesh filename="package://archo_description/meshes/base_link.stl"/>. نباید مسیر
ویندوز (C:\Users\...) یا مسیر مطلق لینوکس (/home/...) در URDF باقی مانده
باشد.
cmake_minimum_required(VERSION 3.8)
project(archo_description)
find_package(ament_cmake REQUIRED)
install(
DIRECTORY
launch
meshes
urdf
DESTINATION share/${PROJECT_NAME}
)
ament_package()
<?xml version="1.0"?>
<package format="3">
<name>archo_description</name>
<version>0.0.1</version>
<description>
URDF description package for the ARCHO mobile robot.
</description>
<maintainer email="pouya@example.com">
Pouya Mansournia
</maintainer>
<license>Apache-2.0</license>
<buildtool_depend>ament_cmake</buildtool_depend>
<exec_depend>robot_state_publisher</exec_depend>
<exec_depend>joint_state_publisher</exec_depend>
<exec_depend>joint_state_publisher_gui</exec_depend>
<exec_depend>rviz2</exec_depend>
<exec_depend>xacro</exec_depend>
<export>
<build_type>ament_cmake</build_type>
</export>
</package>
from pathlib import Path
from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description() -> LaunchDescription:
package_name = "archo_description"
urdf_file_name = "archo.urdf"
package_share = Path(get_package_share_directory(package_name))
urdf_path = package_share / "urdf" / urdf_file_name
if not urdf_path.exists():
raise FileNotFoundError(f"URDF file not found: {urdf_path}")
robot_description = urdf_path.read_text(encoding="utf-8")
return LaunchDescription(
[
Node(
package="robot_state_publisher",
executable="robot_state_publisher",
name="robot_state_publisher",
output="screen",
parameters=[{"robot_description": robot_description}],
),
Node(
package="joint_state_publisher_gui",
executable="joint_state_publisher_gui",
name="joint_state_publisher_gui",
output="screen",
),
Node(
package="rviz2",
executable="rviz2",
name="rviz2",
output="screen",
),
]
)
مستندات رسمی ROS 2 برای نمایش URDF از robot_state_publisher استفاده میکند؛ این Node مدل URDF و وضعیت Jointها را به TF تبدیل میکند تا ابزارهایی مثل RViz ساختار ربات را نمایش دهند.
قبل از اینکه اصلاً سراغ ترمینال و ROS بروی، یک راه سریعتر برای دیدن نتیجه کارت هست: یک
URDF Viewer آنلاین
که فایلهای URDF، XACRO، STL و DAE را با
Drag & Drop در همان مرورگر باز میکند و مدل سهبعدی و حرکت اولیه Jointها را نشان میدهد. این ابزار
جای RViz یا Gazebo را نمیگیرد، اما برای یک چک سریع — آیا جهت ربات درست است؟ چرخها سر جایشاناند؟
Meshها لود شدند؟ — عالی است، و قبل از اینکه وقت بگذاری و همهچیز را در WSL راه بیندازی، خطاهای
واضح را زودتر نشانت میدهد.
بعد از این چک سریع، نوبت بررسی جدیتر میرسد. ابتدا فایل URDF را قبل از هرچیز بررسی کن:
سپس اجرا کن:
در RViz: در قسمت Fixed Frame بنویس base_link، روی Add بزن، و دو نمایشگر RobotModel و TF را اضافه کن. اگر مدل درست باشد، شاسی و چرخها دیده میشوند، درخت TF کامل است، و با Joint State Publisher میتوان Jointها را تغییر داد.
اگر مدل دیده نمیشود اما خطایی هم نیست، این Topicها را بررسی کن:
دستور آخر بعد از چند ثانیه فایل frames.pdf میسازد که در WSL با explorer.exe frames.pdf در ویندوز باز میشود و باید همان درخت پنجشاخهای ARCHO را نشان دهد.
| خطا / علامت | علت محتمل | راهحل |
|---|---|---|
| Two root links found | یک Link هیچ Parent Joint ندارد (مثلاً camera_link) | یک <joint type="fixed"> برای آن Link تعریف کن |
| Could not load resource | نام فایل، حروف بزرگ/کوچک یا مسیر package:// اشتباه است | نام پکیج، پوشه meshes، پسوند stl و مسیر را دقیق بررسی کن |
| مدل خیلی بزرگ است | SolidWorks بر حسب میلیمتر Export کرده ولی URDF متر میخواند | در تگ mesh مقدار scale="0.001 0.001 0.001" اضافه کن — فقط اگر Exporter Scale را خودش مدیریت نکرده باشد |
| مدل خیلی کوچک است | Scale دوبار اعمال شده (STL از قبل بر حسب متر بوده) | scale اضافی را از تگ mesh حذف کن |
| ربات خوابیده دیده میشود | سیستم مختصات SolidWorks با قرارداد X-جلو/Y-چپ/Z-بالای ROS یکی نیست | ترجیحاً Coordinate System را در SolidWorks اصلاح کن؛ راهحل موقت: rpy="1.5708 0 0" (۹۰ درجه بر حسب رادیان) |
| چرخ در جای اشتباه است | مقدار origin xyz در Joint اشتباه است | ترتیب xyz = X Y Z و rpy = Roll Pitch Yaw را دوباره بررسی کن — همه زاویهها برحسب رادیاناند |
| چرخ حول محور اشتباه میچرخد | بردار <axis xyz=".."/> اشتباه است | محورهای 1 0 0/0 1 0/0 0 1 را با هندسه واقعی چرخ تطبیق بده |
| چرخ اصلاً نمیچرخد | Joint از نوع fixed ساخته شده | نوع Joint را به continuous تغییر بده |
| Joint State Publisher برای چرخ Slider نشان نمیدهد | Jointهای continuous محدودیت عددی ثابت ندارند | برای تست موقت آن را revolute با limit lower="-3.14" upper="3.14" کن، بعد دوباره continuous |
این نمونه فقط برای درک ساختار است — اینرسیها و ابعاد (اندازه شاسی، شعاع چرخ) نمونهاند، با مقادیر فصل ۴ یکی نیستند، و نباید مستقیم برای ARCHO واقعی استفاده شوند؛ قبل از استفاده هر عدد را با ابعاد واقعی ربات خودت جایگزین کن:
<?xml version="1.0"?>
<robot name="archo">
<link name="base_link">
<visual>
<geometry>
<mesh filename="package://archo_description/meshes/base_link.stl"/>
</geometry>
</visual>
<collision>
<geometry>
<box size="0.60 0.45 0.20"/>
</geometry>
</collision>
<inertial>
<origin xyz="0 0 0"/>
<mass value="20.0"/>
<inertia
ixx="0.40" ixy="0.0" ixz="0.0"
iyy="0.65" iyz="0.0"
izz="0.80"/>
</inertial>
</link>
<link name="left_wheel_link">
<visual>
<geometry>
<mesh filename="package://archo_description/meshes/left_wheel_link.stl"/>
</geometry>
</visual>
<collision>
<geometry>
<cylinder radius="0.10" length="0.04"/>
</geometry>
</collision>
<inertial>
<mass value="0.8"/>
<inertia
ixx="0.004" ixy="0.0" ixz="0.0"
iyy="0.004" iyz="0.0"
izz="0.007"/>
</inertial>
</link>
<joint name="left_wheel_joint" type="continuous">
<parent link="base_link"/>
<child link="left_wheel_link"/>
<origin xyz="0 0.245 -0.10" rpy="0 0 0"/>
<axis xyz="0 1 0"/>
</joint>
<link name="right_wheel_link">
<visual>
<geometry>
<mesh filename="package://archo_description/meshes/right_wheel_link.stl"/>
</geometry>
</visual>
<collision>
<geometry>
<cylinder radius="0.10" length="0.04"/>
</geometry>
</collision>
<inertial>
<mass value="0.8"/>
<inertia
ixx="0.004" ixy="0.0" ixz="0.0"
iyy="0.004" iyz="0.0"
izz="0.007"/>
</inertial>
</link>
<joint name="right_wheel_joint" type="continuous">
<parent link="base_link"/>
<child link="right_wheel_link"/>
<origin xyz="0 -0.245 -0.10" rpy="0 0 0"/>
<axis xyz="0 1 0"/>
</joint>
</robot>
Export گرفتن از SolidWorks پایان کار نیست؛ یک نقطه شروع است. بعد از Export معمولاً این اصلاحات لازم
میشود: اصلاح نام Linkها و Jointها، تبدیل URDF به Xacro (فصل ۴)، سادهکردن Collisionها، بررسی دوباره
Inertia، اضافهکردن base_footprint، اضافهکردن Sensor Frameها، اضافهکردن
ros2_control (فصل ۸)، اضافهکردن پلاگینهای Gazebo (فصل ۷)، تعریف شعاع و فاصله چرخها، و
تست کامل در RViz و سپس Gazebo قبل از اتصال به کنترلر واقعی.
base_link، left_wheel_link و right_wheel_link را تعریف کردم~/ros2_ws/src منتقل کردمcheck_urdf فایل را بررسی کردم
اسمبلی SolidWorks ARCHO (یا هر ربات دیگری که در دست داری) را طبق مراحل این پیوست آماده کن، URDF را
Export بگیر، و آن را در پکیج archo_description بارگذاری کن. سپس با
check_urdf و RViz تأیید کن که مدل بدون خطا نمایش داده میشود و چرخها حول محور صحیح
حرکت میکنند. در پایان، خروجی view_frames را ذخیره کن و درخت TF را با درخت مورد انتظار
این پیوست مقایسه کن.
همهی این مراحل — از آمادهسازی اسمبلی در SolidWorks تا Export گرفتن URDF و بارگذاری آن در یک پکیج ROS 2 — دقیقاً همان مسیری است که در ریپوی warehouse-amr-ros2 پیادهسازی شده: یک ربات AMR انبار که در آن، بدنه و چرخهایش دقیقاً با همین روش از SolidWorks به URDF تبدیل شدهاند. اگر میخواهی یک نمونه کامل و واقعی از این فرایند را ببینی — نه فقط مثال آموزشی — سراغ این ریپو برو.
این پیوست را میتوان کنار این منابع رسمی خواند:
مستندات رسمی ROS 2 — ساخت یک Robot Model متحرک با URDF
مستندات ros2_control — سینماتیک رباتهای متحرک (Mobile Robot Kinematics)
Online URDF Viewer — همان ابزار سریع Drag & Drop که در بخش تست RViz معرفی شد.
base_link که در مرکز فیزیکی شاسی است.