1297.23 블랙보드 값 비교 조건 노드

1. 블랙보드 값 비교의 개념

블랙보드 값 비교 조건 노드는 블랙보드(blackboard)에 저장된 값을 임계값 또는 다른 블랙보드 값과 비교하여 조건의 참/거짓을 판정하는 조건 노드이다. 이 유형의 조건 노드는 행동 트리(Behavior Tree)에서 가장 빈번하게 사용되는 패턴이며, 센서 데이터의 임계값 검사, 상태 변수의 확인, 목표 도달 여부 판정 등 광범위한 용도에 적용된다(Faconti & Colledanchise, 2022).

2. 비교 연산의 유형

블랙보드 값 비교에 사용되는 주요 연산 유형은 다음과 같다:

비교 유형연산자SUCCESS 조건적용 예시
등호 비교=a = b모드 일치 확인
부등호 비교 (크거나 같음)\geqa \geq b배터리 잔량 확인
부등호 비교 (작거나 같음)\leqa \leq b속도 상한 확인
부등호 비교 (초과)>a > b신호 강도 확인
부등호 비교 (미만)<a < b거리 근접 확인
범위 검사\leq, \geql \leq a \leq u온도 범위 확인
부등 비교\neqa \neq b오류 코드 확인

3. 범용 비교 조건 노드의 설계

다양한 비교 연산을 수행할 수 있는 범용 비교 조건 노드를 설계할 수 있다. 비교 연산의 종류를 매개변수로 받아 하나의 클래스로 복수의 비교 유형을 처리한다.

template <typename T>
class CompareBlackboardValue : public BT::ConditionNode
{
public:
    CompareBlackboardValue(const std::string& name, const BT::NodeConfig& config)
        : BT::ConditionNode(name, config)
    {}

    BT::NodeStatus tick() override
    {
        T value_A;
        auto result_A = getInput("value_A", value_A);
        if (!result_A)
        {
            return BT::NodeStatus::FAILURE;
        }

        T value_B;
        auto result_B = getInput("value_B", value_B);
        if (!result_B)
        {
            return BT::NodeStatus::FAILURE;
        }

        std::string op;
        getInput("operator", op);

        bool condition_met = false;
        if (op == "==" || op == "eq")
        {
            condition_met = (value_A == value_B);
        }
        else if (op == "!=" || op == "ne")
        {
            condition_met = (value_A != value_B);
        }
        else if (op == ">" || op == "gt")
        {
            condition_met = (value_A > value_B);
        }
        else if (op == ">=" || op == "ge")
        {
            condition_met = (value_A >= value_B);
        }
        else if (op == "<" || op == "lt")
        {
            condition_met = (value_A < value_B);
        }
        else if (op == "<=" || op == "le")
        {
            condition_met = (value_A <= value_B);
        }

        return condition_met ? BT::NodeStatus::SUCCESS : BT::NodeStatus::FAILURE;
    }

    static BT::PortsList providedPorts()
    {
        return {
            BT::InputPort<T>("value_A", "비교 대상 값 A"),
            BT::InputPort<T>("value_B", "비교 대상 값 B"),
            BT::InputPort<std::string>("operator", "==", "비교 연산자")
        };
    }
};

4. XML에서의 활용

<Sequence>
    <!-- 배터리가 20% 이상인지 확인 -->
    <Condition ID="CompareDouble"
        value_A="{battery_level}"
        value_B="20.0"
        operator="ge"/>

    <!-- 속도가 최대 허용 속도 이하인지 확인 -->
    <Condition ID="CompareDouble"
        value_A="{current_speed}"
        value_B="{max_allowed_speed}"
        operator="le"/>

    <!-- 두 블랙보드 값 비교 -->
    <Condition ID="CompareDouble"
        value_A="{distance_to_goal}"
        value_B="{goal_tolerance}"
        operator="le"/>

    <Action ID="Navigate"/>
</Sequence>

범용 비교 노드를 사용하면 단일 클래스로 다양한 비교 조건을 표현할 수 있어, 조건 노드 클래스의 수를 줄이고 XML 수준에서의 유연성을 높인다.

5. 특수화된 비교 조건 노드

범용 비교 노드는 유연하지만, 비교 연산의 의미가 노드 이름에 드러나지 않아 트리의 가독성이 저하될 수 있다. 특정 비교 연산에 특수화된 조건 노드를 별도로 정의하면 가독성이 향상된다.

class IsValueGreaterOrEqual : public BT::ConditionNode
{
public:
    IsValueGreaterOrEqual(const std::string& name, const BT::NodeConfig& config)
        : BT::ConditionNode(name, config) {}

    BT::NodeStatus tick() override
    {
        double value, threshold;
        getInput("value", value);
        getInput("threshold", threshold);
        return (value >= threshold) ? BT::NodeStatus::SUCCESS : BT::NodeStatus::FAILURE;
    }

    static BT::PortsList providedPorts()
    {
        return {
            BT::InputPort<double>("value", "비교 대상 값"),
            BT::InputPort<double>("threshold", "비교 기준 값")
        };
    }
};
<!-- 의미가 명확한 XML -->
<Condition ID="IsValueGreaterOrEqual"
    value="{battery_level}" threshold="20.0"/>

6. 두 블랙보드 키 간의 비교

조건 노드의 두 입력 포트를 모두 블랙보드 키에 매핑하면, 동적으로 변경되는 두 값을 비교할 수 있다.

<!-- 현재 속도가 허용 속도 이하인지 동적 비교 -->
<Condition ID="IsValueLessOrEqual"
    value="{current_speed}"
    threshold="{speed_limit}"/>

이 패턴에서 speed_limit은 다른 액션 노드에 의해 런타임에 갱신될 수 있으므로, 상황에 따라 비교 기준이 적응적으로 변경된다.

7. 타입별 비교 시 고려 사항

7.1 부동소수점 비교

부동소수점(double, float) 값의 등호 비교는 수치적 오차로 인해 예기치 않은 결과를 산출할 수 있다. 등호 비교 시에는 허용 오차(epsilon)를 적용해야 한다.

bool isApproximatelyEqual(double a, double b, double epsilon = 1e-6)
{
    return std::abs(a - b) <= epsilon;
}

7.2 문자열 비교

문자열 비교는 대소문자 구분(case sensitivity), 공백 처리 등에 주의해야 한다. 비교 전에 문자열을 정규화(normalization)하는 것이 안전하다.

7.3 불리언 비교

불리언 값의 비교는 등호 비교만이 의미를 가지며, 대소 비교는 논리적으로 무의미하다. 불리언 값에 대해서는 전용 조건 노드(IsFlagSet 등)를 사용하는 것이 명확하다.

8. 참고 문헌

  • 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