move_base 패키지와 파라미터 공부

rbiz 푸드 딜리버리 챌린지를 준비하면서 오랜만에 move_base를 써보게 되었는데, 오랜만에 보니 헷갈리기도 헷갈리고 뭐가 뭔지 모르겠는 느낌이었다. 그래서 공부한 내용을 정리하기로 했다.

혹시 몰라 적어두는 나의 SLAM & Navigation 실행환경은 아래와 같다.

  • Ubuntu 20.04.6 LTS Focal Fossa
  • ROS Noetic

move_base란?

move_base 노드는 (모바일)로봇의 navigation stack과 상호작용하기 위한 ROS 인터페이스(토픽, 서비스, 액션, 파라미터)를 제공한다. 아래 그림(화이트 테마를 키고 보자.)은 일반적인 모바일 로봇의 navigation stack 설정을 나타낸 그림이고, 그 중에서도 가운데 네모가 move_base 노드의 구조를 나타낸다.

move_base

파란색 부분은 로봇(플랫폼)에 따라 달라질 수 있는 부분이고, 회색 부분은 부가적이지만, 모든 시스템을 위해 제공된다. move_base 노드는 이러한 시스템 및 부가적으로 발행되는 정보들과 맞물려서, 로봇이 목적 자세(goal pose)를 달성할수 있도록 한다.

아무튼, move_base 부분만 보면

  • global_planner
  • global_costmap
  • local_planner
  • local_costmap
  • recovery_behaviors

로 구성되어 있다.

우선 Costmap이란 것은 grid cell로 구성되어 있는 지도(occupancy grid map)에서 각 셀들이 장애물/지형의 특성 등을 반영한 cost 값을 가지는 것이다.

Recovery Behavior

move_base의 Recovery Behavior 시행 순서는 아래 그림과 같다.

expected_behavior

  1. 사용자가 설정한 영역외의 장애물들을 로봇의 지도에서 제거한다.
  2. 가능하다면, 로봇은 영역을 초기화하기 위해 제자리 회전 수행한다.
  3. 위 과정이 실패하면, 로봇은 모든 장애물들을 제자리 회전이 가능한 사각형 밖으로 제거해버린다. 그리고 또다른 제자리회전을 수행한다.
  4. 만약 죄다 실패한다면, 목표가 도달불가능한지 고려한 후, 그에 따른 보류 메세지를 출력한다.

파라미터 파일(yaml) 설정

move_base의 launch파일을 보면, rosparam으로 여러 yaml파일을 설정해준다. 대략적인 파일은 아래와 같다.

move_base_only.launch  

<launch>
  <node pkg="move_base" type="move_base" respawn="false" name="move_base" output="screen">
    <rosparam file="$(find pkg)/params/costmap_common_params.yaml" command="load" ns="global_costmap"/>
    <rosparam file="$(find pkg)/params/costmap_common_params.yaml" command="load" ns="local_costmap"/>
    
    <rosparam file="$(find pkg)/params/map_nav_params/local_costmap_params.yaml" command="load"/>
    <rosparam file="$(find pkg)/params/map_nav_params/global_costmap_params.yaml" command="load"/>
    <rosparam file="$(find pkg)/params/base_local_planner_params.yaml" command="load"/>
    <rosparam file="$(find pkg)/params/move_base_params.yaml" command="load"/>
    
    <param name="base_global_planner" type="string" value="navfn/NavfnROS"/>
    <param name="base_local_planner" value="base_local_planner/TrajectoryPlannerROS"/>	
    <param name="clearing_rotation_allowed" value="true"/>
    <!--remap from="odom" to="/odometry/filtered" /-->
  </node>
</launch>  

위 경로는 본인의 경로에 맞춘 것이므로, 각 yaml파일들의 경로는 사람마다 다를 수 있음.

rosparam으로 선언된 파라미터들을 보면, 총 5개의 launch파일들을 태그하고 있다.

  • costmap_common_params.yaml
  • local_cost_params.yaml
  • global_cost_params.yaml
  • base_local_planner.yaml
  • move_base_params.yaml

Costmap Configuration

Navigation stack에서는 장애물 정보가 담긴 2개의 costmap을 사용한다. 하나는 global planning(전체 환경에서의 계획)에 사용되는 global costmap, 다른 하나는 local planning과 장애물 회피에 사용되는 local costmap 이다.

이 두 costmap에 관한 설정을 해줘야 할텐데, global, local costmap이 공통으로 적용되는 설정은 costmap_common_params.yaml에, 각각의 설정은 local_cost_params.yaml, global_cost_params.yaml에 담겨있다. 여기서는 중요한, 그리고 내가 사용한 파라미터 위주로 간단히 훑을 것이다.

우선 costmap_common_params.yaml부터 보자.

#---standard pioneer footprint---
#---(in meters)---

footprint: [[-0.3, -0.25], [-0.3, 0.25], [0.3, 0.25], [0.3,-0.25]]
footprint_padding: 0.03

# Acceptable errors in the robot's sensor data or in areas related to positional transformation
transform_tolerance: 0.1
map_type: costmap

always_send_full_costmap: true

obstacle_layer:
 enabled: true
 obstacle_range: 2.0
 raytrace_range: 4.0
 inflation_radius: 0.2
 track_unknown_space: true
 combination_method: 1
 observation_sources: scan
 scan: {sensor_frame: base_link, data_type: LaserScan, topic: /scan_filtered, marking: true, clearing: true}

inflation_layer:
  enabled:              true
  cost_scaling_factor:  10.0  # exponential rate at which the obstacle cost drops off (default: 10)
  inflation_radius:     0.3  # 0.5, max. distance from an obstacle at which costs are incurred for planning paths.

static_layer:
  enabled:              true
  map_topic:            "/map"

  • footprint: 로봇의 바운더리 설정, 점을 하나씩 추가 할때마다, 로봇의 바운더리 다각형의 꼭짓점 수가 늘어남
  • footprint_padding: footprint의 여유공간, 로봇이 충돌하지 않기 위해 여유공간(패딩)을 주는 것
  • transform_tolerance: 로봇의 위치변환(tf)에 대한 허용 오차, 로봇의 현재 위치와 센서 데이터간의 차이가 이 값 이내로 허용됨
  • obstacle_range: costmap 상에서 장애물을 인지할 최대 거리를 의미함
  • raytrace_range: 주어진 센서값으로 얼마나 탐지 및 초기화할 것인지에 대한 거리
  • inflation_radius: 장애물 주변 팽창(패딩) 거리, 로봇이 장애물에 가까이 다가가지 않도록 해줌

정도가 있다.

다음은 local_costmap_params.yaml이다.

local_costmap:
  global_frame: odom # map
  robot_base_frame: base_link
  update_frequency: 5.0
  publish_frequency: 2.0
  static_map: false
  rolling_window: true
  width: 4.5
  height: 4.5
  resolution: 0.05
  transform_tolerance: 0.3
  inflation_radius:     0.1
  
  plugins:
   - {name: static_layer,        type: "costmap_2d::StaticLayer"}
   - {name: obstacle_layer,      type: "costmap_2d::ObstacleLayer"}
   - {name: inflation_layer,     type: "costmap_2d::InflationLayer"}
  • global_frame: costmap이 실행되는 기준 좌표계
  • update_frequency: costmap 갱신 주기(Hz)
  • publish_frequency: costmap의 시각화(visulization) 퍼블리시 주기(Hz)
  • static_map: costmap이 외부에서 (저장된)지도를 (map_server를 사용해서)가져오는지 여부
  • rolling_window: 로봇이 전진할 때, costmap이 로봇을 중심으로 하는지 여부
  • width, height, resolution(meters/cell): costmap의 너비, 높이, 해상도 설정, 해상도는 사용하는 static map의 해상도(map.yaml??)과 달라도 괜찮으나, 보통 동일하게 설정
  • transform_tolerance, inflation_radius는 위와 동일
  • plugins: local_costmap에 적용할 레이어(플러그인)을 설정하는 부분
    • static_layer: 정적 맵 레이어, 외부에서 제공되는 지도정보를 기반으로 만들어진 costmap layer
    • obstacle_layer: 로봇 주변 장애물 기반 costmap layer
    • inflation_layer: 장애물 주변의 cost를 높인 팽창 layer

이어서 global_costmap_params.yaml이다.

global_costmap:
  global_frame: /map
  robot_base_frame: base_link
  update_frequency: 5.0
  publish_frequency: 0.5
  static_map: true
  transform_tolerance: 0.3

  plugins:
    - {name: static_layer,            type: "costmap_2d::StaticLayer"}
    - {name: obstacle_layer,          type: "costmap_2d::VoxelLayer"}
    - {name: inflation_layer,         type: "costmap_2d::InflationLayer"}

이미 위에서 다 언급된 파라미터이기 때문에 따로 설명하지 않는다. 위에서 정리한 파라미터들은 Navigation Configuration ROS Wiki를 기반으로 작성하였다. 전체 costmap 옵션 대한 설명은 costmap_2d ROS Wiki를 참고하면 된다.

Base Local Planner Configuration

base_local_planner는 로봇의 베이스의 속도 명령을 계산하는 역할을 한다, 즉 평면상에서 모바일 베이스를 구동하는 컨트롤러를 제공한다. planner는 각 경로에 해당하는 grid cell들의 cost가 최소가 되도록 경로를 생성한다. 다음은 예시가 되는 DWA 알고리즘의 그림이다. dwa

base_local_planner에 대한 더 자세한 설명은 base_local_planner ROS Wiki를 참고하자.
base_local_planner 파라미터는 당연히 사용자가 가지고 있는 로봇의 스펙에 맞춰 작성해야 한다.

다음은 나의 base_local_planner.yaml파일이다.

TrajectoryPlannerROS:
  # 로봇의 속도에 관한 파라미터
  max_vel_x: 2.0 
  min_vel_x: 0.1
  max_vel_theta: 1.0
  min_vel_theta: -1.0
  min_in_place_vel_theta: 1.0

  # 로봇의 가속도에 관한 파라미터
  acc_lim_x: 3.0
  acc_lim_theta:  2.5

  holonomic_robot: false
  escape_vel: -0.2

  # 목표지점 도달 허용범위
  yaw_goal_tolerance: 0.157
  xy_goal_tolerance: 0.25
  latch_xy_goal_tolerance: false

  # 전방 시뮬레이션 파라미터
  sim_time: 1.0
  sim_granularity: 0.02
  angular_sim_granularity: 0.02
  vx_samples: 6
  vtheta_samples: 20
  controller_frequency: 20.0

  # Trajectory scoring parameters
  meter_scoring: true # Whether the gdist_scale and pdist_scale parameters should assume that goal_distance and path_distance are expressed in units of meters or cells. Cells are assumed by default (false).
  occdist_scale:  0.1 #The weighting for how much the controller should attempt to avoid obstacles. default 0.01
  path_dist_scale_bias: 2.5  #     The weighting for how much the controller should stay close to the path it was given . default 0.6
  goal_dist_scale_bias: 1.0 #     The weighting for how much the controller should attempt to reach its local goal, also controls speed  default 0.8

  heading_lookahead: 0.1  #How far to look ahead in meters when scoring different in-place-rotation trajectories
  heading_scoring: false  #Whether to score based on the robot's heading to the path or its distance from the path. default false
  heading_scoring_timestep: 0.8   #How far to look ahead in time in seconds along the simulated trajectory when using heading scoring (double, default: 0.8)
  dwa: true #Whether to use the Dynamic Window Approach (DWA)_ or whether to use Trajectory Rollout
  simple_attractor: false
  publish_cost_grid_pc: true

  #Oscillation Prevention Parameters
  oscillation_reset_dist: 0.25 #How far the robot must travel in meters before oscillation flags are reset (double, default: 0.05)
  escape_reset_dist: 0.1
  escape_reset_theta: 0.1

# BaseGlobalPlanner:
#   allow_unknown: false
#   use_dijkstra: false #Use A* instead
#   use_quadratic: true
#   use_grid_path: false
#   old_navfn_behavior: false

간단한 파라미터는 주석을 달아놓았으며, 그 외 설명은 다음과 같다.

  • holonomic_robot: holonomic 로봇 기반 속도명령을 생성할지 여부
  • escape_vel: 로봇의 탈출 속도(m/s), 로봇이 실제로 후진해서 탈출하기 위해서는 음수값이어야 함
  • sim_time: 궤적을 전방 시뮬레이션하는데 걸리는 시간
  • sim_granularity: 주어진 궤적에서 점 사이 간격(m)
  • vx_sample: x속도 공간을 탐사할때 사용하는 샘플의 개수

등이고, 모든 파라미터에 대한 설명은 base_local_planner ROS Wiki를 참고하자.

move_base configuration

마지막으로, move_base_params.yaml에 관한 설명이다.

shutdown_costmaps: false

controller_frequency: 20.0
controller_patience: 15.0

planner_frequency: 20.0
planner_patience: 5.0

oscillation_timeout: 0.0
oscillation_distance: 0.5

recovery_behavior_enabled: true
clearing_rotation_allowed: false # true, 로봇이 회전을 통해 효과적으로 장애물을 제거하거나 회피할 수 있다면 true로 설정하는 것이 좋을 수 있습니다
  • shutdown_costmaps: move_base가 비활성화 상태일때 costmap도 중지할 것인지 여부
  • controller_frequency: 로봇 베이스에 속도 명령을 전달하는 주기 및 제어 주기(Hz)
  • controller_patience: 공간 초기화 작업을 수행하기 전에 컨트롤러가 제어를 받지 않고 몇초간 기다릴 것인가
  • planner_frequency: global planning loop를 수행하기 위한 주기(Hz), 0.0으로 설정 시, 새로운 goal을 받거나 local planner가 경로가 막혔다고 판단하는 경우에만 global planner가 동작
  • planner_patience: planner가 공간 초기화 작업을 수행하기 전 유요한 계획을 찾기 위해 몇초간 기다릴 것인가
  • oscillation_timeout: 복구 동작을 실행하기 전 진동을 허용하는 시간(초), 0.0으로 설정 시 infinite timeout
  • oscillation_distance: 로봇이 진동하지 않는다고 간주되기 위해 얼마나 움직여야 하는지(m)
  • recovery_behavior_enabled: 공간을 초기화하기 위해서 move_base 복구 동작(recovery behavior)를 사용할 것인지 여부
  • clearing_rotation_allowed: 로봇이 공간을 초기화하기 위해 제자리 회전을 시도할 것인지 여부, 로봇이 회전을 통해 효과적으로 장애물을 제거하거나 회피할 수 있다면 true로 설정

move_base_params에 대한 모든 파라미터 설명은 move_base ROS Wiki를 참고하자.