新闻中心
解决Python PDDL框架中的RecursionError:正确定义动作效果

本文深入探讨了在使用Python PDDL框架实现旅行商问题时遇到的`RecursionError`,并提供了解决方案。核心在于如何正确利用`pddl.logic`模块提供的逻辑运算符来定义动作效果,而非简单的字符串拼接。通过一个旅行商问题的实例,文章详细阐述了PDDL域和问题的构建过程,强调了逻辑表达式的正确使用,以避免递归深度超限错误,确保规划域的有效性和可解析性。
1. Python PDDL框架简介
pddl是一个Python库,旨在简化PDDL(Planning Domain Definition Language)域和问题的创建。它允许开发者使用Python代码定义PDDL的谓词、动作、类型、常量等元素,然后将其转换为标准的PDDL文件,供像Fast-Downward这样的规划器执行。这种方式极大地提高了PDDL建模的效率和可维护性。
2. 旅行商问题 (TSP) 的PDDL建模
旅行商问题(Tr*elling Salesman Problem, TSP)是一个经典的组合优化问题:给定一系列城市和每对城市之间的距离,找到访问每个城市一次并返回起点的最短路径。在PDDL中,我们可以将TSP建模为一个规划问题。
2.1 定义问题实体与谓词
首先,我们需要定义问题的基本元素:
- 类型 (Types): 城市可以被定义为position类型。
-
谓词 (Predicates):
- at(city): 表示当前规划器位于city。
- connected(city1, city2): 表示city1和city2之间存在连接。
- visited(city): 表示city已经被访问过。
在tsp_basic.py中,这些元素被这样定义:
from pddl.logic import Predicate, variables
from pddl.core import Domain, Problem
from pddl.action import Action
from pddl.formatter import domain_to_string, problem_to_string
from pddl.requirements import Requirements
class TSPBasic:
def __init__(self, connections, start_city):
self.connections = connections
self.start_city = start_city
unique_cities = set()
for start, end in connections:
unique_cities.update([start, end])
# 将所有城市定义为position类型的变量
self.cities = variables(" ".join(unique_cities), types=["position"])
# 定义动作中使用的单个城市变量
self.start = variables("start", types=["position"])[0]
self.finish = variables("finish", types=["position"])[0]
# 定义谓词
self.at = Predicate("at", self.start)
self.connected = Predicate("connected", self.start, self.finish)
self.visited = Predicate("visited", self.finish)
self.domain = self.create_domain()
self.problem = self.create_problem()2.2 定义动作:move
TSP的核心动作是move,它描述了从一个城市移动到另一个城市的过程。
- 动作名称: move
- 参数: start (起始城市), finish (目标城市),两者都属于position类型。
-
前置条件 (Precondition):
- 当前位于start城市 (at(start))。
- start和finish之间有连接 (connected(start, finish))。
- finish城市尚未被访问 (~self.visited(self.finish))。
-
效果 (Effect):
- 当前位置变为finish城市 (at(finish))。
- finish城市被标记为已访问 (visited(finish))。
- 不再位于start城市 (~at(start))。
3. RecursionError 的根源与解决方案
在原始代码中,move动作的效果定义如下:
# 错误的代码示例
effect="&".join([
str(self.at(self.finish)),
str(self.visited(self.finish)),
"~" + str(self.at(self.start))
])这段代码尝试通过字符串拼接来构建动作效果。然而,pddl库在内部进行类型检查和逻辑表达式解析时,期望的是pddl.logic模块提供的逻辑表达式对象(如And, Not, Predicate等),而不是简单的字符串。当传递字符串时,库的验证机制会尝试将其解析为逻辑对象,导致无限递归,最终触发RecursionError: maximum recursion depth exceeded while calling a Python object。
N世界
一分钟搭建会展元宇宙
138
查看详情
正确的做法是使用pddl.logic模块提供的逻辑运算符(&表示逻辑与,~表示逻辑非)直接构建逻辑表达式。 这些运算符被重载以创建相应的PDDL逻辑结构。
# 正确的代码示例
def create_actions(self):
move = Action(
"move",
parameters=[self.start, self.finish],
precondition=self.at(self.start) &
self.connected(self.start, self.finish) &
~self.visited(self.finish),
effect=self.at(self.finish) & self.visited(self.finish) & ~self.at(self.start)
)
return [move]通过这种方式,effect参数接收的是一个由pddl.logic对象组成的复合逻辑表达式,而不是一个字符串,从而避免了类型不匹配和递归错误。
4. 构建完整的域 (Domain) 和问题 (Problem)
4.1 构建域 (Domain)
域定义了问题的规则、类型、谓词和动作。
# tsp_basic.py 中的 create_domain 方法
def create_domain(self):
# 明确声明所需的需求,例如STRIPS, TYPING, NEG_PRECONDITION
requirements = [Requirements.STRIPS, Requirements.TYPING, Requirements.NEG_PRECONDITION]
domain = Domain(
"tsp_basic_domain",
requirements=requirements,
types={"position": None}, # 定义position类型
constants=[], # 本例中没有全局常量
predicates=[self.at, self.connected, self.visited], # 注册所有谓词
actions=self.create_actions() # 注册所有动作
)
return domain4.2 构建问题 (Problem)
问题定义了具体实例的对象、初始状态和目标状态。
# tsp_basic.py 中的 create_problem 方法
def create_problem(self):
requirements = [Requirements.STRIPS, Requirements.TYPING]
# 将所有唯一城市作为问题对象
objects = self.cities
# 定义初始状态
init = [self.at(variables(self.start_city, types=["position"])[0])] # 初始位于起始城市
for start, finish in self.connections:
# 添加所有城市连接关系
init.append(self.connected(variables(start, types=["position"])[0],
variables(finish, types=["position"])[0]))
# 定义目标状态:所有城市被访问且最终回到起始城市
goal_conditions = [self.visited(city) for city in self.cities if city.name != self.start_city]
goal_conditions.append(self.at(variables(self.start_city, types=["position"])[0]))
# 同样,这里也需要使用逻辑运算符,而非字符串拼接
goal = goal_conditions[0]
for cond in goal_conditions[1:]:
goal &= cond
problem = Problem(
"tsp_basic_problem",
domain=self.domain,
requirements=requirements,
objects=objects,
init=init,
goal=goal
)
return problem注意: 在create_problem方法中,goal的定义也需要使用pddl.logic的逻辑运算符(&)来组合多个目标条件,而非字符串拼接。上述代码已修正此问题。
5. 整合与执行
main.py负责将上述PDDL模型实例化,生成PDDL文件,并调用外部规划器(如Fast-Downward)来求解。
# main.py
from tsp_basic import TSPBasic
import subprocess
# 定义城市连接和起始城市
connections = [("Boston", "NewYork"), ("NewYork", "Boston"), ...]
start_city = "NewYork"
# 实例化TSPBasic类,它会自动构建Domain和Problem对象
tsp = TSPBasic(connections, start_city)
# 获取PDDL域和问题的字符串表示
domain_pddl_str = tsp.get_domain()
problem_pddl_str = tsp.get_problem()
# 将PDDL字符串保存到文件
with open('domain.pddl', 'w') as domain_file:
domain_file.write(domain_pddl_str)
with open('problem.pddl', 'w') as problem_file:
problem_file.write(problem_pddl_str)
# 定义并执行Fast-Downward命令
command = ["/home/mihai/tools/downward/fast-downward.py", "./domain.pddl", "./problem.pddl",
"--heuristic", "h=ff()", "--search", "astar(h)"]
process = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
# 检查执行结果
if process.returncode == 0:
print("Command executed successfully!")
print(process.stdout)
else:
print("Error in command execution.")
print("Error Output:\n", process.stderr)6. 注意事项与最佳实践
- 始终使用pddl.logic运算符: 在定义前置条件、效果和目标时,务必使用pddl.logic模块提供的&、|、~等逻辑运算符来组合表达式,而不是手动进行字符串拼接。这是避免RecursionError的关键。
- 明确声明需求 (Requirements): 根据PDDL域和问题中使用的特性(如负前置条件NEG_PRECONDITION、类型TYPING等),在Domain和Problem的构造函数中明确声明相应的Requirements。这有助于规划器正确解析PDDL文件。
- PDDL文件检查: 如果遇到规划器错误或意外行为,首先检查生成的domain.pddl和problem.pddl文件,确保它们符合PDDL语法且表达了预期的逻辑。
- 变量与常量: 区分variables和constants。variables通常用于动作参数和谓词定义,而constants用于在域级别定义全局不变的对象。在问题定义中,实际的对象(如城市名称)通常通过objects参数传入,它们在PDDL中会被视为常量。
- 错误信息分析: 当Fast-Downward或其他规划器返回错误时,仔细阅读其标准错误输出。这些信息通常会指出PDDL语法或逻辑上的具体问题。
总结
通过Python PDDL框架构建规划域和问题能够显著提高开发效率。然而,理解其内部机制,特别是如何正确构造逻辑表达式至关重要。本文通过解决旅行商问题中的RecursionError,强调了使用pddl.logic模块提供的运算符而非字符串拼接来定义动作效果和目标的重要性。遵循这些最佳实践,可以有效地利用Python PDDL框架,构建健壮且可解析的规划模型。
以上就是解决Python PDDL框架中的RecursionError:正确定义动作效果的详细内容,更多请关注其它相关文章!
# 将其
# 无锡网站建设骏域
# 网站建设理论知识
# 优化网站插件推广方法
# 拼多多网站营销推广策略
# 焦作公司网站推广
# 巩义网站建设最专业
# 秦皇岛智能网站建设操作
# 唐山网站做seo
# 搜索引擎关键词排名怎么做
# 额尔古纳宣传型网站建设
# 如何将
# 数据包
# python
# 而不是
# 转换为
# 是一个
# 的是
# 而非
# 运算符
# 递归
# asic
# red
# ai
# app
# go
相关栏目:
【
科技资讯46185 】
【
网络学院92790 】
相关推荐:
jQuery Mask 插件中实现电话号码固定前导零的教程
mcjs网页版流畅运行 mcjs低配电脑畅玩入口
c++如何使用std::memory_order控制原子操作顺序_c++ C++11内存模型详解
windows10怎么关闭系统提示音_windows10彻底静音设置方法
Win11怎么关闭触摸屏_Windows 11禁用HID符合标准触摸屏
夸克浏览器图书入口 夸克手机浏览器阅读入口
铁路12306改签能改到更早的车次吗_铁路12306改签提前车次规则
HTML长属性值处理:表单action路径优化与代码规范应对
解决Tabulator日期时间排序问题的专业指南
Fabric Mod开发:在1.19.3+版本中正确添加自定义物品并管理物品组
小米Civi 4录制视频过暗_小米Civi 4亮度优化
深入理解J*aScript Promise异步执行与微任务队列
QQ邮箱官方网页版登录 QQ邮箱个人邮箱快速访问
凉拌黄瓜怎么拌更入味 凉拌黄瓜简单家常做法
c++中的const_cast和reinterpret_cast怎么用_c++四种类型转换
Word2013如何插入视频和音频媒体_Word2013媒体插入的多媒体支持
J*a递归快速排序中静态变量导致数据累积的陷阱与解决方案
可靠CSGO开箱平台解析 CSGO开箱网合集
包子漫画官方网站阅读入口-包子漫画在线漫画官网直达链接
UC浏览器网页版登录入口官网 电脑版网址入口
曝R星经典之作开发图 设计简陋但信息密集!
今日头条怎么同步内容到抖音_今日头条内容同步到抖音教程
微信怎么把收藏的内容分类管理 微信收藏内容标签分类方法
浏览器打开即用 美图秀秀网页版入口
电脑安装程序提示“错误1722”怎么办_Windows Installer服务问题解决【教程】
火狐浏览器占用内存高卡顿怎么办 火狐浏览器性能优化设置技巧
高德地图总提示网络异常怎么办 高德地图离线导航设置与网络排查方法
AO3官方可用镜像 Archive of Our Own网页版最新入口
海棠电脑版入口_通过电脑访问海棠官网阅读
QQ邮箱网页版邮箱入口 QQ邮箱官方登录平台
C++如何进行游戏物理模拟_使用Box2D库为C++游戏添加2D物理效果
Win11怎么设置鼠标主按键_Win11鼠标左右键功能互换
AO3官网镜像链接 Archive of Our Own同人文在线浏览
德邦快递查询平台 德邦快递物流信息查询入口
照顾宝贝2小游戏免费秒玩入口
c++ dfs和bfs代码 c++深度广度优先搜索算法
sublime怎么进行远程开发编辑_配置rsub/rmate实现sublime编辑服务器文件
Composer的 archive 命令怎么用_快速打包你的PHP项目及其Composer依赖
c++如何使用TBB库进行任务并行_c++ Intel线程构建模块
ExcelARRAYTOTEXT函数怎么自定义分隔符输出数组文本_ARRAYTOTEXT实现动态生成SQL语句
动漫花园资源网使用步骤_动漫花园资源网下载流程
JUnit5/Mockito:优雅测试内部依赖与异常处理的实践
QQ邮箱登录官网首页 腾讯QQ邮箱网页入口
神庙逃亡小游戏在线玩 神庙逃亡小游戏入口
cad怎么合并重叠的线段_cad清理重复重叠线条的操作方法
如何在Promise链中优雅地中断后续then执行
c++如何实现单例设计模式_c++线程安全的单例模式写法
响应式容器内容自动缩放与宽高比维持教程
在FastAPI中利用lifespan与依赖注入高效管理Redis连接池
Win10双系统截图高效法 截屏快捷键速记【技巧】


2025-11-29
浏览次数:次
返回列表
self.finish = variables("finish", types=["position"])[0]
# 定义谓词
self.at = Predicate("at", self.start)
self.connected = Predicate("connected", self.start, self.finish)
self.visited = Predicate("visited", self.finish)
self.domain = self.create_domain()
self.problem = self.create_problem()