تا اینجا لایههای زیادی از ARCHO را ساختیم: URDF/Xacro ساختار ربات را تعریف کرد، TF2 موقعیت اجزا را دنبال کرد، RViz آن را نشان داد، Gazebo فیزیک را شبیهسازی کرد. اما یک سؤال بزرگ هنوز بیجواب مانده: وقتی میگوییم «با سرعت ۰.۵ متر بر ثانیه جلو برو»، این عدد ساده چطور به چرخش واقعی دو موتور تبدیل میشود؟
ros2_control یک چارچوب استاندارد برای کنترل سختافزار ربات است — لایهای که بین الگوریتمهای سطح بالای ROS 2 (مثل Nav2) و سختافزار واقعی یا مجازی ربات قرار میگیرد و فرمانهای نرمافزاری را به زبان موتورها ترجمه میکند.
فرض کن مدیر یک کارخانه به کارگر میگوید «این محصول را سریعتر تولید کن». اما خود دستگاه صنعتی فقط
دستورهایی مثل Motor speed = 1500 rpm یا Valve = Open را میفهمد. یک نفر
باید دستور سطح بالا را به فرمان دقیق ماشین تبدیل کند. Nav2 همان مدیر است؛ موتور همان دستگاه صنعتی؛
و ros2_control همان سرپرست فنی که بینشان ترجمه میکند.
| مقایسه | سؤالی که میپرسد |
|---|---|
| Nav2 (فصلهای بعد) | ربات کجا باید برود؟ |
| MoveIt (فصل ۱۳) | بازو چه مسیری باید برود؟ |
| ros2_control | Jointها دقیقاً چگونه آن حرکت را اجرا کنند؟ |
| Motor Driver | سختافزار پایینسطح چطور فرمان را روی سیم میفرستد؟ |
Controller Manager قلب ros2_control است: مسئول بارگذاری، فعالسازی و غیرفعالسازی
Controllerها، مدیریت چرخه کنترل، و خواندن/نوشتن روی سختافزار در هر Cycle. در فصل قبل با فرمان
ros2 control list_controllers از همین Node پرسیدیم چه Controllerهایی فعالاند.
| Controller | کار میکند با | وظیفه |
|---|---|---|
joint_state_broadcaster | هر Joint | وضعیت لحظهای هر Joint را روی /joint_states منتشر میکند |
diff_drive_controller | رباتهای دوچرخ دیفرانسیلی (ARCHO) | /cmd_vel را میگیرد و به سرعت جداگانه چرخ چپ/راست تبدیل میکند |
joint_trajectory_controller | بازوهای رباتیک | یک مسیر زمانبندیشده از زوایای Joint دریافت و بهآرامی اجرا میکند |
forward_command_controller | هر Joint (برای تست) | یک فرمان را تقریباً مستقیم به Joint میفرستد |
این Controller حلقهای که در فصل پنجم دیدیم را کامل میکند: Encoderها → ros2_control →
/joint_states → robot_state_publisher → درخت TF → RViz. بدون این Controller،
robot_state_publisher هیچوقت نمیفهمد چرخهای ARCHO الان چقدر چرخیدهاند، و مدل در
RViz ثابت میماند حتی اگر ربات واقعاً حرکت کند.
هر Joint معمولاً با یکی از این سه روش کنترل میشود:
| نوع فرمان | معنی | مثال کاربرد |
|---|---|---|
| Position | مفصل را به این زاویه ببر | بازوی ربات، سرووموتور |
| Velocity | مفصل را با این سرعت بچرخان | چرخهای ARCHO |
| Effort | این مقدار نیرو/گشتاور را اعمال کن | کنترل نیرو در بازوهای صنعتی |
در فصل قبل، بلوک زیر را داخل 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 از قبل میداند Jointها چه نامی دارند و ساختار مکانیکی ربات چیست. منطقی است که تعریف Interfaceهای کنترل هم به همان Jointها متصل باشد، بهجای اینکه در یک فایل کاملاً جدا و بدون ارتباط مستقیم با ساختار فیزیکی نوشته شود.
خود 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
این دو عدد باید دقیقاً با ابعاد واقعی ARCHO (همانهایی که در فصل چهارم در Xacro نوشتیم) یکی باشند.
اگر wheel_radius اشتباه باشد، سرعت واقعی ربات با سرعت گزارششده فرق میکند و Odometry
غلط میشود؛ اگر wheel_separation اشتباه باشد، زاویه چرخش اشتباه محاسبه میشود و ربات
کمتر یا بیشتر از حد لازم میچرخد — خطایی که بهمرور در ناوبری Drift ایجاد میکند.
diff_drive_controller از داده Encoder دو چرخ، حرکت خطی و زاویهای ربات را تخمین میزند و
روی Topic /odom و Transform odom → base_link منتشر میکند — دقیقاً همان
حلقهای که در فصل پنجم دیدیم و در فصل نهم عمیقتر بررسی میکنیم.
یکی از بزرگترین مزیتهای ros2_control این است که معماری نرمافزار بین شبیهسازی و ربات واقعی تقریباً ثابت میماند — فقط یک لایه پایینی عوض میشود:
این یعنی diff_drive_controller که امروز روی ARCHOی شبیهسازیشده تست کردیم، همان
Controller — بدون تغییر کد — روی ARCHOی واقعی هم کار میکند؛ فقط Hardware Interface
از gz_ros2_control/GazeboSimSystem به یک درایور واقعی (که در فصل هجدهم میسازیم) تغییر
میکند. این دقیقاً همان قدرت لایهبندی معماری ROS 2 است که در فصل اول دربارهاش صحبت کردیم.
| خطا | پیامد |
|---|---|
| دو 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 دقیقتر و پایدارتر شود.