1317.9 플래너 노드의 아키텍처

1. Planner 노드의 역할

Planner 노드는 PlanSys2에서 PDDL 도메인과 문제 정보를 수집하여 외부 플래너 엔진에 전달하고, 생성된 계획을 파싱하여 시스템에 반환하는 역할을 수행한다. 이 노드는 플래너 엔진을 직접 내장하지 않고, 플러그인 인터페이스를 통해 다양한 외부 플래너를 교체 가능하게 연결한다.

2. 내부 구조

Planner Node
├── Plan Solver Plugin Interface
│   └── PlanSolverBase (추상 인터페이스)
│       ├── POPFPlanSolver (기본)
│       ├── FDPlanSolver (Fast Downward)
│       └── CustomPlanSolver (사용자 정의)
├── PDDL Text Assembler
│   ├── Domain Expert Client
│   └── Problem Expert Client
├── Plan Parser
│   └── 플래너 출력 → Plan 구조 변환
└── ROS2 Service Server
    └── get_plan

3. 계획 생성 프로세스

1. get_plan 서비스 호출 수신
2. Domain Expert에서 도메인 텍스트 수집
3. Problem Expert에서 문제 텍스트 수집
4. PDDL 파일 생성 (임시 파일)
5. 플래너 플러그인에 도메인/문제 파일 전달
6. 외부 플래너 실행 (프로세스 호출)
7. 플래너 출력 파싱
8. Plan 구조 생성 및 반환

4. 플래너 플러그인 인터페이스

class PlanSolverBase {
public:
    virtual ~PlanSolverBase() = default;
    
    virtual void configure(
        rclcpp_lifecycle::LifecycleNode::SharedPtr node,
        const std::string & plugin_name) = 0;
    
    virtual std::optional<plansys2_msgs::msg::Plan> getPlan(
        const std::string & domain,
        const std::string & problem,
        const std::string & node_namespace = "") = 0;
};

모든 플래너 플러그인은 이 인터페이스를 구현해야 한다. getPlan 메서드가 PDDL 도메인과 문제 텍스트를 입력받아 계획을 반환한다.

5. POPF 플러그인 구현

기본 플래너인 POPF의 플러그인 구현:

class POPFPlanSolver : public PlanSolverBase {
public:
    std::optional<plansys2_msgs::msg::Plan> getPlan(
        const std::string & domain,
        const std::string & problem,
        const std::string & node_namespace) override
    {
        // 1. 임시 PDDL 파일 생성
        writeTempFile(domain_file_, domain);
        writeTempFile(problem_file_, problem);
        
        // 2. POPF 프로세스 실행
        std::string cmd = "popf " + domain_file_ + " " + problem_file_;
        std::string output = executeCommand(cmd);
        
        // 3. 출력 파싱
        return parsePOPFOutput(output);
    }
};

6. 계획의 표현

생성된 계획은 plansys2_msgs::msg::Plan 메시지로 표현된다:

Plan:
  items:
    - time: 0.0
      action: "(move robot1 wp1 wp2)"
      duration: 5.0
    - time: 0.0
      action: "(move robot2 wp3 wp4)"
      duration: 7.0
    - time: 5.0
      action: "(pick robot1 box1 wp2)"
      duration: 3.0

각 계획 항목은 시작 시간, 액션 문자열, 지속 시간을 포함하며, 시간적 계획의 병렬 실행 정보를 표현한다.

7. 플래너 시간 제한

Planner 노드는 외부 플래너의 실행 시간을 제한하여 무한 탐색을 방지한다:

planner:
  plugin: "plansys2/POPFPlanSolver"
  plan_solver_timeout: 30.0  # 초

시간 제한을 초과하면 플래너 프로세스가 종료되고, 계획 생성 실패가 보고된다.

8. 클라이언트 사용 예시

auto planner_client = std::make_shared<plansys2::PlannerClient>();

auto plan = planner_client->getPlan(
    domain_client->getDomain(),
    problem_client->getProblem());

if (plan.has_value()) {
    // 계획 성공
    for (const auto & item : plan.value().items) {
        RCLCPP_INFO(node->get_logger(),
            "Time: %.1f, Action: %s, Duration: %.1f",
            item.time, item.action.c_str(), item.duration);
    }
} else {
    RCLCPP_ERROR(node->get_logger(), "Plan generation failed");
}

9. 참고 문헌

  • Gonzalez, F., Martin, F., Matellán, V., & Rodriguez, F. J. (2021). “PlanSys2: A Planning System Framework for ROS2.” IEEE ICARSC.
  • Coles, A. J., Coles, A. I., Fox, M., & Long, D. (2010). “Forward-Chaining Partial-Order Planning.” Proceedings of ICAPS, 42–49.