커스텀 데코레이터 노드의 설계 (Custom Decorator Node Design)
1. 개요
BehaviorTree.CPP의 내장 데코레이터로 충족되지 않는 특수한 실행 정책이 필요한 경우, 사용자 정의(커스텀) 데코레이터를 구현한다. 커스텀 데코레이터는 BT::DecoratorNode를 상속하여 tick() 메서드를 구현하고, 필요한 경우 halt() 메서드를 오버라이드하며, 입력/출력 포트를 정의한다.
2. 커스텀 데코레이터 설계의 절차
2.1 단계: 동작 명세 정의
데코레이터가 수행할 동작을 명확히 정의한다.
- 자식의 어떤 반환 상태에 대해 어떤 변환을 적용할 것인가?
- 자식을 tick하기 전에 수행할 전처리가 있는가?
- 내부 상태(카운터, 타이머, 플래그)가 필요한가?
- halt 시 어떤 상태를 초기화하여야 하는가?
2.2 단계: 기반 클래스 상속
class MyDecorator : public BT::DecoratorNode
{
public:
MyDecorator(const std::string& name,
const BT::NodeConfiguration& config)
: DecoratorNode(name, config)
{}
};
2.3 단계: providedPorts() 정의
데코레이터가 사용하는 입력/출력 포트를 정의한다.
static BT::PortsList providedPorts()
{
return {
BT::InputPort<int>("max_count", 3, "최대 횟수"),
BT::InputPort<double>("timeout_sec", 5.0, "타임아웃")
};
}
2.4 단계: tick() 구현
데코레이터의 핵심 로직을 tick() 메서드에 구현한다.
BT::NodeStatus tick() override
{
setStatus(BT::NodeStatus::RUNNING);
// 전처리 로직
if (!shouldExecuteChild())
{
return BT::NodeStatus::FAILURE;
}
// 자식 tick
auto child_status = child_node_->executeTick();
// 후처리 로직
return processChildStatus(child_status);
}
2.5 단계: halt() 구현
내부 상태가 있는 경우 halt()에서 초기화한다.
void halt() override
{
counter_ = 0;
timer_started_ = false;
DecoratorNode::halt();
}
2.6 단계: 팩토리 등록
factory.registerNodeType<MyDecorator>("MyDecorator");
3. 설계 원칙
3.1 RUNNING 통과
자식의 RUNNING 상태는 일반적으로 변환하지 않고 그대로 전달한다.
3.2 halt 전파
halt() 내에서 기반 클래스의 halt()를 호출하여 자식의 halt가 전파되도록 한다.
3.3 포트의 명확한 문서화
포트의 이름, 타입, 기본값, 단위를 명확히 문서화한다.
4. 커스텀 데코레이터의 사용 예시
<MyDecorator max_count="5" timeout_sec="10.0">
<Action ID="Task"/>
</MyDecorator>
5. 참고 문헌
- BehaviorTree.CPP 공식 문서. https://www.behaviortree.dev/
- Colledanchise, M., & Ogren, P. (2018). Behavior Trees in Robotics and AI: An Introduction. CRC Press.
| 버전 | 날짜 | 변경 사항 |
|---|---|---|
| v0.1 | 2026-04-05 | 초안 작성 |