네임스페이스 설정 개요

네임스페이스는 ROS2에서 노드와 토픽의 이름을 체계적으로 관리하고 그룹화할 수 있는 중요한 개념이다. 이를 통해 같은 이름을 사용하는 여러 노드나 토픽을 충돌 없이 관리할 수 있다. 기본적으로, ROS2에서 네임스페이스를 설정하면 해당 네임스페이스 내의 모든 노드나 토픽에 적용되며, 전체 시스템 구조를 명확히 할 수 있는 이점이 있다.

네임스페이스 예시

네임스페이스를 설정하는 가장 간단한 예시는 노드를 특정 네임스페이스 안에 배치하는 것이다. 예를 들어, 로봇의 네임스페이스를 /robot1, /robot2로 설정하면, 각각의 로봇이 동일한 노드나 토픽 이름을 사용할 때 충돌을 피할 수 있다. 아래의 코드를 참조하라.

ros2 run <패키지 이름> <노드 이름> --ros-args --namespace /robot1

이 명령어는 노드를 /robot1 네임스페이스 안에서 실행하게 하여, 해당 노드의 이름이 /robot1/<노드 이름>로 설정된다.

네임스페이스와 토픽 이름

토픽 이름도 네임스페이스에 따라 달라진다. 예를 들어, /robot1/cmd_vel/robot2/cmd_vel 토픽은 서로 다른 로봇의 명령을 전달할 수 있는 독립적인 경로를 갖는다.

이를 수식으로 표현하면, 각 로봇의 토픽 경로는 다음과 같이 정의될 수 있다:

\text{토픽 경로} = \text{네임스페이스} + \text{토픽 이름}

따라서 로봇 1의 cmd_vel 토픽은:

\mathbf{T}_\text{robot1} = /\text{robot1}/\text{cmd\_vel}

로봇 2의 cmd_vel 토픽은:

\mathbf{T}_\text{robot2} = /\text{robot2}/\text{cmd\_vel}

로 표현된다.

리매핑 개념

리매핑은 기존의 토픽이나 서비스 이름을 다른 이름으로 변경하는 기능이다. 이를 통해 같은 노드를 여러 상황에서 재사용하거나, 노드 간의 통신 경로를 유연하게 설정할 수 있다.

예를 들어, 노드가 기본적으로 cmd_vel이라는 토픽을 구독하도록 되어 있지만, 다른 네임스페이스 내의 robot1/cmd_vel을 구독하게 하려면 리매핑을 통해 경로를 바꿀 수 있다.

리매핑을 위한 명령어는 다음과 같다.

ros2 run <패키지 이름> <노드 이름> --ros-args --remap __node:=/robot1/cmd_vel

위 명령어를 사용하면, 원래의 토픽 cmd_vel/robot1/cmd_vel로 리매핑된다.

리매핑을 활용한 실습 예제

리매핑을 사용하면 복잡한 시스템에서 동일한 코드로 여러 환경을 쉽게 설정할 수 있다. 예를 들어, 로봇 1과 로봇 2가 동일한 코드를 사용하지만 서로 다른 토픽 이름으로 통신해야 한다고 가정해 보자. 이 경우 리매핑을 통해 간단하게 해결할 수 있다.

퍼블리셔와 서브스크라이버 리매핑

두 개의 퍼블리셔 노드와 서브스크라이버 노드를 생성하고, 각 노드가 서로 다른 네임스페이스에서 통신하도록 리매핑할 수 있다. 다음은 퍼블리셔와 서브스크라이버를 각각 /robot1/cmd_vel/robot2/cmd_vel로 리매핑하는 방법을 설명한다.

먼저, 기본 퍼블리셔 코드는 다음과 같다.

#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"

class SimplePublisher : public rclcpp::Node
{
public:
  SimplePublisher() : Node("simple_publisher")
  {
    publisher_ = this->create_publisher<std_msgs::msg::String>("cmd_vel", 10);
    timer_ = this->create_wall_timer(
      500ms, std::bind(&SimplePublisher::publish_message, this));
  }

private:
  void publish_message()
  {
    auto message = std_msgs::msg::String();
    message.data = "Robot command";
    publisher_->publish(message);
  }

  rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_;
  rclcpp::TimerBase::SharedPtr timer_;
};

int main(int argc, char *argv[])
{
  rclcpp::init(argc, argv);
  rclcpp::spin(std::make_shared<SimplePublisher>());
  rclcpp::shutdown();
  return 0;
}

이 코드는 기본적으로 cmd_vel이라는 토픽에 메시지를 퍼블리싱한다. 이제 이 노드를 두 개의 서로 다른 네임스페이스로 리매핑해 봅시다.

  1. /robot1/cmd_vel로 리매핑: bash ros2 run <패키지 이름> simple_publisher --ros-args --remap cmd_vel:=/robot1/cmd_vel

  2. /robot2/cmd_vel로 리매핑: bash ros2 run <패키지 이름> simple_publisher --ros-args --remap cmd_vel:=/robot2/cmd_vel

리매핑 수식 표현

리매핑을 수식으로 나타내면 다음과 같다. 기본적으로 노드가 퍼블리싱하는 토픽을 \mathbf{T}_{\text{old}}라고 할 때, 리매핑 후 새로운 토픽 경로 \mathbf{T}_{\text{new}}는 다음과 같이 정의된다.

\mathbf{T}_{\text{new}} = \text{리매핑된 경로}

따라서 /robot1/cmd_vel로 리매핑된 토픽 경로는:

\mathbf{T}_{\text{new}} = /\text{robot1}/\text{cmd\_vel}

이고, /robot2/cmd_vel로 리매핑된 경우는:

\mathbf{T}_{\text{new}} = /\text{robot2}/\text{cmd\_vel}

서브스크라이버 리매핑 실습

서브스크라이버 코드도 마찬가지로 특정 네임스페이스 내의 토픽을 구독하도록 리매핑할 수 있다. 서브스크라이버 코드는 다음과 같다.

#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"

class SimpleSubscriber : public rclcpp::Node
{
public:
  SimpleSubscriber() : Node("simple_subscriber")
  {
    subscription_ = this->create_subscription<std_msgs::msg::String>(
      "cmd_vel", 10, std::bind(&SimpleSubscriber::topic_callback, this, std::placeholders::_1));
  }

private:
  void topic_callback(const std_msgs::msg::String::SharedPtr msg) const
  {
    RCLCPP_INFO(this->get_logger(), "I heard: '%s'", msg->data.c_str());
  }

  rclcpp::Subscription<std_msgs::msg::String>::SharedPtr subscription_;
};

int main(int argc, char *argv[])
{
  rclcpp::init(argc, argv);
  rclcpp::spin(std::make_shared<SimpleSubscriber>());
  rclcpp::shutdown();
  return 0;
}

이 서브스크라이버 노드도 퍼블리셔와 마찬가지로 cmd_vel 토픽을 구독하지만, 리매핑을 통해 다른 네임스페이스로 설정할 수 있다.

  1. /robot1/cmd_vel로 리매핑: bash ros2 run <패키지 이름> simple_subscriber --ros-args --remap cmd_vel:=/robot1/cmd_vel

  2. /robot2/cmd_vel로 리매핑: bash ros2 run <패키지 이름> simple_subscriber --ros-args --remap cmd_vel:=/robot2/cmd_vel

이를 통해 각각의 서브스크라이버는 다른 네임스페이스에서 퍼블리셔가 보낸 메시지를 받아볼 수 있다.

리매핑과 네임스페이스 실습을 통한 시나리오 구성

리매핑과 네임스페이스를 실습할 때, 두 개 이상의 로봇을 관리하는 환경을 구성하는 것이 대표적인 예시이다. 이번 실습에서는 두 개의 로봇이 각각 독립적인 네임스페이스에서 작동하고, 같은 코드를 사용하더라도 서로의 통신에 영향을 미치지 않도록 설정할 수 있는 방법을 소개하겠다.

시나리오: 두 로봇 간 독립적인 통신 설정

로봇 1과 로봇 2가 각각 독립적으로 움직이며, 동일한 노드를 사용하지만 다른 네임스페이스를 통해 통신을 구분해야 하는 경우이다. 이 시나리오에서 우리는 두 로봇이 서로 다른 퍼블리셔와 서브스크라이버를 통해 명령을 주고받도록 하겠다.

퍼블리셔 노드 설정 (로봇 1)
ros2 run <패키지 이름> simple_publisher --ros-args --remap cmd_vel:=/robot1/cmd_vel
퍼블리셔 노드 설정 (로봇 2)
ros2 run <패키지 이름> simple_publisher --ros-args --remap cmd_vel:=/robot2/cmd_vel
서브스크라이버 노드 설정 (로봇 1)
ros2 run <패키지 이름> simple_subscriber --ros-args --remap cmd_vel:=/robot1/cmd_vel
서브스크라이버 노드 설정 (로봇 2)
ros2 run <패키지 이름> simple_subscriber --ros-args --remap cmd_vel:=/robot2/cmd_vel

이렇게 설정하면, 두 로봇은 동일한 퍼블리셔와 서브스크라이버 코드를 사용하면서도 서로의 명령어를 받아들이지 않고 독립적으로 작동하게 된다. robot1 네임스페이스에 속한 노드와 robot2 네임스페이스에 속한 노드는 서로 영향을 미치지 않으며, 각각의 퍼블리셔는 자신에게 할당된 네임스페이스 내에서만 메시지를 보낸다.

시나리오 수식 표현

각 로봇의 퍼블리셔와 서브스크라이버 간의 토픽 흐름을 수식으로 표현하면 다음과 같다.

로봇 1에서 퍼블리셔가 보낸 메시지는 다음 경로를 따른다.

\mathbf{T}_\text{publish, robot1} = /\text{robot1}/\text{cmd\_vel}

서브스크라이버는 같은 경로에서 메시지를 수신한다.

\mathbf{T}_\text{subscribe, robot1} = /\text{robot1}/\text{cmd\_vel}

로봇 2에서도 퍼블리셔와 서브스크라이버 간의 메시지 경로는 동일한 구조를 따르지만, 네임스페이스가 다르다.

\mathbf{T}_\text{publish, robot2} = /\text{robot2}/\text{cmd\_vel}
\mathbf{T}_\text{subscribe, robot2} = /\text{robot2}/\text{cmd\_vel}

따라서, 각 로봇은 다른 네임스페이스로 리매핑된 토픽을 사용하여 독립적인 명령어 흐름을 유지할 수 있다.

네임스페이스 및 리매핑 실습 결과 분석

이제 두 로봇 간의 통신 경로가 설정되었으며, 네임스페이스와 리매핑을 통해 서로의 통신 경로가 독립적으로 유지됨을 확인할 수 있다. 이를 통해 복잡한 로봇 시스템에서도 동일한 노드와 토픽 이름을 충돌 없이 사용할 수 있게 된다.

네임스페이스와 리매핑 활용의 장점

  1. 확장성: 동일한 코드 베이스로 여러 로봇을 동시에 제어할 수 있어 코드 재사용성과 유지보수성이 높아진다.
  2. 독립성: 각각의 로봇이 자신만의 네임스페이스에서 통신을 하므로, 다른 로봇과의 충돌을 방지할 수 있다.
  3. 유연성: 리매핑을 통해 기존 시스템에서 특정 노드나 토픽의 이름을 쉽게 변경할 수 있어 상황에 맞게 시스템을 조정할 수 있다.