1295.74 XML에서의 Parallel 노드 정의

1. XML 기반 행동 트리 정의

BehaviorTree.CPP는 XML 형식으로 행동 트리를 정의하고, 런타임에 XML 파일을 파싱하여 트리를 구성하는 방식을 지원한다. XML 기반 정의는 트리의 구조를 코드와 분리하여, 재컴파일 없이 행동 트리를 수정할 수 있게 한다.

2. Parallel 노드의 XML 기본 구문

BehaviorTree.CPP 4.x에서 Parallel 노드는 <ParallelAll> 태그로 정의한다.

<BehaviorTree ID="BasicParallel">
    <ParallelAll max_failures="0">
        <ChildNode_1 />
        <ChildNode_2 />
        <ChildNode_3 />
    </ParallelAll>
</BehaviorTree>

<ParallelAll> 태그의 속성으로 max_failures를 설정하며, 자식 노드는 태그 내부에 순서대로 나열한다.

3. 속성 설정

3.1 상수 값 설정

<ParallelAll max_failures="0">
    ...
</ParallelAll>

max_failures에 정수 상수를 직접 지정한다.

3.2 블랙보드 참조 설정

<ParallelAll max_failures="{allowed_failures}">
    ...
</ParallelAll>

중괄호({})로 감싼 이름은 블랙보드 키를 참조한다. 런타임에 블랙보드의 allowed_failures 키에 저장된 값이 사용된다.

3.3 name 속성

모든 노드에 name 속성을 부여하여 디버깅과 로깅에 활용할 수 있다.

<ParallelAll name="NavigationAndMonitoring" max_failures="0">
    <NavigateToGoal name="Navigation" goal="{target}" />
    <MonitorSafety name="SafetyCheck" />
</ParallelAll>

4. 적용 패턴별 XML 예시

4.1 행동과 감시의 동시 수행

<BehaviorTree ID="ActionWithMonitor">
    <ParallelAll max_failures="0">
        <NavigateToGoal goal="{navigation_goal}" 
                        controller_id="FollowPath" />
        <MonitorBattery critical_level="10.0" />
    </ParallelAll>
</BehaviorTree>

MonitorBatteryFAILURE를 반환하면(배터리 위급) max_failures="0"에 의해 NavigateToGoal에 Halt가 전파된다.

4.2 다중 센서 동시 처리

<BehaviorTree ID="MultiSensorProcessing">
    <ParallelAll max_failures="1">
        <ProcessLidarData scan_topic="/scan" />
        <ProcessCameraData image_topic="/camera/image" />
        <ProcessIMUData imu_topic="/imu/data" />
    </ParallelAll>
</BehaviorTree>

세 센서 중 하나까지 실패를 허용한다.

4.3 행동과 타임아웃

<BehaviorTree ID="ActionWithTimeout">
    <ParallelAll max_failures="0">
        <PickObject object_id="{target_object}" />
        <Timeout msec="5000">
            <AlwaysRunning />
        </Timeout>
    </ParallelAll>
</BehaviorTree>

Timeout 데코레이터가 5초 후 FAILURE를 반환하면 PickObject에 Halt가 전파된다.

4.4 안전 감시 하의 동시 행동 (중첩 구조)

<BehaviorTree ID="SafeParallelAction">
    <ReactiveSequence>
        <IsSafe />
        <ParallelAll max_failures="0">
            <MoveBase goal="{target_pose}" />
            <ScanEnvironment />
        </ParallelAll>
    </ReactiveSequence>
</BehaviorTree>

ReactiveSequence 내부에 ParallelAll을 배치하여, 안전 조건 하에서 이동과 환경 스캔을 동시에 수행한다.

5. 서브트리에서의 Parallel 사용

복잡한 Parallel 구조를 서브트리로 분리하여 재사용할 수 있다.

<!-- 메인 트리 -->
<BehaviorTree ID="MainTree">
    <ReactiveSequence>
        <IsSystemReady />
        <SubTree ID="MissionExecution" />
    </ReactiveSequence>
</BehaviorTree>

<!-- 서브트리 -->
<BehaviorTree ID="MissionExecution">
    <ParallelAll max_failures="0">
        <NavigateWaypoints waypoints="{mission_waypoints}" />
        <RecordMissionData log_path="{data_path}" />
        <MonitorMissionConstraints />
    </ParallelAll>
</BehaviorTree>

서브트리는 독립적인 <BehaviorTree> 태그로 정의되며, <SubTree> 태그를 통해 메인 트리에서 참조된다.

6. BehaviorTree.CPP 3.x와의 XML 호환성

3.x에서는 <Parallel> 태그와 success_count, failure_count 속성을 사용하였다.

<!-- 3.x 방식 -->
<Parallel success_count="2" failure_count="1">
    <Action_A />
    <Action_B />
    <Action_C />
</Parallel>

4.x에서는 이 구문이 지원되지 않으므로, 마이그레이션 시 <ParallelAll> 태그로 변환하여야 한다. success_count가 자식 수와 다른 경우 커스텀 노드가 필요하다.

7. XML 유효성 검증

BehaviorTree.CPP는 XML 로딩 시 다음의 검증을 수행한다.

  1. 노드 존재 확인: XML에 기술된 노드 이름이 팩토리에 등록되어 있는지 확인한다.
  2. 필수 포트 확인: 노드의 필수 입력 포트가 XML에 설정되어 있는지 확인한다.
  3. 포트 타입 확인: 포트에 설정된 값이 선언된 타입으로 변환 가능한지 확인한다.
// XML 로딩과 유효성 검증
BT::BehaviorTreeFactory factory;

// 노드 등록
factory.registerNodeType<NavigateToGoal>("NavigateToGoal");
factory.registerNodeType<MonitorBattery>("MonitorBattery");

// XML에서 트리 생성 (유효성 검증 포함)
auto tree = factory.createTreeFromFile("behavior_tree.xml");

등록되지 않은 노드가 XML에 포함되어 있으면 예외가 발생한다.

8. XML 정의의 모범 사례

  1. 명시적 name 부여: 모든 노드에 name 속성을 부여하여 디버깅 시 식별이 용이하게 하라.

  2. 포트 값의 블랙보드 활용: 하드코딩된 상수보다 블랙보드 키를 참조하여, 런타임 설정 변경이 가능하도록 하라.

  3. 서브트리를 통한 모듈화: 복잡한 Parallel 구조는 서브트리로 분리하여 가독성과 재사용성을 확보하라.

  4. 주석의 활용: XML 주석(<!-- -->)을 사용하여 각 노드와 분기의 목적을 문서화하라.