-
-
Notifications
You must be signed in to change notification settings - Fork 248
ENH: acceleration data to trigger parachutes #911
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
2575950
7ec7ac9
18aab57
0932277
916b0fc
8e88fa8
e84df40
a7f5c3a
ad7e6d0
8f65022
d45b3a5
6d79d62
5619b3a
c66cffc
56c1e51
02d344d
00e94b9
a0cb73d
aba648b
6beab3c
aa89d50
6a9795d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,307 @@ | ||
| Acceleration-Based Parachute Triggers | ||
| ====================================== | ||
|
|
||
| RocketPy supports advanced parachute deployment logic using acceleration data | ||
| from simulated IMU (Inertial Measurement Unit) sensors. This enables realistic | ||
| avionics algorithms that mimic real-world flight computers. | ||
|
|
||
| Overview | ||
| -------- | ||
|
|
||
| Traditional parachute triggers rely on altitude and velocity. Acceleration-based | ||
| triggers provide additional capabilities: | ||
|
|
||
| - **Motor burnout detection**: Implement as a custom trigger with mission-specific thresholds | ||
| - **Apogee detection**: Use acceleration and velocity together for precise apogee | ||
| - **Freefall detection**: Identify ballistic coasting phases | ||
| - **Liftoff detection**: Confirm motor ignition via high acceleration | ||
|
|
||
| These triggers can optionally include sensor noise to simulate realistic IMU | ||
| behavior, making simulations more representative of actual flight conditions. | ||
|
|
||
| Built-in Trigger | ||
| ---------------- | ||
|
|
||
| RocketPy provides one built-in trigger string: | ||
|
|
||
| Apogee Detection | ||
| ~~~~~~~~~~~~~~~~ | ||
|
|
||
| Detects apogee when the rocket starts descending. | ||
|
|
||
| .. code-block:: python | ||
|
|
||
| main = Parachute( | ||
| name="Main", | ||
| cd_s=10.0, | ||
| trigger="apogee", | ||
| sampling_rate=100, | ||
| lag=0.5 | ||
| ) | ||
|
|
||
| **Detection criteria:** | ||
| - Vertical velocity becomes negative (descent starts) | ||
|
|
||
| Custom Triggers | ||
| --------------- | ||
|
|
||
| You can create custom triggers that use acceleration data: | ||
|
|
||
| Motor Burnout Trigger (Custom Example) | ||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
|
|
||
| Motor burnout is highly mission-dependent, so it is recommended as a custom | ||
| trigger with user-defined thresholds: | ||
|
|
||
| .. code-block:: python | ||
|
|
||
| def burnout_trigger_factory( | ||
| min_height=5.0, | ||
| min_vz=0.5, | ||
| az_threshold=-8.0, | ||
| total_acc_threshold=2.0, | ||
| ): | ||
| def burnout_trigger(_pressure, height, state_vector, u_dot): | ||
| if u_dot is None or len(u_dot) < 6: | ||
| return False | ||
|
|
||
| ax, ay, az = u_dot[3], u_dot[4], u_dot[5] | ||
| total_acc = (ax**2 + ay**2 + az**2) ** 0.5 | ||
| vz = state_vector[5] if len(state_vector) > 5 else 0 | ||
|
|
||
| if height < min_height or vz <= min_vz: | ||
| return False | ||
|
|
||
| return az < az_threshold or total_acc < total_acc_threshold | ||
|
|
||
| return burnout_trigger | ||
|
|
||
| drogue = Parachute( | ||
| name="Drogue", | ||
| cd_s=1.0, | ||
| trigger=burnout_trigger_factory( | ||
| min_height=10.0, | ||
| min_vz=2.0, | ||
| az_threshold=-10.0, | ||
| total_acc_threshold=3.0, | ||
| ), | ||
| sampling_rate=100, | ||
| lag=1.5, | ||
| ) | ||
|
|
||
|
Comment on lines
+50
to
+91
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add some description of what is the logic of the trigger here. For example, "to check if burn out state has been achieved, the accelerations are checked.... |
||
| Apogee by Acceleration (Custom Example) | ||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
|
|
||
| .. code-block:: python | ||
|
|
||
| def apogee_acc_trigger(_pressure, _height, state_vector, u_dot): | ||
| vz = state_vector[5] | ||
| az = u_dot[5] | ||
| return abs(vz) < 1.0 and az < -0.1 | ||
|
Comment on lines
+92
to
+100
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add some description of what is the logic of the trigger here. |
||
|
|
||
| Free-fall (Custom Example) | ||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
|
|
||
| .. code-block:: python | ||
|
|
||
| def freefall_trigger(_pressure, height, state_vector, u_dot): | ||
| ax, ay, az = u_dot[3], u_dot[4], u_dot[5] | ||
| total_acc = (ax**2 + ay**2 + az**2) ** 0.5 | ||
| vz = state_vector[5] | ||
| return height > 5.0 and vz < -0.2 and total_acc < 11.5 | ||
|
Comment on lines
+102
to
+111
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add some description of what is the logic of the trigger here. |
||
|
|
||
| Liftoff by Acceleration (Custom Example) | ||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
|
|
||
| .. code-block:: python | ||
|
|
||
| def liftoff_trigger(_pressure, _height, _state_vector, u_dot): | ||
| ax, ay, az = u_dot[3], u_dot[4], u_dot[5] | ||
| total_acc = (ax**2 + ay**2 + az**2) ** 0.5 | ||
| return total_acc > 15.0 | ||
|
|
||
| .. code-block:: python | ||
|
|
||
| def custom_trigger(pressure, height, state_vector, u_dot): | ||
| """ | ||
| Custom trigger using acceleration data. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| pressure : float | ||
| Atmospheric pressure in Pa (with optional noise) | ||
| height : float | ||
| Height above ground level in meters (with optional noise) | ||
| state_vector : array | ||
| [x, y, z, vx, vy, vz, e0, e1, e2, e3, wx, wy, wz] | ||
| u_dot : array | ||
| Derivative: [vx, vy, vz, ax, ay, az, e0_dot, ...] | ||
| Accelerations are at indices [3:6] | ||
|
|
||
| Returns | ||
| ------- | ||
| bool | ||
| True to trigger parachute deployment | ||
| """ | ||
| # Extract acceleration components (m/s²) | ||
| ax = u_dot[3] | ||
| ay = u_dot[4] | ||
| az = u_dot[5] | ||
|
|
||
| # Calculate total acceleration magnitude | ||
| total_acc = (ax**2 + ay**2 + az**2)**0.5 | ||
|
|
||
| # Custom logic: deploy if total acceleration < 5 m/s² while descending | ||
| vz = state_vector[5] | ||
| return total_acc < 5.0 and vz < -10.0 | ||
|
|
||
| # Use custom trigger | ||
| parachute = Parachute( | ||
| name="Custom", | ||
| cd_s=2.0, | ||
| trigger=custom_trigger, | ||
| sampling_rate=100, | ||
| lag=1.0 | ||
| ) | ||
|
Comment on lines
+113
to
+165
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are two trigger functions defined here... One of them seems to be unused
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, I don´t really understand the what this trigger is doing that is different from the previous ones? |
||
|
|
||
| Sensor and Acceleration Triggers | ||
| --------------------------------- | ||
|
|
||
| Triggers can also accept sensor data alongside acceleration: | ||
|
|
||
| .. code-block:: python | ||
|
|
||
| def advanced_trigger(pressure, height, state_vector, sensors, u_dot): | ||
| """ | ||
| Advanced trigger using both sensors and acceleration. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| sensors : list | ||
| List of sensor objects attached to the rocket | ||
| u_dot : array | ||
| State derivative including accelerations | ||
|
|
||
| Returns | ||
| ------- | ||
| bool | ||
| """ | ||
| # Access sensor measurements | ||
| if len(sensors) > 0: | ||
| imu_reading = sensors[0].measurement | ||
|
|
||
| # Define threshold for IMU reading (example value) | ||
| threshold = 100.0 # Adjust based on sensor units and trigger criteria | ||
|
|
||
| # Use acceleration data | ||
| az = u_dot[5] | ||
|
|
||
| # Combine sensor and acceleration logic | ||
| return az < -5.0 and imu_reading > threshold | ||
|
|
||
| parachute = Parachute( | ||
| name="Advanced", | ||
| cd_s=1.5, | ||
| trigger=advanced_trigger, | ||
| sampling_rate=100 | ||
| ) | ||
|
|
||
|
Comment on lines
+167
to
+208
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please make an example more in lines with what would be like using a rocketpy Accelerometer object. We don´t have an IMU sensor object |
||
| Sensor Noise | ||
| ------------ | ||
|
|
||
| For realistic IMU behavior, use RocketPy sensors with their own noise models. | ||
| Parachute trigger functions can receive ``sensors`` and use those measurements | ||
| directly instead of injecting noise in the flight solver. | ||
|
Comment on lines
+209
to
+214
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you make this a
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, please provide a link to the sensors doc pages |
||
|
|
||
| Performance Considerations | ||
| -------------------------- | ||
|
|
||
| Computing acceleration (``u_dot``) requires evaluating the equations of motion, | ||
| which adds computational cost. RocketPy optimizes this by: | ||
|
|
||
| 1. **Lazy evaluation**: ``u_dot`` is only computed if the trigger actually needs it | ||
| 2. **Metadata detection**: The wrapper inspects trigger signatures to determine requirements | ||
| 3. **Caching**: Derivative evaluations are reused when possible | ||
|
|
||
| **Trigger signature detection:** | ||
|
|
||
| - 3 parameters ``(p, h, y)``: Legacy trigger, no ``u_dot`` computed | ||
| - 4 parameters with ``u_dot``: Only acceleration computed | ||
| - 4 parameters with ``sensors``: Only sensors passed | ||
| - 5 parameters: Both sensors and acceleration provided | ||
|
|
||
| Best Practices | ||
| -------------- | ||
|
|
||
| 1. **Choose appropriate sampling rates**: 50-200 Hz is typical for flight computers | ||
| 2. **Add realistic noise**: Real IMUs have noise; simulate it for validation | ||
| 3. **Test edge cases**: Verify triggers work at low altitudes, high speeds, etc. | ||
| 4. **Use robust custom logic**: Add mission-specific guards and thresholds | ||
| 5. **Document custom triggers**: Include detection criteria in docstrings | ||
|
Comment on lines
+216
to
+240
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suggest to remove these sections, they do not add anythin of value in terms of usability |
||
|
|
||
| Example: Complete Dual-Deploy System | ||
| ------------------------------------- | ||
|
|
||
| .. code-block:: python | ||
|
|
||
| from rocketpy import Rocket, Parachute, Flight, Environment | ||
| import numpy as np | ||
|
|
||
| # Environment and rocket setup | ||
| env = Environment(latitude=32.99, longitude=-106.97, elevation=1400) | ||
| env.set_atmospheric_model(type="standard_atmosphere") | ||
|
|
||
| my_rocket = Rocket(...) # Define your rocket | ||
|
|
||
| # Drogue parachute: Deploy using a custom burnout trigger | ||
| def drogue_burnout_trigger(_pressure, height, state_vector, u_dot): | ||
| if u_dot is None or len(u_dot) < 6: | ||
| return False | ||
| az = u_dot[5] | ||
| vz = state_vector[5] if len(state_vector) > 5 else 0 | ||
| return height > 10 and vz > 1 and az < -8.0 | ||
|
|
||
| drogue = Parachute( | ||
| name="Drogue", | ||
| cd_s=1.0, | ||
| trigger=drogue_burnout_trigger, | ||
| sampling_rate=100, | ||
| lag=1.5, | ||
| noise=(0, 8.3, 0.5) # Pressure sensor noise | ||
| ) | ||
| my_rocket.add_parachute(drogue) | ||
|
|
||
| # Main parachute: Deploy at 800m using custom trigger | ||
| def main_deploy_trigger(pressure, height, state_vector, u_dot): | ||
| """Deploy main at 800m while descending with positive vertical acceleration.""" | ||
| vz = state_vector[5] | ||
| az = u_dot[5] | ||
| return height < 800 and vz < -5 and az > -15 | ||
|
|
||
| main = Parachute( | ||
| name="Main", | ||
| cd_s=10.0, | ||
| trigger=main_deploy_trigger, | ||
| sampling_rate=100, | ||
| lag=0.5, | ||
| noise=(0, 8.3, 0.5) | ||
| ) | ||
| my_rocket.add_parachute(main) | ||
|
|
||
| # Flight simulation | ||
| flight = Flight( | ||
| rocket=my_rocket, | ||
| environment=env, | ||
| rail_length=5.2, | ||
| inclination=85, | ||
| heading=0, | ||
| ) | ||
|
|
||
| flight.all_info() | ||
|
|
||
|
Comment on lines
+242
to
+301
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you change this section to be about simulation Drogue parachutes? I think it would make more sense, and make this docs page more complete We simulate drogue chutes by simply considering the disrefeed chute the equivalent to main here, and the refeed chute the drogue, since in rocketpy only one parachute is considered at a time |
||
| See Also | ||
| -------- | ||
|
|
||
| - :doc:`Parachute Class Reference </reference/rocket/parachute>` | ||
| - :doc:`Flight Simulation </user/flight>` | ||
| - :doc:`Sensors and Controllers </user/sensors>` | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is missing the case when trigger is a int,float (
trigger=400)