如何在从 LeafSystem 派生的 class 中正确重置 ContinuousState?
How to properly reset the ContinuousState in a class derived from LeafSystem?
我想编写一个从 LeafSystem
派生的连续时间系统,如果满足某些条件,它可以将其连续状态重置为其他值。但是,该系统并不像我预期的那样工作。为了找出原因,我实现了一个简单的多步积分器系统,如下所示:
class MultiStepIntegrator(LeafSystem):
def __init__(self):
LeafSystem.__init__(self)
self.state_index = self.DeclareContinuousState(1)
self.DeclareStateOutputPort("x", self.state_index)
self.flag_1 = True
self.flag_2 = True
def reset_state(self, context, value):
state = context.get_mutable_continuous_state_vector()
state.SetFromVector(value)
def DoCalcTimeDerivatives(self, context, derivatives):
t = context.get_time()
if t < 2.0:
V = [1]
elif t < 4.0:
if self.flag_1:
self.reset_state(context, [0])
print("Have done the first reset")
self.flag_1 = False
V = [1]
else:
if self.flag_2:
self.reset_state(context, [0])
print("Have done the second reset")
self.flag_2 = False
V = [-1]
derivatives.get_mutable_vector().SetFromVector(V)
我对这个系统的期望是它会给我一个分段的、不连续的轨迹。鉴于我将状态最初设置为 0,首先状态将从 0 变为 2 $t \in [0,2]$,然后 agian 从 0 变为 2 $t \in [2,4]$ 和然后从 0 到 -2 为 $t \in [4,6]$.
然后我模拟这个系统,并用
绘制日志记录
builder = DiagramBuilder()
plant, scene_graph = AddMultibodyPlantSceneGraph(builder, 1e-4)
plant.Finalize()
integrator = builder.AddSystem(MultiStepIntegrator())
state_logger = LogVectorOutput(integrator.get_output_port(), builder, 1e-2)
diagram = builder.Build()
simulator = Simulator(diagram)
log_state = state_logger.FindLog(context)
fig = plt.figure()
t = log_state.sample_times()
plt.plot(t, log_state.data()[0, :])
fig.set_size_inches(10, 6)
plt.tight_layout()
似乎重置从未发生过。但是我确实看到两个日志表明重置已完成:
Have done the first reset
Have done the second reset
这里发生了什么?是否在幕后做了一些检查 ContinuousState
不能跳转(顾名思义)?在满足某些条件的情况下如何重置状态值?
非常感谢您的帮助!
在 DoCalcTimeDerivatives
中,context
是一个 const (input-only) 参数。它不能被修改。 DoCalcTimeDerivatives
唯一能做的就是输出导数,让积分器对连续状态进行积分。
并非所有积分器都使用 fixed-size 时间步长。在决定使用什么步长之前,有些人可能需要多次评估梯度。因此,dx/dt 计算有任何 side-effects 是不合理的。它必须是纯函数,其唯一结果是报告 dx/dt.
要通过纯集成以外的方式更改连续状态值,系统需要使用“无限制更新”事件。该事件可以改变状态的任何和所有元素(包括连续状态)。
如果不连续的时间是周期性的(即使某些事件不改变状态),您可以使用DeclarePeriodicUnrestrictedUpdateEvent来声明更新计算。
如果根据见证函数发生不连续性,请参阅 bouncing_ball or rimless_wheel or compass_gait 示例。
如果您需要针对不连续事件的通用(定制)触发计划,则需要覆盖 DoCalcNextUpdateTime 以手动注入下一个事件计时,例如 LcmSubscriberSystem。据我所知,我们没有很多这方面的好例子。
我想编写一个从 LeafSystem
派生的连续时间系统,如果满足某些条件,它可以将其连续状态重置为其他值。但是,该系统并不像我预期的那样工作。为了找出原因,我实现了一个简单的多步积分器系统,如下所示:
class MultiStepIntegrator(LeafSystem):
def __init__(self):
LeafSystem.__init__(self)
self.state_index = self.DeclareContinuousState(1)
self.DeclareStateOutputPort("x", self.state_index)
self.flag_1 = True
self.flag_2 = True
def reset_state(self, context, value):
state = context.get_mutable_continuous_state_vector()
state.SetFromVector(value)
def DoCalcTimeDerivatives(self, context, derivatives):
t = context.get_time()
if t < 2.0:
V = [1]
elif t < 4.0:
if self.flag_1:
self.reset_state(context, [0])
print("Have done the first reset")
self.flag_1 = False
V = [1]
else:
if self.flag_2:
self.reset_state(context, [0])
print("Have done the second reset")
self.flag_2 = False
V = [-1]
derivatives.get_mutable_vector().SetFromVector(V)
我对这个系统的期望是它会给我一个分段的、不连续的轨迹。鉴于我将状态最初设置为 0,首先状态将从 0 变为 2 $t \in [0,2]$,然后 agian 从 0 变为 2 $t \in [2,4]$ 和然后从 0 到 -2 为 $t \in [4,6]$.
然后我模拟这个系统,并用
绘制日志记录builder = DiagramBuilder()
plant, scene_graph = AddMultibodyPlantSceneGraph(builder, 1e-4)
plant.Finalize()
integrator = builder.AddSystem(MultiStepIntegrator())
state_logger = LogVectorOutput(integrator.get_output_port(), builder, 1e-2)
diagram = builder.Build()
simulator = Simulator(diagram)
log_state = state_logger.FindLog(context)
fig = plt.figure()
t = log_state.sample_times()
plt.plot(t, log_state.data()[0, :])
fig.set_size_inches(10, 6)
plt.tight_layout()
似乎重置从未发生过。但是我确实看到两个日志表明重置已完成:
Have done the first reset
Have done the second reset
这里发生了什么?是否在幕后做了一些检查 ContinuousState
不能跳转(顾名思义)?在满足某些条件的情况下如何重置状态值?
非常感谢您的帮助!
在 DoCalcTimeDerivatives
中,context
是一个 const (input-only) 参数。它不能被修改。 DoCalcTimeDerivatives
唯一能做的就是输出导数,让积分器对连续状态进行积分。
并非所有积分器都使用 fixed-size 时间步长。在决定使用什么步长之前,有些人可能需要多次评估梯度。因此,dx/dt 计算有任何 side-effects 是不合理的。它必须是纯函数,其唯一结果是报告 dx/dt.
要通过纯集成以外的方式更改连续状态值,系统需要使用“无限制更新”事件。该事件可以改变状态的任何和所有元素(包括连续状态)。
如果不连续的时间是周期性的(即使某些事件不改变状态),您可以使用DeclarePeriodicUnrestrictedUpdateEvent来声明更新计算。
如果根据见证函数发生不连续性,请参阅 bouncing_ball or rimless_wheel or compass_gait 示例。
如果您需要针对不连续事件的通用(定制)触发计划,则需要覆盖 DoCalcNextUpdateTime 以手动注入下一个事件计时,例如 LcmSubscriberSystem。据我所知,我们没有很多这方面的好例子。