Skip to content

Commit 9d4e01e

Browse files
DOC: split controllers into a dedicated user-guide page
Addresses the review suggestion to separate controllers from air brakes in the documentation. The generic controller material — the controller-function signature and argument list, the 6/7/8-parameter backward-compatibility notes, the environment argument, and the discrete vs. continuous controller behaviour — now lives in a new docs/user/controllers.rst page. airbrakes.rst keeps the concrete air-brakes example (which still defines its own controller_function so its jupyter-execute cells run) and cross-references the new page. The discrete-vs-continuous-controllers label moved to the new page; existing refs to it resolve unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent fced116 commit 9d4e01e

3 files changed

Lines changed: 149 additions & 103 deletions

File tree

docs/user/airbrakes.rst

Lines changed: 16 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -106,52 +106,12 @@ Defining the Controller Function
106106

107107
Lets start by defining a very simple controller function.
108108

109-
The ``controller_function`` must take in the following arguments, in this
110-
order:
111-
112-
1. ``time`` (float): The current simulation time in seconds.
113-
2. ``sampling_rate`` (float or ``None``): The rate at which the controller
114-
function is called, measured in Hertz (Hz). It is ``None`` for continuous
115-
controllers, so guard any ``1 / sampling_rate`` computation against ``None``.
116-
3. ``state`` (list): The state vector of the simulation. The state
117-
is a list containing the following values, in this order:
118-
119-
- ``x``: The x position of the rocket, in meters.
120-
- ``y``: The y position of the rocket, in meters.
121-
- ``z``: The z position of the rocket, in meters.
122-
- ``v_x``: The x component of the velocity of the rocket, in meters per
123-
second.
124-
- ``v_y``: The y component of the velocity of the rocket, in meters per
125-
second.
126-
- ``v_z``: The z component of the velocity of the rocket, in meters per
127-
second.
128-
- ``e0``: The first component of the quaternion representing the rotation
129-
of the rocket.
130-
- ``e1``: The second component of the quaternion representing the rotation
131-
of the rocket.
132-
- ``e2``: The third component of the quaternion representing the rotation
133-
of the rocket.
134-
- ``e3``: The fourth component of the quaternion representing the rotation
135-
of the rocket.
136-
- ``w_x``: The x component of the angular velocity of the rocket, in
137-
radians per second.
138-
- ``w_y``: The y component of the angular velocity of the rocket, in
139-
radians per second.
140-
- ``w_z``: The z component of the angular velocity of the rocket, in
141-
radians per second.
142-
143-
4. ``state_history`` (list): A record of the rocket's state at each
144-
step throughout the simulation. The state_history is organized as
145-
a list of lists, with each sublist containing a state vector. The
146-
last item in the list always corresponds to the previous state
147-
vector, providing a chronological sequence of the rocket's
148-
evolving states.
149-
5. ``observed_variables`` (list): A list containing the variables that
150-
the controller function returns. The return of each controller
151-
function call is appended to the observed_variables list. The
152-
initial value in the first step of the simulation of this list is
153-
provided by the ``initial_observed_variables`` argument.
154-
6. ``air_brakes`` (AirBrakes): The ``AirBrakes`` instance being controlled.
109+
The ``controller_function`` receives information about the simulation at the
110+
current time step and sets the air brakes' deployment level. See
111+
:ref:`controllers` for the full description of its arguments and call signature.
112+
For air brakes, the controlled object (the ``air_brakes`` argument) is the
113+
:class:`rocketpy.AirBrakes` instance, whose ``deployment_level`` the function
114+
sets.
155115

156116
Our example ``controller_function`` will deploy the air brakes when the rocket
157117
reaches 1500 meters above the ground. The deployment level will be function of the
@@ -227,22 +187,6 @@ Lets define the controller function:
227187

228188
.. note::
229189

230-
- The ``controller_function`` accepts 6, 7, or 8 parameters for backward
231-
compatibility:
232-
233-
* **6 parameters** (original): ``time``, ``sampling_rate``, ``state``,
234-
``state_history``, ``observed_variables``, ``air_brakes``
235-
* **7 parameters** (with sensors): adds ``sensors`` as the 7th parameter
236-
* **8 parameters** (with environment): adds ``sensors`` and ``environment``
237-
as the 7th and 8th parameters
238-
239-
- The **environment parameter** provides access to atmospheric conditions
240-
(wind, temperature, pressure, elevation) without relying on global variables.
241-
This enables proper serialization of rockets with air brakes and improves
242-
code modularity. Available methods include ``environment.elevation``,
243-
``environment.wind_velocity_x(altitude)``, ``environment.wind_velocity_y(altitude)``,
244-
``environment.speed_of_sound(altitude)``, and others.
245-
246190
- The code inside the ``controller_function`` can be as complex as needed.
247191
Anything can be implemented inside the function, including filters,
248192
apogee prediction, and any controller logic.
@@ -252,15 +196,10 @@ Lets define the controller function:
252196
0 or higher than 1. If you want to disable this feature, set ``clamp`` to
253197
``False`` when defining the air brakes.
254198

255-
- Anything can be returned by the ``controller_function``. The returned
256-
values will be saved in the ``observed_variables`` list at every time step
257-
and can then be accessed by the ``controller_function`` at the next time
258-
step. The saved values can also be accessed after the simulation is
259-
finished. This is useful for debugging and for plotting the results.
260-
261-
- The ``controller_function`` can also be defined in a separate file and
262-
imported into the simulation script. This includes importing a ``c`` or
263-
``cpp`` code into Python.
199+
- See :ref:`controllers` for the controller-function signature (including the
200+
6/7/8-parameter forms and the ``environment`` argument), how returned
201+
values are stored in ``observed_variables``, and discrete vs. continuous
202+
controllers.
264203

265204

266205
Defining the Drag Coefficient
@@ -375,40 +314,14 @@ controller function. If you want to disable this feature, set ``clamp`` to
375314
For more information on the :class:`rocketpy.AirBrakes` class
376315
initialization, see :class:`rocketpy.AirBrakes.__init__` section.
377316

378-
.. _discrete-vs-continuous-controllers:
379-
380-
Discrete vs. Continuous Controllers
381-
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
382-
383-
The ``sampling_rate`` argument determines *when* the controller function is
384-
called during the flight simulation:
385-
386-
- **Discrete controller** (``sampling_rate`` set to a number, e.g. ``10``):
387-
the controller function is called at fixed intervals of ``1 / sampling_rate``
388-
seconds. This mirrors a real flight computer that reads its sensors and
389-
updates its actuators at a fixed frequency, and is the recommended choice
390-
when you want the simulation to reflect the actual control-loop rate of your
391-
hardware.
392-
- **Continuous controller** (``sampling_rate=None``): the controller function
393-
is called at *every* solver step of the numerical integrator. Use this when
394-
you want the control law to act as a continuous function of the state rather
395-
than a sampled one (for example, when validating a control model
396-
analytically).
397-
398-
.. warning::
399-
400-
For continuous controllers, ``sampling_rate`` is passed to your controller
401-
function as ``None``. Any computation such as ``1 / sampling_rate`` (a
402-
common pattern for rate-limiting deployment, as shown above) must guard
403-
against ``None`` to avoid a ``TypeError``.
404-
405317
.. note::
406318

407-
Discrete controllers add their sampling instants as time nodes to the
408-
simulation, so remember to set ``time_overshoot=False`` in the ``Flight``
409-
(see below) to make the integrator stop exactly at those instants.
410-
Continuous controllers do not add time nodes; they are evaluated on the
411-
integrator's own steps.
319+
Because our controller uses ``sampling_rate=10``, it is a **discrete**
320+
controller and adds its sampling instants as time nodes to the simulation.
321+
Remember to set ``time_overshoot=False`` in the ``Flight`` (see below) so the
322+
integrator stops exactly at those instants. See
323+
:ref:`discrete-vs-continuous-controllers` for the difference between discrete
324+
and continuous controllers.
412325

413326
Simulating a Flight
414327
-------------------

docs/user/controllers.rst

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
.. _controllers:
2+
3+
Controllers
4+
===========
5+
6+
RocketPy can simulate active, in-flight control systems — such as air brakes or
7+
other actuators — through a *controller function* that you attach to a
8+
controllable object (for example, an :class:`rocketpy.AirBrakes` added with
9+
``Rocket.add_air_brakes``). During the flight simulation, RocketPy calls your
10+
controller function at the appropriate times, letting it read the current
11+
simulation state and command the actuator.
12+
13+
This page describes the controller-function interface and how its call timing is
14+
governed. For a complete, runnable example that puts these pieces together, see
15+
the :doc:`Air Brakes Example <airbrakes>`.
16+
17+
The Controller Function
18+
-----------------------
19+
20+
A controller function receives information about the simulation up to the
21+
current time step and sets the state of the controlled object. It must take in
22+
the following arguments, in this order:
23+
24+
1. ``time`` (float): The current simulation time in seconds.
25+
2. ``sampling_rate`` (float or ``None``): The rate at which the controller
26+
function is called, measured in Hertz (Hz). It is ``None`` for continuous
27+
controllers, so guard any ``1 / sampling_rate`` computation against ``None``.
28+
3. ``state`` (list): The state vector of the simulation. The state
29+
is a list containing the following values, in this order:
30+
31+
- ``x``: The x position of the rocket, in meters.
32+
- ``y``: The y position of the rocket, in meters.
33+
- ``z``: The z position of the rocket, in meters.
34+
- ``v_x``: The x component of the velocity of the rocket, in meters per
35+
second.
36+
- ``v_y``: The y component of the velocity of the rocket, in meters per
37+
second.
38+
- ``v_z``: The z component of the velocity of the rocket, in meters per
39+
second.
40+
- ``e0``: The first component of the quaternion representing the rotation
41+
of the rocket.
42+
- ``e1``: The second component of the quaternion representing the rotation
43+
of the rocket.
44+
- ``e2``: The third component of the quaternion representing the rotation
45+
of the rocket.
46+
- ``e3``: The fourth component of the quaternion representing the rotation
47+
of the rocket.
48+
- ``w_x``: The x component of the angular velocity of the rocket, in
49+
radians per second.
50+
- ``w_y``: The y component of the angular velocity of the rocket, in
51+
radians per second.
52+
- ``w_z``: The z component of the angular velocity of the rocket, in
53+
radians per second.
54+
55+
4. ``state_history`` (list): A record of the rocket's state at each
56+
step throughout the simulation. The state_history is organized as
57+
a list of lists, with each sublist containing a state vector. The
58+
last item in the list always corresponds to the previous state
59+
vector, providing a chronological sequence of the rocket's
60+
evolving states.
61+
5. ``observed_variables`` (list): A list containing the variables that
62+
the controller function returns. The return of each controller
63+
function call is appended to the observed_variables list. The
64+
initial value in the first step of the simulation of this list is
65+
provided by the ``initial_observed_variables`` argument.
66+
6. The **controlled object** (e.g. ``air_brakes``): the instance being
67+
controlled, whose attributes the function sets (for example,
68+
``air_brakes.deployment_level``).
69+
70+
.. note::
71+
72+
- The controller function accepts 6, 7, or 8 parameters for backward
73+
compatibility:
74+
75+
* **6 parameters** (original): ``time``, ``sampling_rate``, ``state``,
76+
``state_history``, ``observed_variables``, and the controlled object.
77+
* **7 parameters** (with sensors): adds ``sensors`` as the 7th parameter.
78+
* **8 parameters** (with environment): adds ``sensors`` and ``environment``
79+
as the 7th and 8th parameters.
80+
81+
- The **environment parameter** provides access to atmospheric conditions
82+
(wind, temperature, pressure, elevation) without relying on global
83+
variables. This enables proper serialization of rockets with controllers
84+
and improves code modularity. Available methods include
85+
``environment.elevation``, ``environment.wind_velocity_x(altitude)``,
86+
``environment.wind_velocity_y(altitude)``,
87+
``environment.speed_of_sound(altitude)``, and others.
88+
89+
- Anything can be returned by the controller function. The returned values
90+
are saved in the ``observed_variables`` list at every time step and can
91+
then be accessed by the controller function at the next time step. The
92+
saved values can also be accessed after the simulation is finished, which
93+
is useful for debugging and for plotting the results.
94+
95+
- The controller function can also be defined in a separate file and
96+
imported into the simulation script. This includes importing ``c`` or
97+
``cpp`` code into Python.
98+
99+
.. _discrete-vs-continuous-controllers:
100+
101+
Discrete vs. Continuous Controllers
102+
-----------------------------------
103+
104+
The ``sampling_rate`` argument determines *when* the controller function is
105+
called during the flight simulation:
106+
107+
- **Discrete controller** (``sampling_rate`` set to a number, e.g. ``10``):
108+
the controller function is called at fixed intervals of ``1 / sampling_rate``
109+
seconds. This mirrors a real flight computer that reads its sensors and
110+
updates its actuators at a fixed frequency, and is the recommended choice
111+
when you want the simulation to reflect the actual control-loop rate of your
112+
hardware.
113+
- **Continuous controller** (``sampling_rate=None``): the controller function
114+
is called at *every* solver step of the numerical integrator. Use this when
115+
you want the control law to act as a continuous function of the state rather
116+
than a sampled one (for example, when validating a control model
117+
analytically).
118+
119+
.. warning::
120+
121+
For continuous controllers, ``sampling_rate`` is passed to your controller
122+
function as ``None``. Any computation such as ``1 / sampling_rate`` (a
123+
common pattern for rate-limiting deployment) must guard against ``None`` to
124+
avoid a ``TypeError``.
125+
126+
.. note::
127+
128+
Discrete controllers add their sampling instants as time nodes to the
129+
simulation, so remember to set ``time_overshoot=False`` in the ``Flight``
130+
to make the integrator stop exactly at those instants. Continuous
131+
controllers do not add time nodes; they are evaluated on the integrator's
132+
own steps.

docs/user/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ RocketPy's User Guide
2727
Flight Comparator Class <flight_comparator.rst>
2828
Parachute Triggers (Acceleration-Based) <parachute_triggers.rst>
2929
Deployable Payload <deployable.rst>
30+
Controllers <controllers.rst>
3031
Air Brakes Example <airbrakes.rst>
3132
../notebooks/sensors.ipynb
3233
../matlab/matlab.rst

0 commit comments

Comments
 (0)