مسیر یادگیری ROS 2  ·  پیوست عملی

پیوست الف: اتصال SolidWorks به URDF

از مدل مکانیکی ARCHO در SolidWorks تا نمایش زنده در RViz
مخاطب: کسی که بدنه ربات را در SolidWorks طراحی کرده
پیش‌نیاز: فصل ۴ (URDF و Xacro) و فصل ۶ (RViz)
پروژه پیوسته: ربات ARCHO
زمان مطالعه: ۶۰ تا ۹۰ دقیقه

قبل از هر چیز یک تصویر ذهنی بساز. آن‌چه در این پیوست یاد می‌گیری، دقیقاً همین مسیر را طی می‌کند: یک ربات متحرک واقعی که روی کاغذ و در SolidWorks طراحی شده، قدم‌به‌قدم به زبانی ترجمه می‌شود که ROS 2 بفهمد.

یک ربات متحرک صنعتی (AMR) — همان نوع بدنه‌ای که در این پیوست از SolidWorks به URDF تبدیل می‌کنیم
یک ربات متحرک صنعتی (AMR) — دقیقاً همان نوع بدنه‌ای که در ادامه از SolidWorks به URDF تبدیل می‌کنیم: یک شاسی، دو چرخ محرک، و چند Caster برای تعادل
در این پیوست چه می‌خوانیم الف.۱نمای کلی مسیر — از اسمبلی تا RViz الف.۲URDF دقیقاً چیست؟ الف.۳روش درست فکر کردن: از مکانیزم تا درخت Exporter الف.۴آماده‌سازی اسمبلی SolidWorks الف.۵سیستم مختصات ROS و Coordinate System الف.۶Material، جرم و اینرسی الف.۷نصب SolidWorks URDF Exporter الف.۸تعریف Linkها، Jointها و محورها الف.۹Collision در برابر Visual الف.۱۰Export و انتقال فایل‌ها به WSL الف.۱۱ساخت پکیج ROS 2 الف.۱۲تست در RViz و بررسی TF الف.۱۳خطاهای رایج و رفع آن‌ها الف.۱۴نمونه کامل URDF الف.۱۵جمع‌بندی، چک‌لیست و تمرین

الف.۱نمای کلی مسیر: از اسمبلی تا RViz

یک اشتباه رایج این است که تصور کنیم SolidWorks مستقیماً و به‌صورت زنده به ROS 2 وصل می‌شود. این‌طور نیست. مسیر واقعی یک خط لوله (Pipeline) چند مرحله‌ای است: یک اسمبلی مکانیکی در SolidWorks آماده می‌کنیم، یک افزونه (Exporter) آن را به فایل‌های URDF و Mesh تبدیل می‌کند، و بعد این فایل‌ها را داخل یک پکیج ROS 2 عادی قرار می‌دهیم — دقیقاً همان چیزی که در فصل ۴ درباره ساختار URDF بدنه ARCHO یاد گرفتی.

flowchart LR A["اسمبلی SolidWorks"] --> B["SolidWorks URDF Exporter"] B --> C["URDF + STL Meshes"] C --> D["پکیج ROS 2"] D --> E["RViz"] E --> F["Gazebo"] F --> G["ros2_control"] style A fill:#2f7de8,stroke:#ffffff,color:#0b1220,font-weight:bold style C fill:#d98c19,stroke:#ffffff,color:#0b1220,font-weight:bold style E fill:#0f9d78,stroke:#ffffff,color:#0b1220,font-weight:bold style G fill:#8b5cf6,stroke:#ffffff,color:#ffffff,font-weight:bold

هدف این پیوست فقط رساندن مدل مکانیکی ARCHO به یک URDF قابل‌فهم برای ROS 2 و دیدن سالم آن در RViz است. بعد از سالم شدن مدل در RViz، سراغ Gazebo و ros2_control (فصل‌های ۷ و ۸) می‌رویم — نه زودتر.

💡 ایده ساده

SolidWorks یک مترجم مکانیکی است، نه یک درایور زنده. کارش این است که هندسه، جرم و نحوه حرکت قطعات نسبت به هم را یک‌بار به زبان URDF ترجمه کند. از آن به بعد، ROS 2 دیگر کاری به SolidWorks ندارد.

الف.۲URDF دقیقاً چیست؟

URDF مخفف Unified Robot Description Format است: یک فایل متنی XML که به ROS می‌گوید ربات از چه قطعاتی تشکیل شده، هر قطعه به کدام قطعه وصل است، مفصل‌ها کجا هستند، محور چرخش یا حرکت هر مفصل چیست، جرم و مرکز جرم هر قطعه چقدر است، و شکل ظاهری و شکل برخورد فیزیکی قطعات چگونه است.

📖 دو مفهوم اصلی URDF

Link = یک جسم صلب (یک قطعه فیزیکی مستقل)

Joint = اتصال بین دو Link که نحوه حرکت نسبی آن‌ها را مشخص می‌کند

برای مثال، درخت Linkهای ARCHO — همان ربات دوچرخی که در فصل اول معرفی شد — این شکلی است:

flowchart TD A["base_link"] --> B["left_wheel_link"] A --> C["right_wheel_link"] A --> D["caster_link"] A --> E["lidar_link"] A --> F["camera_link"] style A fill:#2f7de8,stroke:#ffffff,color:#0b1220,font-weight:bold

و هر اتصال با یک Joint مستقل تعریف می‌شود:

base_link
   ├── left_wheel_joint  → left_wheel_link
   ├── right_wheel_joint → right_wheel_link
   ├── lidar_joint       → lidar_link
   └── camera_joint      → camera_link

الف.۳روش درست فکر کردن: از مکانیزم تا درخت Exporter

قبل از باز کردن Exporter، یک قدم عقب برو. SolidWorks به تو می‌گوید «این قطعه شاسی است، این چرخ است، این پیچ است، این موتور است، این گیربکس است، این فنر است...» — یعنی اسمبلی را بر اساس تعداد قطعات می‌بیند. اما ROS اصلاً نمی‌پرسد «چند قطعه داری؟». ROS فقط یک سؤال دارد:

💡 مهم‌ترین جمله این پیوست

کدام چیزها با هم مثل یک جسم واحد حرکت می‌کنند، و بین این جسم‌ها چه حرکت‌هایی وجود دارد؟
Link = جسم صلبJoint = رابطه حرکتی بین دو جسم صلب.

قدم اول: قبل از Exporter، مکانیزم را روی کاغذ حل کن

هیچ‌وقت مستقیم نرو داخل URDF Exporter. اول از خودت بپرس: کدام چیز ثابت است؟ کدام چیز می‌چرخد؟ کدام چیز بالا‌پایین می‌رود؟ کدام چیز با چیز دیگری یکپارچه حرکت می‌کند؟

مثلاً یک ربات ساده با شاسی، موتور، گیربکس، شفت، چرخ و دوربین را در نظر بگیر. فرض کن موتور و پوسته گیربکس به هم پیچ شده‌اند، شفت خروجی گیربکس می‌چرخد، چرخ روی شفت قفل است، و دوربین روی بدنه ثابت است. از نظر ROS این‌طور دسته‌بندی می‌شود:

شاسی + دوربین      → یک Link (یا Linkهای Fixed)
موتور + پوسته گیربکس → یک Link
شفت + چرخ           → یک Link

چرا شفت و چرخ یک Link شدند؟ چون شفت نسبت به چرخ حرکت ندارد — هر دو با هم می‌چرخند.

قانون طلایی Link

💡 یک سؤال، همیشه

هر بار شک کردی دو قطعه یک Link باشند یا نه، فقط بپرس: آیا این دو قطعه نسبت به هم حرکت می‌کنند؟ اگر جواب «نه» بود، احتمالاً می‌توانند یک Link باشند.

مثلاً Wheel + Hub + Shaft اگر همه قفل باشند، یک wheel_link می‌شوند. اما Wheel و Gearbox Housing نسبت به هم حرکت دارند، پس نمی‌توانند یک Link باشند.

قانون طلایی Joint

Joint یک قطعه فیزیکی نیست. wheel_joint، arm_joint، suspension_joint و camera_joint هیچ‌کدام قطعه‌ای در SolidWorks نیستند؛ Joint فقط می‌گوید «این Link نسبت به آن Link چگونه حرکت می‌کند». مثلاً:

gearbox_link
↓ wheel_joint
wheel_link

یعنی wheel_link نسبت به gearbox_link می‌چرخد.

چهار نوع Joint که ۹۰٪ پروژه‌ها را پوشش می‌دهد

نوعحرکتمثال
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
base_link camera_link fixed بدون حرکت نسبی gearbox_link wheel_link continuous دوران نامحدود arm_link_1 arm_link_2 revolute دوران محدود (-۹۰° تا +۹۰°) base_link linear_stage_link prismatic حرکت خطی
چهار نوع Joint که ۹۰٪ پروژه‌ها را پوشش می‌دهد — رنگ هر برچسب با رنگ همان Joint در جدول بالا یکی است

چرا فنر گیج‌کننده است

فنر یک جسم صلب نیست — کوتاه می‌شود، بلند می‌شود، تغییر شکل می‌دهد؛ ولی URDF معمولی بر اساس Rigid Body ساخته شده. پس باید دو چیز را از هم جدا کنی:

⚠️ Spring mesh ≠ Suspension motion

اگر می‌خواهی خود فنر در RViz دیده شود، یک spring_link بساز و Mesh فنر را داخلش بگذار. اما رفتار فنر (این‌که گیربکس نسبت به شاسی بالا‌پایین می‌رود) را یک Joint دیگر تعریف می‌کند، مثلاً base_link ↓suspension_joint gearbox_link. مدل ظاهری فنر و حرکتی که فنر ایجاد می‌کند، دو چیز جدا هستند.

درخت مفهومی در برابر درخت واقعی Exporter — نکته‌ای که قبلاً باعث خطا شد

فرض کن پروژه‌ای داری با بدنه، گیربکس و چرخ، و حرکت واقعی این‌طور است: بدنه ↓تعلیق گیربکس ↓دوران چرخ. درخت مفهومی URDF (فقط برای فهمیدن سیستم) این‌طور دیده می‌شود:

base_link
└── suspension_joint
    └── gearbox_link
        └── wheel_joint
            └── wheel_link
⚠️ این همان درختی نیست که در SolidWorks Exporter می‌سازی

داخل SolidWorks URDF Exporter فقط Link می‌سازی؛ Jointها هرگز Node جداگانه در Tree نیستند. همان سیستم بالا در Exporter این‌طور می‌شود:

❌ اشتباه:
base_link
└── suspension_joint
    └── gearbox_link

✅ درست:
base_link
└── gearbox_link

و داخل تنظیمات همان gearbox_link می‌نویسی: Joint Name: suspension_joint.

Exporter چطور رابطه Parent/Child را می‌فهمد؟

فرض کن این 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.

SolidWorks URDF Exporter base_link gearbox_link (انتخاب‌شده) wheel_link Link Properties Link Name gearbox_link Joint Name suspension_joint Joint Type Fixed ▾ Parent Link base_link یعنی چه؟ base_link → suspension_joint → gearbox_link
کلیک روی هر Link در Tree، فرم Link Properties همان Link را باز می‌کند — Joint در این فرم تعریف می‌شود، نه به‌عنوان Node جدا در Tree

Component یعنی چه؟

هر 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 کنی.

Root Link و قوانین نام‌گذاری

هر 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 و Coordinate System

اگر 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 بساز و مبدأ را جایی منطقی مثل مرکز شاسی یا مرکز محور چرخ‌ها بگذار.

مثال کامل: از مکانیزم تا Tree نهایی Exporter

فرض کن پروژه جدیدی داری با 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

چک‌لیست قبل از زدن Preview and Export

✅ قبل از Export این‌ها را چک کن
  • در Tree فقط Link وجود دارد — Joint را به شکل Node نساخته‌ام
  • base_link فقط یک عدد است و Joint ندارد
  • هر Child Link یک Joint Name مشخص دارد
  • همه Linkها اسم Unique دارند
  • همه Jointها اسم Unique دارند
  • Componentهای هر Link مشخص و درست Assign شده‌اند
  • قطعاتی که با هم حرکت می‌کنند در یک Link هستند
  • قطعاتی که نسبت به هم حرکت دارند، در Linkهای جدا هستند
  • نوع هر Joint (fixed/continuous/revolute/prismatic) درست انتخاب شده
  • Axis هر Joint با هندسه واقعی مدل تطبیق دارد
  • Coordinate System منطقی است (X-جلو، Y-چپ، Z-بالا)
  • Mass Properties هر قطعه منطقی است
📖 پنج قانونی که اگر فقط این‌ها را حفظ کنی، کافی است

۱. 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، پیاده می‌کنیم.

الف.۴آماده‌سازی اسمبلی SolidWorks

این مهم‌ترین بخش کل کار است. بیشتر مشکلات 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

Fix کردن شاسی، Float کردن چرخ‌ها

در SolidWorks باید شاسی اصلی ثابت (Fixed) باشد. در FeatureManager روی شاسی راست‌کلیک کن و بزن:

Right Click → Fix

کنار شاسی باید علامت (f) دیده شود، مثلاً (f) base_chassis. چرخ‌ها نباید Fixed باشند — اگر بودند، روی آن‌ها راست‌کلیک کن و بزن Float. ساختار صحیح این‌طور دیده می‌شود:

(f) base_chassis
(-) left_wheel
(-) right_wheel
(-) caster

Mateهای درست برای هر چرخ

Exporter از ساختار اسمبلی، Mateها و انتخاب‌های تو برای ساخت Jointها استفاده می‌کند. برای هر چرخ حداقل این دو Mate لازم است:

⚠️ اشتباه رایج

گزینه Lock Rotation نباید فعال باشد، وگرنه چرخ در URDF خروجی نمی‌تواند بچرخد. برای تست، چرخ را با ماوس بچرخان — اگر حول محور خودش آزادانه می‌چرخد، Mate درست تعریف شده است. همین کار را برای هر دو چرخ (چپ و راست) انجام بده.

الف.۵سیستم مختصات ROS و Coordinate System

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
Triad آبی سه‌محوری X-Y-Z که SolidWorks هنگام ساخت یک Coordinate System نشان می‌دهد
همین Triad آبی رنگ را در SolidWorks می‌بینی — قبل از ادامه‌ی کار همیشه جهت X، Y و Z را با این شکل مقایسه کن

مبدأ را ترجیحاً یکی از این نقاط قرار بده: مرکز هندسی شاسی، وسط محور دو چرخ، روی سطح زمین زیر مرکز ربات، یا مرکز صفحه پایه.

در یک اسمبلی واقعی، معمولاً این کار را نه یک‌بار، بلکه برای هر Link و هر Joint جداگانه تکرار می‌کنی — یک Coordinate System برای شاسی، یکی برای هر چرخ، یکی برای هر Caster. نتیجه‌اش شبیه این می‌شود: ده‌ها Triad کوچک، هرکدام روی نقطه‌ای که بعداً محل دقیق یک Joint خواهد بود:

یک اسمبلی واقعی SolidWorks با چندین Coordinate System تعریف‌شده روی چرخ‌ها و Casterها
هر Triad آبی روی این اسمبلی، یک Coordinate System مستقل است — بعداً هرکدام دقیقاً به یک Joint در URDF Exporter وصل می‌شود
🔧 نکته مهندسی

برای یک ربات متحرک، ساختار حرفه‌ای‌تر داشتن دو Link است: base_footprint که روی زمین قرار دارد، و base_link که در مرکز فیزیکی شاسی است. برای اولین Export همین که فقط base_link داشته باشی کافی است؛ base_footprint را بعداً دستی به URDF اضافه می‌کنیم — همان‌طور که در فصل ۵ (TF2) درباره درخت قاب‌های مختصات صحبت کردیم.

الف.۶Material، جرم و اینرسی

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 صحبت کردیم.

صفحه Configure Link Properties — جایی که جرم به عدد تبدیل می‌شود

بعد از این‌که همه Jointها را تعریف کردی، Exporter یک صفحه دیگر هم نشانت می‌دهد: Configure Link Properties. این‌جا دیگر حرف از حرکت نیست؛ حرف از این است که هر Link چقدر سنگین است، جرمش کجا متمرکز شده، و ظاهرش را با چه رنگی ببینیم.

صفحه Configure Link Properties در SolidWorks URDF Exporter با فیلدهای Mass، Moment of Inertia، Visual/Collision Origin، Mesh Detail و Color
صفحه Configure Link Properties برای left_wheel — بخش بالا (Inertial) از خود CAD محاسبه می‌شود، بخش پایین (Visual and Collision Meshes) ظاهر و رفتار فیزیکی Link را تعیین می‌کند

این صفحه هم دو تکه اصلی دارد؛ بیا هرکدام را جدا باز کنیم.

بخش بالا: Inertial — همان چیزی که چند خط قبل درباره‌اش حرف زدیم

اگر 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 دوباره می‌بینیمش.

بخش پایین: Visual and Collision Meshes

فیلدیعنی چه
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، تا رنگ یک‌بار تعریف و در همه‌جا استفاده شود.
🔧 یادآوری قانون طلایی Visual/Collision

این صفحه برای هر Link یک‌بار Visual و یک‌بار Collision می‌سازد — و همان قاعده‌ای که چند صفحه بعد با جزئیات بیشتر می‌بینیم همین‌جا هم برقرار است: Mesh دقیق برای Visual خوب است، اما برای Collision بهتر است از یک Box یا Cylinder ساده استفاده کنی تا Gazebo مجبور نباشد برخورد را با هزاران مثلث کوچک محاسبه کند.

الف.۷نصب SolidWorks URDF Exporter

ابزار کلاسیک و شناخته‌شده، پروژه SolidWorks to URDF Exporter از سازمان ROS است. این ابزار اسمبلی SolidWorks را به URDF، فایل‌های Mesh و ساختار اولیه پکیج ROS تبدیل می‌کند.

بعد از نصب Exporter:

  1. SolidWorks را باز کن.
  2. برو به Tools → Add-Ins.
  3. گزینه SolidWorks to URDF Exporter را پیدا کن.
  4. هر دو تیک Active Add-ins و Start Up را فعال کن.

بعد از این مرحله، دستور Export باید در پایین منوی Tools ظاهر شود: Tools → Export as URDF.

⚠️ اگر Exporter در Add-ins دیده نشد

بررسی کن که SolidWorks هنگام نصب بسته بوده باشد، Installer را با Run as administrator اجرا کرده باشی، نسخه Exporter با نسخه SolidWorks سازگار باشد، Windows فایل DLL را Block نکرده باشد، و SolidWorks را پس از نصب Restart کرده باشی.

🔧 نکته مهندسی — ابزار جدیدتر

برای نسخه‌های جدید SolidWorks، ابزار جدیدتری به نام sw2robot نیز معرفی شده که به‌صورت Standalone از COM استفاده می‌کند، Mateها را تحلیل می‌کند و یک ویرایشگر مرورگری برای اصلاح محور، Joint Limit و Collision دارد. این ابزار می‌تواند در نسخه‌هایی که Add-in کلاسیک مشکل دارد جایگزین مناسبی باشد. با این حال، مسیر آموزشی این پیوست با Exporter کلاسیک است، چون ساختار URDF حاصل از آن را باید دستی بشناسی — درست همان چیزی که در فصل ۴ به‌صورت خام یاد گرفتی.

الف.۹Collision در برابر Visual

هر 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>
⚠️ چرا STL پیچیده برای Collision خوب نیست؟

چون باعث می‌شود شبیه‌سازی سنگین شود، برخوردها ناپایدار شوند، ربات بلرزد، Gazebo پردازش بیشتری انجام دهد و چرخ‌ها به جزئیات کوچک بدنه گیر کنند. قاعده ساده: Visual Mesh دقیق، Collision Mesh ساده.

برای کیفیت Mesh هم همین منطق را رعایت کن؛ کیفیت خیلی بالا حجم فایل را زیاد می‌کند و RViz و Gazebo را کندتر می‌کند. برای ARCHO، Visual Mesh با کیفیت متوسط یا خوب و Collision با اشکال ساده یا ساده‌شده کافی است.

الف.۱۰Export و انتقال فایل‌ها به WSL

یک مسیر ساده در ویندوز انتخاب کن و از فاصله، حروف فارسی یا کاراکترهای خاص در مسیر پرهیز کن:

مسیر بدمسیر خوب
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

انتقال از Windows به WSL

اگر Workspace تو ~/ros2_ws است و خروجی در ویندوز C:\ros_exports\archo_description قرار دارد، در WSL این مسیر برابر است با /mnt/c/ros_exports/archo_description.

$ cd ~/ros2_ws/src
$ cp -r /mnt/c/ros_exports/archo_description .
$ ls
archo_bringup archo_description
$ sudo apt install tree
$ tree ~/ros2_ws/src/archo_description

اصلاح اسم فایل‌های STL

لینوکس به حروف بزرگ و کوچک حساس است؛ base_link.STL و base_link.stl دو فایل کاملاً متفاوت‌اند. بهتر است همه Meshها را lowercase کنی:

$ cd ~/ros2_ws/src/archo_description/meshes
$ mv base_link.STL base_link.stl
$ mv left_wheel_link.STL left_wheel_link.stl
$ mv right_wheel_link.STL right_wheel_link.stl

سپس داخل URDF نیز نام‌ها را از base_link.STL به base_link.stl اصلاح کن.

⚠️ مسیر Mesh را بررسی کن

ساختار صحیح مسیر همیشه package://package_name/folder/file است، مثلاً <mesh filename="package://archo_description/meshes/base_link.stl"/>. نباید مسیر ویندوز (C:\Users\...) یا مسیر مطلق لینوکس (/home/...) در URDF باقی مانده باشد.

الف.۱۱ساخت پکیج ROS 2

CMakeLists.txt

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()

package.xml

<?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>

Launch File برای RViz

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 ساختار ربات را نمایش دهند.

Build

$ cd ~/ros2_ws
$ source /opt/ros/jazzy/setup.bash
$ colcon build --packages-select archo_description --symlink-install
Summary: 1 package finished
$ source install/setup.bash
$ ros2 pkg prefix archo_description
/home/pouya_mn/ros2_ws/install/archo_description

الف.۱۲تست در RViz و بررسی TF

💡 یک میان‌بر قبل از ROS: Online URDF Viewer

قبل از این‌که اصلاً سراغ ترمینال و ROS بروی، یک راه سریع‌تر برای دیدن نتیجه کارت هست: یک URDF Viewer آنلاین که فایل‌های URDF، XACRO، STL و DAE را با Drag & Drop در همان مرورگر باز می‌کند و مدل سه‌بعدی و حرکت اولیه Jointها را نشان می‌دهد. این ابزار جای RViz یا Gazebo را نمی‌گیرد، اما برای یک چک سریع — آیا جهت ربات درست است؟ چرخ‌ها سر جایشان‌اند؟ Meshها لود شدند؟ — عالی است، و قبل از این‌که وقت بگذاری و همه‌چیز را در WSL راه بیندازی، خطاهای واضح را زودتر نشانت می‌دهد.

بعد از این چک سریع، نوبت بررسی جدی‌تر می‌رسد. ابتدا فایل URDF را قبل از هرچیز بررسی کن:

$ sudo apt install liburdfdom-tools ros-jazzy-urdf-tutorial ros-jazzy-joint-state-publisher-gui
$ check_urdf src/archo_description/urdf/archo.urdf
robot name is: archo
---------- Successfully Parsed XML ---------------
root Link: base_link has 5 child(ren)
child(1): left_wheel_link
child(2): right_wheel_link
child(3): caster_link
child(4): lidar_link
child(5): camera_link

سپس اجرا کن:

$ ros2 launch archo_description display.launch.py

در RViz: در قسمت Fixed Frame بنویس base_link، روی Add بزن، و دو نمایشگر RobotModel و TF را اضافه کن. اگر مدل درست باشد، شاسی و چرخ‌ها دیده می‌شوند، درخت TF کامل است، و با Joint State Publisher می‌توان Jointها را تغییر داد.

محیط RViz با پنل Displays سمت چپ، صحنه سه‌بعدی در وسط، و پنل Views سمت راست
محیط RViz — پنل Displays سمت چپ (همان‌جا که RobotModel و TF را اضافه می‌کنی)، صحنه سه‌بعدی در وسط، و پنل Views سمت راست برای کنترل دوربین

اگر مدل دیده نمی‌شود اما خطایی هم نیست، این Topicها را بررسی کن:

$ ros2 topic list
/joint_states
/robot_description
/tf
/tf_static
$ ros2 run tf2_tools view_frames

دستور آخر بعد از چند ثانیه فایل 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

الف.۱۴نمونه کامل URDF

این نمونه فقط برای درک ساختار است — اینرسی‌ها و ابعاد (اندازه شاسی، شعاع چرخ) نمونه‌اند، با مقادیر فصل ۴ یکی نیستند، و نباید مستقیم برای 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 قبل از اتصال به کنترلر واقعی.

✅ چک‌لیست این پیوست
  • اسمبلی اصلی ARCHO را باز و بازبینی کردم
  • شاسی را Fix و چرخ‌ها را Float کردم
  • چرخ‌ها را با Mate صحیح (Concentric + Coincident/Distance) متصل کردم
  • Material و Mass Properties هر قطعه را بررسی کردم
  • یک ROS Coordinate System (X-جلو، Y-چپ، Z-بالا) ساختم
  • SolidWorks URDF Exporter را فعال کردم
  • base_link، left_wheel_link و right_wheel_link را تعریف کردم
  • URDF و Meshها را Export و به ~/ros2_ws/src منتقل کردم
  • با check_urdf فایل را بررسی کردم
  • ربات را بدون خطا در RViz دیدم و درخت TF را تأیید کردم
تمرین عملی

اسمبلی 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 معرفی شد.

واژه‌نامه

URDF
Unified Robot Description Format — فایل XML که ساختار مکانیکی ربات را برای ROS توصیف می‌کند.
Link
یک جسم صلب مستقل در درخت URDF.
Joint
اتصال بین دو Link که نوع و محور حرکت نسبی آن‌ها را مشخص می‌کند.
Continuous Joint
مفصلی که بدون محدودیت زاویه‌ای می‌چرخد — مناسب چرخ‌های محرک.
Revolute Joint
مفصلی که فقط در یک بازه زاویه‌ای مشخص حرکت می‌کند.
Prismatic Joint
مفصلی که به‌جای دوران، حرکت خطی می‌کند — مثل ریل، جک یا بالابر.
Fixed Joint
اتصال بدون هیچ حرکت نسبی — برای سنسورهایی مثل LiDAR و دوربین.
Mimic Joint
حالتی که حرکت یک Joint مستقیماً از روی Joint دیگری محاسبه می‌شود — مثل دو انگشت یک Gripper.
Visual
هندسه‌ای که کاربر در RViz/Gazebo می‌بیند؛ می‌تواند دقیق و پیچیده باشد.
Collision
هندسه‌ای که موتور فیزیک برای محاسبه برخورد استفاده می‌کند؛ باید ساده باشد.
Inertial
تگی در URDF که جرم، مرکز جرم و تانسور اینرسی هر Link را مشخص می‌کند.
base_footprint
Link کمکی روی سطح زمین، جدا از base_link که در مرکز فیزیکی شاسی است.
package://
پیشوند مسیر استاندارد ROS برای ارجاع به فایل‌های داخل یک پکیج، مثل Meshها.