Chapter 1295. 제어 노드: Parallel과 ReactiveFallback (Control Nodes: Parallel and ReactiveFallback)
행동 트리(Behavior Tree)의 제어 노드(Control Node) 중 Sequence와 Fallback이 순차적 실행 흐름을 관장한다면, Parallel 노드와 ReactiveFallback 노드는 동시성(concurrency)과 반응성(reactivity)이라는 상이한 차원의 제어 흐름을 제공한다. Parallel 노드는 다수의 자식 노드를 논리적으로 병렬 실행하여 복합적 행동의 동시 수행을 가능하게 하며, ReactiveFallback 노드는 조건 변화에 즉각 반응하는 폴백 메커니즘을 구현한다. 본 장에서는 이 두 제어 노드의 정의, 동작 원리, 성공·실패 정책, 그리고 로봇 행동 제어에서의 활용 양상을 학술적으로 고찰한다.
1. Parallel 노드의 정의와 개념
Parallel 노드는 행동 트리의 제어 노드 유형 중 하나로, 복수의 자식 노드에 동일한 틱(tick)에서 실행 기회를 부여하는 노드이다. 여기서 “병렬(parallel)“이라는 용어는 운영체제 수준의 멀티스레드 병렬 실행을 의미하는 것이 아니라, 단일 틱 주기 내에서 모든 자식 노드가 순서대로 틱을 수신하되 각각이 독립적으로 상태를 반환하는 논리적 동시성(logical concurrency)을 지칭한다(Colledanchise & Ögren, 2018).
행동 트리의 표준 형식론에서 Parallel 노드는 \parallel 기호로 표기하며, N개의 자식 노드 C_1, C_2, \ldots, C_N을 갖는다. 매 틱마다 Parallel 노드는 C_1부터 C_N까지 순차적으로 틱을 전달하고, 각 자식의 반환 상태를 수집한 뒤, 미리 정의된 성공 정책(success policy)과 실패 정책(failure policy)에 따라 자신의 최종 상태를 결정한다.
이러한 논리적 병렬성은 로봇이 복합적 행동을 동시에 수행해야 하는 상황에서 핵심적 역할을 한다. 예를 들어, 이동 로봇이 목표 지점을 향해 주행하면서 동시에 장애물 감지를 수행하는 경우, 두 행동을 Parallel 노드의 자식으로 배치함으로써 매 틱마다 양쪽 행동 모두를 갱신할 수 있다.
2. Parallel 노드의 성공·실패 임계값 정책
Parallel 노드의 핵심 매개변수는 성공 임계값(success threshold) M이다. 이 값은 Parallel 노드가 SUCCESS를 반환하기 위해 필요한 자식 노드의 최소 성공 개수를 지정한다. BehaviorTree.CPP 라이브러리에서는 이를 success_count 또는 success_threshold 매개변수로 설정한다.
2.1 성공 조건
N개의 자식 노드 중 M개 이상이 SUCCESS를 반환하면 Parallel 노드 자체가 SUCCESS를 반환한다. 수식으로 표현하면 다음과 같다.
\text{ParallelStatus} = \begin{cases} \text{SUCCESS} & \text{if } \sum_{i=1}^{N} \mathbb{1}[\text{status}(C_i) = \text{SUCCESS}] \geq M \\ \text{FAILURE} & \text{if } \sum_{i=1}^{N} \mathbb{1}[\text{status}(C_i) = \text{FAILURE}] > N - M \\ \text{RUNNING} & \text{otherwise} \end{cases}
여기서 \mathbb{1}[\cdot]은 지시 함수(indicator function)이다. 실패 조건은 성공 임계값에 도달하는 것이 구조적으로 불가능해지는 시점, 즉 실패한 자식의 수가 N - M을 초과하는 순간에 판정된다.
2.2 임계값 설정의 의미
| 임계값 설정 | 의미 | 동작 특성 |
|---|---|---|
| M = N | 모든 자식이 성공해야 함 | AND 의미론 |
| M = 1 | 하나의 자식만 성공하면 됨 | OR 의미론 |
| 1 < M < N | 부분적 성공 허용 | 다수결(majority) 의미론 |
M = N으로 설정하면 Parallel 노드는 논리적 AND 연산처럼 동작하여 모든 자식이 성공해야 전체가 성공한다. 반대로 M = 1이면 논리적 OR 연산과 유사하게 하나의 자식만 성공하더라도 전체가 성공으로 판정된다. 중간값을 설정하면 쿼럼(quorum) 기반의 의사결정 구조를 구현할 수 있다.
3. Parallel 노드의 틱 전파와 실행 흐름
Parallel 노드가 틱을 수신하면 다음의 절차를 따른다.
- 자식 노드 C_1, C_2, \ldots, C_N에 순서대로 틱을 전달한다.
- 각 자식 노드가 SUCCESS, FAILURE, 또는 RUNNING 상태를 반환한다.
- 성공 개수와 실패 개수를 집계한다.
- 성공 개수가 M 이상이면 SUCCESS를 반환한다.
- 실패 개수가 N - M을 초과하면 FAILURE를 반환한다.
- 그 외의 경우 RUNNING을 반환한다.
중요한 설계 결정 사항은 Parallel 노드가 SUCCESS 또는 FAILURE로 종료될 때 아직 RUNNING 상태인 자식 노드에 대한 처리 방식이다. BehaviorTree.CPP에서는 Parallel 노드가 종료될 때 RUNNING 상태의 자식 노드에 halt() 메서드를 호출하여 강제 중단(halting)을 수행한다. 이는 불필요한 리소스 점유를 방지하고 행동 트리의 상태 일관성을 보장하기 위한 조치이다.
// BehaviorTree.CPP에서 Parallel 노드의 개념적 동작
NodeStatus ParallelNode::tick()
{
int success_count = 0;
int failure_count = 0;
for (auto& child : children_)
{
NodeStatus status = child->executeTick();
if (status == NodeStatus::SUCCESS)
success_count++;
else if (status == NodeStatus::FAILURE)
failure_count++;
}
if (success_count >= success_threshold_)
{
haltRunningChildren();
return NodeStatus::SUCCESS;
}
if (failure_count > children_.size() - success_threshold_)
{
haltRunningChildren();
return NodeStatus::FAILURE;
}
return NodeStatus::RUNNING;
}
위 의사 코드는 Parallel 노드의 핵심 로직을 간략화하여 보여준다. 실제 BehaviorTree.CPP 구현에서는 이미 완료된 자식 노드의 재틱 방지, 스킵 정책 등 추가적 처리가 포함된다.
4. Parallel 노드의 활용 사례
4.1 이동과 감시의 동시 수행
모바일 로봇이 목표 지점까지 자율 주행하면서 동시에 주변 환경을 감시(surveillance)해야 하는 경우, 다음과 같은 구조를 적용할 수 있다.
<Parallel success_count="1">
<NavigateToGoal goal="{target_pose}"/>
<MonitorSurroundings/>
</Parallel>
이 구조에서 success_count="1"로 설정하면, 내비게이션이 목표에 도달하여 SUCCESS를 반환하는 즉시 Parallel 노드가 성공으로 판정되고, 감시 행동은 중단된다.
4.2 다중 센서 데이터 동시 수집
여러 센서에서 동시에 데이터를 수집하고, 모든 센서가 정상적으로 데이터를 반환해야 다음 단계로 진행하는 경우에는 M = N을 적용한다.
<Parallel success_count="3">
<AcquireLidarData/>
<AcquireCameraData/>
<AcquireIMUData/>
</Parallel>
이 구성에서 세 개의 센서 데이터 수집 노드 모두가 성공해야 Parallel 노드가 SUCCESS를 반환하므로, 센서 데이터의 완전성이 보장된다.
5. ReactiveFallback 노드의 정의와 개념
ReactiveFallback 노드는 표준 Fallback(Selector) 노드의 반응형(reactive) 변형이다. 표준 Fallback 노드는 자식 노드가 RUNNING을 반환하면 다음 틱에서 해당 자식부터 틱을 재개하는 반면, ReactiveFallback 노드는 매 틱마다 항상 첫 번째 자식부터 다시 틱을 전파한다(Colledanchise & Ögren, 2018). 이러한 특성으로 인해 우선순위가 높은 조건의 변화에 즉각적으로 반응할 수 있다.
ReactiveFallback의 동작 규칙은 다음과 같다.
- 매 틱마다 첫 번째 자식 C_1부터 틱을 전달한다.
- C_i가 SUCCESS 또는 RUNNING을 반환하면, C_{i+1}, \ldots, C_N 중 RUNNING 상태인 자식을 모두 중단하고, C_i의 상태를 자신의 상태로 반환한다.
- C_i가 FAILURE를 반환하면, 다음 자식 C_{i+1}로 틱을 전달한다.
- 모든 자식이 FAILURE를 반환하면, ReactiveFallback 노드 자체가 FAILURE를 반환한다.
6. ReactiveFallback과 표준 Fallback의 차이
표준 Fallback 노드와 ReactiveFallback 노드의 핵심적 차이는 RUNNING 상태에서의 틱 재개 위치에 있다.
| 특성 | 표준 Fallback | ReactiveFallback |
|---|---|---|
| 틱 시작 위치 | RUNNING 상태의 자식부터 재개 | 항상 첫 번째 자식부터 시작 |
| 조건 재평가 | RUNNING 중 이전 조건 미재평가 | 매 틱마다 모든 선행 조건 재평가 |
| 반응성 | 낮음 | 높음 |
| 선점(preemption) | 불가 | 가능 |
| 적용 상황 | 정적 우선순위 폴백 | 동적 조건 기반 폴백 |
표준 Fallback에서는 세 번째 자식이 RUNNING을 반환한 경우, 다음 틱에서 세 번째 자식부터 틱이 재개되므로 첫 번째와 두 번째 자식의 조건 변화를 감지하지 못한다. ReactiveFallback에서는 매 틱마다 첫 번째 자식부터 재평가하므로, 더 높은 우선순위의 조건이 성립하면 현재 실행 중인 하위 행동을 선점(preempt)하고 즉시 전환할 수 있다.
7. ReactiveFallback의 실행 흐름 상세
ReactiveFallback 노드의 실행 흐름을 의사 코드로 표현하면 다음과 같다.
NodeStatus ReactiveFallback::tick()
{
for (size_t i = 0; i < children_.size(); i++)
{
NodeStatus status = children_[i]->executeTick();
if (status == NodeStatus::SUCCESS)
{
haltChildren(i + 1); // 이후 자식 중단
return NodeStatus::SUCCESS;
}
else if (status == NodeStatus::RUNNING)
{
haltChildren(i + 1); // 이후 자식 중단
return NodeStatus::RUNNING;
}
// FAILURE이면 다음 자식으로 계속 진행
}
return NodeStatus::FAILURE;
}
이 로직에서 haltChildren(i + 1) 호출은 현재 선택된 자식 이후의 모든 자식 노드를 중단시킨다. 이는 이전 틱에서 하위 자식이 RUNNING 상태에 있었더라도, 상위 우선순위 자식이 SUCCESS 또는 RUNNING을 반환하면 해당 하위 자식의 실행이 선점되는 것을 의미한다.
8. ReactiveFallback의 활용 사례
8.1 조건 기반 행동 전환
ReactiveFallback의 가장 대표적인 활용 패턴은 조건 노드와 행동 노드를 조합한 조건 기반 행동 전환이다.
<ReactiveFallback>
<IsEmergencyDetected/>
<ReactiveSequence>
<IsObstacleNearby/>
<AvoidObstacle/>
</ReactiveSequence>
<NavigateToGoal goal="{target_pose}"/>
</ReactiveFallback>
이 트리에서 매 틱마다 다음의 평가가 이루어진다.
IsEmergencyDetected조건을 먼저 확인한다. 비상 상황이 감지되면(SUCCESS 반환) 즉시 ReactiveFallback이 SUCCESS를 반환하고, 하위 행동이 모두 중단된다.- 비상 상황이 아니면(FAILURE) 다음으로
IsObstacleNearby조건을 확인한다. 장애물이 근접하면AvoidObstacle행동을 실행한다. - 장애물이 없으면
NavigateToGoal을 실행하여 목표로 이동한다.
이 구조의 핵심은 NavigateToGoal이 RUNNING 상태에서 장기 실행 중이더라도, 매 틱마다 비상 조건과 장애물 조건이 재평가된다는 점이다. 조건 변화가 감지되면 내비게이션 행동은 즉시 중단되고 적절한 대응 행동으로 전환된다.
8.2 우선순위 기반 행동 선택
복수의 행동 대안이 존재하고 우선순위에 따라 동적으로 선택해야 하는 경우에도 ReactiveFallback이 적합하다.
<ReactiveFallback>
<Sequence>
<IsBatteryLow/>
<ReturnToChargingStation/>
</Sequence>
<Sequence>
<HasNewTask/>
<ExecuteTask/>
</Sequence>
<Patrol/>
</ReactiveFallback>
배터리 잔량이 낮아지면 현재 수행 중인 작업이나 순찰(patrol)을 중단하고 충전소로 복귀하는 행동이 즉시 선점된다. 이는 ReactiveFallback의 매 틱 재평가 특성에 의해 보장된다.
9. ReactiveSequence 노드와의 관계
ReactiveFallback과 대칭적으로 ReactiveSequence 노드가 존재한다. ReactiveSequence는 표준 Sequence 노드의 반응형 변형으로, 매 틱마다 첫 번째 자식부터 틱을 재전파한다. 두 노드의 관계를 정리하면 다음과 같다.
| 노드 | 기반 노드 | 성공 조건 | 실패 조건 | 반응성 |
|---|---|---|---|---|
| ReactiveSequence | Sequence | 모든 자식 SUCCESS | 하나의 자식 FAILURE | 매 틱 재평가 |
| ReactiveFallback | Fallback | 하나의 자식 SUCCESS | 모든 자식 FAILURE | 매 틱 재평가 |
ReactiveSequence는 조건이 유지되는 동안에만 행동을 계속 실행해야 하는 패턴에서 주로 사용된다. 예를 들어, <ReactiveSequence><IsSafe/><Navigate/></ReactiveSequence> 구조에서 IsSafe 조건이 매 틱마다 재평가되므로, 안전 조건이 위반되는 즉시 Navigate 행동이 중단된다.
10. Parallel 노드와 ReactiveFallback 노드의 비교
두 노드 모두 복합적 행동 관리를 위한 제어 구조이지만, 설계 의도와 적용 영역이 상이하다.
| 비교 항목 | Parallel | ReactiveFallback |
|---|---|---|
| 설계 의도 | 동시적 행동 수행 | 우선순위 기반 반응적 전환 |
| 자식 틱 범위 | 모든 자식에 틱 전달 | 성공 또는 RUNNING 자식까지만 전달 |
| 종료 조건 | 임계값 기반(성공/실패 개수) | 첫 번째 SUCCESS 또는 RUNNING |
| 전형적 자식 구성 | 독립적 행동 노드 다수 | 조건 노드 + 행동 노드 조합 |
| 주요 용도 | 병렬 작업 수행 | 조건 감시 및 행동 선점 |
Parallel 노드는 “여러 행동을 동시에 수행하라“는 의미론을 구현하고, ReactiveFallback 노드는 “조건 변화에 즉시 반응하여 최적의 행동을 선택하라“는 의미론을 구현한다.
11. 설계 시 고려 사항
11.1 Parallel 노드의 부작용 관리
Parallel 노드의 자식 노드들이 동일한 리소스(예: 동일 액추에이터, 공유 변수)에 접근하는 경우, 논리적 병렬 실행으로 인해 상태 충돌이 발생할 수 있다. 자식 노드 간 리소스 독립성을 보장하거나, 블랙보드(Blackboard)를 통한 명시적 데이터 공유 메커니즘을 설계해야 한다.
11.2 ReactiveFallback의 조건 노드 순수성
ReactiveFallback에서 조건 노드는 매 틱마다 반복 호출되므로, 부작용(side effect)이 없는 순수 함수(pure function)로 구현해야 한다. 조건 노드가 상태를 변경하거나 외부 시스템에 영향을 미치는 경우, 매 틱 재평가로 인해 예측 불가능한 동작이 초래될 수 있다.
11.3 중단 처리의 정확성
두 노드 모두 특정 조건에서 RUNNING 상태의 자식 노드를 중단하는 메커니즘을 포함한다. halt() 메서드의 구현이 리소스 해제, 상태 초기화 등을 올바르게 수행하지 않으면, 후속 실행에서 비정상적 동작이 발생할 수 있다. 특히 ROS2 액션 서버와 연동된 행동 노드의 경우, halt() 시점에서 목표 취소(goal cancellation) 요청이 정확하게 전달되어야 한다.
12. 참고 문헌
- Colledanchise, M., & Ögren, P. (2018). Behavior Trees in Robotics and AI: An Introduction. CRC Press.
- Colledanchise, M., & Ögren, P. (2017). “How Behavior Trees Modularize Hybrid Control Systems and Generalize Sequential Behavior Compositions, the Subsumption Architecture, and Decision Trees.” IEEE Transactions on Robotics, 33(2), 372–389.
- Faconti, D., & contributors. (2024). BehaviorTree.CPP Documentation. https://www.behaviortree.dev/
- Macenski, S., Martin, F., White, R., & Clavero, J. G. (2020). “The Marathon 2: A Navigation System.” arXiv preprint arXiv:2003.00368.
Version: 1.0-2026.04.03