系列第八篇。在前面的文章中,我们已经掌握了HTN领域的理论知识和设计方法。本篇将介绍主流的HTN规划器,帮助你选择最适合自己场景的工具。
一、开篇:规划器是HTN的"引擎"
在HTN规划中,规划器(Planner) 是执行任务分解、搜索可行解的核心引擎。
即使你的领域设计得再完美,如果没有合适的规划器,一切都只是"纸上谈兵"。
规划器做什么:
输入:领域定义(Domain)+ 问题描述(Problem)
↓
规划器执行分解搜索
↓
输出:可行的任务执行序列(Plan)
本篇文章目标:
- 了解主流HTN规划器的特点
- 掌握选择规划器的方法
- 学会配置和调优的基本技巧
二、主流HTN规划器介绍
2.1 SHOP2
基本信息:
- 开发:University of Maryland
- 语言:Common Lisp(JSHOP2)和 Java(SHOP2)
- 特点:最经典、应用最广泛的HTN规划器
核心特点:
1. 算法优先:采用任务分解顺序(Task Decomposition Order)策略
2. 支持复杂约束:内置时序约束、资源约束支持
3. 可扩展性强:可自定义推理规则
4. 工业级应用:NASA和ESA的研究项目中有应用案例
优势:
- 成熟稳定,文档丰富
- 适合复杂领域(如航天器规划)
- 支持PDDL扩展
劣势:
- 安装配置较复杂
- 性能在大规模问题上可能受限
适用场景:学术研究、航天器任务规划、工业级应用
2.2 PyHOP
基本信息:
- 开发:Dana Nau(SHOP2作者团队)
- 语言:Python
- 特点:轻量级、教学级HTN规划器
核心特点:
1. 纯Python实现:易于安装和使用
2. 代码即领域:用Python代码定义领域和动作
3. 透明可调试:可以单步跟踪规划过程
4. 适合学习:代码简单,适合理解HTN原理
优势:
- 安装简单:pip install pyhop
- 调试友好
- 与Python生态系统集成
劣势:
- 不支持PDDL
- 性能相对较低
- 不适合大规模复杂问题
适用场景:学习HTN原理、快速原型开发、教学演示
快速上手示例:
from pyhop import hop
# 定义原子动作
def slew(state, satellite, target):
"""调整卫星指向目标"""
if state.satellite_pointing.get(satellite) == target:
return [] # 已经指向,无需操作
state.satellite_pointing[satellite] = target
return state
def take_image(state, satellite, target):
"""拍摄目标图像"""
if state.satellite_pointing.get(satellite) == target:
state.target_images[target] = True
return state
return False
# 定义方法
def observe_by_satellite(state, satellite, target):
"""观测方法:先调整指向,再拍摄"""
return [('slew', satellite, target), ('take_image', satellite, target)]
# 注册
hop.declare_operators(slew, take_image)
hop.declare_methods('observe', observe_by_satellite)
result = hop.pyhop(state, [('observe', 'sat1', 'target1')])
2.3 SIADEX
基本信息:
- 开发:Technical University of Madrid
- 语言:Java
- 特点:时序感知HTN规划器
核心特点:
1. 时序规划结合:HTN + Temporal Planning
2. 支持持续性动作:Durative Actions
3. Timeline优化:适合多智能体协同场景
4. 可视化工具:提供规划过程可视化
优势:
- 适合时序敏感的应用
- 支持多智能体规划
- 有友好的IDE支持
劣势:
- 社区相对较小
- 文档以西班牙语为主
适用场景:多智能体协同、时序约束敏感的应用
为什么选择SIADEX而不是SHOP2?
| 场景 | 推荐 | 原因 |
|---|---|---|
| 需要同时处理时序约束和HTN分解 | SIADEX | SHOP2时序扩展不完善 |
| 多卫星协同任务 | SIADEX | Timeline机制天然支持多Agent |
| 只需要HTN分解 | SHOP2 | 社区更大,文档更全 |
2.4 其他规划器
| 规划器 | 语言 | 特点 | 适用场景 |
|---|---|---|---|
| JSHOP | Java | SHOP2的Java实现 | Java项目集成 |
| PANDA系 | Java | 分层规划研究平台 | 学术研究 |
| FF-REPLAN | C | HTN+经典规划混合(重规划) | 混合规划研究 |
三、规划器对比
3.1 功能对比表
| 特性 | SHOP2 | PyHOP | SIADEX |
|---|---|---|---|
| 语言 | Common Lisp / Java | Python | Java |
| 安装难度 | ⭐⭐⭐ | ⭐ | ⭐⭐ |
| PDDL支持 | ✅ 完整 | ❌ | ✅ 部分 |
| 时序约束 | ✅ | ❌ | ✅ |
| 资源约束 | ✅ | ❌ | ❌ |
| 性能 | ⭐⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐⭐ |
| 调试友好度 | ⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
| 社区活跃度 | ⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐ |
| 文档质量 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐ |
3.2 选择决策树
需要快速原型/学习?
│
├── 是 → PyHOP
│
└── 否 → 需要处理时序约束?
│
├── 是 → SIADEX
│
└── 否 → 需要工业级应用?
│
├── 是 → SHOP2
│
└── 否 → PyHOP(轻量级场景)
3.3 选择考量维度
1. 应用场景
- 学习/教学 → PyHOP
- 学术研究 → SHOP2 / PyHOP
- 工业应用 → SHOP2
- 时序敏感 → SIADEX
2. 技术栈
- Python项目 → PyHOP
- Java项目 → SHOP2 / SIADEX
- Lisp项目 → SHOP2
3. 性能需求
- 小规模问题 → PyHOP足够
- 大规模问题 → SHOP2
4. 约束复杂度
- 只需HTN分解 → 任一都行
- 需要时序约束 → SIADEX
- 需要资源约束 → SHOP2
四、规划器配置与调优
4.1 SHOP2配置指南
⚠️ 以下为概念性参数说明,实际SHOP2配置请参考官方文档。
基本配置参数:
;; 搜索策略设置(概念性语法)
:plan-generator (make-nhtn-plan-generator
:strategy 'depth-first ; 或 'breadth-first, 'best-first
:max-depth 1000
:time-bound 30) ; 超时设置(秒)
;; verbose设置在调用时指定
(plan-all area 'domain 'problem :verbose 3)
调优建议:
1. 搜索策略选择:
- 深度优先:适合解路径较短的问题
- 广度优先:适合需要完整解空间搜索
- 最佳优先:适合有启发式评估的场景
- 超时设置:
- 交互式应用:设置较短超时(5-10秒)
-
批处理应用:可设置较长超时
-
内存优化:
- 限制搜索深度避免内存爆炸
- 使用迭代深化(Iterative Deepening)
4.2 PyHOP调试技巧
单步跟踪:
# 启用详细输出
pyhop.pyhop(state, tasks, verbose=2)
# 观察每一步的分解过程
常见问题排查:
| 问题 | 可能原因 | 解决方法 |
|---|---|---|
返回False |
没有匹配的method | 检查precondition |
| 无限递归 | 递归method缺少终止条件 | 添加基础case |
| 解不完整 | method覆盖不全 | 检查所有情况 |
性能优化:
# 使用状态缓存减少重复计算
from functools import lru_cache
@lru_cache(maxsize=1000)
def complex_precondition(state, arg):
...
4.3 SIADEX配置要点
⚠️ 以下为概念性配置示例,SIADEX的实际配置文件格式请参考其官方文档。
<!-- 概念性配置示例 -->
<configuration>
<temporal>true</temporal>
<duration-constraints>true</duration-constraints>
<timeline-merge>true</timeline-merge>
</configuration>
性能调优:
1. Timeline合并阈值:平衡求解质量与速度
2. 并行搜索:利用多核加速
3. 约束松弛:适当放宽约束加速求解
五、实战:从配置到运行
5.1 PyHOP完整示例
场景:卫星观测任务规划
from pyhop import hop
# 状态定义
class State:
def __init__(self):
self.satellite_pointing = {} # 卫星指向
self.satellite_energy = {} # 卫星电量
self.target_images = {} # 已成像目标
# 定义原子动作
def slew(state, satellite, target):
"""调整卫星指向目标"""
if state.satellite_pointing.get(satellite) == target:
return [] # 已经指向目标,无需操作
state.satellite_pointing[satellite] = target
return state
def take_image(state, satellite, target):
"""拍摄目标图像"""
if state.satellite_pointing.get(satellite) == target:
state.target_images[target] = True
return state
return False
def charge(state, satellite):
"""为卫星充电"""
if state.satellite_energy.get(satellite, 0) >= 100:
return [] # 电量已满,无需充电
state.satellite_energy[satellite] = 100
return state
# 定义方法
def observe_by_satellite(state, satellite, target):
"""基础观测方法"""
return [('slew', satellite, target), ('take_image', satellite, target)]
def observe_with_energy(state, satellite, target):
"""带能量管理的观测方法"""
tasks = []
if state.satellite_energy.get(satellite, 0) < 10:
tasks.append(('charge', satellite))
tasks.extend([('slew', satellite, target), ('take_image', satellite, target)])
return tasks
# 注册
hop.declare_operators(slew, take_image, charge)
hop.declare_methods('observe', observe_by_satellite, observe_with_energy)
# 执行规划
state = State()
state.satellite_pointing = {'sat1': 'target0'}
state.satellite_energy = {'sat1': 50}
state.target_images = {}
result = hop.pyhop(state, [('observe', 'sat1', 'target1')])
print(result)
💡 说明:PyHOP会自动尝试所有注册的method,选择第一个能成功返回有效计划的。这里的
observe_with_energy会在电量不足时自动先充电。
5.2 SHOP2领域文件示例
;; domain file: satellite-domain.pddl
(define (domain satellite-htn)
(:requirements :htn :typing)
(:types satellite target mode)
(:predicates
(pointing ?s - satellite ?t - target)
(calibrated ?s - satellite)
(on-board-storage-full ?s - satellite)
)
;; 原子动作
(:action slew
:parameters (?s - satellite ?t - target)
:precondition (calibrated ?s)
:effect (pointing ?s ?t)
)
(:action take-image
:parameters (?s - satellite ?t - target ?m - mode)
:precondition (and (pointing ?s ?t) (calibrated ?s))
:effect (and (on-board-storage-full ?s) (taken-image ?s ?t ?m))
)
;; HTN方法
(:method observe-satellite
:parameters (?s - satellite ?t - target)
:precondition (calibrated ?s)
:tasks ((slew ?s ?t) (take-image ?s ?t HIGH_RES))
)
)
六、总结与展望
6.1 核心要点回顾
| 规划器 | 核心优势 | 最佳场景 |
|---|---|---|
| SHOP2 | 功能完整、工业级 | 复杂领域、工业应用 |
| PyHOP | 轻量、调试友好 | 学习、原型开发 |
| SIADEX | 时序感知 | 多智能体、时序敏感 |
选择建议:
- 入门学习 → PyHOP
- 科研论文 → SHOP2(可复现)
- 工程落地 → SHOP2 + 定制开发
- 时序约束强 → SIADEX
6.2 下一步
本系列HTN规划专栏至此已覆盖:
- ✅ 理论基础(形式化定义、TFD算法)
- ✅ 表达能力与不可判定性
- ✅ 领域设计实践
- ✅ 规划器选择与配置
后续可能的主题:
- HTN与经典规划器的对比
- 前沿:HTN规划的启发式方法
- 前沿:学习增强的HTN规划
参考文献
- Nau, D. S., Au, T. C., Ilghami, O., Kuter, U., et al. (2003). SHOP2: An HTN planning system. JAIR, 20, 379-404.
- PyHOP Documentation. https://github.com
- SIADEX Project. Technical University of Madrid.
- Alford, R., Kuter, U., & Nau, D. S. (2009). Planning as homotopy of task networks. ICAPS.