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

فصل یازدهم: Navigation2 — مغز حرکت ARCHO

از «کجا هستم؟» تا «چطور برسم؟»
پیش‌نیاز: فصل ۱۰ (SLAM)
پروژه پیوسته: ربات ARCHO
ابزار: Navigation2 (Nav2)
زمان مطالعه: ۱۳۰ تا ۱۵۰ دقیقه
در این فصل چه می‌خوانیم ۱۱.۱Nav2: مغز ربات متحرک ۱۱.۲از نقشه ذخیره‌شده تا موقعیت زنده: Map Server و AMCL ۱۱.۳Footprint: شکل واقعی ARCHO برای Nav2 ۱۱.۴Costmap: نقشه خطر ۱۱.۵دو مغز: Global Planner و Local Controller ۱۱.۶پیکربندی کامل Planner و Controller ۱۱.۷راه‌اندازی کامل و اولین سفر ARCHO ۱۱.۸چرا ربات به مقصد نمی‌رود ۱۱.۹جمع‌بندی، واژه‌نامه و تمرین‌ها

۱۱.۱Nav2: مغز ربات متحرک

در فصل قبل، ARCHO یاد گرفت چطور نقشه انبار را بسازد. حالا سؤال بعدی این است: با داشتن آن نقشه، ARCHO چطور خودش تصمیم بگیرد از کجا برود، از موانع فرار کند، و به مقصد برسد؟ این دقیقاً کاری است که Navigation2 (Nav2) انجام می‌دهد.

🧠 اگر MoveIt مغز بازو بود، Nav2 مغز حرکت است

در فصل‌های قبل دیدیم ros2_control چطور فرمان‌های خام به موتور می‌دهد. Nav2 یک لایه بالاتر از آن نشسته: به‌جای اینکه بگوید «موتور چپ با ۱.۲ رادیان بچرخ»، می‌گوید «باید بروی قفسه ۲۴». این فناوری‌ای است که پشت تقریباً هر AMR، ربات انباری، ربات بیمارستانی و Delivery Robot امروزی قرار دارد.

قبل از این‌که وارد جزئیات شویم، باید چهار سؤال را از هم جدا کنیم — هرکدام به یک بخش متفاوت از Nav2 مربوط است:

سؤالپاسخ‌دهنده
من الان کجا هستم؟Localization (AMCL)
دنیا چه شکلی است؟Map (Map Server)
از کجا رفتن بهتر و امن‌تر است؟Costmap
چطور از اینجا به مقصد برسم؟Global Planner + Local Controller

۱۱.۲از نقشه ذخیره‌شده تا موقعیت زنده: Map Server و AMCL

روز اول ARCHO با SLAM Toolbox نقشه ساخت. از روز دوم به بعد، دیگر نیازی به ساخت دوباره نقشه نیست:

flowchart LR A["نقشه ذخیره‌شده
warehouse.pgm/.yaml"] --> B["Map Server"] B --> C["AMCL"] D["LaserScan
/scan"] --> C C --> E["Transform: map → odom"] style B fill:#eef0ff,stroke:#3d4bf5 style C fill:#eef0ff,stroke:#3d4bf5
📖 AMCL چیست

Adaptive Monte Carlo Localization — با استفاده از داده LiDAR زنده و مقایسه آن با نقشه ذخیره‌شده، به این سؤال پاسخ می‌دهد: «ربات دقیقاً کجای این نقشه ایستاده؟» بر خلاف SLAM Toolbox، AMCL نقشه‌ای نمی‌سازد — فقط موقعیت را روی نقشه‌ای که از قبل داری پیدا می‌کند.

نصب کامل Nav2:

sudo apt install \
  ros-jazzy-navigation2 \
  ros-jazzy-nav2-bringup
⚠️ یک قانون کلی مهم

Navigation2 برای یک ربات سفارشی مثل ARCHO به چهار چیز درست‌شده نیاز دارد: Map، TF، Odometry و LaserScan معتبر، به‌همراه Footprint دقیق. Nav2 هیچ‌کدام از خطاهای لایه‌های پایین‌تر (فصل‌های ۵، ۸ و ۹) را جادویی حل نمی‌کند — روی همان پایه‌ای کار می‌کند که تو ساخته‌ای.

۱۱.۳Footprint: شکل واقعی ARCHO برای Nav2

Nav2 باید بداند ARCHO دقیقاً چه شکل و اندازه‌ای دارد تا بتواند برخورد را پیش‌بینی کند.

# مدل مستطیلی — نمای دقیق‌تر
footprint: "[[0.25, 0.18], [0.25, -0.18], [-0.25, -0.18], [-0.25, 0.18]]"

# یا مدل دایره‌ای ساده
robot_radius: 0.30
خطاپیامد
Footprint کوچک‌تر از ربات واقعیبرخورد واقعی با قفسه یا دیوار
Footprint بزرگ‌تر از ربات واقعیARCHO از درها و راهروهای باریک رد نمی‌شود

۱۱.۴Costmap: نقشه خطر

نقشه معمولی فقط می‌گوید کجا دیوار است. Costmap یک لایه هوشمندتر روی آن نقشه است که می‌گوید کجا رفتن «گران» یا خطرناک است — حتی جاهایی که هنوز دیواری وجود ندارد اما به آن نزدیک است.

Nav2 دو Costmap جدا دارد، هرکدام برای یک هدف متفاوت:

Global CostmapLocal Costmap
Frame مرجعmapodom
پوششکل نقشهیک پنجره کوچک اطراف ربات (مثلاً ۴×۴ متر)
نرخ به‌روزرسانیکند (۱-۲ هرتز)سریع (۵-۱۰ هرتز)
کاربردبرنامه‌ریزی مسیر کلیواکنش آنی به مانع نزدیک
🔧 چرا Local Costmap روی فریم odom است، نه map

Local Controller به یک حرکت نرم و پیوسته نیاز دارد. فریم map ممکن است هر بار AMCL تصحیح انجام دهد کمی جهش کند؛ اما فریم odom همیشه پیوسته و بدون پرش است (یادت هست، فصل ۵). برای کنترل لحظه‌به‌لحظه، پیوستگی مهم‌تر از دقت مطلق است.

# global_costmap.yaml
global_costmap:
  global_costmap:
    ros__parameters:
      global_frame: map
      robot_base_frame: base_link
      update_frequency: 2.0
      publish_frequency: 1.0
      resolution: 0.05
      track_unknown_space: true
      plugins:
        - static_layer
        - obstacle_layer
        - inflation_layer

# local_costmap.yaml
local_costmap:
  local_costmap:
    ros__parameters:
      global_frame: odom
      robot_base_frame: base_link
      rolling_window: true
      width: 4.0
      height: 4.0
      resolution: 0.05
      update_frequency: 10.0
      publish_frequency: 5.0

لایه‌های Costmap

# Obstacle Layer — مانع را از LiDAR اضافه می‌کند
obstacle_layer:
  plugin: nav2_costmap_2d::ObstacleLayer
  observation_sources: scan
  scan:
    topic: /scan
    data_type: LaserScan
    clearing: true
    marking: true
    max_obstacle_height: 2.0
    obstacle_max_range: 3.5
    raytrace_max_range: 4.0

# Inflation Layer — حاشیه امنیتی دور موانع می‌کشد
inflation_layer:
  plugin: nav2_costmap_2d::InflationLayer
  inflation_radius: 0.45
  cost_scaling_factor: 4.0
مفهوممعنی
markingوقتی LiDAR مانعی می‌بیند، آن را روی Costmap ثبت کن
clearingاگر فضایی که قبلاً اشغال بود دوباره آزاد دیده شد، پاکش کن
inflation_radiusشعاع حاشیه امنی دور هر مانع
⚠️ تنظیم اشتباه Inflation Radius

اگر inflation_radius خیلی کم باشد، ARCHO به دیوار و قفسه می‌چسبد و ریسک برخورد بالا می‌رود. اگر خیلی زیاد باشد، راهروهای باریک انبار به‌کل «بسته» دیده می‌شوند و Planner مسیری برایشان پیدا نمی‌کند — حتی اگر عملاً عبور از آن‌ها ممکن باشد.

۱۱.۵دو مغز: Global Planner و Local Controller

یک مثال از زندگی واقعی: وقتی به فرودگاه می‌روی، دو نوع تصمیم کاملاً متفاوت می‌گیری.

برنامه کلی سفرتصمیم لحظه‌ای رانندگی
مثالخانه → بزرگراه → خروجی ۵ → فرودگاهکمی فرمان به چپ، ترمز، دور زدن یک دوچرخه
در Nav2Global PlannerLocal Controller
flowchart TB A["Goal"] --> B["Global Planner"] B --> C["Global Path"] C --> D["Local Controller"] E["Localization + Costmap"] --> B E --> D D --> F["cmd_vel"] F --> G["ros2_control"] G --> H["Wheel Motors"] style B fill:#eef0ff,stroke:#3d4bf5 style D fill:#f4effe,stroke:#8b5cf6

Global Planner

وظیفه‌اش پیدا کردن بهترین مسیر کلی از نقطه شروع تا مقصد است — بدون توجه به این‌که چند درجه فرمان باید چرخید. ورودی‌اش Map + Costmap + Goal است، خروجی‌اش فقط یک Path.

پلاگین Plannerویژگی
NavFnساده و سریع، مبتنی بر جستجوی گراف پایه
Smac Plannerپشتیبانی از A* و Hybrid A*، برای ربات‌های غیر‌دایره‌ای مناسب‌تر
Theta*مسیرهای صاف‌تر با کمترین تعداد پیچ

Local Controller

Planner فقط مسیر کلی را می‌دهد؛ نمی‌گوید موتور چپ چند رادیان بر ثانیه بچرخد. این کار Local Controller است — که هر چند میلی‌ثانیه یک‌بار (مثلاً ۲۰-۵۰ بار در ثانیه) تصمیم می‌گیرد.

Controllerایده اصلی
Pure Pursuitبه‌جای کل مسیر، فقط به یک نقطه چند متر جلوتر نگاه می‌کند — دقیقاً مثل رانندگی که به سپر جلو نگاه نمی‌کنی
DWB (Dynamic Window Approach)ده‌ها ترکیب سرعت خطی و زاویه‌ای را امتحان می‌کند و بهترین را انتخاب می‌کند
MPPI (Model Predictive Path Integral)آینده را شبیه‌سازی می‌کند: «اگر این فرمان را بدهم، ۲ ثانیه بعد کجا خواهم بود؟» و از میان صدها احتمال بهترین را انتخاب می‌کند
🧠 چرا Planner همیشه اجرا نمی‌شود ولی Controller تقریباً همیشه فعال است

اگر Planner هر ۲۰ میلی‌ثانیه دوباره مسیر ۲۰۰ متری را محاسبه کند، پردازنده ربات فلج می‌شود. پس Planner فقط وقتی اجرا می‌شود که لازم باشد: مقصد جدید، مسیر مسدود، یا مانع بزرگ. اما Controller باید دائماً فعال باشد چون ربات هر لحظه در حال حرکت است — یک کارگر که از مقابل ARCHO رد می‌شود نیازی به محاسبه دوباره مسیر کلی ندارد، فقط یک اصلاح کوچک لحظه‌ای لازم است. اما اگر یک لیفتراک کل راهرو را ببندد، Controller دیگر نمی‌تواند کاری کند و از Planner کمک می‌خواهد.

🌍 یک سناریوی واقعی در انبار

سیستم مدیریت انبار به ARCHO می‌گوید «برو قفسه ۲۴». Planner مسیر کلی را طراحی می‌کند و ARCHO حرکت می‌کند. یک کارگر از مقابلش رد می‌شود — Local Controller سرعت را کم می‌کند و مسیر را کمی اصلاح می‌کند، بدون این‌که Planner خبردار شود. چند لحظه بعد یک لیفتراک کل راهرو را می‌بندد — این‌بار اصلاح جزئی کافی نیست، پس Controller دوباره از Planner یک مسیر کاملاً جدید از راهروی دیگر می‌خواهد.

۱۱.۶پیکربندی کامل Planner و Controller برای ARCHO

# planner_server.yaml
planner_server:
  ros__parameters:
    planner_plugins:
      - GridBased
    GridBased:
      plugin: nav2_navfn_planner::NavfnPlanner
      tolerance: 0.5
      use_astar: false
      allow_unknown: true

# controller_server.yaml
controller_server:
  ros__parameters:
    controller_frequency: 20.0
    controller_plugins:
      - FollowPath
    FollowPath:
      plugin: nav2_regulated_pure_pursuit_controller::RegulatedPurePursuitController
      desired_linear_vel: 0.4
      lookahead_dist: 0.6
      min_lookahead_dist: 0.3
      max_lookahead_dist: 0.9
      use_velocity_scaled_lookahead_dist: true
      use_collision_detection: true

۱۱.۷راه‌اندازی کامل و اولین سفر ARCHO

ترتیب راه‌اندازی برای اولین‌بار اهمیت دارد:

ترتیبچه چیزی روشن می‌شود
۱Gazebo (فصل ۷)
۲Robot + Controllers (فصل ۸)
۳Map Server
۴AMCL
۵Nav2 (Planner + Controller + Behavior Tree)
۶RViz

در RViz:

  1. روی 2D Pose Estimate کلیک کن و موقعیت اولیه ARCHO را مشخص کن.
  2. صبر کن تا ذرات AMCL تثبیت شوند (یعنی همه‌شان تقریباً یک نقطه را نشان دهند).
  3. روی Nav2 Goal کلیک کن و مقصد را مشخص کن.
dev@archo:~$ ros2 lifecycle nodes /map_server /amcl /planner_server /controller_server /bt_navigator dev@archo:~$ ros2 run tf2_ros tf2_echo map base_link Translation: [4.821, 2.103, 0.000] Rotation: in Quaternion [0.0, 0.0, 0.021, 0.999]

برای بررسی کامل سیستم:

ros2 topic list
ros2 node list
ros2 lifecycle nodes
ros2 action list
ros2 control list_controllers
RViz2 — Nav2 Goal در انبار ARCHO Goal: Shelf 24 ARCHO Fixed Frame: map
مسیر برنامه‌ریزی‌شده ARCHO (نقطه‌چین سبز) از موقعیت فعلی تا Nav2 Goal، با اجتناب از دو قفسه (بنفش تیره).

۱۱.۸چرا ربات به مقصد نمی‌رود

Goal پذیرفته شد ولی حرکت نمی‌کند

مسیر اصلاً ساخته نمی‌شود

ربات دور خودش می‌چرخد

ARCHO کنار مانع گیر می‌کند

معماری پایانی Nav2 Goal Behavior Tree Planner Server Controller Server diff_drive_controller Map + AMCL → TF LiDAR → Costmaps
معماری پایانی Nav2 که همه اجزای این فصل را در یک نمای واحد کنار هم می‌گذارد.
📖 جمله طلایی این فصل

Navigation2 زمانی خوب کار می‌کند که ربات از نظر TF، Odometry، Footprint و Sensor Data از قبل درست مهندسی شده باشد؛ Nav2 نمی‌تواند خطاهای لایه‌های پایین‌تر را جادویی حل کند.

۱۱.۹جمع‌بندی فصل یازدهم

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

✅ نقطه بازبینی یادگیری
  • می‌توانم تفاوت Map Server و AMCL را توضیح دهم.
  • می‌دانم چرا Local Costmap روی فریم odom تعریف می‌شود، نه map.
  • می‌توانم Marking و Clearing در Obstacle Layer را توضیح دهم.
  • می‌دانم چرا Global Planner به‌ندرت اجرا می‌شود اما Local Controller تقریباً همیشه فعال است.
  • می‌توانم حداقل سه دلیل رایج «حرکت نکردن ربات با وجود پذیرفتن Goal» را نام ببرم.
🌍 ارتباط با پروژه اصلی

پروژه ARCHO اکنون بسته archo_navigation با Map Server، AMCL، Costmapهای دوگانه، Planner Server و Controller Server کامل پیکربندی شده و ARCHO برای اولین بار به‌طور کاملاً خودکار در انبار مسیر پیدا می‌کند.

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

در فصل دوازدهم وارد قلب تصمیم‌گیری Nav2 می‌شویم: Behavior Tree و Lifecycle Nodes — جایی که می‌بینی Nav2 اصلاً یک برنامه خطی نیست، بلکه مثل مغز یک ربات واقعی فکر می‌کند.

واژه‌نامه فصل یازدهم

Navigation2 (Nav2)
مجموعه‌بسته اصلی ROS 2 برای مسیریابی خودکار ربات‌های متحرک.
AMCL
Adaptive Monte Carlo Localization؛ تخمین موقعیت ربات روی نقشه ذخیره‌شده با استفاده از LiDAR.
Footprint
شکل و اندازه دقیق بدنه ربات که برای تشخیص برخورد استفاده می‌شود.
Costmap
لایه‌ای روی نقشه که «هزینه» یا خطر عبور از هر نقطه را نشان می‌دهد.
Inflation Layer
لایه‌ای که حاشیه امنیتی مصنوعی دور موانع واقعی ایجاد می‌کند.
Global Planner
مؤلفه‌ای که مسیر کلی از نقطه شروع تا مقصد را روی کل نقشه پیدا می‌کند.
Local Controller
مؤلفه‌ای که هر لحظه فرمان سرعت واقعی (cmd_vel) را برای دنبال‌کردن مسیر تولید می‌کند.
Pure Pursuit
الگوریتم کنترل که به‌جای کل مسیر، فقط یک نقطه چند متر جلوتر را دنبال می‌کند.

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