[ROS2] Keepout & speed Costmap Filter 정리

TaerinLog·2025년 12월 16일

Costmap Filter

  • Costmap Filter는 지도(map)에서 특정 영역에 추가적인 의미를 부여하기 위한 기능
  • filter를 사용하면 다음과 같은 제어가 가능
    ㄴ 특정 구역 출입 금지 (Keepout)
    ㄴ 특정 구역 속도 제한 (Speed Limit)

지도 기반의 규칙이며, 센서 기반 장애물 회피와는 다른 개념
공식문서

Keepout Filter

  • 로봇이 절대 들어가면 안 되는 영역을 지도 상에서 강제로 차단하는 필터
  • 경로 계획 단계에서 해당 영역을 장애물로 취급

Speed Filter

  • 로봇이 지도 상에서 영역별로 속도를 강제로 제한하는 필터
  • speed_limit값은 다음 두 가지 의미 중 하나
    ㄴ 로봇 최대 속도 대비 백분율로 표시
    ㄴ 절대값으로 표시됩니다(예: m/s)

필터 활성화 방법

Costmap Filter 구성 요소

  • Costmap Filter는 항상 2개의 서버 노드로 구성
    ㄴ costmap_filter_info_server
    ㄴ filter_mask_server
  • 두개의 노드를 launch파일에 추가
launch
def generate_launch_description() -> LaunchDescription:

bringup_dir = get_package_share_directory('nav2_bringup')

namespace = LaunchConfiguration('namespace')
speed_mask_yaml_file = LaunchConfiguration('speed_mask')
use_sim_time = LaunchConfigAsBool('use_sim_time')
autostart = LaunchConfigAsBool('autostart')
params_file = LaunchConfiguration('params_file')
use_composition = LaunchConfigAsBool('use_composition')
container_name = LaunchConfiguration('container_name')
container_name_full = (namespace, '/', container_name)
use_respawn = LaunchConfigAsBool('use_respawn')
use_speed_zones = LaunchConfigAsBool('use_speed_zones')
log_level = LaunchConfiguration('log_level')

lifecycle_nodes = ['speed_filter_mask_server', 'speed_costmap_filter_info_server']

# Map fully qualified names to relative ones so the node's namespace can be prepended.
remappings = [('/tf', 'tf'), ('/tf_static', 'tf_static')]

yaml_substitutions = {
    'SPEED_ZONE_ENABLED': use_speed_zones,
}

configured_params = ParameterFile(
    RewrittenYaml(
        source_file=params_file,
        root_key=namespace,
        param_rewrites={},
        value_rewrites=yaml_substitutions,
        convert_types=True,
    ),
    allow_substs=True,
)

stdout_linebuf_envvar = SetEnvironmentVariable(
    'RCUTILS_LOGGING_BUFFERED_STREAM', '1'
)

declare_namespace_cmd = DeclareLaunchArgument(
    'namespace', default_value='', description='Top-level namespace'
)

declare_speed_mask_yaml_cmd = DeclareLaunchArgument(
    'speed_mask',
    default_value='',
    description='Full path to speed mask yaml file to load',
)

declare_use_sim_time_cmd = DeclareLaunchArgument(
    'use_sim_time',
    default_value='false',
    description='Use simulation (Gazebo) clock if true',
)

declare_params_file_cmd = DeclareLaunchArgument(
    'params_file',
    default_value=os.path.join(bringup_dir, 'params', 'nav2_params.yaml'),
    description='Full path to the ROS2 parameters file to use for all launched nodes',
)

declare_use_composition_cmd = DeclareLaunchArgument(
    'use_composition',
    default_value='False',
    description='Use composed bringup if True',
)

declare_container_name_cmd = DeclareLaunchArgument(
    'container_name',
    default_value='nav2_container',
    description='the name of container that nodes will load in if use composition',
)

declare_use_respawn_cmd = DeclareLaunchArgument(
    'use_respawn',
    default_value='False',
    description='Whether to respawn if a node crashes. Applied when composition is disabled.',
)

declare_use_speed_zones_cmd = DeclareLaunchArgument(
    'use_speed_zones', default_value='True',
    description='Whether to enable speed zones or not'
)

declare_log_level_cmd = DeclareLaunchArgument(
    'log_level', default_value='info', description='log level'
)

load_nodes = GroupAction(
    condition=IfCondition(PythonExpression(['not ', use_composition])),
    actions=[
        PushROSNamespace(namespace),
        SetParameter('use_sim_time', use_sim_time),
        Node(
            condition=IfCondition(use_speed_zones),
            package='nav2_map_server',
            executable='map_server',
            name='speed_filter_mask_server',
            output='screen',
            respawn=use_respawn,
            respawn_delay=2.0,
            parameters=[configured_params, {'yaml_filename': speed_mask_yaml_file}],
            arguments=['--ros-args', '--log-level', log_level],
            remappings=remappings,
        ),
        Node(
            condition=IfCondition(use_speed_zones),
            package='nav2_map_server',
            executable='costmap_filter_info_server',
            name='speed_costmap_filter_info_server',
            output='screen',
            respawn=use_respawn,
            respawn_delay=2.0,
            parameters=[configured_params],
            arguments=['--ros-args', '--log-level', log_level],
            remappings=remappings,
        ),
        Node(
            package='nav2_lifecycle_manager',
            executable='lifecycle_manager',
            name='lifecycle_manager_speed_zone',
            output='screen',
            arguments=['--ros-args', '--log-level', log_level],
            parameters=[{'autostart': autostart}, {'node_names': lifecycle_nodes}],
        ),
    ],
)
# LoadComposableNode for map server twice depending if we should use the
# value of map from a CLI or launch default or user defined value in the
# yaml configuration file. They are separated since the conditions
# currently only work on the LoadComposableNodes commands and not on the
# ComposableNode node function itself
load_composable_nodes = GroupAction(
    condition=IfCondition(use_composition),
    actions=[
        PushROSNamespace(namespace),
        SetParameter('use_sim_time', use_sim_time),
        LoadComposableNodes(
            target_container=container_name_full,
            condition=IfCondition(use_speed_zones),
            composable_node_descriptions=[
                ComposableNode(
                    package='nav2_map_server',
                    plugin='nav2_map_server::MapServer',
                    name='speed_filter_mask_server',
                    parameters=[
                        configured_params,
                        {'yaml_filename': speed_mask_yaml_file}
                    ],
                    remappings=remappings,
                ),
                ComposableNode(
                    package='nav2_map_server',
                    plugin='nav2_map_server::CostmapFilterInfoServer',
                    name='speed_costmap_filter_info_server',
                    parameters=[configured_params],
                    remappings=remappings,
                ),
            ],
        ),

        LoadComposableNodes(
            target_container=container_name_full,
            composable_node_descriptions=[
                ComposableNode(
                    package='nav2_lifecycle_manager',
                    plugin='nav2_lifecycle_manager::LifecycleManager',
                    name='lifecycle_manager_speed_zone',
                    parameters=[
                        {'autostart': autostart, 'node_names': lifecycle_nodes}
                    ],
                ),
            ],
        ),
    ],
)

# Create the launch description and populate
ld = LaunchDescription()

# Set environment variables
ld.add_action(stdout_linebuf_envvar)

# Declare the launch options
ld.add_action(declare_namespace_cmd)
ld.add_action(declare_speed_mask_yaml_cmd)
ld.add_action(declare_use_sim_time_cmd)
ld.add_action(declare_params_file_cmd)
ld.add_action(declare_use_composition_cmd)
ld.add_action(declare_container_name_cmd)
ld.add_action(declare_use_respawn_cmd)
ld.add_action(declare_use_speed_zones_cmd)
ld.add_action(declare_log_level_cmd)

# Add the actions to launch all of the map modifier nodes
ld.add_action(load_nodes)
ld.add_action(load_composable_nodes)

return ld
  • 세부 파라메터는 yaml에서 정의
    ㄴ filter.yaml
costmap_filter_info_server:
  ros__parameters:
    use_sim_time: true
    filter_info_topic: "/costmap_filter_info"
    type: 0
    mask_topic: "/keepout_filter_mask"
    base: 0.0
    multiplier: 1.0

filter_mask_server:
  ros__parameters:
    use_sim_time: true
    frame_id: "map"
    topic_name: "/keepout_filter_mask"
    yaml_filename: "/path/to/cost_filter.yaml"

Global / Local costmap에 명시적으로 Layer를 추가

  • global_costmap
global_costmap:
  global_costmap:
    ros__parameters:
      ...
      plugins: ["static_layer", "obstacle_layer", "inflation_layer"]
      filters: ["speed_filter"] 
      or 
      filters: ["keepout_filter"]
      ...
      speed_filter:
        plugin: "nav2_costmap_2d::SpeedFilter" 
        or 
        plugin: "nav2_costmap_2d::KeepoutFilter" 
        enabled: True
        filter_info_topic: "/speed_costmap_filter_info"
        speed_limit_topic: "/speed_limit"
  • local_costmap
local_costmap:
  local_costmap:
    ros__parameters:
      ...
      plugins: ["voxel_layer", "inflation_layer"]
      filters: ["speed_filter"] 
      or 
      filters: ["keepout_filter"]
      speed_filter:
        plugin: "nav2_costmap_2d::SpeedFilter" 
        or 
        plugin: "nav2_costmap_2d::KeepoutFilter" 
        enabled: True
        filter_info_topic: "/speed_costmap_filter_info"
        speed_limit_topic: "/speed_limit"
		...

pgm 파일 수정

  • gimp로 pgm파일 수정 및 yaml파일과 짝을 맞춰주어야 함
profile
taerin

0개의 댓글