در فصل قبل، جمله طلایی این بود: «RViz پنجره اطلاعات ربات است.» حالا وقتش رسیده که جمله دوم را کامل کنیم: Gazebo آزمایشگاه مجازی ربات است. تفاوت این دو در یک آزمایش ساده روشن میشود: اگر ARCHO را یک متر بالای زمین قرار بدهی، در RViz همانجا در هوا معلق میماند — چون RViz فقط چیزی را نشان میدهد که از شبکه دریافت کرده. اما در Gazebo، گرانش اعمال میشود، ARCHO سقوط میکند و با زمین برخورد میکند.
اگر یک خودرو طراحی کرده باشی، نرمافزار نقشهکشی فقط ظاهرش را نشان میدهد. اما یک شبیهساز رانندگی نشان میدهد آیا حرکت میکند، آیا واژگون میشود، آیا با دیوار برخورد میکند، آیا ترمز میگیرد. Gazebo دقیقاً همین کار را برای ARCHO انجام میدهد — پیش از آنکه حتی یک قطعه واقعی خریداری یا مونتاژ شود.
Gazebo یک موتور فیزیک کامل دارد که این موارد را محاسبه میکند:
| مفهوم فیزیکی | اثر روی ARCHO |
|---|---|
| گرانش | اگر Collision یا Inertia اشتباه باشد، ربات سقوط میکند یا داخل زمین فرو میرود |
| جرم و اینرسی | همان مقادیری که در فصل چهارم برای بدنه و چرخها محاسبه کردیم |
| اصطکاک | اگر خیلی کم باشد، چرخ میچرخد اما ربات حرکت نمیکند (مثل حرکت روی یخ) |
| برخورد (Contact) | تماس چرخ با زمین، بدنه با دیوار انبار |
نسبت سرعت شبیهسازی به زمان واقعی. RTF = 1.0 یعنی یک ثانیه شبیهسازی دقیقاً برابر یک
ثانیه واقعی است. RTF = 0.5 یعنی شبیهسازی نصف سرعت واقعی پیش میرود — معمولاً بهدلیل
Mesh سنگین، تعداد زیاد حسگر، یا سختافزار ضعیف. برای تستهای روزمره، RTF نزدیک به ۱ ایدهآل است.
Gazebo فیزیک را در گامهای زمانی کوچک (مثلاً ۰.۰۰۱ ثانیه) محاسبه میکند. گام کوچکتر یعنی دقت بیشتر اما سرعت کمتر؛ گام بزرگتر یعنی سرعت بیشتر اما دقت کمتر. این همان تعادلی است که در فصل چهارم بین Visual دقیق و Collision ساده دیدیم — دوباره سروکارمان با تبادل دقت در برابر کارایی است.
در Gazebo فقط ربات وجود ندارد — یک World کامل داریم: زمین، دیوارها، قفسهها، نور و
موانع. فایل World (پسوند .sdf یا قدیمیتر .world) این محیط را توصیف میکند.
archo_ws/src/archo_gazebo/
├── worlds/
│ └── warehouse.world
├── models/
├── launch/
└── config/
یک warehouse.world ساده میتواند شامل کف، چند دیوار، چند قفسه فلزی، و نور محیط باشد —
دقیقاً صحنهای که ARCHO در فصلهای بعد باید در آن نقشه بسازد و مسیر پیدا کند.
مسیر معمول ساخت مدل: طراحی در یک نرمافزار CAD → خروجی Mesh → تعریف در URDF/Xacro (فصل ۴) → بارگذاری در Gazebo. اما نکته مهم: URDF فقط ظاهر را تعریف نمیکند؛ برای اینکه Gazebo رفتار فیزیکی درستی نشان دهد، جرم، Inertia، Collision و Joint هم باید معتبر باشند — همانهایی که با دقت در فصل چهارم نوشتیم.
برای ROS 2 Jazzy، مسیر استاندارد gz_ros2_control است (نسل جدید Gazebo، نه Gazebo Classic
قدیمی). ابتدا بستههای لازم را نصب میکنیم:
sudo apt update
sudo apt install \
ros-jazzy-ros-gz \
ros-jazzy-gz-ros2-control \
ros-jazzy-ros2-control \
ros-jazzy-ros2-controllers
حالا داخل archo.urdf.xacro، به هر Joint چرخ میگوییم چه رابطی با Gazebo دارد:
<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>
<gazebo>
<plugin
filename="gz_ros2_control-system"
name="gz_ros2_control::GazeboSimROS2ControlPlugin">
<parameters>$(find archo_bringup)/config/controllers.yaml</parameters>
</plugin>
</gazebo>
| مفهوم | معنی |
|---|---|
command_interface | چه چیزی از بیرون به این Joint فرمان میدهیم (اینجا: سرعت) |
state_interface | چه چیزی از این Joint میخوانیم (موقعیت، سرعت واقعی) |
این بلوک فقط رابط را تعریف میکند؛ اینکه دقیقاً چطور فرمان سرعت را به دو چرخ توزیع کنیم — یعنی خود Controller — موضوع فصل بعد (ros2_control) است.
معماری Launch کامل این چهار مرحله را دنبال میکند:
ros2 launch ros_gz_sim gz_sim.launch.py gz_args:=warehouse.world
ros2 run ros_gz_sim create -topic robot_description -name archo -z 0.2
بعد از ظاهرشدن ربات، بررسی کن که Controllerها فعال شدهاند:
اگر خودکار فعال نشدند:
ros2 run controller_manager spawner joint_state_broadcaster
ros2 run controller_manager spawner diff_drive_controller
یکی از بهترین قابلیتهای Gazebo، شبیهسازی حسگرهاست — LiDAR، دوربین، IMU و انکودر. LiDAR ARCHO را روی
همان laser_link که در فصل چهارم ساختیم، تعریف میکنیم:
<gazebo reference="laser_link">
<sensor name="lidar_sensor" type="gpu_lidar">
<topic>scan</topic>
<update_rate>10</update_rate>
<lidar>
<scan>
<horizontal>
<samples>720</samples>
<min_angle>-3.14159</min_angle>
<max_angle>3.14159</max_angle>
</horizontal>
</scan>
<range>
<min>0.10</min>
<max>12.0</max>
<resolution>0.01</resolution>
</range>
</lidar>
<always_on>true</always_on>
</sensor>
</gazebo>
ros2 topic list | grep scan
ros2 topic hz /scan
در دنیای واقعی، هیچ حسگری کامل نیست — LiDAR ممکن است فاصله ۲.۰۰۰ متر را ۱.۹۹۴ یا ۲.۰۰۶ اندازه بگیرد. اگر شبیهسازی هیچ نویزی نداشته باشد، الگوریتمهایی که رویش تست و تنظیم شدهاند، وقتی به ربات واقعی منتقل شوند ممکن است شکست بخورند. Gazebo امکان اضافهکردن نویز کنترلشده به LiDAR، IMU و دوربین را میدهد تا شبیهسازی به واقعیت نزدیکتر باشد.
نکته مهم: الگوریتمهایی مثل SLAM Toolbox یا Nav2 که در فصلهای بعد میبینیم، اصلاً نمیفهمند LiDAR
واقعی است یا مجازی. آنها فقط به Topic /scan با نوع پیام sensor_msgs/LaserScan
گوش میدهند. همین ویژگی است که Gazebo را به ابزاری قدرتمند برای توسعه بدون سختافزار واقعی تبدیل
میکند.
حالا همهچیز آماده است تا حلقه کامل حرکت ARCHO را ببینیم:
برای تست دستی، یک فرمان حرکت مستقیم بفرست:
ros2 topic pub /diff_drive_controller/cmd_vel geometry_msgs/msg/TwistStamped \
"{twist: {linear: {x: 0.3}, angular: {z: 0.0}}}"
و برای چرخش:
ros2 topic pub /diff_drive_controller/cmd_vel geometry_msgs/msg/TwistStamped \
"{twist: {linear: {x: 0.0}, angular: {z: 0.7}}}"
وقتی به ربات میگویی «با سرعت خطی ۰.۴ متر بر ثانیه برو»، در واقعیت باید سرعت هرکدام از دو چرخ جدا
محاسبه شود. اگر v سرعت خطی، ω سرعت زاویهای، L فاصله بین
چرخها و r شعاع چرخ باشد:
ω_left = (v − ωL/2) / r
ω_right = (v + ωL/2) / r
مثلاً با v=0.4، ω=0، L=0.4، r=0.08، هر دو چرخ
باید ۵ رادیان بر ثانیه بچرخند. این محاسبه را خودمان نمینویسیم — diff_drive_controller
در فصل بعد این تبدیل را برای ما انجام میدهد.
ros2 control list_controllers هر دو Controller را active نشان میدهد./scan در حال انتشار است و در RViz نقاط LiDAR دیده میشوند.
ARCHO حالا فقط یک مدل ایستا نیست — در یک دنیای فیزیکی زندگی میکند، وزنش را حس میکند، با زمین انبار
برخورد میکند، و برای اولین بار با یک فرمان cmd_vel واقعاً حرکت میکند. LiDARش دادههای
واقعگرایانه (با نویز) روی /scan منتشر میکند — همان دو Topic و مفهومی که در فصلهای بعد،
SLAM و Nav2 روی آن بنا میشوند.
پروژه ARCHO اکنون یک Package جدید — archo_gazebo — با یک دنیای انبار و اتصال کامل به فیزیک دارد. هنوز فرمان سرعت را خودمان دستی میفرستیم؛ فصل بعد نشان میدهد چطور ros2_control این فرمانها را بهدرستی بین دو چرخ توزیع میکند.
در فصل هشتم، لایه ros2_control را از نزدیک بررسی میکنیم: Controller Manager، انواع Controller، و تنظیمات دقیق diff_drive_controller که همین امروز بهصورت خلاصه دیدیم.
ros-jazzy-ros2-controllers و مشاهده خطای Controller not found.-z 0.2).