1297.17 조건 노드의 블랙보드 포트 설계

1. 블랙보드 포트의 개념

블랙보드 포트(blackboard port)는 BehaviorTree.CPP에서 노드와 블랙보드 사이의 데이터 교환을 위한 타입 안전(type-safe) 인터페이스이다. 각 노드는 providedPorts() 정적 메서드를 통해 자신이 사용하는 포트를 선언하며, 트리 빌더는 이 정보를 기반으로 XML 정의의 유효성을 검증하고 포트 매핑을 설정한다(Faconti & Colledanchise, 2022).

포트는 입력 포트(InputPort)와 출력 포트(OutputPort)로 구분된다. 입력 포트는 블랙보드에서 값을 읽어오는 데 사용되며, 출력 포트는 블랙보드에 값을 쓰는 데 사용된다.

2. 조건 노드의 포트 설계 원칙

2.1 입력 포트 중심 설계

조건 노드는 부작용 금지 원칙에 따라 블랙보드의 값을 변경해서는 안 된다. 따라서 조건 노드의 포트는 입력 포트만으로 구성하는 것이 원칙이다. 출력 포트의 선언 및 사용은 부작용에 해당하므로 자제해야 한다.

static BT::PortsList providedPorts()
{
    return {
        BT::InputPort<double>("value", "평가 대상 값"),
        BT::InputPort<double>("threshold", 1.0, "비교 임계값")
        // OutputPort는 조건 노드에서 사용하지 않음
    };
}

2.2 명시적 타입 지정

모든 포트는 템플릿 매개변수를 통해 데이터 타입을 명시적으로 지정해야 한다. 이를 통해 컴파일 시점에 타입 안전성이 보장되며, XML 정의에서 문자열로 전달된 값이 올바른 타입으로 변환되는지 검증된다.

static BT::PortsList providedPorts()
{
    return {
        BT::InputPort<double>("distance"),        // double 타입
        BT::InputPort<int>("count"),              // int 타입
        BT::InputPort<std::string>("mode"),       // string 타입
        BT::InputPort<bool>("is_enabled")         // bool 타입
    };
}

2.3 기본값의 설정

입력 포트에 기본값(default value)을 설정하면, XML 트리 정의에서 해당 속성을 생략할 수 있다. 기본값은 조건 노드의 범용성을 높이면서도 사용의 편의성을 제공한다.

static BT::PortsList providedPorts()
{
    return {
        BT::InputPort<double>("value", "측정된 센서 값"),
        BT::InputPort<double>("threshold", 0.5, "임계값 (기본값: 0.5)"),
        BT::InputPort<double>("tolerance", 0.01, "허용 오차 (기본값: 0.01)")
    };
}

기본값이 없는 포트는 XML에서 반드시 값을 지정해야 하며, 누락 시 트리 빌드 시점에 오류가 발생한다.

3. 포트 매핑의 유형

XML 트리 정의에서 포트에 값을 할당하는 방식은 세 가지이다.

3.1 정적 값 매핑

포트에 상수값을 직접 할당한다. 트리 실행 중 값이 변경되지 않는다.

<Condition ID="IsValueAbove" value="{sensor_data}" threshold="10.0"/>

위 예시에서 threshold는 정적 값 10.0으로 고정된다.

3.2 블랙보드 키 매핑

중괄호({})를 사용하여 블랙보드 키에 매핑한다. tick() 호출 시마다 블랙보드에서 최신 값이 읽힌다.

<Condition ID="IsValueAbove" value="{sensor_data}" threshold="{dynamic_threshold}"/>

이 경우 valuethreshold 모두 블랙보드에서 동적으로 읽히므로, 외부에서 블랙보드 값을 갱신하면 조건 평가의 기준이 런타임에 변경된다.

3.3 기본값 사용

포트에 기본값이 설정되어 있고, XML에서 해당 속성을 생략하면 기본값이 사용된다.

<!-- threshold 생략 → 기본값 0.5 사용 -->
<Condition ID="IsValueAbove" value="{sensor_data}"/>

4. 포트 설계의 실무적 지침

4.1 평가 대상과 기준의 분리

조건 노드의 포트를 설계할 때, 평가 대상(what to evaluate)과 평가 기준(how to evaluate)을 별도의 포트로 분리하는 것이 재사용성을 높인다.

static BT::PortsList providedPorts()
{
    return {
        // 평가 대상
        BT::InputPort<double>("current_value", "현재 측정값"),
        // 평가 기준
        BT::InputPort<double>("min_value", "최소 허용값"),
        BT::InputPort<double>("max_value", "최대 허용값")
    };
}

이 설계에서 동일한 조건 노드 클래스를 배터리 잔량 확인, 온도 범위 확인, 속도 범위 확인 등 다양한 용도에 재사용할 수 있다.

4.2 포트 이름의 명명 규칙

포트 이름은 저장되는 데이터의 의미를 명확히 나타내야 한다. 약어의 사용을 지양하고, 스네이크 케이스(snake_case) 규칙을 따르는 것이 BehaviorTree.CPP의 관례이다.

권장비권장
battery_levelbat
obstacle_distancedist
max_temperaturemaxT
localization_qualityloc_q

4.3 포트 설명 문자열

InputPortOutputPort의 마지막 매개변수로 설명 문자열을 제공할 수 있다. 이 문자열은 Groot 등의 시각화 도구에서 포트 정보를 표시할 때 사용된다.

BT::InputPort<double>("battery_level", "배터리 잔량 (%, 0~100)")

4.4 사용자 정의 타입의 포트

BehaviorTree.CPP는 기본 타입(int, double, string, bool) 외에 사용자 정의 타입도 포트에 사용할 수 있다. 이를 위해 해당 타입에 대한 BT::convertFromString<T>() 특수화를 제공해야 한다.

// 사용자 정의 타입
struct Point2D
{
    double x;
    double y;
};

// 문자열 변환 특수화
namespace BT
{
template <>
inline Point2D convertFromString(StringView str)
{
    auto parts = splitString(str, ';');
    Point2D p;
    p.x = convertFromString<double>(parts[0]);
    p.y = convertFromString<double>(parts[1]);
    return p;
}
}

// 포트 선언
static BT::PortsList providedPorts()
{
    return {
        BT::InputPort<Point2D>("position", "로봇 위치 (x;y)"),
        BT::InputPort<Point2D>("goal", "목표 위치 (x;y)"),
        BT::InputPort<double>("tolerance", 0.5, "도달 허용 오차")
    };
}

5. 참고 문헌

  • Colledanchise, M., & Ogren, P. (2018). Behavior Trees in Robotics and AI: An Introduction. CRC Press.
  • Faconti, D., & Colledanchise, M. (2022). BehaviorTree.CPP Documentation. https://www.behaviortree.dev/

version: 0.1.0