PDDL入门
PDDL的组成部分
- Objects:对象,世界上我们感兴趣的事物。
- Predicates:谓词,我们感兴趣的事实(例如对象的属性),可以为真,也可以为假。
- An initial state:初始化状态,我们开始所处的世界状态,即开始时为真的事物。
- Goal specification:目标状态,我们想要结束的世界的状态,即我们希望最终成为真实的事情。
- Actions/Operators:动作/操作,改变世界状态的方式,即发生的事情改变了事实。
PDDL语法
PDDL 文件的扩展名通常是.pddl
要素:一份完整的PDDL程序是由domain文件和problem文件
The Domain File:域文件建立了世界的上下文。它决定了状态可以包含哪些类型的细节(Predicates),以及我们可以做什么在世界中的状态之间移动(actions)。
domain file的基础语法:
(define (domain <domain name>)
(:predicates
<predicate-list>
)
(:action
<action-details>
)
)
其中,<domain-name>
是世界的名称。
The Problem File:问题文件代表着我们在域中建立一个世界的实例,它决定了在计划开始时什么是真的(初始状态),以及我们希望在计划结束时什么是真的(目标状态)。
problem file的基础语法:
(define (problem <title>)
(:domain <domain-name>)
(:objects
<object-list>
)
(:init
<predicates>
)
(:goal
<predicates>
)
)
<title>
是problem file 的标题,<domain-name>
是指相应域文件的名称。
Eg: Let’s Eat!
让我们想象一下,我们有一个机器人手臂,一个纸杯蛋糕和一个盘子。夹钳是空的,纸杯蛋糕在桌子上,我们想把纸杯蛋糕放在盘子上。
在我们在PDDL中对此进行建模之前,让我们先看看PDDL问题的组成部分:
- 首先我们定义域
(define (domain letseat) ;域名为letseat
- 然后我们定义objects:盘子、夹钳、纸杯蛋糕。我们还将把杯子蛋糕和夹钳标记为可定位的,稍后我们将创建一个predicate来帮助我们查询这些对象的位置。
(:requirements :typing) ;声明typing
(:types
location locatable - object ;类型定义, location locatable
bot cupcake - locatable ;声明子类型bot和cupcake属于locatable
robot - bot ;声明子类型robot属于bot
)
- 我们还需要定义一些predicates。夹钳是空的吗?纸杯蛋糕在哪里?
(:predicates ; 谓词,用来定义动作
(on ?obj - locatable ?loc - location) ;-xx属于某类型,定义谓词on:obj在loc上
(holding ?arm - locatable ?cupcake - locatable) ;定义谓词holding:arm持有cupcake
(arm-empty) ;定义谓词arm-empty,定义夹钳为空
(path ?location1 - location ?location2 - location) ;定义谓词path:locatiob1到location2的路径
)
- 我们还必须定义actions/operators,我们需要能够拿起和放下纸杯蛋糕,以及在桌子和盘子之间移动手臂。
(:action pick-up ;定义动作捡起
:parameters ;参数arm属于bot类型,参数cupcake属于locatable类型,参数loc属于location类型
(?arm - bot
?cupcake - locatable
?loc - location)
:precondition ;前提
(and
(on ?arm ?loc) ;使用前面的谓词on,arm在loc上
(on ?cupcake ?loc) ;使用前面的谓词on,cupcake在loc上
(arm-empty) ;使用前面的谓词arm-empty,夹钳为空
)
:effect ;结果
(and
(not (on ?cupcake ?loc)) ;cupcake不在loc上
(holding ?arm ?cupcake) ;arm持有cupcake
(not (arm-empty)) ;夹钳不空
)
)
(:action drop ;动作放下
:parameters ;参数arm属于bot类型,cupcake属于locatable,loc属于location类型
(?arm - bot
?cupcake - locatable
?loc - location)
:precondition ;前提
(and
(on ?arm ?loc) ;arm在loc上
(holding ?arm ?cupcake) ;arm持有cupcake
)
:effect ;结果
(and
(on ?cupcake ?loc) ;cupcake在loc上
(arm-empty) ;夹钳为空
(not (holding ?arm ?cupcake)) ;arm不再持有cupcake
)
)
(:action move ;定义动作移动
:parameters ;参数arm为bot类型,from和to为location类型
(?arm - bot
?from - location
?to - location)
:precondition ;前提
(and
(on ?arm ?from) ;arm在from
(path ?from ?to) ;从from到to
)
:effect ;效果
(and
(not (on ?arm ?from)) ; arm不在from
(on ?arm ?to) ;arm在to
)
)
综合上述内容,我们就可以得到一个 domain file!
现在我们将会查看problem file。
- 我们首先让它知道它关联的是哪个域,定义这个世界上存在的对象。
(define (problem letseat-simple)
(:domain letseat) ;定义problem所属的域
(:objects ;定义对象,arm属于robot,cupcake属于cupcake,table和plate属于location
arm - robot
cupcake - cupcake
table - location
plate - location
)
- 然后,我们将会定义初始化状态:夹钳是空的,纸杯蛋糕在桌子上,手臂可以在两者之间移动。
(:init ;
(on arm table) ;arm on table
(on cupcake table) ;cupcake on table
(arm-empty)
(path table plate) ; table path plate
)
- 最后,我们定义了目标规划:纸杯蛋糕在盘子上。
(:goal
(on cupcake plate) ; cupcake on plate
)
综合上述内容,我们就可以得到一个problem file!
如果使用 OPTIC运行,你可以得到如下的解决方案:
Initial heuristic = 3
Initial stats: t=0s, 4299060kb
b (2 @ n=3, t=0s, 4300084kb)b (1 @ n=6, t=0s, 4308276kb)
;;;; Solution Found
; Time 0.00
; Peak memory 4308276kb
; Nodes Generated: 5
; Nodes Expanded: 3
; Nodes Evaluated: 6
; Nodes Tunneled: 1
; Nodes memoised with open actions: 0
; Nodes memoised without open actions: 6
; Nodes pruned by memoisation: 0
0: (pick-up arm cupcake table) [1]
1: (move arm table plate) [1]
2: (drop arm cupcake plate) [1]
练习
- 在桌子上添加第二个蛋糕,并将其添加到目标规划中,以确保它也放在盘子上。
;这是我写的一个简单实现,能够成功将两块蛋糕都放到盘子里
(define (problem letseat-)
(:domain letseat)
(:objects
arm - robot
cupcake1 - cupcake
cupcake2 - cupcake
plate - location
table - location
)
(:init
(on arm table)
(on cupcake1 table)
(on cupcake2 table)
(arm-empty)
(path table plate)
(path plate table)
)
(:goal (and
(on cupcake1 plate)
(on cupcake2 plate)
))
)
- 在域中添加一个unicorn对象,给unicorn设定一个吃纸杯蛋糕的目标,并且unicorn只能吃盘子里的纸杯蛋糕。
复杂例子
如果你想了解一些更复杂的东西,请查看 driverlog domain。
持续的动作
实际上,您可以为操作指定在时间域中工作的持续时间,每个条件和效果都被给定了它应该发生的时间。有几种类型的时间约束:
(at start (<condition/effect>))
这意味着这必须是真的,或者在动作开始时发生。(at end (<condition/effect>))
这意味着这必须是真的,或者在动作结束时发生。(over all (<condition>))
,这意味着在整个行动期间,这必须是真的。
下面是一个(move)
动作的例子,从我们前面的例子转换为持续动作。
(:durative-action move
:duration (= ?duration 10) ; Duration goes here.
:parameters
(?arm - bot
?from - location
?to - location)
:condition ; Note how this is "condition" not "pre-condition"
(and
(at start (on ?arm ?from))
(over all (path ?from ?to))
)
:effect
(and
(at start (not (on ?arm ?from)))
(at end (on ?arm ?to))
)
)