0

I'm trying to load a system and then override its dynamics, such that classes like Simulator use the new dynamics instead. This is straightforward for systems declared in python, but doesn't seem to work for ones that are C++ bindings---for instance, the example below:

from pydrake.all import DirectCollocation, Simulator, VectorSystem
from pydrake.examples.pendulum import PendulumPlant

import numpy as np


class CustomVectorSystem(VectorSystem):
    def __init__(self):
        VectorSystem.__init__(self, 1, 0)
        self.DeclareContinuousState(2)

    def DoCalcVectorTimeDerivatives(self, context, u, x, x_dot):
        x_dot[0] += u


class OverwrittenVectorSystem(CustomVectorSystem):
    def __init__(self):
        CustomVectorSystem.__init__(self)

    def DoCalcVectorTimeDerivatives(self, context, u, x, x_dot):
        x_dot = np.zeros_like(x_dot)


class OverwrittenPendulumPlant(PendulumPlant):
    def __init__(self):
        PendulumPlant.__init__(self)

    def DoCalcTimeDerivatives(self, context, derivatives):
        derivatives = np.zeros_like(derivatives)


def run_sim_step(sys, t=0.1):
    context = sys.CreateDefaultContext()
    simulator = Simulator(sys)
    context = simulator.get_mutable_context()
    context.SetContinuousState([0, 1])
    context.FixInputPort(index=0, data=[1.0])
    simulator.AdvanceTo(t)
    return context.get_continuous_state_vector().get_value()


vector_sys = CustomVectorSystem()
overwritten_vector_sys = OverwrittenVectorSystem()

print(
    "CustomVectorSystem results are {}, and {} for overwrite".format(
        run_sim_step(vector_sys), run_sim_step(overwritten_vector_sys)
    )
)

pendulum = PendulumPlant()
overwritten_pendulum = OverwrittenPendulumPlant()

print(
    "PendulumPlant results are {}, and {} for overwrite".format(
        run_sim_step(pendulum), run_sim_step(overwritten_pendulum)
    )
)

returns the following:

CustomVectorSystem results are [1.4075 1.    ], and [0. 1.] for overwrite
PendulumPlant results are [0.11427129 1.24633296], and [0.11427129 1.24633296] for overwrite

And likewise for discrete systems. Is there any way to get a new method to override the dynamics in this case?

Joao Loula
  • 91
  • 4

1 Answers1

1

This is the behavior I would have expected -- the C++ systems that do allow overloading have some special "trampoline" logic in the pybind11 layer. But this also isn't a workflow that I had anticipated. Are you actually trying to use PendulumPlant, or some other base class? Is it really that much worse to overload from VectorSystem?

Note that, even in C++, PendulumPlant is marked final, and we didn't expect users to derive from it. (I don't think this has any bearing on your code; I just mention it as further evidence of our design thinking).

Russ Tedrake
  • 4,703
  • 1
  • 7
  • 10
  • Gotcha. The reason for this workflow is that I want to load a system, generate a bunch of data using its true dynamics and learn a model from it, and test it by running the same procedures as the real model. I thought overriding the dynamics would be better than creating a new custom system, since in the latter case I'd have to reimplement every functionality the original system had (PendulumPlant is a good example of this, having a custom PendulumState, a visualizer etc.) – Joao Loula Apr 30 '20 at 15:37
  • 1
    Ah, that helps me understand. If I were writing a system identification workflow like that, I would probably do something like we do for `Linearize` β€” it’s a method that takes the original system in and spits out a new system with the same input and output ports that can be used as a surrogate. If you are just trying to do it for one or two systems, I do think that overloading `LeafSystem`/`VectorSystem` is probably the best workflow. – Russ Tedrake May 01 '20 at 00:36