네임스페이스(Namespace)

ROS2에서 네임스페이스는 시스템의 계층 구조와 모듈화를 가능하게 하는 중요한 개념 중 하나이다. 네임스페이스를 통해 동일한 이름을 가진 여러 노드를 서로 구분할 수 있으며, 이를 통해 복잡한 로봇 시스템을 보다 효율적으로 구성할 수 있다. 네임스페이스는 주로 다음과 같은 목적을 가진다:

ROS2에서 네임스페이스는 node_name, topic_name, service_name과 같은 이름 앞에 추가된다. 예를 들어, /robot1/laser_scan과 같은 형식이 된다. 네임스페이스는 슬래시(/)를 사용하여 구분되며, 전역 네임스페이스는 /로 시작한다.

예시

rclcpp::NodeOptions options;
options.namespace_("/robot1");
auto node = rclcpp::Node::make_shared("laser_node", options);

위 코드에서는 laser_node/robot1이라는 네임스페이스 하에서 생성되었다. 이 노드는 /robot1/laser_node로 접근할 수 있다.

런치 시스템(Launch System)

ROS2에서 런치 시스템은 여러 노드와 관련된 설정을 동시에 실행하고, 시스템 전체를 통합적으로 관리하기 위한 도구이다. 특히 복잡한 로봇 시스템에서 여러 노드를 효과적으로 관리하기 위해 런치 파일을 사용한다. 런치 파일은 ROS1에 비해 ROS2에서 더욱 유연하게 설계되었으며, Python 기반의 DSL(Domain Specific Language)을 사용하여 작성된다.

런치 파일의 주요 기능은 다음과 같다:

기본 런치 파일 예시

from launch import LaunchDescription
from launch_ros.actions import Node

def generate_launch_description():
    return LaunchDescription([
        Node(
            package='demo_nodes_cpp',
            executable='talker',
            namespace='robot1',
            name='talker',
            output='screen'
        ),
        Node(
            package='demo_nodes_cpp',
            executable='listener',
            namespace='robot1',
            name='listener',
            output='screen'
        )
    ])

위 런치 파일은 demo_nodes_cpp 패키지의 talkerlistener 노드를 /robot1 네임스페이스 하에서 실행한다. 이처럼 ROS2의 런치 시스템은 각 노드에 네임스페이스를 할당하여 로봇의 모듈성을 높인다.

런치 파일에서 네임스페이스 적용

런치 파일을 사용하여 노드를 실행할 때 네임스페이스를 설정하면, 해당 네임스페이스는 노드가 생성하는 모든 토픽, 서비스, 액션에 자동으로 적용된다. 예를 들어, robot1 네임스페이스에 속한 노드가 scan이라는 토픽을 발행한다면, 실제 토픽 이름은 /robot1/scan이 된다. 이를 통해 복잡한 네트워크를 간소화할 수 있다.

네임스페이스와 노드 간의 상호작용

여러 노드를 사용할 때, 각 노드에 네임스페이스를 어떻게 설정하느냐에 따라 노드 간의 통신 구조가 달라진다. 예를 들어, 동일한 네임스페이스를 사용하는 노드들은 자연스럽게 서로의 토픽을 구독하고 발행할 수 있지만, 다른 네임스페이스에 있는 노드와의 통신은 더 복잡한 설정을 필요로 할 수 있다.

graph LR A[robot1/talker] --> B[robot1/listener] C[robot2/talker] --> D[robot2/listener]

위와 같은 구조에서, /robot1/talker/robot1/listener는 동일한 네임스페이스 내에 있기 때문에 자동으로 통신이 이루어지지만, /robot1/robot2 네임스페이스 간의 통신을 설정하려면 추가적인 설정이 필요하다.

런치 파일에서 조건부 실행

런치 파일에서는 특정 조건에 따라 노드를 실행하거나 제외할 수 있는 기능을 제공한다. 이를 통해 로봇의 상태나 외부 입력에 따라 런치 파일을 동적으로 제어할 수 있다. 조건부 실행을 구현하는 방법은 주로 IfCondition이나 UnlessCondition을 사용하는 것이다. 이를 통해 조건부로 네임스페이스를 설정하거나 노드 실행을 조정할 수 있다.

조건부 실행 예시

from launch import LaunchDescription
from launch_ros.actions import Node
from launch.conditions import IfCondition
from launch.substitutions import LaunchConfiguration

def generate_launch_description():
    return LaunchDescription([
        Node(
            package='demo_nodes_cpp',
            executable='talker',
            namespace='robot1',
            name='talker',
            output='screen',
            condition=IfCondition(LaunchConfiguration('enable_talker'))
        ),
        Node(
            package='demo_nodes_cpp',
            executable='listener',
            namespace='robot1',
            name='listener',
            output='screen'
        )
    ])

위 코드에서는 enable_talker라는 런치 설정에 따라 talker 노드가 실행될지 여부를 결정할 수 있다. 이렇게 조건부 실행을 설정하면, 로봇이 특정 상황에서만 필요한 노드를 실행하도록 할 수 있으며, 이를 통해 시스템 자원의 효율적 사용이 가능해진다.

런치 파일의 파라미터 전달

런치 파일을 통해 각 노드에 파라미터를 전달하는 것은 ROS2 시스템의 중요한 특징 중 하나이다. 이를 통해 런타임에 노드의 동작을 변경할 수 있으며, 이는 로봇의 환경 설정이나 작업 특성에 맞춰 유연하게 변경할 수 있다.

파라미터는 노드 실행 시 전달되며, YAML 파일을 사용하여 대규모 파라미터를 관리할 수 있다. 파라미터 전달은 노드의 parameters 옵션을 통해 설정된다.

파라미터 전달 예시

from launch import LaunchDescription
from launch_ros.actions import Node

def generate_launch_description():
    return LaunchDescription([
        Node(
            package='demo_nodes_cpp',
            executable='talker',
            namespace='robot1',
            name='talker',
            output='screen',
            parameters=[{'publish_rate': 10}]
        ),
        Node(
            package='demo_nodes_cpp',
            executable='listener',
            namespace='robot1',
            name='listener',
            output='screen'
        )
    ])

위 예시에서는 talker 노드에 publish_rate라는 파라미터를 전달하여 초당 10회의 메시지를 발행하도록 설정하고 있다. 이처럼 런치 파일을 사용하여 노드의 동작을 유연하게 변경할 수 있다.

네임스페이스와 파라미터의 관계

네임스페이스는 파라미터에도 영향을 미칠 수 있다. 특정 네임스페이스에서 실행된 노드가 가지는 파라미터는 해당 네임스페이스 내에서만 유효하다. 이를 통해 동일한 노드가 다른 네임스페이스에서 서로 다른 파라미터를 가지고 실행될 수 있다. 예를 들어, 동일한 노드를 여러 로봇에서 사용하지만, 각 로봇의 특성에 맞는 파라미터를 적용하고자 할 때 유용하다.

수학적으로는 네임스페이스 내에서의 파라미터 전달을 다음과 같이 표현할 수 있다. 어떤 노드 N이 파라미터 p를 가지며, 네임스페이스 \mathcal{N}_1\mathcal{N}_2에서 각각 다른 파라미터 값을 설정한 경우:

N_{\mathcal{N}_1}(p_1), \quad N_{\mathcal{N}_2}(p_2)

여기서 p_1 \neq p_2인 경우, 각 네임스페이스에서 노드는 서로 다른 파라미터 값을 가지게 된다. 이를 통해 시스템의 유연성을 높일 수 있다.

런치 파일에서 그룹화된 네임스페이스

런치 파일에서는 여러 노드를 하나의 네임스페이스 하에 그룹화할 수 있다. 이를 통해 관련된 노드들을 논리적으로 묶고, 동일한 네임스페이스 안에서 효율적으로 관리할 수 있다. 그룹화를 통해 여러 노드가 동일한 환경에서 동작하도록 설정할 수 있으며, 네임스페이스 충돌을 방지할 수 있다.

그룹화된 네임스페이스 예시

from launch import LaunchDescription
from launch_ros.actions import Node
from launch_ros.actions import PushRosNamespace

def generate_launch_description():
    return LaunchDescription([
        PushRosNamespace('robot1'),
        Node(
            package='demo_nodes_cpp',
            executable='talker',
            name='talker',
            output='screen'
        ),
        Node(
            package='demo_nodes_cpp',
            executable='listener',
            name='listener',
            output='screen'
        )
    ])

위 코드에서는 PushRosNamespace를 사용하여 talkerlistener 노드를 robot1 네임스페이스 하에 그룹화하였다. 이를 통해 동일한 네임스페이스에서 노드가 실행되며, 관련된 노드들의 네임스페이스 관리가 용이해진다.

네임스페이스 충돌 해결

여러 노드를 실행할 때 네임스페이스 충돌이 발생할 수 있다. 특히, 다수의 로봇이 동일한 토픽이나 서비스를 사용하는 경우, 네임스페이스를 적절히 설정하지 않으면 의도하지 않은 통신이 이루어질 수 있다. 이를 방지하기 위해 각 로봇이나 모듈에 별도의 네임스페이스를 할당하여 충돌을 방지해야 한다.

네임스페이스 충돌 문제는 특히 여러 로봇이 동일한 환경에서 동작할 때 빈번하게 발생한다. 예를 들어, 두 로봇이 동일한 주제를 발행하거나 구독하려고 할 때, 다음과 같은 상황이 발생할 수 있다:

\text{robot1}/\text{scan} \quad \text{vs} \quad \text{robot2}/\text{scan}

두 로봇이 모두 scan이라는 주제를 사용할 경우, 네임스페이스가 적절히 설정되지 않으면 주제 충돌이 발생하게 된다. 이를 방지하기 위해 각 로봇에 고유한 네임스페이스를 할당하여 관리해야 한다.

런치 파일에서 환경 변수 설정

ROS2 런치 시스템은 환경 변수를 사용하여 노드 실행 시 다양한 설정을 동적으로 조정할 수 있다. 환경 변수는 시스템의 특정 설정을 제어하거나 조건부 실행을 제어하는 데 유용하다. 예를 들어, 로봇이 구동되는 환경에 따라 다르게 동작해야 하는 경우 환경 변수를 사용하면, 런타임에서 쉽게 설정을 변경할 수 있다.

환경 변수는 런치 파일에서 env 옵션을 통해 설정할 수 있으며, 특정 환경 변수를 참조하여 노드가 실행되도록 할 수 있다. 이를 통해 로봇의 센서 구성, 통신 설정, 또는 시뮬레이션과 실제 환경을 구분하는 등의 유연한 구성이 가능하다.

환경 변수 설정 예시

from launch import LaunchDescription
from launch_ros.actions import Node

def generate_launch_description():
    return LaunchDescription([
        Node(
            package='demo_nodes_cpp',
            executable='talker',
            namespace='robot1',
            name='talker',
            output='screen',
            env={'ROS_LOG_LEVEL': 'DEBUG'}
        ),
        Node(
            package='demo_nodes_cpp',
            executable='listener',
            namespace='robot1',
            name='listener',
            output='screen'
        )
    ])

위 예시에서는 ROS_LOG_LEVEL이라는 환경 변수를 설정하여, talker 노드가 디버그 모드로 실행되도록 하였다. 이를 통해 런타임에서 시스템의 로그 레벨을 동적으로 제어할 수 있다.

네임스페이스와 런치 파일에서의 다중 로봇 지원

다수의 로봇을 ROS2 시스템에서 동시에 제어할 때, 각각의 로봇에 대해 독립적인 네임스페이스를 설정하는 것이 중요하다. 이를 통해 각 로봇은 자신만의 고유한 주제, 서비스, 액션을 사용할 수 있으며, 다른 로봇과의 통신 충돌을 방지할 수 있다.

다중 로봇 시스템을 설정할 때는 각 로봇에 네임스페이스를 부여하고, 해당 네임스페이스 내에서 모든 노드가 실행되도록 해야 한다. 이 경우, 로봇의 구성 요소가 서로 독립적으로 동작하며, 각 로봇의 동작을 보다 쉽게 모니터링하고 제어할 수 있다.

다중 로봇 시스템에서의 런치 파일 예시

from launch import LaunchDescription
from launch_ros.actions import Node
from launch_ros.actions import PushRosNamespace

def generate_launch_description():
    return LaunchDescription([
        # 첫 번째 로봇
        PushRosNamespace('robot1'),
        Node(
            package='demo_nodes_cpp',
            executable='talker',
            name='talker',
            output='screen'
        ),
        Node(
            package='demo_nodes_cpp',
            executable='listener',
            name='listener',
            output='screen'
        ),

        # 두 번째 로봇
        PushRosNamespace('robot2'),
        Node(
            package='demo_nodes_cpp',
            executable='talker',
            name='talker',
            output='screen'
        ),
        Node(
            package='demo_nodes_cpp',
            executable='listener',
            name='listener',
            output='screen'
        )
    ])

위 코드에서는 두 개의 로봇, robot1robot2에 대해 각각의 네임스페이스가 설정되어 있으며, 각 로봇에서 talkerlistener 노드가 독립적으로 실행된다. 이처럼 다중 로봇 환경에서는 네임스페이스를 통해 각 로봇의 동작을 분리하여 관리할 수 있다.

런치 파일에서 파라미터 파일 사용

런치 파일을 통해 파라미터를 설정할 때, 개별 파라미터를 직접 설정하는 방법 외에도 파라미터 파일을 사용하여 대규모 설정을 한번에 적용할 수 있다. 파라미터 파일은 YAML 형식으로 작성되며, 각 노드에 필요한 여러 파라미터를 미리 정의할 수 있다.

파라미터 파일은 런치 파일에서 parameters 옵션을 사용하여 적용된다. 이를 통해 로봇 시스템의 구성 요소들이 복잡한 설정을 요구할 때, 해당 설정을 YAML 파일로 관리함으로써 일관된 설정 적용이 가능해진다.

파라미터 파일 사용 예시

from launch import LaunchDescription
from launch_ros.actions import Node

def generate_launch_description():
    return LaunchDescription([
        Node(
            package='demo_nodes_cpp',
            executable='talker',
            namespace='robot1',
            name='talker',
            output='screen',
            parameters=['config/talker_params.yaml']
        ),
        Node(
            package='demo_nodes_cpp',
            executable='listener',
            namespace='robot1',
            name='listener',
            output='screen'
        )
    ])

여기서는 talker 노드에 talker_params.yaml 파일을 파라미터로 전달하였다. 해당 YAML 파일에는 노드에 필요한 다양한 설정값이 포함될 수 있다.

네임스페이스와 상위-하위 구조의 표현

ROS2 네임스페이스는 계층 구조를 형성할 수 있으며, 상위-하위 구조를 통해 시스템의 복잡한 계층을 쉽게 표현할 수 있다. 예를 들어, 각 로봇이 여러 센서와 액추에이터를 가지고 있을 때, 이러한 센서와 액추에이터에 각각의 하위 네임스페이스를 할당할 수 있다.

상위 네임스페이스는 일반적으로 로봇 전체를 대표하고, 하위 네임스페이스는 로봇 내의 특정 모듈이나 센서를 나타낸다. 이때 상위 네임스페이스는 하위 네임스페이스의 이름 앞에 추가되어 전체 계층 구조를 명확히 표현한다.

상위-하위 구조 예시

/robot1/camera/image_raw
/robot1/lidar/scan
/robot2/camera/image_raw
/robot2/lidar/scan

여기서 robot1robot2는 각각 로봇의 상위 네임스페이스이며, cameralidar는 각 로봇에 설치된 센서들의 하위 네임스페이스이다. 이를 통해 각 로봇의 센서 데이터를 구분하여 처리할 수 있다.

네임스페이스 사용의 수학적 모델

ROS2의 네임스페이스는 이름 충돌을 방지하고 시스템을 계층적으로 구성하기 위한 일종의 함수적 매핑 역할을 한다. 이를 수학적으로 나타내면, 각 노드의 이름은 네임스페이스 함수 \mathcal{N}에 의해 정의될 수 있다.

노드 n_i가 네임스페이스 \mathcal{N} 내에 있을 때, 그 전체 이름은 다음과 같이 정의된다:

\text{FullName}(n_i) = \mathcal{N}(n_i)

예를 들어, 노드 talkerrobot1이라는 네임스페이스 내에 있을 경우, 다음과 같이 표현된다:

\text{FullName}(\text{talker}) = \mathcal{N}_{\text{robot1}}(\text{talker}) = /robot1/talker

이를 통해 네임스페이스는 노드의 이름을 유일하게 식별할 수 있게 하며, 계층적인 구조를 유지하게 한다.

런치 시스템에서 리스폰(Respawn) 설정

ROS2 런치 시스템에서는 특정 노드가 비정상 종료되었을 때, 자동으로 다시 실행(리스폰)하도록 설정할 수 있다. 이는 시스템의 안정성을 보장하기 위한 중요한 기능 중 하나이다. 특히 자율 주행 로봇이나 장시간 동작하는 시스템에서, 노드가 예기치 않게 종료되었을 경우 해당 노드를 다시 실행시킴으로써 시스템이 지속적으로 동작하도록 유지할 수 있다.

리스폰 설정은 노드의 respawn 옵션을 통해 제어되며, 필요한 경우 지연 시간을 두고 리스폰을 설정할 수도 있다.

리스폰 설정 예시

from launch import LaunchDescription
from launch_ros.actions import Node

def generate_launch_description():
    return LaunchDescription([
        Node(
            package='demo_nodes_cpp',
            executable='talker',
            namespace='robot1',
            name='talker',
            output='screen',
            respawn=True,
            respawn_delay=2.0
        ),
        Node(
            package='demo_nodes_cpp',
            executable='listener',
            namespace='robot1',
            name='listener',
            output='screen'
        )
    ])

위 예시에서는 talker 노드에 대해 respawn=True로 설정하여, 노드가 비정상 종료되었을 때 2초 후에 자동으로 다시 실행되도록 설정하였다. 이를 통해 로봇의 핵심 기능이 중단 없이 유지될 수 있다.

런치 파일에서 그룹(Group) 기능

런치 파일에서는 여러 노드를 그룹화하여 하나의 논리적 단위로 처리할 수 있다. 그룹 기능을 사용하면 여러 노드를 함께 실행하거나, 특정 설정(예: 네임스페이스)을 그룹 내 노드 전체에 일괄 적용할 수 있다. 이 기능은 복잡한 시스템에서 관련된 노드들을 묶어 관리할 때 매우 유용하다.

그룹화된 노드들은 공통의 설정을 공유할 수 있으며, 그룹 단위로 조건부 실행, 네임스페이스 설정, 파라미터 전달 등을 할 수 있다.

그룹 기능 예시

from launch import LaunchDescription
from launch_ros.actions import Node
from launch_ros.actions import PushRosNamespace
from launch.actions import GroupAction

def generate_launch_description():
    return LaunchDescription([
        GroupAction([
            PushRosNamespace('robot1'),
            Node(
                package='demo_nodes_cpp',
                executable='talker',
                name='talker',
                output='screen'
            ),
            Node(
                package='demo_nodes_cpp',
                executable='listener',
                name='listener',
                output='screen'
            )
        ]),
        GroupAction([
            PushRosNamespace('robot2'),
            Node(
                package='demo_nodes_cpp',
                executable='talker',
                name='talker',
                output='screen'
            ),
            Node(
                package='demo_nodes_cpp',
                executable='listener',
                name='listener',
                output='screen'
            )
        ])
    ])

위 코드에서는 GroupAction을 사용하여 robot1robot2 네임스페이스 내의 노드를 각각 그룹화하였다. 이처럼 그룹 기능을 사용하면 관련 노드들을 모아 일관된 설정을 적용할 수 있으며, 시스템의 유지보수성과 가독성을 높일 수 있다.

런치 파일에서 매개변수 설정 시 고급 기능

ROS2 런치 파일에서 파라미터를 전달하는 기본적인 방법 외에도, 고급 설정 기능을 사용하여 보다 복잡한 설정을 적용할 수 있다. 이를 통해 파라미터를 동적으로 변경하거나, 외부 환경에 따라 설정을 달리할 수 있다.

특히, 런치 시스템의 LaunchConfiguration을 사용하면 실행 중 파라미터 값을 동적으로 설정할 수 있으며, 시스템의 상태나 입력 값에 따라 노드의 동작을 변화시킬 수 있다. 또한, 파라미터를 런치 파일의 명령줄 인수로 전달하여, 런타임 시 사용자가 직접 값을 변경할 수 있도록 할 수도 있다.

동적 파라미터 설정 예시

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

def generate_launch_description():
    return LaunchDescription([
        Node(
            package='demo_nodes_cpp',
            executable='talker',
            namespace='robot1',
            name='talker',
            output='screen',
            parameters=[{'publish_rate': LaunchConfiguration('publish_rate')}]
        )
    ])

위 예시에서는 publish_rate 파라미터를 런타임 시 설정할 수 있도록 하였다. 런치 파일 실행 시 다음과 같이 명령줄 인수를 통해 값을 전달할 수 있다.

ros2 launch my_launch_file.py publish_rate:=10

이렇게 설정된 값은 런타임에서 동적으로 적용되어 노드의 동작에 영향을 미치게 된다.

런치 파일에서 노드 간의 의존성 설정

노드 간의 의존성 관계를 설정하는 것은 자율 주행 로봇과 같은 복잡한 시스템에서 중요한 기능이다. 일부 노드가 다른 노드의 실행 상태에 의존하는 경우, 특정 노드가 정상적으로 실행된 이후에만 다른 노드를 실행해야 한다. 이러한 의존성은 런치 파일을 통해 설정할 수 있다.

노드 의존성 설정 예시

from launch import LaunchDescription
from launch_ros.actions import Node
from launch.actions import RegisterEventHandler
from launch.event_handlers import OnProcessExit

def generate_launch_description():
    talker_node = Node(
        package='demo_nodes_cpp',
        executable='talker',
        namespace='robot1',
        name='talker',
        output='screen'
    )

    listener_node = Node(
        package='demo_nodes_cpp',
        executable='listener',
        namespace='robot1',
        name='listener',
        output='screen'
    )

    return LaunchDescription([
        talker_node,
        RegisterEventHandler(
            OnProcessExit(
                target_action=talker_node,
                on_exit=[listener_node]
            )
        )
    ])

위 코드에서는 talker 노드가 종료된 이후에만 listener 노드가 실행되도록 설정하였다. 이는 노드 간의 실행 순서를 제어하거나, 특정 노드가 정상 종료된 후에 다른 노드를 실행해야 할 때 유용하다.

네임스페이스와 런치 시스템의 결합: 동적 구성과 유연성

ROS2 네임스페이스와 런치 시스템의 결합은 자율 주행 로봇과 같은 복잡한 시스템에서 매우 중요한 역할을 한다. 네임스페이스를 사용하면 로봇의 모듈성을 강화할 수 있으며, 런치 시스템을 통해 여러 모듈을 중앙에서 관리할 수 있다. 특히, 동적 파라미터 설정, 리스폰 기능, 그룹화된 네임스페이스, 환경 변수 설정 등 다양한 기능을 통해 로봇 시스템의 유연성을 극대화할 수 있다.

수학적으로, 네임스페이스와 런치 시스템의 동작을 함수로 모델링할 수 있다. 노드 N이 파라미터 p, 네임스페이스 \mathcal{N}, 런치 설정 L에 의해 제어되는 시스템에서, 노드의 전체 실행 상태는 다음과 같이 나타낼 수 있다:

S(N) = f(\mathcal{N}(N), p, L)

여기서 f는 노드 실행에 대한 전반적인 함수로, 네임스페이스와 파라미터, 런치 설정의 조합에 의해 노드의 최종 동작이 결정된다. 이를 통해 다수의 로봇 시스템을 효과적으로 관리할 수 있으며, 복잡한 시스템의 확장성을 높일 수 있다.