ROS2 파라미터 기반 조건 노드 (ROS2 Parameter-Based Condition Nodes)
1. 개요
ROS2 파라미터 기반 조건 노드는 ROS2 노드의 파라미터(parameter) 값을 읽어 조건을 평가하는 조건 노드이다. ROS2 파라미터 시스템은 노드의 설정 값을 런타임에 동적으로 변경할 수 있는 메커니즘을 제공하며, 이를 통해 로봇의 동작 모드, 임계값, 기능 활성화 여부 등을 외부에서 ���어할 수 있다. 파라미터 기반 조건 노드는 이러한 파라미터 값을 행동 트리의 의사 결정에 반영하여, 운용자의 설정 변경이 로봇의 행동에 즉시 반영되도록 한다.
2. ROS2 파라미터 시스템
2.1 파라미터의 구조와 타입
ROS2 파라미터는 이름-값 쌍(name-value pair)으로 구성되며, 다음 타입을 지원한다.
| 파라미터 타입 | rclcpp::ParameterType | C++ 타입 | 설명 |
|---|---|---|---|
| 불리언 | PARAMETER_BOOL | bool | 참/거짓 |
| 정수 | PARAMETER_INTEGER | int64_t | 64비트 정수 |
| 부동소수점 | PARAMETER_DOUBLE | double | 64비트 부동소수점 |
| 문자열 | PARAMETER_STRING | std::string | 문자열 |
| 바이트 배열 | PARAMETER_BYTE_ARRAY | std::vector<uint8_t> | 바이트 배열 |
| 불리언 배열 | PARAMETER_BOOL_ARRAY | std::vector<bool> | 불리언 배열 |
| 정수 배열 | PARAMETER_INTEGER_ARRAY | std::vector<int64_t> | 정수 배열 |
| 부동소수점 배열 | PARAMETER_DOUBLE_ARRAY | std::vector<double> | 부동소수점 배열 |
| 문자열 배열 | PARAMETER_STRING_ARRAY | std::vector<std::string> | 문자열 배열 |
2.2 파라미터 접근 방식
조건 노드에서 ROS2 파라미터에 접근하는 방법은 두 가지로 구분된다.
- 로컬 파라미터 접근: 행동 트리가 실행되는 ROS2 노드 자체의 파라미터를 직접 읽는 방식이다.
rclcpp::Node::get_parameter()메서드를 사용한다. - 원격 파라미터 접근: 다른 ROS2 노드의 파라미터를 파라미터 클라이언트(
rclcpp::AsyncParametersClient)를 통해 읽는 방식이다. 이 경우 서비스 호출이 수반되므로 비동기 처리가 필요하다.
3. 로컬 파라미터 기반 조��� 노드
3.1 불리언 파라미터 확인
가장 단순한 형태는 불리언 파라미터의 값을 직접 확인하는 것이다.
class IsParameterTrue : public BT::ConditionNode
{
public:
IsParameterTrue(const std::string& name,
const BT::NodeConfiguration& config,
rclcpp::Node::SharedPtr node)
: BT::ConditionNode(name, config), node_(node)
{}
static BT::PortsList providedPorts()
{
return {
BT::InputPort<std::string>("param_name",
"확인할 파라미터 이름")
};
}
BT::NodeStatus tick() override
{
std::string param_name;
getInput("param_name", param_name);
if (!node_->has_parameter(param_name))
{
return BT::NodeStatus::FAILURE;
}
rclcpp::Parameter param = node_->get_parameter(param_name);
if (param.get_type() != rclcpp::ParameterType::PARAMETER_BOOL)
{
return BT::NodeStatus::FAILURE;
}
if (param.as_bool())
{
return BT::NodeStatus::SUCCESS;
}
return BT::NodeStatus::FAILURE;
}
private:
rclcpp::Node::SharedPtr node_;
};
파라미터가 존재하지 않거나 타입이 불리언이 아닌 경우 FAILURE를 반환한다. 이는 잘못된 파라미터 이름 지정이나 타입 불일치를 안전하게 처리한다.
3.2 수치 파라미터 비교
수치형 파라미터를 기준값과 비교하는 조건 노드이다.
class IsParameterInRange : public BT::ConditionNode
{
public:
IsParameterInRange(const std::string& name,
const BT::NodeConfiguration& config,
rclcpp::Node::SharedPtr node)
: BT::ConditionNode(name, config), node_(node)
{}
static BT::PortsList providedPorts()
{
return {
BT::InputPort<std::string>("param_name",
"확인할 파라미터 이름"),
BT::InputPort<double>("min_value", "하한값"),
BT::InputPort<double>("max_value", "상한값")
};
}
BT::NodeStatus tick() override
{
std::string param_name;
getInput("param_name", param_name);
if (!node_->has_parameter(param_name))
{
return BT::NodeStatus::FAILURE;
}
rclcpp::Parameter param = node_->get_parameter(param_name);
double value;
if (param.get_type() == rclcpp::ParameterType::PARAMETER_DOUBLE)
{
value = param.as_double();
}
else if (param.get_type() ==
rclcpp::ParameterType::PARAMETER_INTEGER)
{
value = static_cast<double>(param.as_int());
}
else
{
return BT::NodeStatus::FAILURE;
}
double min_val, max_val;
getInput("min_value", min_val);
getInput("max_value", max_val);
if (value >= min_val && value <= max_val)
{
return BT::NodeStatus::SUCCESS;
}
return BT::NodeStatus::FAILURE;
}
private:
rclcpp::Node::SharedPtr node_;
};
정수 파라미터와 부동소수점 파라미터를 모두 지원하기 위해, 정수 타입의 경우 double로 변환하여 비교한다.
3.3 문자열 파라미터 비교
문자열 파라미터를 기대 값과 비교하는 조건 노드이다. 동작 모드, 상태 레이블, 설정 값 등을 확인하는 데 활용된다.
class IsParameterEqual : public BT::ConditionNode
{
public:
IsParameterEqual(const std::string& name,
const BT::NodeConfiguration& config,
rclcpp::Node::SharedPtr node)
: BT::ConditionNode(name, config), node_(node)
{}
static BT::PortsList providedPorts()
{
return {
BT::InputPort<std::string>("param_name",
"확인할 파라미터 이름"),
BT::InputPort<std::string>("expected_value",
"기대하는 파라미터 값")
};
}
BT::NodeStatus tick() override
{
std::string param_name, expected_value;
getInput("param_name", param_name);
getInput("expected_value", expected_value);
if (!node_->has_parameter(param_name))
{
return BT::NodeStatus::FAILURE;
}
rclcpp::Parameter param = node_->get_parameter(param_name);
if (param.get_type() != rclcpp::ParameterType::PARAMETER_STRING)
{
return BT::NodeStatus::FAILURE;
}
if (param.as_string() == expected_value)
{
return BT::NodeStatus::SUCCESS;
}
return BT::NodeStatus::FAILURE;
}
private:
rclcpp::Node::SharedPtr node_;
};
4. 원격 파라미터 기반 조��� 노드
다른 ROS2 ��드의 파라미터를 질의하기 위해서는 rclcpp::AsyncParametersClient를 사용한다. 원격 파라미터 접근은 내부적으로 서비스 호출을 수반하므로, 비동기 호출과 결과 캐싱 전략을 적용하여야 한다.
class IsRemoteParameterTrue : public BT::ConditionNode
{
public:
IsRemoteParameterTrue(const std::string& name,
const BT::NodeConfiguration& config,
rclcpp::Node::SharedPtr node)
: BT::ConditionNode(name, config),
node_(node),
cached_value_(false),
has_cache_(false),
request_pending_(false)
{
std::string remote_node_name;
getInput("remote_node", remote_node_name);
param_client_ =
std::make_shared<rclcpp::AsyncParametersClient>(
node_, remote_node_name);
}
static BT::PortsList providedPorts()
{
return {
BT::InputPort<std::string>("remote_node",
"원격 노드 이름"),
BT::InputPort<std::string>("param_name",
"확인할 파라미터 이름"),
BT::InputPort<double>("cache_duration_sec", 2.0,
"캐시 유효 기간 (초)")
};
}
BT::NodeStatus tick() override
{
double cache_duration;
getInput("cache_duration_sec", cache_duration);
// 캐시 유효성 확인
if (has_cache_)
{
auto elapsed =
(node_->get_clock()->now() - cache_time_).seconds();
if (elapsed < cache_duration)
{
return cached_value_ ? BT::NodeStatus::SUCCESS
: BT::NodeStatus::FAILURE;
}
}
// 비동기 요청 발행
if (!request_pending_ && param_client_->service_is_ready())
{
std::string param_name;
getInput("param_name", param_name);
request_pending_ = true;
auto future = param_client_->get_parameters(
{param_name});
future.then(
[this](std::shared_future<
%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20std::vector<rclcpp::Parameter>>%20result) {
std::lock_guard<std::mutex> lock(mutex_);
auto params = result.get();
if (!params.empty() &&
params[0].get_type() ==
rclcpp::ParameterType::PARAMETER_BOOL)
{
cached_value_ = params[0].as_bool();
}
else
{
cached_value_ = false;
}
cache_time_ = node_->get_clock()->now();
has_cache_ = true;
request_pending_ = false;
});
}
if (has_cache_)
{
return cached_value_ ? BT::NodeStatus::SUCCESS
: BT::NodeStatus::FAILURE;
}
return BT::NodeStatus::FAILURE;
}
private:
rclcpp::Node::SharedPtr node_;
std::shared_ptr<rclcpp::AsyncParametersClient> param_client_;
std::mutex mutex_;
bool cached_value_;
bool has_cache_;
bool request_pending_;
rclcpp::Time cache_time_;
};
원격 파라미터 접근은 로컬 접근에 비해 지연이 크므로, 캐싱 전략이 필수적이다. 파라미터가 빈번히 변경되지 않는 경우 수 초 이상의 캐시 유효 기간을 설정하여 서비스 호출 빈도를 최소화한다.
5. 파라미터 이벤트 구독 기반 구현
ROS2는 파라미터 변경 시 이벤트를 발행하는 메커니즘을 제공한다. ParameterEventHandler를 활용하면 파라미터 변경을 구독하여 최신 값을 유지할 수 있으며, 이 방식은 주기적 폴링보다 효율적��다.
class IsParameterEnabled : public BT::ConditionNode
{
public:
IsParameterEnabled(const std::string& name,
const BT::NodeConfiguration& config,
rclcpp::Node::SharedPtr node)
: BT::ConditionNode(name, config),
node_(node),
current_value_(false),
initialized_(false)
{
std::string param_name;
getInput("param_name", param_name);
// 초기값 읽기
if (node_->has_parameter(param_name))
{
current_value_ =
node_->get_parameter(param_name).as_bool();
initialized_ = true;
}
// 파라미터 변경 이벤트 구독
param_handler_ =
std::make_shared<rclcpp::ParameterEventHandler>(node_);
callback_handle_ = param_handler_->add_parameter_callback(
param_name,
[this](const%20rclcpp::Parameter&%20param) {
std::lock_guard<std::mutex> lock(mutex_);
if (param.get_type() ==
rclcpp::ParameterType::PARAMETER_BOOL)
{
current_value_ = param.as_bool();
initialized_ = true;
}
});
}
static BT::PortsList providedPorts()
{
return {
BT::InputPort<std::string>("param_name",
"감시할 파라미터 이름")
};
}
BT::NodeStatus tick() override
{
std::lock_guard<std::mutex> lock(mutex_);
if (!initialized_)
{
return BT::NodeStatus::FAILURE;
}
return current_value_ ? BT::NodeStatus::SUCCESS
: BT::NodeStatus::FAILURE;
}
private:
rclcpp::Node::SharedPtr node_;
std::shared_ptr<rclcpp::ParameterEventHandler> param_handler_;
std::shared_ptr<rclcpp::ParameterCallbackHandle> callback_handle_;
std::mutex mutex_;
bool current_value_;
bool initialized_;
};
이 구현에서 ParameterEventHandler는 파라미터 변경 시 콜백을 호출하여 current_value_를 갱신한다. tick() 메서드는 갱신된 값을 읽기만 하므로 서비스 호출이나 파라미터 질의 없이 즉시 결과를 반환한다.
6. XML 행동 트리에서의 활용
6.1 동작 모드에 따른 행동 분기
<BehaviorTree ID="ModeBasedBehavior">
<Fallback>
<Sequence>
<Condition ID="IsParameterEqual"
param_name="operation_mode"
expected_value="autonomous"/>
<Action ID="AutonomousNavigation"/>
</Sequence>
<Sequence>
<Condition ID="IsParameterEqual"
param_name="operation_mode"
expected_value="manual"/>
<Action ID="ManualControl"/>
</Sequence>
<Action ID="SafeStop"/>
</Fallback>
</BehaviorTree>
운용자가 ros2 param set 명령으로 operation_mode 파라미터를 변경하면, 다음 tick에서 행동 트리가 변경된 모드에 맞는 행동을 선택한다.
6.2 기능 활성화/비활성화 제���
<BehaviorTree ID="ConditionalFeature">
<ReactiveSequence>
<Condition ID="IsParameterEnabled"
param_name="obstacle_avoidance_enabled"/>
<Action ID="ObstacleAvoidance"/>
</ReactiveSequence>
</BehaviorTree>
obstacle_avoidance_enabled 파라미터를 false로 설정하면 장애물 회피 기능이 비활성화된다. 파라미터 이벤트 구독 방식을 사용하면 변경이 즉시 반영된다.
7. 설계 시 고려 사항
7.1 파라미터 선언과 기본값
ROS2에서 파라미터를 사용하기 전에 declare_parameter() 메서드로 파라미터를 선언하여야 한다. 선언되지 않은 파라미터에 접근하면 예외가 발생한다. 조건 노드가 참조하는 파라미터는 행동 트리 실행 전에 해당 ROS2 노드에서 사전 선언되어 있어야 한다.
node->declare_parameter("operation_mode", "autonomous");
node->declare_parameter("obstacle_avoidance_enabled", true);
node->declare_parameter("max_speed", 1.5);
7.2 파라미터 타입 안전성
ROS2 파라미터는 런타임에 타입이 결정되므로, 조건 노드에서 파라미터 값을 읽을 때 타입을 검증하여야 한다. rclcpp::Parameter::get_type() 메서드를 통해 파라미터의 실제 타입을 확인하고, 기대하는 타입과 일치하지 않으면 FAILURE를 반환하는 방어적 처리가 필요하다.
7.3 로컬 접근과 원격 접근의 선택 기준
| 기준 | 로컬 접근 | 원격 접근 |
|---|---|---|
| 대상 노드 | 행동 트리 실행 노드 자체 | 다른 ROS2 노드 |
| 접근 방식 | get_parameter() 직접 호출 | 파라미터 서비스 호출 |
| 지연 | 무시할 수 있음 | 밀리초 ~ 수십 밀리초 |
| 캐싱 필요성 | 불필요 | 필수 |
| 파라미터 변경 감지 | 콜백으로 즉시 감지 가능 | 파라미터 이벤트 토픽 구독 또는 폴링 필요 |
행동 트리에서 참조하는 파라미터가 행동 트리 실행 노드에 정의된 경우 로컬 접근을 사용하고, 다른 노드의 파라미터를 참조하여야 하는 경우에만 원격 접근을 사용한다. 가능한 한 로컬 접근을 우선적으로 적용하는 것이 성능 측면에서 유리하다.
7.4 파라미터와 블랙보드의 역할 구분
파라미터와 블랙보드는 모두 행동 트리에 외부 데이터를 전달하는 메커니즘이나, 그 용도는 구별된다. 파라미터는 운용자나 외부 시스템이 로봇의 설정을 제어하기 위한 인터페이스이며, 블랙보드는 행동 트리 내부의 노드 간 데이터를 공유하기 위한 메커니즘이다. 센서 데이터나 계산 결과와 같이 행동 트리 실행 중 동적으로 생성되는 값은 블랙보드를 통해 공유하고, 운용 모드나 임계값과 같은 설정 값은 파라미터를 통해 제어하는 것이 적절하다.
8. 참고 문헌
- Colledanchise, M., & Ogren, P. (2018). Behavior Trees in Robotics and AI: An Introduction. CRC Press.
- ROS2 파라미터 공식 문서. https://docs.ros.org/en/humble/Concepts/Basic/About-Parameters.html
- BehaviorTree.CPP 공식 문서. https://www.behaviortree.dev/
| 버전 | 날��� | 변경 사항 |
|---|---|---|
| v0.1 | 2026-04-04 | 초안 작성 |