1293.22 노드의 Idle에서 Running으로의 전이
1. IDLE에서 RUNNING 전이의 의미
IDLE에서 RUNNING으로의 전이는 행동 트리(Behavior Tree) 노드가 처음으로 Tick을 수신하여 작업을 개시하는 시점을 나타낸다. IDLE 상태는 노드가 아직 Tick되지 않았거나, 이전 실행이 Halt에 의해 중단되어 초기 상태로 복귀한 상태이다. 이 상태에서 Tick이 전달되면 노드는 자신의 tick() 메서드를 실행하고, 작업이 즉시 완료되지 않는 경우 RUNNING을 반환한다(Colledanchise & Ogren, 2018).
이 전이는 노드의 생명 주기(lifecycle)에서 작업 시작(activation)에 해당하며, 자원 할당, 외부 시스템과의 연결 초기화, 비동기 작업의 개시 등 초기화 작업이 이 시점에서 수행된다.
2. 전이 발생 조건
IDLE → RUNNING 전이가 발생하기 위해서는 다음 조건이 모두 충족되어야 한다.
- 노드의 현재 상태가 IDLE: 노드가 아직 활성화되지 않은 초기 상태에 있어야 한다.
- 부모 노드로부터 Tick이 전파: 제어 노드의 Tick 전파 규칙에 따라 해당 노드가 Tick 대상으로 선택되어야 한다.
- 작업이 즉시 완료되지 않음: 노드의
tick()메서드가 호출된 후 작업이 완료되지 않아 RUNNING을 반환해야 한다.
세 번째 조건이 충족되지 않는 경우, 즉 작업이 즉시 완료되면 IDLE → SUCCESS 또는 IDLE → FAILURE 전이가 대신 발생한다. 이는 동기 노드(SyncActionNode, ConditionNode)의 전형적인 동작이다.
3. BehaviorTree.CPP에서의 구현 메커니즘
BehaviorTree.CPP 프레임워크에서 IDLE → RUNNING 전이는 TreeNode::executeTick() 메서드 내에서 관리된다. 이 메서드는 노드의 현재 상태를 확인하고, IDLE 상태에서 tick()이 호출되어 RUNNING이 반환되면 내부 상태를 갱신한다(Faconti, 2022).
// BehaviorTree.CPP 내부의 상태 전이 관리 (개념적 표현)
NodeStatus TreeNode::executeTick() {
NodeStatus prev_status = status_;
if (prev_status == NodeStatus::IDLE) {
// 첫 번째 Tick: onStart() 호출
setStatus(NodeStatus::RUNNING);
// 실제 tick() 실행
}
NodeStatus new_status = tick();
if (new_status != prev_status) {
// 상태 변화 콜백 호출
notifyStatusChange(prev_status, new_status);
}
setStatus(new_status);
return new_status;
}
4. StatefulActionNode에서의 IDLE → RUNNING 전이
BehaviorTree.CPP v4에서 도입된 StatefulActionNode는 IDLE → RUNNING 전이를 onStart() 콜백으로 명시적으로 분리한다. IDLE 상태에서 처음 Tick될 때 onStart()가 호출되고, 이후 RUNNING 상태에서의 반복 Tick은 onRunning()이 호출된다.
class NavigateToGoal : public BT::StatefulActionNode {
public:
// IDLE → RUNNING 전이 시 호출
BT::NodeStatus onStart() override {
// 초기화 작업 수행
auto goal = getInput<geometry_msgs::msg::PoseStamped>("goal");
action_client_->send_goal(goal.value());
return BT::NodeStatus::RUNNING;
}
// RUNNING 상태에서의 반복 Tick 시 호출
BT::NodeStatus onRunning() override {
if (action_client_->is_result_available()) {
return action_client_->get_result().success
? BT::NodeStatus::SUCCESS
: BT::NodeStatus::FAILURE;
}
return BT::NodeStatus::RUNNING;
}
// Halt 시 호출
void onHalted() override {
action_client_->cancel_goal();
}
};
이 설계에서 onStart()는 오직 IDLE → RUNNING 전이 시에만 호출되므로, 초기화 로직과 진행 확인 로직이 명확히 분리된다. 이는 비동기 작업의 개시와 상태 확인을 혼동하는 오류를 방지한다.
5. 초기화 작업의 내용
IDLE → RUNNING 전이 시 수행되는 전형적인 초기화 작업은 다음과 같다.
5.1 자원 할당
작업 수행에 필요한 자원을 할당한다. 메모리 버퍼, 파일 핸들, 네트워크 연결 등이 이에 해당한다. 할당된 자원은 작업 완료 또는 Halt 시 반드시 해제되어야 한다.
5.2 ROS2 통신 개시
ROS2 액션 클라이언트의 목표 전송, 서비스 요청 발송, 또는 토픽 발행을 통한 명령 전달이 이 시점에서 수행된다. 예를 들어, 네비게이션 목표를 Nav2에 전달하거나, 매니퓰레이터의 동작 명령을 전송하는 작업이 해당한다.
5.3 내부 상태 초기화
노드의 내부 변수를 초기 값으로 설정한다. 타이머 시작, 카운터 초기화, 이전 실행의 잔여 상태 정리 등이 포함된다.
5.4 블랙보드 데이터 읽기
입력 포트(input port)를 통해 블랙보드에서 작업에 필요한 데이터를 읽어온다. 목표 위치, 속도 제한, 타임아웃 값 등의 매개변수가 이 시점에서 결정된다.
6. 제어 노드에서의 IDLE → RUNNING 전이
제어 노드(Sequence, Fallback, Parallel 등)도 IDLE → RUNNING 전이를 경험한다. 제어 노드의 RUNNING 상태는 하나 이상의 자식 노드가 RUNNING 상태에 있음을 의미한다.
제어 노드의 IDLE → RUNNING 전이 과정은 다음과 같다.
- 제어 노드가 IDLE 상태에서 Tick을 수신한다.
- 첫 번째 자식 노드에 Tick을 전파한다.
- 자식 노드가 RUNNING을 반환하면, 제어 노드도 RUNNING을 반환한다.
- 제어 노드의 상태가 IDLE에서 RUNNING으로 전이된다.
7. IDLE → RUNNING 전이와 부모 노드의 상호작용
자식 노드의 IDLE → RUNNING 전이는 부모 노드의 후속 동작에 영향을 미친다.
| 부모 노드 유형 | 자식이 RUNNING 반환 시 부모의 동작 |
|---|---|
| Sequence | 후속 자식 Tick 중단, RUNNING 반환 |
| Fallback | 후속 자식 Tick 중단, RUNNING 반환 |
| Parallel | 다른 자식 계속 Tick, 정책에 따라 RUNNING 반환 |
| ReactiveSequence | RUNNING 반환 (다음 Tick에서 재평가) |
| Decorator | 데코레이터 정책에 따라 변환 후 반환 |
Sequence와 Fallback 노드에서 자식이 RUNNING을 반환하면, 해당 자식의 인덱스를 기억(WithMemory 모드)하거나 다음 Tick에서 처음부터 재평가(Reactive 모드)한다.
8. 전이 실패의 처리
IDLE → RUNNING 전이 과정에서 초기화 작업이 실패하는 경우가 있다. 예를 들어, ROS2 액션 서버가 응답하지 않거나, 필요한 블랙보드 데이터가 존재하지 않는 경우이다.
이러한 상황에서 노드는 RUNNING 대신 FAILURE를 반환하여 IDLE → FAILURE 전이를 수행해야 한다. RUNNING을 반환한 후 내부적으로 실패 상태에 있으면, 후속 Tick에서 해당 실패를 감지하기 어려울 수 있으므로, 초기화 실패는 가능한 한 첫 Tick에서 즉시 FAILURE로 보고하는 것이 바람직하다.
BT::NodeStatus onStart() override {
auto goal = getInput<PoseStamped>("goal");
if (!goal) {
// 입력 데이터 부재: 즉시 FAILURE 반환
return BT::NodeStatus::FAILURE;
}
if (!action_client_->wait_for_action_server(
std::chrono::milliseconds(100))) {
// 액션 서버 미응답: 즉시 FAILURE 반환
return BT::NodeStatus::FAILURE;
}
action_client_->send_goal(goal.value());
return BT::NodeStatus::RUNNING;
}
참고 문헌
- Colledanchise, M., & Ogren, P. (2018). Behavior Trees in Robotics and AI: An Introduction. CRC Press.
- Faconti, D. (2022). BehaviorTree.CPP documentation and API reference. https://www.behaviortree.dev/