如何在从 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。据我所知,我们没有很多这方面的好例子。