Skip to content

Commit 93c66ed

Browse files
authored
Add python state-space examples (#2619)
1 parent 3a7f5f1 commit 93c66ed

File tree

4 files changed

+114
-2
lines changed

4 files changed

+114
-2
lines changed

source/docs/software/advanced-controls/state-space/state-space-debugging.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,13 @@ Reliable data of the :term:`system's <system>` :term:`state`\s, :term:`input`\s
3333
NetworkTableInstance::GetDefault().Flush();
3434
}
3535

36+
.. code-block:: python
37+
38+
from ntcore import NetworkTableInstance
39+
40+
def robotPeriodic(self):
41+
NetworkTableInstance.getDefault().flush()
42+
3643
Compensating for Input Lag
3744
--------------------------
3845
Often times, some sensor input data (i.e. velocity readings) may be delayed due to onboard filtering that smart motor controllers tend to perform. By default, LQR's K gain assumes no input delay, so introducing significant delay on the order of tens of milliseconds can cause instability. To combat this, the LQR's K gain can be reduced, trading off performance for stability. A code example for how to compensate for this latency in a mathematically rigorous manner is available :ref:`here <docs/software/advanced-controls/state-space/state-space-intro:LQR and Measurement Latency Compensation>`.

source/docs/software/advanced-controls/state-space/state-space-flywheel-walkthrough.rst

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ The goal of this tutorial is to provide "end-to-end" instructions on implementin
1313

1414
This tutorial is intended to be approachable for teams without a great deal of programming expertise. While the WPILib library offers significant flexibility in the manner in which its state-space control features are implemented, closely following the implementation outlined in this tutorial should provide teams with a basic structure which can be reused for a variety of state-space systems.
1515

16-
The full example is available in the state-space flywheel (`Java <https://github.com/wpilibsuite/allwpilib/blob/main/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceflywheel/Robot.java>`__/`C++ <https://github.com/wpilibsuite/allwpilib/blob/main/wpilibcExamples/src/main/cpp/examples/StateSpaceFlywheel/cpp/Robot.cpp>`__) and state-space flywheel system identification (`Java <https://github.com/wpilibsuite/allwpilib/blob/main/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceflywheelsysid/Robot.java>`__/`C++ <https://github.com/wpilibsuite/allwpilib/blob/main/wpilibcExamples/src/main/cpp/examples/StateSpaceFlywheelSysId/cpp/Robot.cpp>`__) example projects.
16+
The full example is available in the state-space flywheel (`Java <https://github.com/wpilibsuite/allwpilib/blob/main/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceflywheel/Robot.java>`__/`C++ <https://github.com/wpilibsuite/allwpilib/blob/main/wpilibcExamples/src/main/cpp/examples/StateSpaceFlywheel/cpp/Robot.cpp>`__/`Python <https://github.com/robotpy/examples/blob/main/StateSpaceFlywheel/robot.py>`__) and state-space flywheel system identification (`Java <https://github.com/wpilibsuite/allwpilib/blob/main/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceflywheelsysid/Robot.java>`__/`C++ <https://github.com/wpilibsuite/allwpilib/blob/main/wpilibcExamples/src/main/cpp/examples/StateSpaceFlywheelSysId/cpp/Robot.cpp>`__/`Python <https://github.com/robotpy/examples/blob/main/StateSpaceFlywheelSysId/robot.py>`__) example projects.
1717

1818
Why Use State-Space Control?
1919
----------------------------
@@ -91,13 +91,27 @@ The ``LinearSystem`` class contains methods for easily creating state-space syst
9191
:linenos:
9292
:lineno-start: 30
9393

94+
.. tab-item:: Python
95+
96+
.. remoteliteralinclude:: https://raw.githubusercontent.com/robotpy/examples/d89b0587a1e1111239728140466c7dc4324d4005/StateSpaceFlywheelSysId/robot.py
97+
:language: python
98+
:lines: 23-27
99+
:linenos:
100+
:lineno-start: 23
101+
102+
.. remoteliteralinclude:: https://raw.githubusercontent.com/robotpy/examples/d89b0587a1e1111239728140466c7dc4324d4005/StateSpaceFlywheelSysId/robot.py
103+
:language: python
104+
:lines: 37-48
105+
:linenos:
106+
:lineno-start: 37
107+
94108

95109
Modeling Using Flywheel Moment of Inertia and Gearing
96110
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
97111

98112
A flywheel can also be modeled without access to a physical robot, using information about the motors, gearing and flywheel's :term:`moment of inertia`. A full derivation of this model is presented in Section 12.3 of `Controls Engineering in FRC <https://file.tavsys.net/control/controls-engineering-in-frc.pdf>`__.
99113

100-
The ``LinearSystem`` class contains methods to easily create a model of a flywheel from the flywheel's motors, gearing and :term:`moment of inertia`. The moment of inertia can be calculated using :term:`CAD` software or using physics. The examples used here are detailed in the flywheel example project (`Java <https://github.com/wpilibsuite/allwpilib/tree/v2023.2.1/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceflywheel>`__/`C++ <https://github.com/wpilibsuite/allwpilib/blob/v2023.2.1/wpilibcExamples/src/main/cpp/examples/StateSpaceFlywheel/cpp/Robot.cpp>`__).
114+
The ``LinearSystem`` class contains methods to easily create a model of a flywheel from the flywheel's motors, gearing and :term:`moment of inertia`. The moment of inertia can be calculated using :term:`CAD` software or using physics. The examples used here are detailed in the flywheel example project (`Java <https://github.com/wpilibsuite/allwpilib/tree/v2023.2.1/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceflywheel>`__/`C++ <https://github.com/wpilibsuite/allwpilib/blob/v2023.2.1/wpilibcExamples/src/main/cpp/examples/StateSpaceFlywheel/cpp/Robot.cpp>`__/`Python <https://github.com/robotpy/examples/blob/main/StateSpaceFlywheel/robot.py>`__).
101115

102116
.. note:: For WPILib's state-space classes, gearing is written as output over input -- that is, if the flywheel spins slower than the motors, this number should be greater than one.
103117

@@ -129,6 +143,21 @@ The ``LinearSystem`` class contains methods to easily create a model of a flywhe
129143
:linenos:
130144
:lineno-start: 31
131145

146+
.. tab-item:: Python
147+
:sync: python
148+
149+
.. remoteliteralinclude:: https://raw.githubusercontent.com/robotpy/examples/d89b0587a1e1111239728140466c7dc4324d4005/StateSpaceFlywheel/robot.py
150+
:language: python
151+
:lines: 21-25
152+
:linenos:
153+
:lineno-start: 21
154+
155+
.. remoteliteralinclude:: https://raw.githubusercontent.com/robotpy/examples/d89b0587a1e1111239728140466c7dc4324d4005/StateSpaceFlywheel/robot.py
156+
:language: python
157+
:lines: 37-46
158+
:linenos:
159+
:lineno-start: 37
160+
132161
Kalman Filters: Observing Flywheel State
133162
----------------------------------------
134163

@@ -169,6 +198,15 @@ Because the feedback controller computes error using the :term:`x-hat` estimated
169198
:linenos:
170199
:lineno-start: 48
171200

201+
.. tab-item:: Python
202+
:sync: python
203+
204+
.. remoteliteralinclude:: https://raw.githubusercontent.com/robotpy/examples/d89b0587a1e1111239728140466c7dc4324d4005/StateSpaceFlywheel/robot.py
205+
:language: python
206+
:lines: 48-54
207+
:linenos:
208+
:lineno-start: 48
209+
172210
Because Kalman filters use our state-space model in the :ref:`docs/software/advanced-controls/state-space/state-space-observers:Predict step`, it is important that our model is as accurate as possible. One way to verify this is to record a flywheel's input voltage and velocity over time, and replay this data by calling only ``predict`` on the Kalman filter. Then, the kV and kA gains (or moment of inertia and other constants) can be adjusted until the model closely matches the recorded data.
173211

174212
.. todo:: do we need to elaborate on this^ more?
@@ -206,6 +244,15 @@ Much like ``SimpleMotorFeedforward`` can be used to generate feedforward voltage
206244
:linenos:
207245
:lineno-start: 54
208246

247+
.. tab-item:: Python
248+
:sync: python
249+
250+
.. remoteliteralinclude:: https://raw.githubusercontent.com/robotpy/examples/d89b0587a1e1111239728140466c7dc4324d4005/StateSpaceFlywheel/robot.py
251+
:language: python
252+
:lines: 56-66
253+
:linenos:
254+
:lineno-start: 56
255+
209256
Bringing it All Together: LinearSystemLoop
210257
------------------------------------------
211258

@@ -237,6 +284,15 @@ LinearSystemLoop combines our system, controller, and observer that we created e
237284
:linenos:
238285
:lineno-start: 71
239286

287+
.. tab-item:: Python
288+
:sync: python
289+
290+
.. remoteliteralinclude:: https://raw.githubusercontent.com/robotpy/examples/d89b0587a1e1111239728140466c7dc4324d4005/StateSpaceFlywheel/robot.py
291+
:language: python
292+
:lines: 68-71
293+
:linenos:
294+
:lineno-start: 68
295+
240296
Once we have our ``LinearSystemLoop``, the only thing left to do is actually run it. To do that, we'll periodically update our Kalman filter with our new encoder velocity measurements and apply new voltage commands to it. To do that, we first set the :term:`reference`, then ``correct`` with the current flywheel speed, ``predict`` the Kalman filter into the next timestep, and apply the inputs generated using ``getU``.
241297

242298
.. tab-set::
@@ -265,6 +321,15 @@ Once we have our ``LinearSystemLoop``, the only thing left to do is actually run
265321
:linenos:
266322
:lineno-start: 92
267323

324+
.. tab-item:: Python
325+
:sync: python
326+
327+
.. remoteliteralinclude:: https://raw.githubusercontent.com/robotpy/examples/d89b0587a1e1111239728140466c7dc4324d4005/StateSpaceFlywheel/robot.py
328+
:language: python
329+
:lines: 87-109
330+
:linenos:
331+
:lineno-start: 87
332+
268333
Angle Wrap with LQR
269334
-------------------
270335

@@ -283,3 +348,9 @@ Mechanisms with a continuous angle can have that angle wrapped by calling the co
283348
Eigen::Vector<double, 2> error = lqr.R() - x;
284349
error(0) = frc::AngleModulus(units::radian_t{error(0)}).value();
285350
Eigen::Vector<double, 2> u = lqr.K() * error;
351+
352+
.. code-block:: python
353+
354+
error = lqr.R() - x
355+
error[0] = wpimath.angleModulus(error[0])
356+
u = lqr.K() * error

source/docs/software/advanced-controls/state-space/state-space-intro.rst

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,25 @@ For example, we might use the following Q and R for an elevator system with posi
199199
// our dt
200200
0.020_s};
201201

202+
.. code-block:: python
203+
204+
from wpimath.controller import LinearQuadraticRegulator_2_1
205+
from wpimath.system.plant import LinearSystemId
206+
207+
208+
# Example system -- must be changed to match your robot.
209+
elevatorSystem = LinearSystemId.identifyPositionSystemMeters(5, 0.5)
210+
controller = LinearQuadraticRegulator_2_1(
211+
elevatorSystem,
212+
# q's elements
213+
(0.02, 0.4),
214+
# r's elements
215+
(12.0,),
216+
# our dt
217+
0.020,
218+
)
219+
220+
202221
LQR: example application
203222
^^^^^^^^^^^^^^^^^^^^^^^^
204223

@@ -254,6 +273,13 @@ The code below shows how to adjust the LQR controller's K gain for sensor input
254273
// input delay as arguments.
255274
controller.LatencyCompensate(elevatorSystem, 20_ms, 25_ms);
256275

276+
.. code-block:: python
277+
278+
# Adjust our LQR's controller for 25 ms of sensor input delay. We
279+
# provide the linear system, discretization timestep, and the sensor
280+
# input delay as arguments.
281+
controller.latencyCompensate(elevatorSystem, 0.020, 0.025)
282+
257283
Linearization
258284
-------------
259285

source/docs/software/advanced-controls/state-space/state-space-observers.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,14 @@ WPILib's Kalman Filter classes' constructors take a linear system, a vector of p
119119
:linenos:
120120
:lineno-start: 48
121121

122+
.. tab-item:: Python
123+
124+
.. remoteliteralinclude:: https://raw.githubusercontent.com/robotpy/examples/d89b0587a1e1111239728140466c7dc4324d4005/StateSpaceFlywheel/robot.py
125+
:language: python
126+
:lines: 48-54
127+
:linenos:
128+
:lineno-start: 48
129+
122130
Footnotes
123131
---------
124132

0 commit comments

Comments
 (0)