Skip to content

Commit 8eebd8f

Browse files
Update scenario to use StatefulSysModel
1 parent e23bf5f commit 8eebd8f

1 file changed

Lines changed: 31 additions & 16 deletions

File tree

examples/mujoco/scenarioDeployPanels.py

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,13 @@
2222
#. ``examples/mujoco/scenarioArmWithThrusters.py``
2323
2424
This script demonstrates how to simulate a spacecraft with solar panels deployed
25-
using a Proportional-Derivative (PD) controller. This script uses the MuJoCo-based
26-
:ref:`DynamicObject<dynamicObject>` :ref:`MJScene<MJScene>`.
25+
using a Proportional-Integral-Derivative (PID) controller. This script uses the
26+
MuJoCo-based :ref:`DynamicObject<dynamicObject>` :ref:`MJScene<MJScene>`.
2727
2828
In ``mujoco/scenarioArmWithThrusters.py``, we saw how we can constrain joints to
2929
follow a specific angle by letting the dynamic engine figure out and apply
3030
the necessary torques. In this script, we are controlling the joints using a
31-
PD controller. This is a more adequate simulation setup when you want to simulate
31+
PID controller. This is a more adequate simulation setup when you want to simulate
3232
or design the control system for these joints. It is also generally more
3333
computationally efficient than letting the dynamic engine figure out the torques.
3434
@@ -45,11 +45,13 @@
4545
plots and in the 3D visualization how the panels never get deployed over their
4646
joint limit.
4747
48-
The deployment of the panels is controlled using a Proportional-Derivative (PD)
49-
controller. The desired position and velocity profiles for the joints are
48+
The deployment of the panels is controlled using an analog PID controller.
49+
The desired position and velocity profiles for the joints are
5050
generated using a trapezoidal/triangular velocity profile. These profiles are
51-
then used as inputs to the PD controller, which computes the torque required to achieve
52-
the desired motion.
51+
then used as inputs to the PID controller, which computes the torque required to achieve
52+
the desired motion. Note that the controller class extends ``StatefulSysModel``,
53+
instead of ``SysModel``, since we need to register the integral error as a
54+
continuous state.
5355
5456
The simulation is run for 80 minutes and the state of the system is recorded.
5557
The desired and achieved joint angles, as well as the torque applied to each
@@ -65,7 +67,7 @@
6567
from Basilisk.simulation import mujoco
6668
from Basilisk.utilities import SimulationBaseClass
6769
from Basilisk.utilities import macros
68-
from Basilisk.architecture import sysModel
70+
from Basilisk.simulation import StatefulSysModel
6971
from Basilisk.architecture import messaging
7072
from Basilisk.simulation import svIntegrators
7173

@@ -252,7 +254,7 @@ def run(initialSpin: bool = False, showPlots: bool = False, visualize: bool = Fa
252254
# the measured position and velocity of the joint (in this case the
253255
# exact values are used, but in a real system these may be the product
254256
# of a sensor), and the output is the torque to be applied to the joint.
255-
pdController = PDController()
257+
pdController = PIDController()
256258
pdController.ModelTag = f"{actuatorName}_controller"
257259

258260
# Connect the interpolators to the PD controller for the desired
@@ -385,13 +387,17 @@ def run(initialSpin: bool = False, showPlots: bool = False, visualize: bool = Fa
385387

386388
# The following is an example of a Python-based SysModel that
387389
# can be added to the dynamics task of a MJScene.
388-
class PDController(sysModel.SysModel):
390+
class PIDController(StatefulSysModel.StatefulSysModel):
389391
"""
390-
A Proportional-Derivative (PD) Controller class for controlling joint states.
392+
A Proportional-Integral-Derivative (PID) Controller class for controlling joint states.
393+
394+
This models an analog PID controller, which means that its output evolves in continuous
395+
time, not discrete time. Thus, it should be used within the dynamics task of ``MJScene``.
391396
392397
Attributes:
393-
K (float): Proportional gain.
394-
P (float): Derivative gain.
398+
K_p (float): Proportional gain.
399+
K_d (float): Derivative gain.
400+
K_i (float): Integral gain.
395401
396402
measuredInMsg (messaging.ScalarJointStateMsgReader): Reader for the measured joint state.
397403
desiredInMsg (messaging.ScalarJointStateMsgReader): Reader for the desired joint state.
@@ -404,8 +410,9 @@ class PDController(sysModel.SysModel):
404410
def __init__(self, *args: Any):
405411
"""Initialize"""
406412
super().__init__(*args)
407-
self.K = 0.1
408-
self.P = 0.002
413+
self.K_p = 0.1
414+
self.K_d = 0.002
415+
self.K_i = 0.0001
409416

410417
self.measuredInMsg = messaging.ScalarJointStateMsgReader()
411418
self.desiredInMsg = messaging.ScalarJointStateMsgReader()
@@ -415,15 +422,23 @@ def __init__(self, *args: Any):
415422

416423
self.outputOutMsg = messaging.SingleActuatorMsg()
417424

425+
def registerStates(self, registerer: StatefulSysModel.DynParamRegisterer):
426+
self.integralErrorState = registerer.registerState(1, 1, "integralError")
427+
self.integralErrorState.setState([[0]]) # explicitely zero initialize
428+
418429
def UpdateState(self, CurrentSimNanos: int):
419430
"""Computes the control command from the measured and desired
420431
joint position and velocity."""
421432
# Compute the error in the state and its derivative
422433
stateError = self.desiredInMsg().state - self.measuredInMsg().state
423434
stateDotError = self.desiredDotInMsg().state - self.measuredDotInMsg().state
435+
stateIntegralError = self.integralErrorState.getState()[0][0]
424436

425437
# Compute the control output
426-
control_output = self.K * stateError + self.P * stateDotError
438+
control_output = self.K_p * stateError + self.K_d * stateDotError + self.K_i * stateIntegralError
439+
440+
# Set the derivative of the integral error inner state
441+
self.integralErrorState.setDerivative([[stateError]])
427442

428443
# Write the control output to the output message
429444
payload = messaging.SingleActuatorMsgPayload()

0 commit comments

Comments
 (0)