# 파라미터 동적 변경(Dynamic Reconfigure) 기초

ROS(로봇 운영체제) 환경에서 동적으로 로봇의 동작 방식을 바꾸기 위해서는 파라미터의 실시간 변경이 중요하다. ROS1 시절에는 `dynamic_reconfigure` 패키지를 통해 노드가 사용 중인 파라미터 값을 외부에서 쉽게 변경할 수 있었다. ROS2에서도 비슷한 기능이 필요하며, 이를 위해 파라미터 콜백, 파라미터 서버, 파라미터 이벤트 등을 적극적으로 활용한다.

ROS2 Humble 환경에서 제공되는 동적 파라미터 설정 방식은 다음과 같은 특징을 가진다.

* 파라미터 콜백(Parameter Callback) 등록
* 파라미터 이벤트(Parameter Event) 트리거
* `rcl_interfaces/msg/ParameterEvent` 토픽 구독 및 활용
* 파라미터 인터페이스(Parameter Interface)를 통한 클라이언트/서버 구조 확립

이 섹션에서는 ROS2에서 파라미터를 동적으로 변경하기 위한 기본 개념과 예시 코드를 살펴보며, 파라미터 변경 시 발생하는 이벤트 및 콜백 구조를 단계별로 분석한다.

#### ROS2 파라미터 구조 이해

ROS2 노드에서 파라미터를 사용하기 위해서는 먼저 다음과 같은 핵심 구성 요소를 이해해야 한다.

1. **파라미터 서버(Parameter Server)**
   * 별도의 전역 서버가 존재했던 ROS1과 달리, ROS2에서는 노드 자체가 파라미터 서버 역할을 수행한다.
   * 노드에 선언된 파라미터는 그 노드 고유의 이름공간(namespace)을 통해 관리된다.
2. **파라미터 클라이언트(Parameter Client)**
   * 다른 노드(혹은 자신)를 위한 파라미터 설정이나 조회를 요청할 수 있다.
   * 파라미터의 선언, 변경, 삭제 등을 모두 서비스 형태로 요청한다.
3. **파라미터 이벤트(Parameter Event)**
   * 노드에서 파라미터가 변경될 때마다 메시지가 발행된다.
   * 이를 통해 다른 노드들이 변경 상황을 감지하고 처리할 수 있다.
4. **파라미터 콜백(Parameter Callback)**
   * 노드 내부에서 특정 파라미터가 변경될 때 자동으로 호출되어야 할 로직을 작성할 수 있다.
   * 파라미터가 변경되면, 콜백이 실행되어 새로운 파라미터 값을 바탕으로 알고리즘을 재계산하거나 동작 모드를 변경하는 등의 작업을 수행한다.

#### 파라미터 선언과 동적 변경

ROS2에서는 노드 내부에서 파라미터를 사용하기 전에 반드시 선언해야 한다. 파라미터 선언 시, 기본값(default value)과 함께 타입(정수, 실수, 문자열, 불리언 등)을 지정한다. 예컨대, 노드 생성자나 `on_configure()` 함수(라이프사이클 노드 사용 시)에서 다음과 같이 선언할 수 있다.

```cpp
this->declare_parameter<int>("threshold_value", 10);
this->declare_parameter<double>("gain", 0.5);
this->declare_parameter<std::string>("control_mode", "normal");
```

이러한 파라미터들은 선언 후에는 `set_parameter()` 함수를 통해 값을 변경하거나, 외부에서 아래와 같은 서비스 호출 혹은 CLI(Command Line Interface) 명령어를 통해 재설정할 수 있다.

```bash
ros2 param set <노드이름> threshold_value 15
ros2 param set <노드이름> gain 1.0
ros2 param set <노드이름> control_mode "advanced"
```

#### 파라미터 콜백 구조

노드에서 파라미터 값이 바뀔 때마다 특정 함수를 자동으로 호출하려면 콜백 함수 등록이 필요하다. ROS2 C++ 예시 코드는 다음과 같다.

```cpp
// 노드 클래스 멤버 함수 예시
void MyNode::registerParamCallback()
{
  // 파라미터 변경 시점에 호출될 콜백 등록
  param_callback_handle_ = this->add_on_set_parameters_callback(
    [this](const std::vector<rclcpp::Parameter> &parameters)
    {
      // 변경 요청된 파라미터를 하나씩 확인
      for (auto &param : parameters)
      {
        if (param.get_name() == "threshold_value")
        {
          // 새 값 확인
          int new_value = param.as_int();
          RCLCPP_INFO(this->get_logger(), "Threshold changed to: %d", new_value);
          // 유효성 검사 등 필요한 로직 수행
        }
        else if (param.get_name() == "gain")
        {
          double new_gain = param.as_double();
          RCLCPP_INFO(this->get_logger(), "Gain changed to: %f", new_gain);
          // 파라미터 유효 범위 제한 등 추가 처리
        }
        // 필요한 다른 파라미터들에 대해서도 반복
      }
      // 성공적으로 변경된 경우 rcl_interfaces::msg::SetParametersResult 반환
      rcl_interfaces::msg::SetParametersResult result;
      result.successful = true;
      return result;
    }
  );
}
```

위의 예시에서 `add_on_set_parameters_callback()` 함수를 사용하여 콜백을 등록하면, 특정 파라미터가 변경될 때마다 자동으로 해당 람다(lambda) 함수가 실행되어 새 파라미터 값을 처리한다. 이를 통해 노드가 실행 중이라도 외부(예: `ros2 param set` 명령어 등)에서 파라미터 값을 조정하면 즉시 알고리즘 파라미터를 갱신할 수 있다.

#### 동적 파라미터 제한과 검증

동적으로 파라미터가 변경될 때마다, 노드가 정상적으로 동작하는지 검증할 필요가 있다. 예를 들어, 제어 이득(gain) 파라미터가 음수가 되지 않도록 하거나, 특정 변수가 가능한 범위를 넘어가지 않도록 하는 등의 유효성 검증(Validation)이 필수적이다.

검증 알고리즘의 예시를 수식으로 간단히 표현해보면, 만약 $K$라는 게인(gain) 파라미터를 최소값 0, 최대값 10으로 제한하고 싶다면 다음과 같은 처리를 한다.

$$
0 \leq K \leq 10
$$

콜백 함수 안에서 새로운 $K$가 위의 범위를 벗어나는 경우에는 `SetParametersResult`를 통해 `successful = false`를 리턴해 파라미터 갱신을 거부할 수 있다.

```cpp
if (new_gain < 0.0 || new_gain > 10.0) {
  result.successful = false;
  result.reason = "Gain must be between 0.0 and 10.0";
  return result;
}
```

#### 동적 파라미터를 활용한 예시 시나리오

동적 파라미터 변경이 활발히 활용되는 대표적 상황은 실시간 제어 또는 온라인 튜닝(Online Tuning)이다. 예를 들어, 다음과 같은 장면을 생각해볼 수 있다.

1. **로봇 조인트 제어기**: P, I, D 게인을 노드 실행 중에도 변경하여, 로봇 조인트의 제어 응답을 실시간으로 개선한다.
2. **센서 필터 파라미터 조정**: 센서 노이즈를 줄이는 필터 파라미터(예: 이동 평균 윈도우 크기)를 로봇 구동 상황에 맞추어 적절히 조절한다.
3. **경로 탐색 파라미터**: 경로 탐색 알고리즘(A\*, RRT 등)의 허용 오차, 최대 탐색 시간 등을 동적으로 조정하여 미션 수행 중에 환경 변화에 대응한다.

#### 파라미터 이벤트와 모니터링

ROS2에서는 파라미터 변경 시마다 `ParameterEvent` 메시지가 발행되며, 이는 `rcl_interfaces/msg/ParameterEvent` 타입을 통해 확인할 수 있다. 해당 토픽을 직접 구독(subscription)하거나, 아래처럼 CLI 명령어로 실시간 모니터링이 가능하다.

```bash
ros2 topic echo /parameter_events
```

이렇게 이벤트를 구독함으로써, 외부에서 누가 파라미터를 어떻게 바꿨는지 실시간으로 확인하여 전체 시스템이 파라미터 상태를 추적할 수 있다.

#### 파라미터 파일(Parameter Files) 활용

ROS2에서 노드를 실행할 때 파라미터를 일일이 명령어로 설정하기보다는, 파라미터 파일(.yaml 등)을 사용하면 편리하다. 파라미터 파일을 이용하면 특정 노드에 대한 파라미터 값을 일괄 설정하여 노드 실행 시 자동으로 적용할 수 있다. 예시 .yaml 파일은 다음과 같다.

```yaml
my_node_name:
  ros__parameters:
    threshold_value: 20
    gain: 1.5
    control_mode: "advanced"
```

이렇게 정의한 뒤 노드를 실행할 때, 아래와 같은 명령어로 파라미터 파일을 로드할 수 있다.

```bash
ros2 run my_package my_node_executable --ros-args --params-file path/to/params.yaml
```

만약 launch 파일을 사용한다면, 파이썬 launch 스크립트에서 `DeclareLaunchArgument` 혹은 `Node` 객체의 `parameters` 인자 등에 위 파일 경로를 지정하여 파라미터 파일을 로드할 수 있다.

#### 파라미터 Descriptor와 Constraints

ROS2에서는 파라미터를 선언할 때, 단순히 값과 타입만 지정하는 것이 아니라, 보다 정교한 메타데이터를 설정할 수 있다. 예를 들어, 최소/최대 범위, 단위, 설명(Documentation) 등을 포함하는 `ParameterDescriptor` 구조체를 활용할 수 있다. 이는 사용자가 동적 파라미터를 변경할 때, 더욱 구체적인 제한 조건이나 안내문을 제공하는 데 유용하다.

아래는 `ParameterDescriptor`를 설정하는 C++ 예시이다.

```cpp
#include "rcl_interfaces/msg/parameter_descriptor.hpp"
#include "rcl_interfaces/msg/integer_range.hpp"

rcl_interfaces::msg::ParameterDescriptor desc;
desc.name = "threshold_value";
desc.description = "Threshold for detection";
desc.integer_range.resize(1);
desc.integer_range[0].from_value = 0;
desc.integer_range[0].to_value = 100;
desc.integer_range[0].step = 1;

this->declare_parameter<int>("threshold_value", 10, desc);
```

이렇게 선언해두면, CLI나 다른 노드에서 해당 파라미터를 변경할 때 최소값 0, 최대값 100 범위 내에서만 조정되도록 안내하게 된다(단, 범위를 벗어날 경우 노드 측에서 별도 validation 로직이 필요할 수 있다).

#### Python 노드에서의 동적 파라미터 예시

C++뿐 아니라 Python 노드에서도 간단히 동적 파라미터 콜백을 설정할 수 있다. 예를 들어, `rclpy`를 사용한 Python 코드 예시는 다음과 같다.

```python
import rclpy
from rclpy.node import Node
from rcl_interfaces.msg import SetParametersResult

class DynamicParamNode(Node):
    def __init__(self):
        super().__init__('dynamic_param_node')
        self.declare_parameter('speed', 1.0)
        self._param_callback = self.add_on_set_parameters_callback(self.param_callback)

    def param_callback(self, params):
        for param in params:
            if param.name == 'speed':
                new_speed = param.value
                self.get_logger().info(f"Speed changed to {new_speed}")
                # 예: speed가 0보다 작지 않도록 제한
                if new_speed < 0.0:
                    return SetParametersResult(successful=False, reason="Speed must be >= 0.0")
        return SetParametersResult(successful=True)

def main(args=None):
    rclpy.init(args=args)
    node = DynamicParamNode()
    rclpy.spin(node)
    rclpy.shutdown()
```

위 코드에서는 `add_on_set_parameters_callback()`를 통해 동적으로 들어오는 파라미터 변경 요청을 받고, 만약 `speed` 파라미터의 새로운 값이 0보다 작으면 변경을 거부한다.

#### Launch 파일에서 동적 파라미터 활용

ROS2의 Launch 시스템은 파라미터 파일뿐 아니라, 동적으로 파라미터를 지정하거나 CLI 인자를 통해 노드에 값을 전달할 수 있다. 다음과 같은 예시 Python Launch 스크립트를 살펴보자.

```python
import os
from launch import LaunchDescription
from launch_ros.actions import Node
from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfiguration

def generate_launch_description():
    param_file_arg = DeclareLaunchArgument(
        'param_file',
        default_value=os.path.join(
            os.path.dirname(__file__),
            'params.yaml'
        ),
        description='Path to the parameter file'
    )

    param_file = LaunchConfiguration('param_file')

    my_node = Node(
        package='my_package',
        executable='my_node_executable',
        name='my_node_name',
        parameters=[param_file]
    )

    return LaunchDescription([
        param_file_arg,
        my_node
    ])
```

* `DeclareLaunchArgument`를 통해 `param_file`이라는 인자를 받는다.
* 실제 노드 생성(`Node`) 시 `parameters` 목록에 `LaunchConfiguration('param_file')`를 할당해 실행 시점에 파라미터 파일이 전달되도록 한다.
* 필요하다면, launch 실행 시 `ros2 launch my_package my_launch.py param_file:=/path/custom_params.yaml` 식으로 다른 파라미터 파일을 지정할 수도 있다.

#### 파라미터 변경 흐름 시각화

동적으로 파라미터가 변경될 때, 노드와 파라미터 서버, 파라미터 이벤트 토픽 간의 흐름을 시각적으로 표현하면 아래와 같은 구조를 생각해볼 수 있다.

{% @mermaid/diagram content="flowchart LR
A\["Param Setter (CLI/노드)"] -->|Service call| B\["Target Node (Parameter Server)"]
B -->|Publishes| C("(/parameter\_events topic)")
D\["Subscriber Node(s)"] -->|Receives Event| C
B -->|Executes param callback| B" %}

* 파라미터 변경 요청이 들어오면(`Param Setter`에서 서비스 콜), 대상 노드(`Target Node`)가 그 요청을 수락하거나 거부함
* 변경 성공 시, `ParameterEvent` 토픽으로 이벤트 발행
* 다른 노드들이 해당 토픽을 구독하여 변경 상황 파악
* 대상 노드 자체도 파라미터 콜백을 통해 동작 모드 변경, 알고리즘 파라미터 갱신 등을 수행

#### 라이프사이클 노드에서 파라미터 관리

ROS2에는 라이프사이클(Lifecycle) 노드 개념이 존재해, 노드가 구성(Configure), 활성(Activate), 비활성(Deactivate), 종료(Shutdown) 등의 상태를 가진다. 이 라이프사이클 노드 구조에서도 파라미터를 안전하게 변경하기 위해 다음과 같은 방식으로 관리할 수 있다.

1. **on\_configure()**: 노드가 `unconfigured` 상태에서 `inactive` 상태로 전이될 때, 파라미터를 선언하고 초기화한다.
2. **on\_activate()**: 노드가 실제 동작을 시작하기 전, 파라미터 콜백을 등록해 동적 변경에 대비한다.
3. **on\_deactivate()**: 노드가 중지(Deactivate)될 때, 파라미터 변경으로 인해 문제 없이 재설정되도록 준비한다.

이를 통해, 노드가 활성화되지 않은 상태에서 파라미터를 안전하게 설정한 뒤 노드를 동작시키는 등 다양한 시나리오를 설계할 수 있다.

#### 파라미터 동기화(Synchronization) 기법

여러 노드가 동시에 동일한 파라미터를 공유하거나, 하나의 노드에서 변경된 파라미터를 다른 노드가 즉시 인식해야 하는 상황이 있을 수 있다. 이때 사용할 수 있는 기법은 다음과 같다.

* **Shared memory 또는 공통 데이터베이스**: ROS 자체 기능이라기보다는, 외부적으로 Redis, SQLite 등 DB를 활용해 노드 간 파라미터를 공유·동기화한다.
* **ParameterEvent 토픽 활용**: 모든 노드가 `/parameter_events`를 구독해 필요한 파라미터가 변경되는 즉시 콜백을 통해 자체 상태를 업데이트한다.
* **Parameter Client/Server**: 특정 노드가 파라미터 서버 역할을 하고, 다른 노드들이 Parameter Client를 통해 주기적으로 혹은 이벤트 기반으로 값을 동기화한다.

#### 파라미터 변경 시 주의사항

1. **실시간성**: 파라미터 변경은 서비스 호출을 통해 이뤄지므로, 하드 실시간 제어 루프에 직접 반영되는 경우 지연(latency)을 고려해야 한다.
2. **원자성**: 여러 파라미터를 동시에 변경해야 하는 경우, 중간 상태가 적용되는 문제를 방지해야 한다. ROS2의 파라미터 변경은 기본적으로 개별 단위로 처리되기 때문에, 복수 파라미터 변경 시 원자적(atomic) 처리가 필요하면 별도 로직을 구성해야 한다.
3. **유효 범위**: 앞서 언급했듯이 범위 검사, 타입 검사 등 유효성 검증 로직을 빈틈없이 구성해야 한다.
4. **보안(Security)**: 민감한 설정 값을 파라미터로 노출하면, 외부에서 손쉽게 값을 변경해버릴 수 있으므로 주의해야 한다. 필요한 경우 DDS의 보안 기능 등을 연동해 접근 권한을 제한한다.

#### 고급 파라미터 클라이언트 활용

ROS2 노드에서 파라미터를 동적으로 변경하기 위해서는, 흔히 `rclcpp::Node`의 메서드나 CLI(`ros2 param set`)를 사용한다. 그러나 더 복잡한 시나리오에서는 별도의 파라미터 클라이언트(예: `rclcpp::SyncParametersClient` 또는 `rclcpp::AsyncParametersClient`)를 직접 생성하여 필요에 따라 서비스 호출을 세밀하게 제어할 수 있다. 이를 통해 여러 파라미터를 한꺼번에, 혹은 특정 시점에 정확히 변경하는 로직을 작성할 수 있다.

간단한 C++ 예시를 살펴보자.

```cpp
#include <rclcpp/rclcpp.hpp>
#include <rcl_interfaces/msg/set_parameters_result.hpp>

class ParamClientNode : public rclcpp::Node
{
public:
  ParamClientNode() : Node("param_client_node")
  {
    // 타겟 노드의 이름을 인자로 받아 클라이언트 생성
    client_ = std::make_shared<rclcpp::SyncParametersClient>(this, "target_node_name");

    // 파라미터 클라이언트 연결 대기
    while (!client_->wait_for_service(std::chrono::seconds(1))) {
      RCLCPP_INFO(this->get_logger(), "Waiting for parameter server...");
    }

    // 파라미터를 묶어서 설정
    std::vector<rclcpp::Parameter> params;
    params.push_back(rclcpp::Parameter("threshold_value", 30));
    params.push_back(rclcpp::Parameter("gain", 2.0));

    auto results = client_->set_parameters(params);
    for (auto &res : results) {
      if (!res.successful) {
        RCLCPP_WARN(this->get_logger(),
          "Failed to set param: %s (reason: %s)",
          res.name.c_str(), res.reason.c_str());
      }
    }
    RCLCPP_INFO(this->get_logger(), "Parameter update attempted.");
  }

private:
  std::shared_ptr<rclcpp::SyncParametersClient> client_;
};

int main(int argc, char **argv)
{
  rclcpp::init(argc, argv);
  auto node = std::make_shared<ParamClientNode>();
  rclcpp::spin(node);
  rclcpp::shutdown();
  return 0;
}
```

* `target_node_name` 노드의 파라미터 서버와 통신하기 위해 `SyncParametersClient`를 구성한다.
* 여러 파라미터를 벡터로 생성하여 한꺼번에 설정 요청(`set_parameters`)을 보낼 수 있다.
* 응답(`SetParametersResult`)을 통해 성공/실패를 확인 가능하다.

#### 노드 컴포지션(Composition)과 동적 파라미터

ROS2에서 노드 컴포지션을 사용할 경우, 하나의 프로세스 내에 여러 노드가 동시에 존재한다. 이때도 각 노드는 고유한 파라미터 서버를 가지고 동작하므로, 각 노드별로 파라미터를 따로 선언하고 콜백을 등록해야 한다. 노드 간 파라미터를 직접 주고받으려면, 위에서 설명한 파라미터 클라이언트/서버 구조나, `/parameter_events` 구독을 통해 동기화 로직을 작성해야 한다.

컴포지션을 사용하는 이유는 노드 간의 통신 지연을 줄이고, 메모리 사용량을 최적화하기 위함이다. 하지만 모든 노드가 독립된 파라미터 공간을 갖는다는 점을 인지하고, 필요하다면 각 노드의 동적 파라미터 콜백을 적절히 연결하거나, 공용 파라미터 노드를 두고 여러 노드가 해당 노드의 파라미터 서버를 사용하는 식으로 구조를 설계할 수 있다.

#### 파라미터 갱신 스케줄링(Scheduling)

파라미터를 실제 변경하기 전후로 특정 작업을 예약하거나, 특정 이벤트가 발생했을 때만 변경하도록 스케줄링하는 경우도 있다. 예를 들어 다음 시나리오를 고려해볼 수 있다.

1. **주행 로봇의 속도 제한(safety limit) 파라미터**: 매분마다 센서 데이터를 종합 분석하고, 위험도가 높아지면 자동으로 속도 제한 파라미터를 낮춤.
2. **야간 모드 전환 파라미터**: 밤 10시가 되면 자동으로 조명 밝기를 낮추는 파라미터를 활성화.
3. **조건부 업데이트**: 내부 상태 머신(State Machine)에서 특정 상태 전이에 진입할 때만 파라미터가 업데이트되도록 설계.

이런 스케줄링이나 조건부 업데이트는 파라미터 콜백뿐 아니라, 타이머나 액션 서버 등을 활용하여 구체적으로 구현할 수 있다.

#### 파라미터 Debugging & Logging

동적 파라미터 활용 시, 예상치 못한 값으로 변경되어 시스템이 이상 동작하는 경우가 발생할 수 있다. 다음과 같은 디버깅 방법을 활용할 수 있다.

1. **로깅(Log Level) 활용**: 파라미터 콜백에서 변경된 값을 `RCLCPP_INFO` 혹은 `RCLCPP_DEBUG`로 출력하여, 로그를 통해 누가 언제 어떤 값으로 바꿨는지 추적한다.
2. **`/parameter_events` 주기적 모니터링**: `$ ros2 topic echo /parameter_events` 명령어로 파라미터 변경 이력을 확인한다.
3. **`ros2 param` CLI**: 노드 실행 중에 `$ros2 param list`, `$ ros2 param get <node> <param>` 등으로 현재 파라미터 상태를 확인한다.
4. **에러 상황 재현**: 동적 파라미터가 특정 범위를 벗어난 경우 노드가 종료되는 버그가 있다면, CI 파이프라인이나 테스트 스크립트에서 의도적으로 해당 파라미터를 설정해보고 로그를 확인한다.

#### 파라미터 워처(Parameter Watcher) 노드

특정 노드의 파라미터가 바뀔 때마다 자동으로 시각화하거나, 외부 데이터베이스(예: InfluxDB, Firebase)에 기록하는 "파라미터 워처" 노드를 구성할 수도 있다. 아래와 같은 구조를 간략히 생각해볼 수 있다.

{% @mermaid/diagram content="flowchart LR
A\[Target Node] --> B\[/parameter\_events/]
B --> C\[Watcher Node]
C -->|Store in| D\[(Database)]
C -->|Plot or Visualize| E\[Realtime Dashboard]" %}

* "파라미터 워처" 노드는 `/parameter_events`를 구독한다.
* 파라미터가 변경될 때마다, 변경된 파라미터 이름과 값, 타임스탬프 등을 DB에 저장하거나 대시보드에 표시한다.
* 이를 통해 여러 노드에 걸쳐 발생하는 파라미터 변경 이력을 한눈에 파악할 수 있다.

#### 공통 파라미터 패턴(Common Parameter Pattern)

실제 프로젝트에서는 다음과 같은 패턴으로 파라미터를 구조화하면 관리가 용이하다.

1. **파라미터 이름 계층 구조**
   * 예: `control/pid/p_gain`, `control/pid/i_gain`, `control/pid/d_gain` 식으로 계층적으로 네이밍
   * 관련 파라미터를 한 덩어리로 묶어 파악하기 쉬움
2. **파라미터 타입 명시**
   * 예: `enable_feature`(bool), `robot_name`(string), `max_speed`(double) 등
   * 이름만 봐도 어떤 타입인지 어느 정도 유추 가능하도록 짓는다.
3. **설명 및 범위(Descriptor) 철저히 기록**
   * `ParameterDescriptor`의 `description` 필드를 적극 활용하고, `integer_range` 혹은 `floating_point_range`를 등록해 실수 방지.
4. **불필요한 파라미터 정리**
   * 릴리즈(Release) 시점마다 사용하지 않게 된 파라미터는 제거하거나 주석 처리하여 관리 복잡도를 줄인다.

#### 대규모 시스템에서의 파라미터 관리

로봇 플랫폼이 커질수록(수십~~수백 개 노드), 파라미터도 수백~~수천 개에 이를 수 있다. 이를 체계적으로 관리하기 위해 다음 기법을 활용할 수 있다.

* **다중 파라미터 파일 분할**: 기능별, 모듈별, 로봇 하드웨어별 YAML 파일을 여러 개로 구분하고, Launch 파일에서 합쳐서 로드.
* **버전 관리**: Git 등의 버전 관리 시스템에서 파라미터 파일 변경 이력을 추적. 파라미터 변경 전후 성능 로그를 함께 기록해두면, 언제 어떤 변경이 시스템에 영향을 주었는지 추적하기 쉽다.
* **템플릿 파라미터**: 특정 노드들 사이에 공통으로 사용되는 파라미터 세트를 템플릿화하여 재사용.
* **시뮬레이션 파라미터와 실제 로봇 파라미터 분리**: 시뮬레이션 환경에서 필요한 파라미터(예: 센서 드라이버 비활성화, 가상 센서 보정 등)와 실제 로봇에서 사용하는 파라미터를 명확히 구분한다.

#### 동적 파라미터와 보안(Security)

ROS2는 DDS를 기반으로 한 통신 구조를 사용하며, DDS Security(Access Control, Encryption 등) 레이어를 활용할 수 있다. 민감한 설정(예: 보안 토큰, 암호 키 등)을 파라미터로서 외부에서 변경 가능하게 둘 경우, 악의적인 접근자가 임의로 값을 바꿔 시스템을 오작동시키는 위험이 생긴다. 이를 방지하려면 다음과 같은 방법을 고려할 수 있다.

1. **노드 이름/네임스페이스 정책**
   * 중요 노드(예: 보안 관련 파라미터를 다루는 노드)의 접근 권한을 엄격히 제한한다.
   * 네임스페이스를 철저히 관리해 특정 그룹 이상의 권한을 가진 주체만이 파라미터 변경 서비스를 호출할 수 있도록 설정한다.
2. **SROS2**
   * SROS2를 사용하면 노드별 정책(Policy 파일)으로 서비스 접근과 토픽 발행/구독 권한을 제어할 수 있다.
   * `keystore`를 생성하고 인증서·키를 배포하여 허가된 노드만 파라미터를 변경할 수 있도록 제약한다.
3. **TLS/DTLS 기반 보안**
   * DDS 보안 설정을 통해 파라미터 서비스 호출 과정에서 암호화(Encryption)를 적용한다.
   * 중간자 공격(Man-in-the-middle)을 방지하여, 파라미터 변경이 승인된 주체로부터만 이뤄지도록 한다.
4. **감사(Logging/Auditing) 강화**
   * 파라미터 변경 시도에 대한 로그를 상세히 남기고, `/parameter_events`를 별도 노드에서 보관하여 추후 감사(Audit) 시 활용한다.

#### 파라미터 QoS 고려사항

ROS2 파라미터는 기본적으로 서비스와 토픽(`/parameter_events`) 형태로 동작한다. 이때 적용되는 QoS(Quality of Service) 설정에 따라 파라미터 변경 알림이 유실되거나, 서비스 호출 타임아웃 등이 발생할 수 있다. 예를 들어:

1. **Reliable vs Best Effort**
   * `/parameter_events` 토픽은 일반적으로 Reliable 전달을 사용하나, 대역폭 문제가 심각할 경우 Best Effort로 변경할 수도 있다.
   * 보통은 파라미터 변경 이벤트가 반드시 전달되어야 하므로 Reliable QoS를 사용한다.
2. **Durability**
   * 파라미터 이벤트를 새로 구독하는 노드는 과거 이벤트를 받아볼 필요가 있는지 검토한다(Transient Local vs Volatile).
   * 일반적으로는 과거 이벤트가 큰 의미가 없어서 Volatile을 많이 사용하지만, 시스템 요구사항에 따라 달라질 수 있다.
3. **Service Timeout**
   * 파라미터 설정 서비스가 일정 시간 내에 응답하지 않으면 타임아웃이 발생한다.
   * 실제 물리 로봇 환경에서 네트워크 지연이 클 수 있으므로, 적절한 타임아웃 값을 설정한다(예: 2\~5초 등).

#### 테스트(Testing)와 CI 파이프라인에서의 동적 파라미터

CI(Continuous Integration) 환경에서 노드가 정상 동작하는지 자동으로 확인하기 위해, 다음과 같은 테스트를 구성할 수 있다.

1. **유효 범위 테스트**
   * 파라미터에 허용되지 않는 값(음수, 오버플로우 등)을 시도해보고, 노드가 적절히 SetParametersResult로 거부하는지 검증한다.
2. **경계값 테스트(Boundary Testing)**
   * 파라미터의 최소값이나 최대값 근처에서 노드가 정상적으로 동작하는지 확인한다.
   * 예를 들어, 제어 이득(gain)을 0.0, 10.0 근방으로 변경해보고 오작동이 없는지 확인한다.
3. **대량 파라미터 테스트(Stress Test)**
   * 매우 많은 파라미터를 동시에 변경하는 상황에서 노드가 타임아웃 없이 잘 응답하는지 확인한다.
   * 이는 대규모 시스템 또는 고빈도 튜닝(High-frequency tuning) 시나리오에서 중요하다.
4. **Lifecycle 연계 테스트**
   * 라이프사이클 노드에서 `on_configure()`, `on_activate()`, `on_deactivate()` 단계마다 파라미터가 문제없이 설정·변경·재설정되는지 점검한다.

#### 동적 파라미터 변경 구현 시 팁

1. **콜백 최소화**
   * 파라미터가 자주 바뀌는 노드라면, 콜백 함수 내에서 복잡한 로직을 최대한 피하고 값 검증이나 재할당만 빠르게 수행한다.
   * 추가적인 계산이 필요하다면, 콜백에서 임시 변수를 세팅하고 주기적 타이머에서 반영하는 구조를 택한다.
2. **파라미터 선언 순서**
   * 노드 내부에서 사용하는 파라미터들은 사용 시점 이전에 선언되어 있어야 한다.
   * 라이프사이클 노드를 쓰지 않는 경우에도, 노드 생성자에서 필수 파라미터를 먼저 선언한다.
3. **CLI-friendly 파라미터 이름**
   * CLI에서 직관적으로 변경 가능하도록, 너무 긴 파라미터 이름은 피하고 적절히 축약 혹은 계층화를 준수한다.
   * 예: `algo_mode`보다는 `motion_planner/algo_mode` 식으로 하위 계층화해 의미를 명확히 표현한다.
4. **에러 핸들링**
   * 파라미터 설정이 실패했을 때(콜백에서 `SetParametersResult.successful=false`), 사용자에게 적절한 `reason`을 제공해 디버깅을 돕는다.
   * 파라미터 콜백에서 throw를 사용하는 대신, 반드시 `SetParametersResult`를 통해 결과를 반환하는 것이 바람직하다.

아래는 ROS2 동적 파라미터 변경 과정을 단순화한 절차를 도식으로 표현한 예시다.

{% @mermaid/diagram content="sequenceDiagram
participant User
participant ParamCLI
participant Node(ParamServer)
participant Callback
User->>ParamCLI: ros2 param set <br/> NodeName <br/> param\_name new\_value
ParamCLI->>Node(ParamServer): set\_parameters <br/> srv request
Node(ParamServer)->>Callback: on\_set\_parameter <br/> callback
Callback->>Node(ParamServer): returns <br/> SetParametersResult
Node(ParamServer)->>ParamCLI: srv response
Node(ParamServer)->>/parameter\_events: publish <br/> ParameterEvent" %}
