1297.15 ConditionNode의 tick() 메서드 구현
1. tick() 메서드의 역할
tick() 메서드는 조건 노드의 핵심 실행 지점으로, 부모 제어 노드로부터 tick 신호가 전달될 때 호출되어 조건 평가를 수행한다. 사용자 정의 조건 노드를 구현할 때, 이 메서드에 조건 평가 로직을 작성하는 것이 개발자의 주된 작업이다(Faconti & Colledanchise, 2022).
virtual BT::NodeStatus tick() override;
이 메서드는 BT::TreeNode에서 순수 가상 함수(pure virtual function)로 선언되어 있으므로, 모든 사용자 정의 조건 노드에서 반드시 구현해야 한다.
2. tick() 메서드의 실행 흐름
트리 실행 엔진이 조건 노드의 tick을 처리하는 과정은 다음과 같다:
- 부모 제어 노드가 자식 노드에 tick을 전달한다.
executeTick()메서드가 호출되어 노드 상태를IDLE에서 활성 상태로 전환한다.- 사용자가 구현한
tick()메서드가 호출된다. tick()이SUCCESS또는FAILURE를 반환한다.- 반환 상태가 검증되고(RUNNING 반환 시 오류 발생), 부모 노드에 전달된다.
// 트리 실행 엔진의 내부 처리 (개념적)
NodeStatus TreeNode::executeTick()
{
NodeStatus prev_status = status();
setStatus(NodeStatus::RUNNING); // 임시 상태
NodeStatus new_status = tick(); // 사용자 구현 호출
setStatus(new_status);
return new_status;
}
3. tick() 구현의 기본 구조
조건 노드의 tick() 메서드는 일반적으로 다음의 세 단계로 구성된다.
3.1 단계: 입력 값 획득
블랙보드에서 조건 평가에 필요한 입력값을 읽는다.
BT::NodeStatus tick() override
{
// 1단계: 입력 값 획득
double value;
auto result = getInput("value", value);
if (!result)
{
return BT::NodeStatus::FAILURE; // 입력 불가 시 FAILURE
}
3.2 단계: 조건 평가
획득한 입력값에 대하여 조건 판정을 수행한다.
// 2단계: 조건 평가
double threshold;
getInput("threshold", threshold);
bool condition_met = (value >= threshold);
3.3 단계: 결과 반환
조건 평가 결과를 SUCCESS 또는 FAILURE로 변환하여 반환한다.
// 3단계: 결과 반환
return condition_met
? BT::NodeStatus::SUCCESS
: BT::NodeStatus::FAILURE;
}
4. 입력 값 획득의 오류 처리
getInput() 메서드는 블랙보드에서 값을 읽는 과정에서 다음과 같은 이유로 실패할 수 있다:
- 지정된 키가 블랙보드에 존재하지 않는 경우
- 저장된 값의 타입이 요청한 타입과 일치하지 않는 경우
- 포트 매핑이 올바르게 설정되지 않은 경우
BT::NodeStatus tick() override
{
auto expected = getInput<double>("distance");
if (!expected)
{
// 오류 원인 로깅 후 FAILURE 반환
RCLCPP_WARN(logger_, "Failed to get input 'distance': %s",
expected.error().c_str());
return BT::NodeStatus::FAILURE;
}
double distance = expected.value();
// ... 조건 평가 계속
}
입력 획득 실패 시 FAILURE를 반환하는 것은 실패 안전(fail-safe) 원칙에 부합한다. 데이터가 불완전한 상태에서 조건을 참으로 판정하는 것보다 거짓으로 판정하여 후속 행동의 실행을 차단하는 것이 안전하다.
5. 다양한 조건 평가 패턴
5.1 단순 비교 패턴
단일 값을 임계값과 비교하는 가장 기본적인 패턴이다.
BT::NodeStatus IsBatteryOk::tick()
{
double battery;
getInput("battery_level", battery);
double threshold;
getInput("threshold", threshold);
return (battery >= threshold) ? BT::NodeStatus::SUCCESS : BT::NodeStatus::FAILURE;
}
5.2 범위 검사 패턴
값이 지정된 범위 내에 있는지를 확인하는 패턴이다.
BT::NodeStatus IsAltitudeInRange::tick()
{
double altitude, min_alt, max_alt;
getInput("altitude", altitude);
getInput("min_altitude", min_alt);
getInput("max_altitude", max_alt);
return (altitude >= min_alt && altitude <= max_alt)
? BT::NodeStatus::SUCCESS
: BT::NodeStatus::FAILURE;
}
5.3 거리 기반 비교 패턴
두 위치 사이의 유클리드 거리를 계산하여 임계값과 비교하는 패턴이다.
BT::NodeStatus IsGoalReached::tick()
{
double rx, ry, gx, gy, tolerance;
getInput("robot_x", rx);
getInput("robot_y", ry);
getInput("goal_x", gx);
getInput("goal_y", gy);
getInput("tolerance", tolerance);
double distance = std::hypot(rx - gx, ry - gy);
return (distance <= tolerance) ? BT::NodeStatus::SUCCESS : BT::NodeStatus::FAILURE;
}
5.4 불리언 확인 패턴
블랙보드에 저장된 불리언 값을 직접 확인하는 패턴이다.
BT::NodeStatus IsEmergencyStop::tick()
{
bool e_stop;
auto result = getInput("emergency_stop", e_stop);
if (!result)
{
return BT::NodeStatus::FAILURE;
}
return e_stop ? BT::NodeStatus::SUCCESS : BT::NodeStatus::FAILURE;
}
5.5 비동기 데이터 참조 패턴
ROS2 토픽을 통해 비동기적으로 수신된 데이터를 참조하는 패턴이다. 데이터 수신은 콜백에서 처리하고, tick()에서는 저장된 최신 값만을 읽는다.
BT::NodeStatus IsObstacleNear::tick()
{
double threshold;
getInput("threshold", threshold);
// 원자적 변수에서 최신 값 읽기 (콜백에서 비동기 갱신)
double min_range = latest_min_range_.load();
return (min_range < threshold) ? BT::NodeStatus::SUCCESS : BT::NodeStatus::FAILURE;
}
6. tick() 구현 시 주의 사항
6.1 RUNNING 반환 금지
tick() 메서드에서 BT::NodeStatus::RUNNING을 반환하면 안 된다. BehaviorTree.CPP는 조건 노드의 RUNNING 반환을 논리 오류로 처리한다.
// 잘못된 구현
BT::NodeStatus BadCondition::tick()
{
return BT::NodeStatus::RUNNING; // 런타임 오류 발생
}
6.2 차단적 연산 금지
tick() 내에서 std::this_thread::sleep_for(), future.get(), 동기적 서비스 호출 등 현재 스레드를 차단하는 연산을 수행하면 안 된다.
6.3 예외 처리
tick() 내에서 발생한 예외(exception)는 트리 실행 엔진으로 전파되어 전체 트리의 실행을 중단시킬 수 있다. 예외가 발생할 가능성이 있는 연산은 try-catch로 처리하고, 예외 발생 시 FAILURE를 반환한다.
BT::NodeStatus SafeCondition::tick()
{
try
{
double value;
getInput("value", value);
return (value > 0.0) ? BT::NodeStatus::SUCCESS : BT::NodeStatus::FAILURE;
}
catch (const std::exception& e)
{
RCLCPP_ERROR(logger_, "Exception in condition: %s", e.what());
return BT::NodeStatus::FAILURE;
}
}
7. 참고 문헌
- 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