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

فصل هفتم: Gazebo — آزمایشگاه مجازی ARCHO

جایی که گرانش، اصطکاک و برخورد برای اولین بار واقعی می‌شوند
پیش‌نیاز: فصل ۴ تا ۶
پروژه پیوسته: ربات ARCHO
ابزار: Gazebo (gz_ros2_control)
زمان مطالعه: ۱۱۰ تا ۱۴۰ دقیقه
در این فصل چه می‌خوانیم ۷.۱از دیدن به زندگی‌کردن ۷.۲Gazebo دقیقاً چه چیزی را شبیه‌سازی می‌کند ۷.۳World: دنیای مجازی انبار ۷.۴وصل‌کردن ARCHO به Gazebo ۷.۵Spawn کردن ربات ۷.۶حسگرهای شبیه‌سازی‌شده و نویز ۷.۷بستن حلقه: cmd_vel تا حرکت واقعی ۷.۸جمع‌بندی، واژه‌نامه و تمرین‌ها

۷.۱از دیدن به زندگی‌کردن

در فصل قبل، جمله طلایی این بود: «RViz پنجره اطلاعات ربات است.» حالا وقتش رسیده که جمله دوم را کامل کنیم: Gazebo آزمایشگاه مجازی ربات است. تفاوت این دو در یک آزمایش ساده روشن می‌شود: اگر ARCHO را یک متر بالای زمین قرار بدهی، در RViz همان‌جا در هوا معلق می‌ماند — چون RViz فقط چیزی را نشان می‌دهد که از شبکه دریافت کرده. اما در Gazebo، گرانش اعمال می‌شود، ARCHO سقوط می‌کند و با زمین برخورد می‌کند.

🧠 تشبیه ساده

اگر یک خودرو طراحی کرده باشی، نرم‌افزار نقشه‌کشی فقط ظاهرش را نشان می‌دهد. اما یک شبیه‌ساز رانندگی نشان می‌دهد آیا حرکت می‌کند، آیا واژگون می‌شود، آیا با دیوار برخورد می‌کند، آیا ترمز می‌گیرد. Gazebo دقیقاً همین کار را برای ARCHO انجام می‌دهد — پیش از آنکه حتی یک قطعه واقعی خریداری یا مونتاژ شود.

flowchart LR A["URDF/Xacro
مدل ربات"] --> B["Gazebo
موتور فیزیک"] B --> C["حرکت، برخورد،
اصطکاک، گرانش"] C --> D["حسگرهای شبیه‌سازی‌شده"] D --> E["ROS 2 Topics"] E --> F["RViz / Nav2 / SLAM / ros2_control"] style B fill:#eef0ff,stroke:#3d4bf5,color:#211f1a,font-weight:bold style E fill:#eafaf3,stroke:#0e9e6e,color:#211f1a

۷.۲Gazebo دقیقاً چه چیزی را شبیه‌سازی می‌کند

Gazebo یک موتور فیزیک کامل دارد که این موارد را محاسبه می‌کند:

مفهوم فیزیکیاثر روی ARCHO
گرانشاگر Collision یا Inertia اشتباه باشد، ربات سقوط می‌کند یا داخل زمین فرو می‌رود
جرم و اینرسیهمان مقادیری که در فصل چهارم برای بدنه و چرخ‌ها محاسبه کردیم
اصطکاکاگر خیلی کم باشد، چرخ می‌چرخد اما ربات حرکت نمی‌کند (مثل حرکت روی یخ)
برخورد (Contact)تماس چرخ با زمین، بدنه با دیوار انبار
📖 Real-Time Factor (RTF)

نسبت سرعت شبیه‌سازی به زمان واقعی. RTF = 1.0 یعنی یک ثانیه شبیه‌سازی دقیقاً برابر یک ثانیه واقعی است. RTF = 0.5 یعنی شبیه‌سازی نصف سرعت واقعی پیش می‌رود — معمولاً به‌دلیل Mesh سنگین، تعداد زیاد حسگر، یا سخت‌افزار ضعیف. برای تست‌های روزمره، RTF نزدیک به ۱ ایده‌آل است.

🔧 Simulation Step

Gazebo فیزیک را در گام‌های زمانی کوچک (مثلاً ۰.۰۰۱ ثانیه) محاسبه می‌کند. گام کوچک‌تر یعنی دقت بیشتر اما سرعت کمتر؛ گام بزرگ‌تر یعنی سرعت بیشتر اما دقت کمتر. این همان تعادلی است که در فصل چهارم بین Visual دقیق و Collision ساده دیدیم — دوباره سروکارمان با تبادل دقت در برابر کارایی است.

۷.۳World: دنیای مجازی انبار

در Gazebo فقط ربات وجود ندارد — یک World کامل داریم: زمین، دیوارها، قفسه‌ها، نور و موانع. فایل World (پسوند .sdf یا قدیمی‌تر .world) این محیط را توصیف می‌کند.

archo_ws/src/archo_gazebo/
├── worlds/
│   └── warehouse.world
├── models/
├── launch/
└── config/
Gazebo — warehouse.world کف انبار با اصطکاک واقعی، دو قفسه فلزی، ARCHO با LiDAR در حال اسکن محیط
شکل ۷.۱ — طرحی از صحنه Gazebo: ARCHO درون یک انبار شبیه‌سازی‌شده با قفسه‌ها، در حالی که پرتوهای LiDAR محیط را اسکن می‌کنند.

یک warehouse.world ساده می‌تواند شامل کف، چند دیوار، چند قفسه فلزی، و نور محیط باشد — دقیقاً صحنه‌ای که ARCHO در فصل‌های بعد باید در آن نقشه بسازد و مسیر پیدا کند.

🌍 مدل از کجا می‌آید

مسیر معمول ساخت مدل: طراحی در یک نرم‌افزار CAD → خروجی Mesh → تعریف در URDF/Xacro (فصل ۴) → بارگذاری در Gazebo. اما نکته مهم: URDF فقط ظاهر را تعریف نمی‌کند؛ برای اینکه Gazebo رفتار فیزیکی درستی نشان دهد، جرم، Inertia، Collision و Joint هم باید معتبر باشند — همان‌هایی که با دقت در فصل چهارم نوشتیم.

۷.۴وصل‌کردن ARCHO به Gazebo

برای 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) است.

۷.۵Spawn کردن ربات

معماری Launch کامل این چهار مرحله را دنبال می‌کند:

flowchart TB A["اجرای Gazebo با warehouse.world"] --> B["انتشار robot_description"] B --> C["Spawn کردن ARCHO داخل World"] C --> D["فعال‌سازی Controller Manager و Controllerها"] style A fill:#eef0ff,stroke:#3d4bf5,color:#211f1a style D fill:#eafaf3,stroke:#0e9e6e,color:#211f1a,font-weight:bold
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ها فعال شده‌اند:

dev@archo:~$ ros2 control list_controllers joint_state_broadcaster active diff_drive_controller active

اگر خودکار فعال نشدند:

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 و دوربین را می‌دهد تا شبیه‌سازی به واقعیت نزدیک‌تر باشد.

🌍 Nav2 و SLAM اهمیتی نمی‌دهند

نکته مهم: الگوریتم‌هایی مثل SLAM Toolbox یا Nav2 که در فصل‌های بعد می‌بینیم، اصلاً نمی‌فهمند LiDAR واقعی است یا مجازی. آن‌ها فقط به Topic /scan با نوع پیام sensor_msgs/LaserScan گوش می‌دهند. همین ویژگی است که Gazebo را به ابزاری قدرتمند برای توسعه بدون سخت‌افزار واقعی تبدیل می‌کند.

۷.۷بستن حلقه: cmd_vel تا حرکت واقعی

حالا همه‌چیز آماده است تا حلقه کامل حرکت ARCHO را ببینیم:

flowchart TB A["/cmd_vel
فرمان سرعت (دستی یا Nav2)"] --> B["diff_drive_controller"] B --> C["ros2_control"] C --> D["چرخ‌های Gazebo"] D --> E["حرکت واقعی ربات در World"] E --> F["/joint_states"] F --> G["robot_state_publisher"] G --> H["TF Tree"] H --> I["RViz"] style A fill:#eef0ff,stroke:#3d4bf5,color:#211f1a style I fill:#eafaf3,stroke:#0e9e6e,color:#211f1a,font-weight:bold

برای تست دستی، یک فرمان حرکت مستقیم بفرست:

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}}}"
🔧 پشت پرده Differential Drive

وقتی به ربات می‌گویی «با سرعت خطی ۰.۴ متر بر ثانیه برو»، در واقعیت باید سرعت هرکدام از دو چرخ جدا محاسبه شود. اگر 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 در فصل بعد این تبدیل را برای ما انجام می‌دهد.

✅ Checkpoint فصل هفتم
  • ARCHO در Gazebo روی زمین می‌ایستد؛ نه سقوط می‌کند، نه منفجر می‌شود، نه شناور است.
  • ros2 control list_controllers هر دو Controller را active نشان می‌دهد.
  • فرمان دستی cmd_vel باعث حرکت واقعی ربات در World می‌شود.
  • /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 که همین امروز به‌صورت خلاصه دیدیم.

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

Gazebo
شبیه‌ساز فیزیکی ربات و محیط؛ گرانش، جرم، اصطکاک، برخورد و حسگرها را محاسبه می‌کند.
World
فایل توصیف محیط شبیه‌سازی — زمین، دیوار، اشیا، نور.
Real-Time Factor (RTF)
نسبت سرعت شبیه‌سازی به زمان واقعی.
gz_ros2_control
پلاگین رسمی که ros2_control را به موتور فیزیک Gazebo متصل می‌کند.
command_interface / state_interface
رابط‌های ورودی (فرمان) و خروجی (وضعیت واقعی) هر Joint در ros2_control.
Spawn
عمل قراردادن یک مدل ربات (از robot_description) داخل یک World در حال اجرا.

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