Search code examples
drake

Can I assume the running order of LeafSystem's CalcOutput function?


I am working on a LeafSystem like this:

class exampleLeafSystem(LeafSystem):
    def __init__(self, plant):
        self._plant = plant
        self._plant_context = plant.CreateDefaultContext()

        self.DeclareVectorIutputPort("q_v", BasicVector(6))
        self.DeclareVectorOutputPort("tau", BasicVector(3), self.TauCalcOutput)
        self.DeclareVectorOutputPort("xe", BasicVector(3), self.xeCalcOutput)
    
    def TauCalcOutput(self, context, output):
        q_v = self.get_input_port(0).Eval(context)
        self._plant.SetPositionsAndVelocities(self._plant_context, q_v)

        # Do some calculation with self._plant and self._plant_context to get the output
        output.SetFromVector(tau)

    def xeCalcOutput(self, context, output):
        q_v = self.get_input_port(0).Eval(context)
        self._plant.SetPositionsAndVelocities(self._plant_context, q_v)

        # Do some calculation with self._plant and self._plant_context to get the output
        output.SetFromVector(xe)

In the two methods TauCalcOutput and xeCalcOutput here, I need frist update the MultibodyPlant's state information and then do the calculation to compute the output. However, since I do not know the order of the two CalcOutput method being called, in order for this two method to use the newest state information, I have to write

q_v = self.get_input_port(0).Eval(context)
self._plant.SetPositionsAndVelocities(self._plant_context, q_v)

in both methods, which seems a bit unnecessary. If I can assume the order of the CalcOutput methods being runned, for example, say that TauCalcOutput always runs first, then I can only have

q_v = self.get_input_port(0).Eval(context)
self._plant.SetPositionsAndVelocities(self._plant_context, q_v)

in TauCalcOutput method, without worrying that XeCalcOutput method will use MultibodyPlant's state information that is one step lagged.

So my question is that is there a specific order for the CalcOutput methods being called?


Solution

  • No. The contract is that output ports can be called in any order at any time, and are only called when they are evaluated (e.g. by a downstream system). The order that they will be called will depend on the other systems in the Diagram; they might not all get called during a single simulation step (e.g. if one system is consumed by a discrete time system with time step 0.1, and another by time step 0.2), or may not be called at all if they are not connected. Users can even call get_output_port().Eval() manually.

    For a general approach to avoiding duplicate computation in output ports, you should store the result of the shared computation in the Context, either as state or as a "cache entry". See DeclareCacheEntry for more details.

    For this workflow, specifically, perhaps the simplest solution is to check whether the positions and velocities are already set to the same value, just to avoid repeating the kinematics evaluation, for instance as you see here: https://github.com/RobotLocomotion/drake/blob/6e6e37ffa677362245773f13c0628f0042b47414/multibody/inverse_kinematics/kinematic_constraint_utilities.cc#L47-L54