1296.97 액션 노드 포트 데이터 검사
1. 개요
액션 노드 포트 데이터 검사는 행동 트리의 블랙보드를 통해 입출력되는 데이터의 값, 타입, 유효성을 런타임에 확인하는 디버깅 기법이다. 블랙보드 포트는 행동 트리 내 노드 간 데이터 교환의 핵심 메커니즘이며, 포트 데이터의 오류는 액션 노드의 비정상 동작, 잘못된 의사 결정, 임무 실패 등의 원인이 된다.
포트 데이터 검사를 통해 입력 포트의 값이 예상 범위 내에 있는지, 출력 포트에 올바른 결과가 기록되었는지, 포트 바인딩이 올바르게 구성되었는지를 체계적으로 검증할 수 있다.
2. 블랙보드 포트의 데이터 흐름
2.1 데이터 흐름 구조
[XML 정적 값 / 블랙보드 키 바인딩]
↓
[입력 포트] → getInput() → [액션 노드 내부 로직]
↓
setOutput() → [출력 포트]
↓
[블랙보드 키]
↓
[후속 노드의 입력 포트]
2.2 포트 바인딩 방식
| 바인딩 방식 | XML 표현 | 의미 |
|---|---|---|
| 정적 값 | port="42.0" | 고정 상수값 |
| 블랙보드 키 | port="{key_name}" | 블랙보드에서 동적으로 읽기 |
| 기본값 | (생략) | providedPorts()에 정의된 기본값 사용 |
3. 런타임 포트 데이터 검사
3.1 getInput() 반환값 검사
getInput()의 반환값을 검사하여 포트 읽기 성공 여부를 확인한다.
BT::NodeStatus onStart() override
{
double target;
auto result = getInput("target_altitude", target);
if (!result)
{
RCLCPP_ERROR(node_->get_logger(),
"[%s] 포트 'target_altitude' 읽기 실패: %s",
name().c_str(),
result.error().c_str());
return BT::NodeStatus::FAILURE;
}
RCLCPP_DEBUG(node_->get_logger(),
"[%s] target_altitude = %.2f",
name().c_str(), target);
// 값 유효성 검사
if (target < 0.0 || target > 500.0)
{
RCLCPP_ERROR(node_->get_logger(),
"[%s] target_altitude 범위 초과: %.2f",
name().c_str(), target);
return BT::NodeStatus::FAILURE;
}
// ...
}
3.2 포트 값 로깅 노드
블랙보드의 특정 키 값을 콘솔에 출력하는 전용 디버깅 노드를 구현한다.
class InspectPort : public BT::SyncActionNode
{
public:
InspectPort(const std::string& name,
const BT::NodeConfiguration& config)
: BT::SyncActionNode(name, config)
{
}
static BT::PortsList providedPorts()
{
return {
BT::InputPort<std::string>("key",
"검사할 블랙보드 키"),
BT::InputPort<std::string>("label", "",
"출력 레이블")
};
}
BT::NodeStatus tick() override
{
std::string key, label;
getInput("key", key);
getInput("label", label);
auto blackboard = config().blackboard;
auto entry = blackboard->getEntry(key);
if (!entry)
{
std::cout << "[InspectPort] "
<< (label.empty() ? key : label)
<< ": <키 없음>"
<< std::endl;
}
else
{
std::cout << "[InspectPort] "
<< (label.empty() ? key : label)
<< " = " << entry->value.toStr()
<< " (타입: "
<< entry->value.type().name()
<< ")" << std::endl;
}
return BT::NodeStatus::SUCCESS;
}
};
3.3 XML에서의 활용
<Sequence>
<InspectPort key="target_pose"
label="목표 위치" />
<NavigateToPose goal="{target_pose}" />
<InspectPort key="nav_result"
label="내비게이션 결과" />
</Sequence>
4. 블랙보드 전체 덤프
4.1 전체 키-값 출력
블랙보드에 저장된 모든 데이터를 한 번에 출력하는 디버깅 노드이다.
class DumpBlackboard : public BT::SyncActionNode
{
public:
static BT::PortsList providedPorts()
{
return {
BT::InputPort<std::string>("prefix", "BB",
"출력 접두어")
};
}
BT::NodeStatus tick() override
{
std::string prefix;
getInput("prefix", prefix);
auto blackboard = config().blackboard;
auto keys = blackboard->getKeys();
std::cout << "=== [" << prefix
<< "] Blackboard Dump ===" << std::endl;
for (const auto& key : keys)
{
auto entry = blackboard->getEntry(key);
if (entry)
{
std::cout << " " << key << " = "
<< entry->value.toStr()
<< std::endl;
}
else
{
std::cout << " " << key
<< " = <null>" << std::endl;
}
}
std::cout << "=== 총 " << keys.size()
<< "개 키 ===" << std::endl;
return BT::NodeStatus::SUCCESS;
}
};
5. 포트 타입 검사
5.1 타입 불일치 감지
블랙보드에 저장된 값의 타입과 포트에서 기대하는 타입이 일치하지 않으면 getInput()이 실패한다. 이러한 타입 불일치를 사전에 감지하기 위해, 포트 매니페스트를 검사할 수 있다.
void validatePortTypes(const BT::Tree& tree)
{
for (const auto& subtree : tree.subtrees)
{
for (const auto& node : subtree->nodes)
{
auto manifest =
node->getManifest();
for (const auto& [port_name, port_info] :
manifest.ports)
{
if (port_info.direction() ==
BT::PortDirection::INPUT)
{
// 포트에 바인딩된 블랙보드 키 확인
auto remapped =
node->getRawPortValue(port_name);
if (remapped.empty())
{
std::cout << "[경고] "
<< node->name() << "."
<< port_name
<< ": 바인딩 없음"
<< std::endl;
}
}
}
}
}
}
5.2 포트 연결 그래프
입출력 포트 간의 데이터 흐름을 그래프로 표현하면, 데이터 의존성을 시각적으로 파악할 수 있다.
[Takeoff]
out: reached_altitude → {takeoff_alt}
↓
[FlyToWaypoint]
in: altitude ← {takeoff_alt}
out: final_distance → {nav_dist}
↓
[LogMessage]
in: message ← {nav_dist}
6. 값 범위 검증
6.1 어서션 기반 검증
액션 노드 내부에서 포트 값의 유효 범위를 어서션으로 검증한다.
BT::NodeStatus onStart() override
{
double latitude, longitude, altitude;
getInput("latitude", latitude);
getInput("longitude", longitude);
getInput("altitude", altitude);
// 위도 범위 검사 (-90 ~ 90)
if (latitude < -90.0 || latitude > 90.0)
{
RCLCPP_ERROR(node_->get_logger(),
"위도 범위 초과: %.6f", latitude);
return BT::NodeStatus::FAILURE;
}
// 경도 범위 검사 (-180 ~ 180)
if (longitude < -180.0 || longitude > 180.0)
{
RCLCPP_ERROR(node_->get_logger(),
"경도 범위 초과: %.6f", longitude);
return BT::NodeStatus::FAILURE;
}
// 고도 범위 검사 (0 ~ 500m)
if (altitude < 0.0 || altitude > 500.0)
{
RCLCPP_ERROR(node_->get_logger(),
"고도 범위 초과: %.1f", altitude);
return BT::NodeStatus::FAILURE;
}
// ...
}
6.2 NaN 및 무한대 검사
부동소수점 값에 대해 NaN(Not a Number)과 무한대(infinity) 여부를 검사한다.
template <typename T>
bool isValidNumeric(T value)
{
return std::isfinite(value);
}
// 사용
double target;
getInput("target", target);
if (!isValidNumeric(target))
{
RCLCPP_ERROR(node_->get_logger(),
"유효하지 않은 수치: %f", target);
return BT::NodeStatus::FAILURE;
}
7. 포트 변화 감시
7.1 블랙보드 키 변화 추적
특정 블랙보드 키의 값이 변경될 때 콜백을 실행하는 감시 메커니즘을 구현한다.
class BlackboardWatcher
{
public:
void watch(BT::Blackboard::Ptr blackboard,
const std::string& key,
std::function<void(const std::string&)>
callback)
{
watchers_[key] = {blackboard, callback, ""};
}
void checkChanges()
{
for (auto& [key, watcher] : watchers_)
{
auto entry =
watcher.blackboard->getEntry(key);
if (entry)
{
std::string current =
entry->value.toStr();
if (current != watcher.last_value)
{
watcher.callback(current);
watcher.last_value = current;
}
}
}
}
private:
struct WatchEntry
{
BT::Blackboard::Ptr blackboard;
std::function<void(const std::string&)> callback;
std::string last_value;
};
std::unordered_map<std::string, WatchEntry> watchers_;
};
8. XML 행동 트리에서의 활용
8.1 디버그 모드 조건부 검사
<Sequence>
<Precondition if="{debug_mode}" else="SKIP">
<DumpBlackboard prefix="PRE_FLIGHT" />
</Precondition>
<Takeoff target_altitude="{alt}"
reached_altitude="{reached}" />
<Precondition if="{debug_mode}" else="SKIP">
<InspectPort key="reached"
label="도달 고도" />
</Precondition>
</Sequence>
debug_mode 블랙보드 키를 통해 배포 환경에서는 검사를 건너뛰고, 디버깅 시에만 포트 데이터를 검사할 수 있다.
9. 참고 문헌
- Colledanchise, M. and Ögren, P., “Behavior Trees in Robotics and AI: An Introduction,” CRC Press, 2018.
- Faconti, D. and Contributors, “BehaviorTree.CPP: A C++ library to build Behavior Trees,” GitHub Repository, https://github.com/BehaviorTree/BehaviorTree.CPP.
- Faconti, D., “Groot2: A GUI for BehaviorTree.CPP,” https://www.behaviortree.dev/groot/.
버전: 2026-04-04