مسیر یادگیری ROS 2  ·  کتاب آموزشی تصویری

فصل هشتم: ros2_control

مترجم بین الگوریتم‌های ROS و موتورهای واقعی یا مجازی
پیش‌نیاز: فصل ۷ (Gazebo)
پروژه پیوسته: ربات ARCHO
ابزار: controller_manager
زمان مطالعه: ۱۰۰ تا ۱۲۰ دقیقه
در این فصل چه می‌خوانیم ۸.۱چه چیزی واقعاً موتور را می‌چرخاند؟ ۸.۲معماری چهار لایه‌ای ۸.۳چهار Controller معروف ۸.۴Command و State Interface روی خود URDF ۸.۵پیکربندی کامل diff_drive_controller ۸.۶شبیه‌سازی و ربات واقعی: یک معماری، دو سخت‌افزار ۸.۷خطاهای رایج ros2_control ۸.۸جمع‌بندی، واژه‌نامه و تمرین‌ها

۸.۱چه چیزی واقعاً موتور را می‌چرخاند؟

تا اینجا لایه‌های زیادی از ARCHO را ساختیم: URDF/Xacro ساختار ربات را تعریف کرد، TF2 موقعیت اجزا را دنبال کرد، RViz آن را نشان داد، Gazebo فیزیک را شبیه‌سازی کرد. اما یک سؤال بزرگ هنوز بی‌جواب مانده: وقتی می‌گوییم «با سرعت ۰.۵ متر بر ثانیه جلو برو»، این عدد ساده چطور به چرخش واقعی دو موتور تبدیل می‌شود؟

📖 تعریف ros2_control

ros2_control یک چارچوب استاندارد برای کنترل سخت‌افزار ربات است — لایه‌ای که بین الگوریتم‌های سطح بالای ROS 2 (مثل Nav2) و سخت‌افزار واقعی یا مجازی ربات قرار می‌گیرد و فرمان‌های نرم‌افزاری را به زبان موتورها ترجمه می‌کند.

🧠 تشبیه ساده: مدیر کارخانه و دستگاه صنعتی

فرض کن مدیر یک کارخانه به کارگر می‌گوید «این محصول را سریع‌تر تولید کن». اما خود دستگاه صنعتی فقط دستورهایی مثل Motor speed = 1500 rpm یا Valve = Open را می‌فهمد. یک نفر باید دستور سطح بالا را به فرمان دقیق ماشین تبدیل کند. Nav2 همان مدیر است؛ موتور همان دستگاه صنعتی؛ و ros2_control همان سرپرست فنی که بینشان ترجمه می‌کند.

مقایسهسؤالی که می‌پرسد
Nav2 (فصل‌های بعد)ربات کجا باید برود؟
MoveIt (فصل ۱۳)بازو چه مسیری باید برود؟
ros2_controlJointها دقیقاً چگونه آن حرکت را اجرا کنند؟
Motor Driverسخت‌افزار پایین‌سطح چطور فرمان را روی سیم می‌فرستد؟

۸.۲معماری چهار لایه‌ای

flowchart TB A["Nav2 / MoveIt / Teleop
فرمان سطح بالا"] --> B["Controller Manager
رئیس کل کنترلرها"] B --> C["Controllers
diff_drive_controller و مشابه"] C --> D["Hardware Interface
مجازی یا واقعی"] D --> E["موتورها / Encoderها / سنسورها"] style B fill:#eef0ff,stroke:#3d4bf5,color:#211f1a,font-weight:bold style D fill:#fdf3e4,stroke:#c8862c,color:#211f1a

Controller Manager قلب ros2_control است: مسئول بارگذاری، فعال‌سازی و غیرفعال‌سازی Controllerها، مدیریت چرخه کنترل، و خواندن/نوشتن روی سخت‌افزار در هر Cycle. در فصل قبل با فرمان ros2 control list_controllers از همین Node پرسیدیم چه Controllerهایی فعال‌اند.

۸.۳چهار Controller معروف

Controllerکار می‌کند باوظیفه
joint_state_broadcasterهر Jointوضعیت لحظه‌ای هر Joint را روی /joint_states منتشر می‌کند
diff_drive_controllerربات‌های دوچرخ دیفرانسیلی (ARCHO)/cmd_vel را می‌گیرد و به سرعت جداگانه چرخ چپ/راست تبدیل می‌کند
joint_trajectory_controllerبازوهای رباتیکیک مسیر زمان‌بندی‌شده از زوایای Joint دریافت و به‌آرامی اجرا می‌کند
forward_command_controllerهر Joint (برای تست)یک فرمان را تقریباً مستقیم به Joint می‌فرستد
🌍 چرا joint_state_broadcaster این‌قدر مهم است

این Controller حلقه‌ای که در فصل پنجم دیدیم را کامل می‌کند: Encoderها → ros2_control → /joint_states → robot_state_publisher → درخت TF → RViz. بدون این Controller، robot_state_publisher هیچ‌وقت نمی‌فهمد چرخ‌های ARCHO الان چقدر چرخیده‌اند، و مدل در RViz ثابت می‌ماند حتی اگر ربات واقعاً حرکت کند.

هر Joint معمولاً با یکی از این سه روش کنترل می‌شود:

نوع فرمانمعنیمثال کاربرد
Positionمفصل را به این زاویه ببربازوی ربات، سرووموتور
Velocityمفصل را با این سرعت بچرخانچرخ‌های ARCHO
Effortاین مقدار نیرو/گشتاور را اعمال کنکنترل نیرو در بازوهای صنعتی

۸.۴Command و State Interface روی خود URDF

در فصل قبل، بلوک زیر را داخل Xacro دیدیم — حالا معنای دقیق هر خط را باز می‌کنیم:

<ros2_control name="ArchoGazeboSystem" type="system">
  <hardware>
    <plugin>gz_ros2_control/GazeboSimSystem</plugin>
  </hardware>
  <joint name="left_wheel_joint">
    <command_interface name="velocity"/>
    <state_interface name="position"/>
    <state_interface name="velocity"/>
  </joint>
  <joint name="right_wheel_joint">
    <command_interface name="velocity"/>
    <state_interface name="position"/>
    <state_interface name="velocity"/>
  </joint>
</ros2_control>

این بلوک می‌گوید: یک سیستم کنترل به‌نام ArchoGazeboSystem داریم؛ سخت‌افزارش پلاگین شبیه‌سازی Gazebo است؛ و Jointهای چپ و راست هرکدام فرمان سرعت می‌گیرند (command_interface) و موقعیت و سرعت واقعی‌شان را گزارش می‌دهند (state_interface).

🔧 چرا این بلوک داخل URDF است، نه یک فایل جدا؟

چون URDF از قبل می‌داند Jointها چه نامی دارند و ساختار مکانیکی ربات چیست. منطقی است که تعریف Interfaceهای کنترل هم به همان Jointها متصل باشد، به‌جای اینکه در یک فایل کاملاً جدا و بدون ارتباط مستقیم با ساختار فیزیکی نوشته شود.

۸.۵پیکربندی کامل diff_drive_controller

خود Controllerها در یک فایل YAML جدا تعریف و تنظیم می‌شوند:

# archo_bringup/config/controllers.yaml
controller_manager:
  ros__parameters:
    update_rate: 50
    joint_state_broadcaster:
      type: joint_state_broadcaster/JointStateBroadcaster
    diff_drive_controller:
      type: diff_drive_controller/DiffDriveController

diff_drive_controller:
  ros__parameters:
    left_wheel_names: [left_wheel_joint]
    right_wheel_names: [right_wheel_joint]
    wheel_separation: 0.40
    wheel_radius: 0.09
    base_frame_id: base_link
    odom_frame_id: odom
    publish_rate: 50.0
    enable_odom_tf: true
    use_stamped_vel: true
    linear.x.has_velocity_limits: true
    linear.x.max_velocity: 0.7
    linear.x.min_velocity: -0.3
    angular.z.has_velocity_limits: true
    angular.z.max_velocity: 1.5
⚠️ چرا wheel_separation و wheel_radius این‌قدر حساس‌اند

این دو عدد باید دقیقاً با ابعاد واقعی ARCHO (همان‌هایی که در فصل چهارم در Xacro نوشتیم) یکی باشند. اگر wheel_radius اشتباه باشد، سرعت واقعی ربات با سرعت گزارش‌شده فرق می‌کند و Odometry غلط می‌شود؛ اگر wheel_separation اشتباه باشد، زاویه چرخش اشتباه محاسبه می‌شود و ربات کم‌تر یا بیشتر از حد لازم می‌چرخد — خطایی که به‌مرور در ناوبری Drift ایجاد می‌کند.

diff_drive_controller از داده Encoder دو چرخ، حرکت خطی و زاویه‌ای ربات را تخمین می‌زند و روی Topic /odom و Transform odom → base_link منتشر می‌کند — دقیقاً همان حلقه‌ای که در فصل پنجم دیدیم و در فصل نهم عمیق‌تر بررسی می‌کنیم.

۸.۶شبیه‌سازی و ربات واقعی: یک معماری، دو سخت‌افزار

یکی از بزرگ‌ترین مزیت‌های ros2_control این است که معماری نرم‌افزار بین شبیه‌سازی و ربات واقعی تقریباً ثابت می‌ماند — فقط یک لایه پایینی عوض می‌شود:

flowchart LR subgraph SIM["شبیه‌سازی"] C1["Controller"] --> H1["Simulated Hardware Interface"] --> G1["Gazebo Joint"] end subgraph REAL["ربات واقعی"] C2["Controller (همان کد)"] --> H2["Real Hardware Interface"] --> G2["Motor Driver / CAN"] end style C1 fill:#eef0ff,stroke:#3d4bf5 style C2 fill:#eef0ff,stroke:#3d4bf5

این یعنی diff_drive_controller که امروز روی ARCHOی شبیه‌سازی‌شده تست کردیم، همان Controller — بدون تغییر کد — روی ARCHOی واقعی هم کار می‌کند؛ فقط Hardware Interface از gz_ros2_control/GazeboSimSystem به یک درایور واقعی (که در فصل هجدهم می‌سازیم) تغییر می‌کند. این دقیقاً همان قدرت لایه‌بندی معماری ROS 2 است که در فصل اول درباره‌اش صحبت کردیم.

۸.۷خطاهای رایج ros2_control

خطاپیامد
دو Controller روی یک Interface یکسانConflict و رد شدن فعال‌سازی یکی از آن‌ها
Encoder Sign اشتباهچرخ جلو می‌رود اما Encoder مقدار منفی می‌دهد → Odometry اشتباه
واحد اشتباه (RPM به‌جای rad/s)ROS انتظار Position بر حسب radian و Velocity بر حسب rad/s دارد؛ عدم تطابق باعث رفتار غیرمنتظره می‌شود
Wheel Radius اشتباهخطای سرعت و Odometry
Update Rate نامناسبکنترل ناپایدار یا واکنش کند
نبود Timeout روی فرماناگر ارتباط قطع شود، موتور آخرین فرمان را برای همیشه ادامه می‌دهد — خطرناک در دنیای واقعی
تمرین سخت‌تر

فرض کن wheel_radius در YAML به‌اشتباه ۰.۰۷ نوشته شده، درحالی‌که شعاع واقعی چرخ ARCHO ۰.۰۹ متر است. اگر ربات واقعاً ۱ متر حرکت کند، Odometry چه عددی گزارش می‌دهد — بیشتر یا کمتر از ۱ متر؟ استدلالت را بنویس.

۸.۸جمع‌بندی فصل هشتم

حالا می‌دانیم دقیقاً چه اتفاقی بین یک فرمان ساده cmd_vel و چرخش واقعی موتورهای ARCHO می‌افتد: Controller Manager، دو Controller اصلی (joint_state_broadcaster و diff_drive_controller)، و یک Hardware Interface که می‌تواند مجازی یا واقعی باشد — بدون اینکه لایه‌های بالایی‌تر لازم باشد چیزی درباره‌اش بدانند.

🌍 ارتباط با پروژه اصلی

پروژه ARCHO اکنون یک controllers.yaml کامل و درست‌تنظیم‌شده دارد. فرمان cmd_vel دیگر یک عدد خام نیست — واقعاً به چرخش دو چرخ تبدیل می‌شود و Odometry تولید می‌کند.

فصل بعد چه چیزی اضافه می‌کند

در فصل نهم، همین /odom که diff_drive_controller تولید می‌کند را با داده IMU ترکیب می‌کنیم — Sensor Fusion با robot_localization — تا تخمین موقعیت ARCHO دقیق‌تر و پایدارتر شود.

واژه‌نامه فصل هشتم

ros2_control
چارچوب استاندارد ROS 2 برای ترجمه فرمان‌های نرم‌افزاری به حرکت واقعی یا مجازی سخت‌افزار.
Controller Manager
Nodeای که Controllerها را بارگذاری، فعال، غیرفعال و مدیریت می‌کند.
Controller
واحد نرم‌افزاری که یک رفتار حرکتی مشخص (مثل رانندگی دوچرخ یا مسیر مفاصل) را کنترل می‌کند.
Hardware Interface
لایه‌ای که Controller را به سخت‌افزار واقعی یا شبیه‌سازی‌شده وصل می‌کند.
command_interface / state_interface
به‌ترتیب: چه فرمانی به Joint می‌دهیم، و چه وضعیتی از آن می‌خوانیم.
diff_drive_controller
Controller استاندارد برای ربات‌های دوچرخ دیفرانسیلی؛ cmd_vel را به سرعت هر چرخ تبدیل می‌کند و Odometry تولید می‌کند.

خطاهای رایج فصل هشتم — جمع‌بندی