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.