학습 목표

  • Topic, Service, Action과 Parameter를 요구사항에 맞게 구분할 수 있다.
  • ROS 2 Parameter의 Node 소유권, Type과 수명을 설명할 수 있다.
  • CLI로 Parameter를 조회·설명·변경·저장·복원할 수 있다.
  • Python Node에서 Parameter를 선언하고 Descriptor와 범위를 지정할 수 있다.
  • 변경 전 검증과 변경 후 적용을 분리하여 원자적으로 처리할 수 있다.
  • YAML과 Launch에서 Node 이름·Namespace·Type을 정확히 적용할 수 있다.
  • 다른 Node의 Parameter를 비동기로 변경하고 Parameter Event를 관찰할 수 있다.
  • 실제 Robot에서 동적 변경 가능 값과 재시작이 필요한 값을 구분하고 안전하게 적용할 수 있다.

1. Parameter란 무엇인가

Parameter는 특정 Node가 소유하며 이름, Type과 값을 가진 설정 Data입니다. 최대 속도, 정지 거리, Camera 노출, PID Gain과 Frame 이름처럼 실행 동작을 조정하는 값에 사용합니다.

/speed_governor Node
├── max_speed_mps: 0.5
├── stop_distance_m: 0.4
├── publish_rate_hz: 20.0
└── robot_name: "pinky"

ROS 2에는 ROS 1처럼 모든 설정을 보관하는 중앙 Parameter Server가 기본 구조로 존재하지 않습니다. /robot1/controller/robot2/controller가 같은 max_speed_mps 이름을 사용해도 서로 다른 Parameter입니다.

Parameter는 일반적으로 Node Process가 살아 있는 동안 유지됩니다. 실행 중 ros2 param set으로 바꾼 값은 자동으로 원본 YAML에 저장되지 않으며 Node를 재시작하면 Code 기본값이나 시작 시 Override 값으로 돌아갑니다.


2. Topic·Service·Action·Parameter 선택

요구사항 권장 Interface 이유
LiDAR 거리 Data가 계속 생성됨 Topic 시간에 따라 흐르는 Stream
Sensor Zeroing을 지금 수행 Service 짧은 명령과 한 번의 결과
목표 지점까지 이동 Action 진행률·취소·최종 상태 필요
최대 속도 설정을 유지·조회 Parameter Node가 소유하는 설정값
Emergency Stop 독립 Safety 계층 일반 통신 하나에 안전을 의존하면 안 됨

다음 질문으로 판단합니다.

시간에 따라 계속 흐르는가? ── Yes → Topic
        │ No
작업을 지금 수행하라는 명령인가?
        ├ 짧고 단발성 → Service
        └ 길고 Feedback·Cancel 필요 → Action
        │ No
Node가 기억하고 조회할 설정인가? ── Yes → Parameter

Parameter를 명령처럼 사용하지 마십시오. start_motion=true처럼 값의 변화를 동작 Trigger로 사용하면 요청별 성공·실패, 중복 호출과 실행 완료를 표현하기 어렵습니다.


3. Parameter Type과 선언 규칙

ROS 2 Parameter는 다음 Type을 지원합니다.

  • bool, integer, double, string
  • byte array
  • bool[], integer[], double[], string[]

기본적으로 선언 시 Type이 정해지고 실행 중 다른 Type으로 변경할 수 없습니다.

self.declare_parameter('enabled', True)          # bool
self.declare_parameter('retry_count', 3)         # integer
self.declare_parameter('max_speed_mps', 0.5)     # double
self.declare_parameter('frame_id', 'base_link')  # string
self.declare_parameter('zones', ['A', 'B'])      # string array

0은 integer이고 0.0은 double입니다. 다음 변경은 Type 불일치로 거절됩니다.

# max_speed_mps가 0으로 선언됐다면 double 0.7을 받을 수 없음
ros2 param set /speed_governor max_speed_mps 0.7

dynamic_typing=True로 Type 변경을 허용할 수 있지만 Consumer Code가 모든 가능한 Type을 안전하게 처리해야 하므로 일반 Robot 설정에는 고정 Type을 권장합니다.


4. CLI로 Turtlesim Parameter 체험

Terminal A에서 Turtlesim을 실행합니다.

source /opt/ros/$ROS_DISTRO/setup.bash
ros2 run turtlesim turtlesim_node

Terminal B에서 Node와 Parameter를 조사합니다.

ros2 node list
ros2 param list /turtlesim
ros2 param get /turtlesim background_r
ros2 param describe /turtlesim background_r

배경색을 변경합니다.

ros2 param set /turtlesim background_r 30
ros2 param set /turtlesim background_g 30
ros2 param set /turtlesim background_b 120

현재 값을 저장하고 다른 값으로 바꾼 뒤 다시 불러옵니다.

ros2 param dump /turtlesim
ros2 param dump /turtlesim --output-dir /tmp
ros2 param load /turtlesim /tmp/turtlesim.yaml

배포판별 CLI 옵션은 ros2 param dump -h로 확인하십시오. dump는 현재 값을 YAML로 내보내지만 실행 중 변경을 원본 구성 파일에 자동 기록하지는 않습니다.


5. Parameter CLI 명령 전체 흐름

ros2 param list
ros2 param list /speed_governor
ros2 param get /speed_governor max_speed_mps
ros2 param describe /speed_governor max_speed_mps
ros2 param set /speed_governor max_speed_mps 0.4
ros2 param dump /speed_governor
ros2 param load /speed_governor config/tuned.yaml
명령 목적
list 선언된 Parameter 이름 확인
get 현재 Type과 값 확인
describe 설명, 제약과 읽기 전용 여부 확인
set 실행 중 값 변경 요청
dump 현재 값들을 YAML로 출력
load YAML의 값들을 실행 중 Node에 설정

Parameter가 보이지 않으면 먼저 정확한 Node 이름과 Namespace를 확인합니다.

ros2 node list
ros2 node info /robot1/speed_governor

6. Python 실습 Package 생성

mkdir -p ~/ros2_ws/src
cd ~/ros2_ws/src
ros2 pkg create parameter_lab_py \
  --build-type ament_python \
  --dependencies rclpy rcl_interfaces geometry_msgs

setup.py에 실행 파일을 등록합니다.

entry_points={
    'console_scripts': [
        'basic_parameter = parameter_lab_py.basic_parameter:main',
        'speed_governor = parameter_lab_py.speed_governor:main',
        'parameter_client = parameter_lab_py.parameter_client:main',
        'parameter_monitor = parameter_lab_py.parameter_monitor:main',
    ],
},

7. 선언·읽기·사용하는 가장 작은 Node

# parameter_lab_py/basic_parameter.py
import rclpy
from rclpy.node import Node


class BasicParameterNode(Node):
    def __init__(self):
        super().__init__('basic_parameter')
        self.declare_parameter('greeting', '안녕하세요')
        self.declare_parameter('period_sec', 1.0)

        period = self.get_parameter('period_sec').value
        self.timer = self.create_timer(period, self.tick)

    def tick(self):
        greeting = self.get_parameter('greeting').value
        self.get_logger().info(greeting)


def main(args=None):
    rclpy.init(args=args)
    node = BasicParameterNode()
    try:
        rclpy.spin(node)
    except KeyboardInterrupt:
        pass
    finally:
        node.destroy_node()
        rclpy.shutdown()


if __name__ == '__main__':
    main()

Build하고 실행합니다.

cd ~/ros2_ws
colcon build --packages-select parameter_lab_py --symlink-install
source install/setup.bash
ros2 run parameter_lab_py basic_parameter

다른 Terminal에서 값을 변경합니다.

ros2 param get /basic_parameter greeting
ros2 param set /basic_parameter greeting '반갑습니다'

이 예제는 tick() 때마다 값을 읽으므로 greeting 변경이 바로 반영됩니다. 그러나 period_sec는 Timer 생성 시 한 번만 읽었으므로 값만 바꿔도 주기는 달라지지 않습니다.


8. ParameterDescriptor로 문서와 범위 제공

Descriptor는 Parameter의 설명, 읽기 전용 여부와 숫자 범위를 Graph에 공개합니다.

from rcl_interfaces.msg import FloatingPointRange, ParameterDescriptor

speed_descriptor = ParameterDescriptor(
    description='최대 전진 속도. 단위 m/s',
    additional_constraints='Robot 사양과 운용 구역 제한 중 작은 값을 사용',
    floating_point_range=[
        FloatingPointRange(
            from_value=0.0,
            to_value=1.5,
            step=0.0,
        )
    ],
)

self.declare_parameter('max_speed_mps', 0.5, speed_descriptor)

읽기 전용 Parameter 예시입니다.

robot_descriptor = ParameterDescriptor(
    description='Robot 고유 이름',
    read_only=True,
)
self.declare_parameter('robot_name', 'pinky_01', robot_descriptor)
ros2 param describe /speed_governor max_speed_mps
ros2 param set /speed_governor robot_name other

Descriptor는 사용자가 범위를 발견하도록 돕고 기본 검증을 제공합니다. 두 값 사이 관계나 운용 상태에 따른 조건은 변경 Callback에서 추가로 검사합니다.


9. 안전한 SpeedGovernor 전체 예제

requested_speed Topic의 명령을 max_speed_mps로 제한해 /cmd_vel_governed로 내보냅니다. 실제 시스템에서는 이 출력도 Safety Filter와 Motor Driver를 거쳐야 합니다.

# parameter_lab_py/speed_governor.py
import math
import threading

import rclpy
from geometry_msgs.msg import Twist
from rcl_interfaces.msg import (
    FloatingPointRange,
    ParameterDescriptor,
    SetParametersResult,
)
from rclpy.node import Node


class SpeedGovernor(Node):
    def __init__(self):
        super().__init__('speed_governor')
        self._lock = threading.Lock()

        self.declare_parameter(
            'max_speed_mps',
            0.5,
            ParameterDescriptor(
                description='전진·후진 절대 속도 제한 m/s',
                floating_point_range=[FloatingPointRange(
                    from_value=0.0, to_value=1.5, step=0.0)],
            ),
        )
        self.declare_parameter('stop_distance_m', 0.4)
        self.declare_parameter('enabled', True)
        self.declare_parameter(
            'output_topic',
            'cmd_vel_governed',
            ParameterDescriptor(
                description='출력 Topic. 시작 후 변경 불가',
                read_only=True,
            ),
        )

        self._apply_current_parameters()
        output_topic = self.get_parameter('output_topic').value
        self.publisher = self.create_publisher(Twist, output_topic, 10)
        self.subscription = self.create_subscription(
            Twist, 'requested_speed', self.on_command, 10)

        self._on_set_handle = self.add_on_set_parameters_callback(
            self.validate_parameters)
        self._post_set_handle = self.add_post_set_parameters_callback(
            self.apply_parameters)

    def _apply_current_parameters(self):
        with self._lock:
            self.max_speed = self.get_parameter('max_speed_mps').value
            self.stop_distance = self.get_parameter('stop_distance_m').value
            self.enabled = self.get_parameter('enabled').value

    def validate_parameters(self, parameters):
        proposed = {
            'max_speed_mps': self.get_parameter('max_speed_mps').value,
            'stop_distance_m': self.get_parameter('stop_distance_m').value,
            'enabled': self.get_parameter('enabled').value,
        }
        for parameter in parameters:
            if parameter.name in proposed:
                proposed[parameter.name] = parameter.value

        speed = proposed['max_speed_mps']
        distance = proposed['stop_distance_m']
        if not isinstance(speed, float) or not math.isfinite(speed):
            return SetParametersResult(
                successful=False,
                reason='max_speed_mps는 유한한 double이어야 합니다',
            )
        if not 0.0 <= speed <= 1.5:
            return SetParametersResult(
                successful=False,
                reason='max_speed_mps 허용 범위는 0.0~1.5입니다',
            )
        if not isinstance(distance, float) or not math.isfinite(distance):
            return SetParametersResult(
                successful=False,
                reason='stop_distance_m는 유한한 double이어야 합니다',
            )
        if not 0.1 <= distance <= 3.0:
            return SetParametersResult(
                successful=False,
                reason='stop_distance_m 허용 범위는 0.1~3.0입니다',
            )

        # 여러 값의 관계도 한 번에 검증할 수 있다.
        if speed > 1.0 and distance < 0.5:
            return SetParametersResult(
                successful=False,
                reason='1.0 m/s 초과에서는 정지 거리가 0.5 m 이상이어야 합니다',
            )
        return SetParametersResult(successful=True)

    def apply_parameters(self, parameters):
        # 모든 검증을 통과해 실제 값이 반영된 뒤 호출된다.
        self._apply_current_parameters()
        changed = ', '.join(p.name for p in parameters)
        self.get_logger().info(f'Parameter 적용 완료: {changed}')

    def on_command(self, message):
        output = Twist()
        with self._lock:
            enabled = self.enabled
            limit = self.max_speed
        if enabled:
            output.linear.x = max(-limit, min(message.linear.x, limit))
            output.angular.z = message.angular.z
        self.publisher.publish(output)


def main(args=None):
    rclpy.init(args=args)
    node = SpeedGovernor()
    try:
        rclpy.spin(node)
    except KeyboardInterrupt:
        pass
    finally:
        node.destroy_node()
        rclpy.shutdown()


if __name__ == '__main__':
    main()

add_post_set_parameters_callback()은 비교적 최신 API입니다. 사용하는 ROS 2 배포판의 rclpy에 없다면 on-set Callback은 검증만 수행하고, Parameter Event 또는 짧은 Timer에서 성공적으로 반영된 값을 읽어 Cache를 갱신하십시오.


10. 검증과 적용을 분리해야 하는 이유

한 요청에 여러 Parameter가 들어올 수 있습니다. 원자적 요청에서 하나라도 거절되면 전체가 적용되지 않습니다.

요청: max_speed=1.2, stop_distance=0.2
                ↓
현재값과 요청값을 합친 후보 상태 구성
                ↓
두 값의 범위·관계 검증 ── 실패 → 전부 유지
                ↓ 성공
Parameter Store 반영 → Cache·Timer·Filter 적용

검증 Callback에서 self.max_speed = p.value처럼 Side Effect를 먼저 만들면 뒤 Parameter가 거절될 때 Parameter Store는 원래 값인데 Cache만 새 값이 되는 문제가 생깁니다.

원칙은 다음과 같습니다.

  1. On-set Callback에서는 후보 값만 만들어 검증합니다.
  2. Hardware I/O, 파일 읽기와 긴 작업을 하지 않습니다.
  3. 성공 후 Post-set Callback이나 별도 적용 Loop에서 Cache를 갱신합니다.
  4. 공유 상태는 Lock 또는 단일 실행 흐름으로 보호합니다.

11. 동적으로 Timer 주기 변경하기

Timer는 publish_rate_hz 값만 바꾼다고 자동 재생성되지 않습니다. 성공적으로 변경된 뒤 기존 Timer를 취소하고 새 Timer를 만듭니다.

def recreate_timer(self):
    rate = self.get_parameter('publish_rate_hz').value
    if self.timer is not None:
        self.timer.cancel()
        self.destroy_timer(self.timer)
    self.timer = self.create_timer(1.0 / rate, self.tick)

def apply_parameters(self, parameters):
    names = {p.name for p in parameters}
    if 'publish_rate_hz' in names:
        self.recreate_timer()

Callback 안에서 Entity 생성·파괴가 배포판과 Executor 구성에 어떤 영향을 주는지 시험하십시오. 더 안전한 구조는 Callback이 reconfigure_requested=True만 설정하고 다음 관리 Timer에서 재구성하는 방식입니다.

def apply_parameters(self, parameters):
    if any(p.name == 'publish_rate_hz' for p in parameters):
        self.reconfigure_requested = True

def maintenance_tick(self):
    if self.reconfigure_requested:
        self.reconfigure_requested = False
        self.recreate_timer()

12. YAML Parameter 파일

config/speed_governor.yaml을 만듭니다.

speed_governor:
  ros__parameters:
    max_speed_mps: 0.6
    stop_distance_m: 0.5
    enabled: true
    output_topic: cmd_vel_governed

실행합니다.

ros2 run parameter_lab_py speed_governor --ros-args \
  --params-file ~/ros2_ws/src/parameter_lab_py/config/speed_governor.yaml

파일과 CLI Override를 함께 사용할 수 있습니다.

ros2 run parameter_lab_py speed_governor --ros-args \
  --params-file config/speed_governor.yaml \
  -p max_speed_mps:=0.3

YAML 확인 사항:

  • Node 이름 아래에 ros__parameters가 와야 합니다. 밑줄은 두 개입니다.
  • 1은 integer, 1.0은 double입니다.
  • truefalse는 Boolean으로 씁니다.
  • Tab 대신 Space를 사용합니다.
  • Node 이름과 Namespace가 실제 Graph 이름에 맞아야 합니다.

13. Namespace와 YAML Wildcard

Robot마다 값을 다르게 지정할 수 있습니다.

/robot1/speed_governor:
  ros__parameters:
    max_speed_mps: 0.4

/robot2/speed_governor:
  ros__parameters:
    max_speed_mps: 0.7

/**:
  ros__parameters:
    use_sim_time: false

/**/speed_governor:
  ros__parameters:
    stop_distance_m: 0.5

*는 Slash로 구분된 한 Token, **는 0개 이상의 Token과 대응합니다. 너무 넓은 /**max_speed_mps 같은 일반 이름을 넣으면 의도하지 않은 Node에도 Override가 전달될 수 있으므로 공통 Parameter에만 제한적으로 사용합니다.

ros2 run parameter_lab_py speed_governor --ros-args -r __ns:=/robot1 \
  --params-file config/fleet.yaml

14. Python Launch에서 YAML과 Override 적용

launch/speed_governor.launch.py 예시입니다.

from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfiguration
from launch_ros.actions import Node


def generate_launch_description():
    namespace = LaunchConfiguration('namespace')
    params_file = LaunchConfiguration('params_file')

    return LaunchDescription([
        DeclareLaunchArgument('namespace', default_value='robot1'),
        DeclareLaunchArgument(
            'params_file',
            default_value='config/speed_governor.yaml',
        ),
        Node(
            package='parameter_lab_py',
            executable='speed_governor',
            name='speed_governor',
            namespace=namespace,
            parameters=[
                params_file,
                {'enabled': True},
            ],
            output='screen',
        ),
    ])

실행합니다.

ros2 launch parameter_lab_py speed_governor.launch.py \
  namespace:=robot1 \
  params_file:=/absolute/path/to/speed_governor.yaml

실제 Package에서는 get_package_share_directory()PathJoinSubstitution으로 설치된 share 경로의 YAML을 찾는 방식을 권장합니다. setup.pydata_files에도 launchconfig 파일을 설치해야 합니다.

data_files=[
    ('share/ament_index/resource_index/packages',
     ['resource/parameter_lab_py']),
    ('share/parameter_lab_py', ['package.xml']),
    ('share/parameter_lab_py/launch', ['launch/speed_governor.launch.py']),
    ('share/parameter_lab_py/config', ['config/speed_governor.yaml']),
],

15. 다른 Node의 Parameter를 비동기로 변경

Parameter Service를 직접 조립하기보다 AsyncParameterClient를 사용합니다.

# parameter_lab_py/parameter_client.py
import rclpy
from rclpy.node import Node
from rclpy.parameter import Parameter
from rclpy.parameter_client import AsyncParameterClient


class ParameterClientNode(Node):
    def __init__(self):
        super().__init__('parameter_client')
        self.client = AsyncParameterClient(self, '/speed_governor')

    def set_safe_profile(self):
        if not self.client.wait_for_services(timeout_sec=5.0):
            raise RuntimeError('Parameter Service를 찾지 못했습니다')

        parameters = [
            Parameter('max_speed_mps', Parameter.Type.DOUBLE, 0.3),
            Parameter('stop_distance_m', Parameter.Type.DOUBLE, 0.6),
        ]
        future = self.client.set_parameters_atomically(parameters)
        future.add_done_callback(self.on_result)

    def on_result(self, future):
        try:
            result = future.result().result
            if result.successful:
                self.get_logger().info('안전 Profile 적용 완료')
            else:
                self.get_logger().error(f'적용 거절: {result.reason}')
        except Exception as exc:
            self.get_logger().error(f'Parameter 요청 실패: {exc}')
        rclpy.shutdown()


def main(args=None):
    rclpy.init(args=args)
    node = ParameterClientNode()
    node.set_safe_profile()
    try:
        rclpy.spin(node)
    finally:
        node.destroy_node()


if __name__ == '__main__':
    main()

set_parameters()는 각 Parameter 결과를 개별적으로 받을 수 있고, set_parameters_atomically()는 묶음 전체를 하나의 후보 상태로 적용하거나 모두 거절합니다. 서로 의존하는 속도·정지 거리에는 원자적 설정이 적합합니다.


16. Parameter Event 관찰

Parameter가 선언·변경·삭제되면 /parameter_events에 Event가 발행됩니다.

ros2 topic echo /parameter_events

Python에서는 ParameterEventHandler로 특정 Node의 특정 Parameter를 관찰할 수 있습니다.

# parameter_lab_py/parameter_monitor.py
import rclpy
from rclpy.node import Node
from rclpy.parameter_event_handler import ParameterEventHandler


class ParameterMonitor(Node):
    def __init__(self):
        super().__init__('parameter_monitor')
        self.handler = ParameterEventHandler(self)
        self.callback_handle = self.handler.add_parameter_callback(
            parameter_name='max_speed_mps',
            node_name='/speed_governor',
            callback=self.on_speed_change,
        )

    def on_speed_change(self, parameter):
        self.get_logger().info(
            f'{parameter.name} 변경 감지: {parameter.value}')


def main(args=None):
    rclpy.init(args=args)
    node = ParameterMonitor()
    try:
        rclpy.spin(node)
    except KeyboardInterrupt:
        pass
    finally:
        node.destroy_node()
        rclpy.shutdown()

반환된 callback_handle을 멤버 변수로 보관해야 Callback 등록이 유지됩니다. Event는 변경 통지에 유용하지만 중요한 동작 상태를 Parameter Event에만 의존하지 마십시오. 현재 안전 상태와 Mode는 의미가 명확한 상태 Topic으로 제공하는 편이 좋습니다.


17. 초기값 Override와 선언

Command Line과 YAML에 값을 적어도 Node가 해당 Parameter를 선언하지 않으면 일반적인 선언 기반 Node에서는 사용할 수 없습니다.

# Override가 있다면 그 값을, 없다면 0.5를 사용해 선언
self.declare_parameter('max_speed_mps', 0.5)

Node Option으로 전달된 Parameter를 자동 선언하는 기능도 있지만 오타가 새 Parameter로 조용히 등록될 수 있어 교육용·운영용 Robot Node에는 명시적 선언을 우선 권장합니다.

시작 값의 출처는 다음처럼 겹칠 수 있습니다.

Code 기본값
   ↓ 시작 시 Override
YAML·Launch·Command Line 인자
   ↓ 실행 중 변경
CLI·Parameter Client

여러 파일과 Inline Dictionary의 세부 병합 순서에 기대기보다 최종 값을 시작 Log와 ros2 param get으로 검증하고, Profile별 최종 YAML을 명확히 관리하십시오.


18. 실제 Robot에서 Parameter를 나누는 기준

Parameter 실행 중 변경 적용 방법
max_speed_mps 조건부 허용 범위·운용 Mode 검증 후 즉시 Cache 반영
stop_distance_m 조건부 허용 속도와 함께 원자적으로 검증
PID Gain 시험·상태 조건부 출력 제한, 기록과 단계적 적용
Camera Exposure Driver 지원 시 허용 Frame 경계에서 반영 확인
Serial Port 보통 금지 Read-only, 재시작 후 초기화
frame_id 보통 금지 TF 계약이 바뀌므로 재시작·통합 검증
Wheel Radius 운용 중 금지 권장 Odometry 일관성을 위해 정지·재시작
Emergency Stop Parameter로 구현 금지 독립 안전 입력과 상태 Machine

Parameter 범위는 Hardware 최대치만 보면 안 됩니다. Robot 사양, 하중, 바닥, 작업 구역과 현재 Mode에서 허용하는 값 중 가장 보수적인 제한을 적용합니다.

if self.robot_is_moving and parameter.name == 'wheel_radius_m':
    return SetParametersResult(
        successful=False,
        reason='Robot 정지 후에만 wheel_radius_m를 변경할 수 있습니다',
    )

안전 한계는 사용자가 Parameter를 바꾸면 사라지는 하나의 Software if에만 의존하지 않습니다. Driver의 Saturation, Watchdog와 독립 Safety Controller에서도 방어합니다.


19. 구성 Profile과 변경 이력

실제 운용에서는 환경별 YAML을 분리합니다.

config/
├── defaults.yaml
├── simulation.yaml
├── lab_low_speed.yaml
├── warehouse.yaml
└── robot_001_calibration.yaml

좋은 관리 원칙:

  1. YAML을 Version Control에 저장합니다.
  2. 단위와 허용 범위를 Code Descriptor와 문서에 기록합니다.
  3. Robot 개체별 Calibration과 환경별 운용 설정을 분리합니다.
  4. 누가, 언제, 왜 값을 바꿨는지 변경 이력을 남깁니다.
  5. dump 결과를 그대로 승인된 구성으로 간주하지 말고 검토 후 반영합니다.
  6. Secret, Password와 API Key를 Parameter에 평문 저장하지 않습니다.

실행 시작 시 중요한 설정과 구성 Version을 Log로 남기면 현장 문제를 재현하기 쉬워집니다.

self.get_logger().info(
    f'profile=warehouse-v3 max_speed={self.max_speed} '
    f'stop_distance={self.stop_distance}')

20. 진단 절차

ros2 node list
ros2 param list /robot1/speed_governor
ros2 param get /robot1/speed_governor max_speed_mps
ros2 param describe /robot1/speed_governor max_speed_mps
ros2 topic echo /parameter_events
ros2 service list | grep speed_governor
증상 확인할 항목
Parameter가 목록에 없음 선언 여부, 정확한 Node 이름·Namespace
YAML이 적용되지 않음 ros__parameters, Node key, 경로, 설치 여부
set이 Type 오류 선언 기본값 Type과 YAML Scalar Type
set 성공인데 동작 불변 Cache·Timer·Driver에 변경 후 적용했는지
여러 값 중 일부만 예상과 다름 atomic/non-atomic API와 검증 Side Effect
재시작 후 값 사라짐 CLI 변경을 YAML에 반영했는지
Parameter Service 응답 없음 Executor Blocking, Callback의 긴 I/O
다른 Robot 값이 바뀜 절대 Node 이름, Namespace와 Wildcard 범위

진단은 “설정했다고 생각한 값”이 아니라 Node가 실제 보유한 값부터 확인합니다. 이후 Parameter Store, Application Cache, 출력 Message와 Hardware 적용 상태를 차례로 비교합니다.

YAML·CLI 입력 → Node Parameter Store → Application Cache
             → 출력 Command → Driver 내부 설정 → 실제 Robot

21. 흔한 실수와 교정

  1. 설정 변경을 Trigger 명령으로 사용한다. 작업 요청은 Service나 Action으로 표현합니다.
  2. 실수 기본값을 0으로 선언한다. 0.0으로 Type을 명확히 합니다.
  3. 선언하지 않은 Parameter를 읽는다. 시작 시 명시적으로 선언합니다.
  4. 검증 Callback에서 Cache부터 수정한다. 검증과 성공 후 적용을 분리합니다.
  5. Callback에서 Hardware 재초기화를 오래 수행한다. Flag를 세우고 별도 실행 흐름에서 처리합니다.
  6. Parameter 값만 바꾸면 Timer도 변한다고 생각한다. 관련 Entity를 명시적으로 재구성합니다.
  7. YAML의 ros__parameters를 틀린다. Node 이름과 들여쓰기를 함께 확인합니다.
  8. 광범위한 /**를 남용한다. 공통 Parameter에만 사용합니다.
  9. CLI 변경이 영구 저장된다고 믿는다. 검토한 값을 YAML과 Version Control에 반영합니다.
  10. 여러 의존 값을 하나씩 변경한다. set_parameters_atomically()를 사용합니다.
  11. read_only면 안전이 완성된다고 믿는다. Hardware·Driver 계층에서도 한계를 둡니다.
  12. Password를 Parameter로 배포한다. Secret 관리 체계를 사용합니다.

22. 실습 체크리스트

  • [ ] Turtlesim의 Parameter를 CLI로 조회하고 변경했다.
  • [ ] list·get·describe·set·dump·load를 사용했다.
  • [ ] Python Node에서 다섯 가지 Type을 선언했다.
  • [ ] Descriptor에 설명, 읽기 전용과 숫자 범위를 지정했다.
  • [ ] Type 불일치와 범위 밖 값을 시험했다.
  • [ ] 검증 Callback과 성공 후 적용을 분리했다.
  • [ ] 두 Parameter의 관계를 원자적으로 검증했다.
  • [ ] Timer 주기를 안전하게 재구성했다.
  • [ ] YAML의 Node 이름과 ros__parameters를 확인했다.
  • [ ] Namespace 두 개에 다른 값을 적용했다.
  • [ ] Launch에서 YAML과 Inline Override를 전달했다.
  • [ ] AsyncParameterClient로 묶음 설정을 적용했다.
  • [ ] /parameter_events와 Event Handler를 시험했다.
  • [ ] 실제 Robot의 동적·정적 Parameter를 구분했다.
  • [ ] 변경값을 YAML과 Version Control에 기록했다.

23. 정리

Parameter는 Node가 소유하는 조회 가능한 설정값입니다. 선언 시 Type과 기본값을 정하고 Descriptor로 의미와 범위를 공개하며 YAML과 Launch로 재현 가능한 초기 구성을 제공합니다.

실행 중 변경에서는 검증과 적용을 분리하는 것이 핵심입니다. On-set Callback은 현재값과 요청값으로 후보 상태를 만들어 Side Effect 없이 검사하고, 성공 후에 Cache, Timer와 Filter를 갱신해야 합니다. 서로 의존하는 값은 원자적으로 설정합니다.

실제 Robot에서는 모든 설정을 동적으로 바꾸지 않습니다. Frame, Hardware Port와 Calibration처럼 System 계약을 바꾸는 값은 정지·재시작 절차를 요구하고, 속도와 안전 거리는 상태와 관계를 함께 검증합니다. Parameter는 편리한 설정 Interface이지 독립 Safety 기능이나 영구 Database가 아닙니다.


ROBOT GLOSSARY

용어 정리

전체 용어 찾아보기 →
Parameter파라미터
특정 ROS 2 Node가 소유하며 이름, Type과 값을 가진 조회 가능한 설정 Data입니다.
Parameter Declaration파라미터 선언
Node가 사용할 Parameter의 이름, 기본값, Type과 Descriptor를 등록하는 과정입니다.
Parameter Override파라미터 덮어쓰기
Node 시작 시 YAML, Launch 또는 Command Line 값으로 Code 기본값을 대체하는 설정입니다.
ParameterDescriptor파라미터 설명자
Parameter의 설명, 읽기 전용, 동적 Type과 숫자 범위 같은 Metadata와 제약을 정의합니다.
Read-only Parameter읽기 전용 파라미터
시작 시 초기화할 수 있지만 Node 실행 중에는 변경 요청을 받지 않는 Parameter입니다.
Dynamic Typing동적 타입
실행 중 Parameter Type 변경을 허용하는 선택 기능으로 Consumer가 모든 Type을 처리해야 합니다.
Parameter Range파라미터 범위
Integer 또는 Floating Point Parameter에 허용되는 최소·최대·단계 조건입니다.
ros__parametersROS 파라미터 YAML 키
YAML 파일에서 특정 Node에 적용할 Parameter Map을 표시하는 예약 Key입니다.
Parameter File파라미터 파일
Node 이름과 ros__parameters 구조로 재현 가능한 초기 설정을 기록한 YAML 파일입니다.
Parameter Callback파라미터 콜백
Parameter 변경 전 검증하거나 성공 후 Application 상태에 적용하기 위해 호출되는 함수입니다.
On-set Callback변경 전 검증 콜백
Parameter Store에 값이 반영되기 전에 후보 값을 검증하고 수락 또는 거절하는 Callback입니다.
Post-set Callback변경 후 적용 콜백
모든 검증을 통과해 Parameter가 성공적으로 설정된 뒤 Cache와 실행 상태를 갱신하는 Callback입니다.
Atomic Set원자적 설정
여러 Parameter를 모두 함께 적용하거나 하나라도 실패하면 전부 적용하지 않는 변경 방식입니다.
AsyncParameterClient비동기 파라미터 클라이언트
다른 Node의 Parameter Service를 발견하고 비동기로 조회·설정하는 rclpy Client입니다.
Parameter Event파라미터 이벤트
Node에서 Parameter가 선언·변경·삭제될 때 /parameter_events로 발행되는 변경 통지입니다.
ParameterEventHandler파라미터 이벤트 처리기
자신 또는 다른 Node의 특정 Parameter 변경을 Callback으로 관찰하는 rclpy 도구입니다.
YAML WildcardYAML 와일드카드
Parameter 파일에서 별표를 사용해 여러 Node 이름이나 Namespace에 공통 설정을 대응시키는 문법입니다.
Configuration Profile구성 프로파일
Simulation, 시험실, 창고 또는 Robot 개체처럼 운용 조건별로 관리하는 Parameter 설정 묶음입니다.
Application Cache응용 캐시
반복 조회 비용을 줄이기 위해 Node 멤버 변수 등에 복사해 사용하는 현재 Parameter 값입니다.
Side Effect부수 효과
검증 결과 반환 외에 Cache, Hardware나 외부 상태를 변경하는 동작으로 검증 Callback에서는 피해야 합니다.

연습 문제

  1. ROS 2 Parameter의 소유 주체와 수명을 설명하세요.
  2. 최대 속도에는 Parameter, 배터리 잔량에는 Topic이 적합한 이유는 무엇인가요?
  3. 00.0으로 선언한 Parameter의 Type 차이는 무엇인가요?
  4. declare_parameter, get_parameter.value의 역할을 설명하세요.
  5. ParameterDescriptor의 description, read_only, 숫자 Range가 제공하는 기능은 무엇인가요?
  6. ros2 param list·get·describe·set·dump·load의 역할을 각각 설명하세요.
  7. YAML의 Node 이름, ros__parameters와 값 Type을 확인해야 하는 이유는 무엇인가요?
  8. Namespace가 있는 Node에 YAML이 적용되지 않을 때 무엇을 비교해야 하나요?
  9. 검증 Callback에서 Cache를 즉시 변경하면 원자적 요청에서 어떤 문제가 생기나요?
  10. On-set 검증과 Post-set 적용을 분리해야 하는 이유는 무엇인가요?
  11. set_parameters()set_parameters_atomically()의 차이를 설명하세요.
  12. publish_rate_hz 값만 변경해도 기존 Timer 주기가 바뀌지 않는 이유와 해결 방법은 무엇인가요?
  13. Parameter Event Handler의 Callback Handle을 보관해야 하는 이유는 무엇인가요?
  14. Wheel Radius와 Emergency Stop을 각각 Parameter로 어떻게 다뤄야 하는지 설명하세요.
  15. Parameter 변경 성공 후 Robot 동작이 그대로일 때 YAML부터 Hardware까지 진단 순서를 설명하세요.

참고 자료