无法将含PointCloud列表的AbstractValue用作Drake的AbstractPort/AbstractState
问题分析与解决方案
错误原因
当你尝试用AbstractValue.Make包装list[PointCloud]并声明端口时,触发cannot pickle 'pydrake.perception.PointCloud' object错误的核心原因是:
- Python默认的
deepcopy逻辑会递归尝试对列表中的每个PointCloud对象进行pickle序列化,但PointCloud并未实现pickle支持。 - 单个
PointCloud可以正常工作,是因为Drake为该类型提供了专门的AbstractValue实现,内置了自定义的拷贝逻辑,不需要依赖pickle。
解决方案
以下两种方法均可解决该问题,核心都是绕开默认的pickle机制,改用PointCloud自身的Copy()方法完成对象拷贝:
方案一:为列表类型自定义拷贝函数
使用pydrake.common.value.Value指定列表类型,并传入自定义的拷贝函数:
from pydrake.common.value import Value from pydrake.perception import PointCloud # 定义列表的拷贝逻辑:手动拷贝每个PointCloud def copy_pcd_list(src: list[PointCloud]) -> list[PointCloud]: return [pcd.Copy() for pcd in src] # 创建带自定义拷贝逻辑的AbstractValue pcd_list_template = Value[list[PointCloud]] abstract_pcd_list = pcd_list_template.Make( [PointCloud(0) for _ in range(5)], copy_func=copy_pcd_list ) # 声明输入端口 self.DeclareAbstractInputPort("block_pcds", abstract_pcd_list)
方案二:封装成自定义类并实现深拷贝
创建包含PointCloud列表的类,实现__deepcopy__方法来控制拷贝行为:
import copy from pydrake.perception import PointCloud from pydrake.common.value import AbstractValue class PointCloudList: def __init__(self, pcds: list[PointCloud]): self.pcds = pcds def __deepcopy__(self, memo): # 手动拷贝每个PointCloud实例 copied_pcds = [pcd.Copy() for pcd in self.pcds] return PointCloudList(copied_pcds) # 创建自定义类实例并包装为AbstractValue pcd_list = PointCloudList([PointCloud(0) for _ in range(5)]) abstract_pcd_list = AbstractValue.Make(pcd_list) # 声明输入端口 self.DeclareAbstractInputPort("block_pcds", abstract_pcd_list)
扩展说明
- 上述两种方法同样适用于
AbstractOutputPort和AbstractState的声明,只需替换对应的DeclareAbstractOutputPort或DeclareAbstractState方法即可。 - 始终优先使用
PointCloud.Copy()方法拷贝此类对象,这是Drake官方推荐的正确方式,能保证数据完整性和兼容性。
内容的提问来源于stack exchange,提问作者Clément Jambon
相关产品推荐
相关产品推荐

