使用TemplateSystem实现可标量转换自定义LeafSystem的语法问询
我正在尝试创建一个支持不同标量类型间转换的自定义LeafSystem。为了自主实现该系统,我一直在研究如下示例代码以理清实现逻辑:
@TemplateSystem.define("MySystem_") def MySystem_(T): class Impl(LeafSystem_[T]): def _construct(self, value, converter=None): LeafSystem_[T].__init__(self, converter=converter) self.value = value def _construct_copy(self, other, converter=None): Impl._construct(self, other.value, converter=converter) return Impl MySystem = MySystem_[None] # Default instantiation.
我对Python语法掌握程度有限,若部分问题属于Python语法范畴而非Drake框架相关内容还请谅解,现有如下技术疑问需解答:
- 我不熟悉
@TemplateSystem.define("MySystem_")这一语法写法,不清楚其具体实现的功能是什么? - 示例代码中为何要将
Impl类定义在MySystem_函数内部? - 如果我需要像开发常规
LeafSystem一样实现自定义功能函数,是否需要将这些函数定义在Impl类中? - 我对类型转换的具体实现逻辑存在疑惑:假设我定义了系统
MySystem = MySystem_[float],后续需要基于该系统构建DirectCollocation,DirectCollocation是否会自动调用_construct_copy方法将系统转换为AutoDiffXd类型,还是需要我提前手动完成类型转换操作?
此外,若能提供TemplateSystem的相关使用示例,将帮助我快速理清其使用逻辑,消除现有疑惑。
关于
@TemplateSystem.define("MySystem_")的功能
这是Drake提供的装饰器语法,核心作用是把你编写的、接收标量类型T作为入参的类生成函数,包装成支持多标量类型索引的模板系统对象。装饰器执行后会自动完成不同标量类型(float/AutoDiffXd/Expression等)的系统类绑定,后续你通过MySystem_[T]的写法就能直接拿到对应标量类型的系统实现,不需要手动为每个标量类型重复编写类定义,同时它会自动注册类型转换接口,打通不同标量类型系统实例的转换链路。把
Impl类定义在MySystem_函数内部的原因
这是Python闭包特性的典型工程用法:- 每次传入不同的
T调用MySystem_函数时,都会独立生成一个绑定了当前T类型的新Impl类,不同标量类型对应的Impl类互相隔离,不会出现类型属性混淆的问题 - 内部类可以直接访问当前函数作用域内的
T参数,不需要额外在类初始化时重复传入类型参数,代码更简洁 - 可以避免把不同标量类型的实现类暴露到全局作用域,减少命名冲突。
- 每次传入不同的
自定义功能函数的定义位置
所有和系统逻辑相关的自定义方法,包括端口计算、事件处理、状态更新这类常规LeafSystem需要实现的功能函数,全部都要定义在Impl类内部。因为Impl才是真正继承自对应标量类型LeafSystem_[T]的实体类,外层的MySystem_只是用来生成不同类型Impl类的工厂函数,定义在外层的方法不会被系统实例继承调用。类型转换的调用逻辑
不需要手动提前做类型转换。DirectCollocation在初始化时接收你传入的float类型系统实例后,会内部自动触发标量转换流程,这个流程会自动调用你实现的_construct_copy方法,传入对应AutoDiffXd类型的转换器,生成适配自动微分的系统实例用于轨迹优化计算。你只需要保证_construct_copy方法完整拷贝了原系统的所有自定义参数(比如示例里的self.value),就不会出现转换后参数丢失的问题。
下面是一个带实际端口计算逻辑的完整TemplateSystem实现,可以直接参考:
import numpy as np from pydrake.systems.framework import LeafSystem_, TemplateSystem from pydrake.autodiffutils import AutoDiffXd @TemplateSystem.define("GainSystem_") def GainSystem_(T): class Impl(LeafSystem_[T]): def _construct(self, gain, converter=None): super().__init__(converter=converter) # 存储自定义参数 self.gain = gain # 声明输入输出端口 self.DeclareVectorInputPort("u", 1) self.DeclareVectorOutputPort("y", 1, self.CalcOutput) def _construct_copy(self, other, converter=None): # 拷贝原实例的参数,传入新实例构造流程 self._construct(other.gain, converter=converter) # 自定义输出计算逻辑,定义在Impl类内 def CalcOutput(self, context, output): u = self.get_input_port(0).Eval(context) output.SetAtIndex(0, self.gain * u[0]) return Impl # 默认float类型实例 GainSystem = GainSystem_[None] if __name__ == "__main__": # 测试float类型 sys_float = GainSystem(gain=2.0) print("Float type system created:", type(sys_float)) # 手动测试转换为AutoDiffXd类型(DirectCollocation内部就是该逻辑) sys_ad = sys_float.ToAutoDiffXd() print("AutoDiff type system created:", type(sys_ad)) print("Copied gain value:", sys_ad.gain)
该示例实现了一个固定增益系统,既支持直接实例化float类型做仿真,也可以直接传给DirectCollocation自动转换类型做优化,不需要额外修改代码。
内容的提问来源于stack exchange,提问作者jeh15

