# Launch 파일 파라미터 충돌 사례

#### 문제 상황 개요

ROS2 Humble에서 Launch 파일을 통해 여러 노드를 동시에 실행할 때, 서로 다른 노드가 동일한 파라미터 이름을 사용하거나, 동일 노드가 여러 곳에서 다른 값으로 설정된 파라미터를 받아 충돌이 발생하는 사례가 종종 보고된다. 예를 들어 센서 노드와 알고리즘 노드가 서로 같은 이름의 파라미터를 다른 값으로 설정하려 할 경우, 특정 노드의 파라미터가 예상치 못하게 무시되거나 덮어씌워질 수 있다.

이 충돌은 주로 아래와 같은 상황에서 자주 발생한다.

* 공통적으로 사용되는 파라미터 이름을 여러 노드가 공유
* 동일 노드가 복수의 Launch 파일 혹은 여러 번의 IncludeLaunchDescription을 통해 합쳐질 때
* 명령줄에서 전달되는 파라미터 값과 Launch 파일 내부에서 설정되는 값이 상충

이러한 파라미터 충돌은 노드의 동작이 예측 불가능해지는 원인이 되며, 디버깅 과정에서 많은 시간을 소모하게 만든다. 특히, 로봇 시스템처럼 소프트웨어 컴포넌트들이 복잡하게 얽힌 환경에서는 빠르게 문제를 식별하고 원인을 파악하는 과정이 쉽지 않다.

#### 파라미터 선언과 할당 방식

ROS2에서는 노드가 파라미터를 사용하려면 우선 내부 코드에서 해당 파라미터를 선언해야 하며, 그 후 Launch 파일 등을 통해 할당이 이뤄진다. 파라미터 충돌 문제를 살펴보기 위해선 다음 개념을 명확히 이해해야 한다.

* **선언(Declare)**: 노드 소스 코드 혹은 노드가 포함된 컴포넌트에서 $declare\_parameter$와 같은 메서드를 통해 파라미터의 이름, 타입, 기본값 등을 정의한다.
* **할당(Assignment)**: Launch 파일에서 `{'param_name': value}` 형태로 해당 노드에 값을 전달하거나, 명령줄에서 `--ros-args -p param_name:=value`와 같은 방식을 통해 설정한다.

ROS2 Foxy 이후 버전에서는 Node 오브젝트에 `parameters` 또는 `remappings` 인자를 전달해 파라미터를 설정하는 방식이 대표적이다. 예를 들어 Python 기반 Launch 파일에서 아래와 같이 Node를 생성하며 파라미터를 전달할 수 있다:

```python
from launch import LaunchDescription
from launch_ros.actions import Node

def generate_launch_description():
    return LaunchDescription([
        Node(
            package='my_robot_pkg',
            executable='my_robot_node',
            name='robot_node',
            parameters=[{'speed': 1.0, 'enable_safety': True}]
        )
    ])
```

하지만 여러 Launch 파일이 함께 사용되거나, 공통되는 변수 혹은 파라미터 이름을 재활용하게 될 경우, 서로 다른 Launch 파일에서 다른 값을 가지는 동일 파라미터가 충돌을 일으키는 상황이 발생할 수 있다.

#### 충돌 원인 분석

* **중복 이름** 로봇 시스템에서 자주 사용하는 파라미터 이름(예: `speed`, `resolution`, `threshold` 등)이 여러 컴포넌트에서 우연히 겹치는 경우가 많다. 같은 이름이지만 의도하는 용도나 단위가 다를 수 있으므로, 파라미터 설정 시 혼동을 줄 수 있다.
* **Launch 파일 합성(IncludeLaunchDescription)** 하나의 최종 Launch 파일이 여러 개의 하위 Launch 파일을 `IncludeLaunchDescription` 형태로 불러오면서 파라미터를 각각 설정할 때, 동일 노드나 동일 파라미터가 중복으로 설정되기 쉽다.
* **명령줄 인자와 Launch 파일 인자 충돌** 명령줄에서 `ros2 launch` 명령어를 실행할 때 추가로 전달하는 파라미터($--ros-args -p key:=value$)가 Launch 파일 내부에서 이미 정의된 값과 충돌할 수 있다. 실제 런타임에 어떤 파라미터가 우선 적용되는지는 Launch 파일 구조나 노드 설정에 따라 다르게 나타난다.
* **서브 노드(sub-node) 컨텍스트 충돌** ROS2 Composable Node 구조를 사용하면, 하나의 프로세스 안에서 여러 컴포넌트가 같은 파라미터 이름을 참조하는 상황이 발생할 수 있다. 명시적으로 네임스페이스를 설정하지 않으면 파라미터가 서로 엉켜 동작할 수도 있다.

#### 충돌 재현 예시

예시로, 서로 다른 패키지의 노드 A와 노드 B가 각각 `resolution`이라는 파라미터를 사용한다고 하자. 노드 A는 센서 해상도를 의미하는 용도로, 노드 B는 맵 그리드 해상도를 의미하는 용도로 사용한다. 두 노드가 다음과 같이 Launch 파일에서 동시에 선언되어 있다면 어떤 문제가 발생할까?

```python
from launch import LaunchDescription
from launch_ros.actions import Node

def generate_launch_description():
    node_a_params = [
        {'resolution': 0.1, 'sensor_fov': 120.0}
    ]

    node_b_params = [
        {'resolution': 1.0, 'map_size': 1024}
    ]

    return LaunchDescription([
        Node(
            package='sensor_pkg',
            executable='sensor_node',
            name='sensor_node',
            parameters=node_a_params
        ),
        Node(
            package='mapping_pkg',
            executable='map_node',
            name='map_node',
            parameters=node_b_params
        )
    ])
```

각 노드는 자체적으로 파라미터를 선언한다고 가정하지만, 실제로는 런타임 환경에서 파라미터 이름이 동일하고 Node 이름이나 네임스페이스가 적절히 구분되지 않으면 예상치 못한 값이 전달될 가능성이 있다. 특히, 어떤 Launch 시스템 설정이나 패키지 구조에 따라 동일 파라미터가 병합되어 노드에 주입될 수도 있다.

ROS2 Galactic 이전 버전이나, 네임스페이스 설정이 제대로 안 된 경우에 이런 충돌이 더욱 자주 보고되었다. Humble 버전에서도 마찬가지로 Launch 파일 상에서 파라미터 이름이 같으면 헷갈릴 수 있으므로 주의가 필요하다.

#### 충돌 상태에서의 디버깅 접근 방법

파라미터 충돌이 의심될 때 가장 먼저 해야 할 일은 시스템 전체에서 해당 파라미터가 어디서 어떻게 설정되는지 철저히 추적하는 것이다. 이를 위해선 다음 방법들이 주로 활용된다.

**Node 실행 시 파라미터 목록 확인**:

ROS2에서 노드를 실행하면, `$ros2 param list` 명령이나 `$ros2 param get <node_name> <param_name>` 등을 통해 실제로 어떤 파라미터가 노드에 설정되었는지 확인할 수 있다.

예:

```bash
ros2 param list
ros2 param get /map_node resolution
```

노드 실행 중에 변경된 파라미터 값도 확인 가능하다. 이를 통해 예상치 못한 파라미터 값이 들어간 경우를 비교적 빠르게 찾을 수 있다.

**Launch 파일에서 파라미터 출력 (Print launch arguments)**:

Launch 파일 내부에서 로그 또는 디버깅 메시지를 이용해, 전달하려는 파라미터가 최종적으로 어떤 값으로 설정되는지 출력할 수 있다. 이를 통해 Launch 파일 여러 개가 합쳐졌을 때 어떤 우선순위로 값이 적용되는지 확인 가능하다.

예: Python Launch File에서 `launch.actions.LogInfo`를 사용하여 노드 파라미터를 출력할 수 있다.

```python
from launch.actions import LogInfo

...
LogInfo(msg=f"Node A Params: {node_a_params}")
LogInfo(msg=f"Node B Params: {node_b_params}")
```

* Launch 파일 합성(IncludeLaunchDescription) 시 파라미터가 덮어씌워지는 과정을 추적할 때 유용하다.

**로깅 레벨 설정**: 디버깅을 위해 노드에 설정된 로깅 레벨을 `DEBUG`나 `TRACE`로 높이면, 파라미터 선언 혹은 변경 이벤트가 더 자세히 로그로 남아 문제 파악이 쉬워진다.

예:

```bash
ros2 run my_robot_pkg my_robot_node --ros-args --log-level DEBUG
```

Launch 파일에서 `output='screen'` 옵션과 함께 로깅 레벨을 추가 인자로 설정해 디버깅하기도 한다.

**네임스페이스와 Node 명 구분 확인**: ROS2에서는 파라미터가 Node 이름(또는 풀 네임)과 1:1로 매핑되어 관리된다. 따라서 노드 이름이 같거나, 네임스페이스가 중복되어 있으면 파라미터 충돌 가능성이 커진다.

노드를 생성할 때 `name` 혹은 `namespace`를 명시적으로 구분해주면, 파라미터가 서로 충돌할 여지를 줄일 수 있다.

예:

```python
Node(
    package='sensor_pkg',
    executable='sensor_node',
    name='sensor_node',
    namespace='sensors',
    parameters=[{'resolution': 0.1}]
)
```

#### 문제 해결 전략

파라미터 충돌 문제를 예방하고 해결하기 위한 대표 전략은 아래와 같다.

**명명 규칙 확립**: 여러 패키지나 팀이 협업할 때, 파라미터 이름에 프로젝트나 노드의 고유 접두사(prefix)를 부여해 중복을 줄인다. 예컨대, 센서 패키지에서 사용하는 `sensor_resolution`, 맵 패키지에서 사용하는 `map_resolution`처럼 의도를 분명히 하거나 네임스페이스를 활용한다.

**Launch 파일 구조 단순화**: 복잡하게 중첩된 Launch 파일 구조를 단순화하여, 파라미터 설정이 중복되거나 덮어쓰는 상황을 명확히 파악할 수 있도록 관리한다.

서브 Launch 파일에선 파라미터를 최소한으로만 설정하고, 상위 Launch 파일에서만 최종 파라미터를 결정하도록 역할을 구분하는 방법도 있다.

**이름 충돌 방지 (Namespace 사용)**: ROS2는 파라미터를 배포할 때 노드 단위로 관리하지만, 네임스페이스를 적극 활용해 충돌을 방지할 수 있다. 예컨대, 네임스페이스를 `sensor_ns`, `map_ns`로 설정하면 내부 파라미터 구조가 구분되어 파라미터 충돌 우려가 감소한다.

**유닛 테스트와 통합 테스트**:

* 단위 테스트에서 특정 파라미터가 올바르게 전달되고 있는지 확인한다.
* 통합 테스트에서 여러 노드를 동시에 실행하여 파라미터 충돌이 발생하지 않는지 점검한다.

**템플릿화된 파라미터 관리**:

* YAML, JSON 등 별도 파일을 두고, 파라미터를 중앙 관리한다.
* 여러 노드에 공통으로 필요한 파라미터라도, 각 노드용 설정 파일에서 별도의 이름 or 네임스페이스를 두어 공유 범위를 제한한다.

이와 같은 접근 방법을 통해서라도 파라미터 충돌이 발생하는 경우, 시스템의 복잡성을 다시 한 번 점검해보고, 로컬 및 글로벌 파라미터 스코프를 명확히 재설정하는 것이 필요하다.
