1317.22 PlanSys2의 플래너 통합 인터페이스
1. 플래너 통합의 필요성
자동화된 계획 수립(automated planning)은 다양한 플래너 알고리즘에 의해 수행되며, 각 플래너는 고유한 입출력 형식, 지원하는 PDDL 요구사항 수준, 계산 성능 특성을 가진다. PlanSys2는 특정 플래너에 종속되지 않고, 다양한 외부 플래너를 교체 가능하게 통합하기 위한 추상 인터페이스 계층을 제공한다. 이 플래너 통합 인터페이스(planner integration interface)는 PlanSys2의 플래너 노드(Planner Node)와 외부 플래너 엔진 사이의 중개 역할을 수행하며, 플래너의 교체가 상위 계층의 코드 변경 없이 이루어질 수 있도록 설계되어 있다(Martín et al., 2021).
2. 플래너 노드의 구조와 통합 계층
2.1 플래너 노드의 책임
플래너 노드는 도메인 전문가 노드로부터 도메인 모델을, 문제 전문가 노드로부터 문제 인스턴스를 수신하고, 이를 결합하여 외부 플래너에 전달한 뒤 생성된 계획(plan)을 파싱하여 실행기 노드에 반환하는 역할을 담당한다. 플래너 노드의 내부 구조는 다음과 같이 계층화되어 있다.
- 서비스 계층(Service Layer): ROS2 서비스 인터페이스를 통해 계획 수립 요청을 수신하고 결과를 반환한다.
- 플래너 관리 계층(Planner Management Layer): 플래너 플러그인의 로딩, 초기화, 호출을 관리한다.
- 플래너 플러그인 계층(Planner Plugin Layer): 구체적인 플래너와의 통신을 담당하는 플러그인이 위치한다.
2.2 추상 플래너 인터페이스
PlanSys2의 플래너 통합 인터페이스는 C++ 추상 기반 클래스(abstract base class)로 정의된다. 이 인터페이스는 PlannerInterface라는 이름의 클래스로 구현되며, 모든 플래너 플러그인이 구현해야 하는 순수 가상 함수(pure virtual function)를 선언한다.
핵심 인터페이스 메서드는 다음과 같다.
| 메서드 | 반환 타입 | 설명 |
|---|---|---|
getPlan(domain, problem, node) | std::optional<Plan> | 도메인과 문제를 입력받아 계획을 반환한다 |
getPlan 메서드는 PDDL 도메인 문자열과 PDDL 문제 문자열을 인자로 수신하며, 계획 수립이 성공하면 계획 객체를 반환하고, 실패하면 빈 값(std::nullopt)을 반환한다. 이 단일 메서드를 통해 플래너의 구체적 구현이 추상화된다.
3. 플래너 호출의 절차
3.1 입력 준비
플래너 호출에 앞서, 플래너 노드는 다음과 같은 입력을 준비한다.
- 도메인 문자열 획득: 도메인 전문가 노드의 서비스를 호출하여 현재 로딩된 도메인의 PDDL 텍스트 표현을 획득한다.
- 문제 문자열 획득: 문제 전문가 노드의 서비스를 호출하여 현재 구성된 문제 인스턴스의 PDDL 텍스트 표현을 획득한다.
- 임시 파일 생성: 대부분의 외부 플래너는 파일 기반 입력을 요구하므로, 획득한 도메인 및 문제 PDDL 텍스트를 임시 파일(temporary file)로 저장한다.
3.2 외부 플래너 실행
플래너 플러그인은 준비된 입력을 사용하여 외부 플래너 프로세스를 호출한다. 이 과정은 일반적으로 다음과 같은 방식으로 수행된다.
<planner_executable> <domain_file> <problem_file> <output_file>
외부 플래너는 독립적인 프로세스로 실행되며, PlanSys2는 해당 프로세스의 완료를 대기한다. 프로세스의 종료 코드(exit code)와 표준 출력(stdout), 표준 오류(stderr)를 수집하여 계획 수립의 성공 여부를 판단한다.
3.3 출력 파싱
외부 플래너가 성공적으로 종료되면, 플래너 플러그인은 출력 파일에 기록된 계획을 파싱한다. 일반적인 PDDL 플래너의 출력 형식은 시간 인덱싱된 액션 시퀀스이다.
0.000: (move robot1 kitchen bedroom) [5.000]
5.001: (pick robot1 cup bedroom) [3.000]
각 행은 시작 시각, 액션 이름과 인자, 지속 시간으로 구성된다. 플래너 플러그인은 이 텍스트를 파싱하여 PlanSys2 내부의 Plan 자료 구조로 변환한다.
4. ROS2 서비스 기반 계획 수립 요청
플래너 노드는 get_plan이라는 ROS2 서비스를 통해 계획 수립 요청을 수신한다. 이 서비스의 요청-응답 구조는 다음과 같다.
요청(Request):
- 도메인 PDDL 문자열 또는 도메인 자동 획득 플래그
- 문제 PDDL 문자열 또는 문제 자동 획득 플래그
응답(Response):
- 계획 수립 성공 여부
- 계획 항목(plan item)의 배열: 각 항목은 시각, 액션 문자열, 지속 시간을 포함한다
- 오류 발생 시 오류 메시지
이 서비스 인터페이스를 통해 실행기 노드나 외부 클라이언트가 계획 수립을 요청하고, 결과를 비동기적으로 수신할 수 있다.
5. pluginlib 기반 플래너 로딩
PlanSys2는 ROS2의 pluginlib 프레임워크를 활용하여 플래너 플러그인을 동적으로 로딩한다. pluginlib은 공유 라이브러리(shared library)로 컴파일된 플러그인 클래스를 런타임에 이름 기반으로 로딩하는 메커니즘을 제공한다. 플래너 노드는 파라미터로 지정된 플래너 플러그인의 클래스 이름을 pluginlib::ClassLoader에 전달하여 해당 플러그인의 인스턴스를 생성한다.
플래너 플러그인의 등록은 다음과 같은 절차를 따른다.
- 플러그인 클래스 구현:
PlannerInterface추상 클래스를 상속하여getPlan메서드를 구현한다. - 플러그인 매크로 선언:
PLUGINLIB_EXPORT_CLASS매크로를 사용하여 플러그인 클래스를pluginlib에 등록한다. - 플러그인 XML 기술: 플러그인의 라이브러리 경로, 클래스 이름, 기반 클래스를 기술하는 XML 파일을 작성한다.
- CMakeLists.txt 등록:
pluginlib_export_plugin_description_file매크로를 통해 플러그인 XML을 빌드 시스템에 등록한다.
이 과정을 통해, 새로운 플래너를 PlanSys2에 통합할 때 플래너 노드의 코드를 수정할 필요 없이 플러그인만 추가하면 된다.
6. 플래너 선택과 구성
플래너 노드는 ROS2 파라미터를 통해 사용할 플래너 플러그인을 지정받는다. 런치 파일에서 다음과 같이 플래너를 구성할 수 있다.
Node(
package='plansys2_planner',
executable='planner_node',
name='planner',
parameters=[{
'plan_solver_plugins': ['POPF'],
'POPF': {
'plugin': 'plansys2_popf_plan_solver/POPFPlanSolver'
}
}]
)
plan_solver_plugins 파라미터는 사용할 플래너 플러그인의 이름 목록을 지정하며, 각 이름에 대응하는 플러그인 클래스의 전체 경로(fully qualified name)가 하위 파라미터로 제공된다. 이러한 파라미터 기반 구성은 코드 재컴파일 없이 런치 시점에 플래너를 교체할 수 있게 한다.
7. 오류 처리와 시간 제한
플래너 통합 인터페이스는 외부 플래너 실행 과정에서 발생할 수 있는 다양한 오류 상황에 대비한다.
- 플래너 실행 실패: 외부 플래너 프로세스가 비정상 종료 코드를 반환할 경우, 오류 메시지를 포함한 실패 응답을 생성한다.
- 해 부재(unsolvable): 플래너가 주어진 도메인과 문제에 대해 유효한 계획을 찾지 못한 경우, 이를 감지하고 적절한 오류 코드를 반환한다.
- 시간 초과(timeout): 플래너의 실행 시간이 설정된 제한 시간을 초과하면 프로세스를 강제 종료하고 시간 초과 오류를 반환한다. 이는 NP-난해(NP-hard)인 계획 수립 문제에서 플래너가 무한히 탐색하는 상황을 방지한다.
- 파싱 실패: 플래너의 출력이 기대한 형식과 일치하지 않을 경우, 출력 파싱 오류를 보고한다.
이러한 오류 처리 메커니즘을 통해, 플래너 노드는 외부 플래너의 다양한 동작 양상에 강건하게 대응할 수 있다(Martín et al., 2021).
8. 통합 인터페이스의 설계 원칙
PlanSys2의 플래너 통합 인터페이스는 다음과 같은 소프트웨어 공학적 설계 원칙을 따른다.
- 개방-폐쇄 원칙(Open-Closed Principle): 새로운 플래너의 추가에 대해 개방적이면서, 기존 코드의 수정에 대해서는 폐쇄적이다.
- 의존성 역전 원칙(Dependency Inversion Principle): 플래너 노드는 구체적 플래너 구현이 아닌 추상 인터페이스에 의존한다.
- 단일 책임 원칙(Single Responsibility Principle): 각 플래너 플러그인은 특정 플래너와의 통신만을 담당한다.
이 설계를 통해 PlanSys2는 POPF, TFD, Fast Downward 등 다양한 외부 플래너를 일관된 방식으로 통합할 수 있는 확장 가능한 아키텍처를 제공한다(Martín et al., 2021; Gamma et al., 1995).
참고 문헌
- Martín, F., Cañas, J. M., Ginés, J., & Fuentetaja, R. (2021). “PlanSys2: A Planning System Framework for ROS2.” IEEE/RSJ International Conference on Intelligent Robots and Systems (IROS).
- Gamma, E., Helm, R., Johnson, R., & Vlissides, J. (1995). Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley.
- Coles, A. J., Coles, A. I., Fox, M., & Long, D. (2010). “Forward-Chaining Partial-Order Planning.” Proceedings of the 20th International Conference on Automated Planning and Scheduling (ICAPS).
버전: v1.0