Files
PX4-Autopilot/docs/en/actuators
Gennaro Guidone 3a35c014be feat(control_allocator): reversible-motor failure recovery for hexarotors (#28078)
* feat(control_allocator): add reversible-motor failure recovery for hexarotors

On a single motor failure (CA_FAILURE_MODE), handle the failed motor's
geometric-opposite motor on a hexarotor to recover the lost control authority:
  1 = remove failed + shut off opposite (restores symmetry)
  2 = remove failed + make opposite reversible (assumes a reverse-capable ESC)
Non-hex frames have no opposite and keep the upstream remove-only behavior.

The opposite motor is found geometrically (opposite spin, closest antipode) and
only for hexarotors. Reverse thrust is modeled as CA_REV_THR_FRAC of forward
thrust and the reversible actuator range is rescaled to [-1, 0] on publish.
Consumes failure_detector_status.motor_failure_mask, independent of detection.

* feat(uavcan): render reversible motor outputs as signed RawCommand

Re-encode reversible channels from the mixer's [min,max] range to a signed
RawCommand (negative = reverse). A channel at its disarmed value is passed
through untouched, so a stopped motor is never inverted to reverse.

* fix(dshot): match per-channel minimums to the 3D encoding

calculate_output_value() applies the 3D split when DSHOT_3D_ENABLE is set or the
channel is reversible, but the per-channel minimum was derived from the
reversible bit alone. Two problems followed.

A reversible channel made reversible at runtime kept a stale minimum, so its
neutral no longer landed on the mid-throttle split. A non-reversible channel
with 3D enabled was worse: it mapped from DSHOT_MIN, which sits below the split,
so zero commanded thrust decoded as near-full reverse.

Select the minimum from the same two facts the encoder uses. A reversible
channel keeps the range bottom so neutral lands on the split, a forward-only
channel starts just above it, and without 3D the DSHOT_MIN idle floor applies as
before. For a split channel the idle floor is applied per direction inside
convert_output_to_3d_scaling(), so it must not be added to the minimum. The
deadband is now applied only to reversible channels, since a channel that cannot
be commanded negative has no neutral to guard.

Apply the minimums from a new OutputModuleInterface::reversibleMaskChanged(),
called once the new mask is known and before any output is computed with it.
Applying them from Run() left the range one cycle behind the mask it depends on,
which in 3D mode changed the sign of the output on the transition.

The reversible mask can now change while armed, since failure recovery reverses a
motor in flight, so the reversibleOutputs() contract is updated to say so.

Changes output values for every DSHOT_3D_ENABLE=1 setup that has any
non-reversible motor. Verified on fmu-v4 with 3D-mode ESCs: forward-only
channels floor at 1001 with no samples below the split across 85 s armed, and a
runtime-reversed recovery motor tracks yaw input through the split in both
directions.

* docs(control_allocator): document motor failure recovery

Add a Motor Failure Recovery page covering CA_FAILURE_MODE, the hexarotor
stop-or-reverse behaviour of the opposite motor, CA_REV_THR_FRAC, the
detection prerequisites and which output protocols can drive a motor in
reverse.

Document the signed DroneCAN RawCommand encoding for reversible motors,
and correct the failure detector section, which claimed motor failures
are non-latching while the current-based check holds a flagged motor
until the vehicle disarms.

Add the page to the navigation and to the v1.18 release notes.

* docs(docs): subedit

* docs(docs): subedit

* refactor(control_allocator): align CA_REV_THR_FRAC naming and description

Rename the parameter member to _param_ca_rev_thr_frac to match the
parameter name, and note in the description that the reverse thrust
fraction is mostly a property of the propeller.

* docs(motor_failure_recovery): move under Actuators and target v1.19

The page documents how control allocation is reconfigured, so list it
under Actuators next to Actuator Allocation instead of under Safety, and
link it from the Actuators and multicopter Features indexes. Safety
configuration still links to it from the motor failure trigger section.

The release branch for v1.18 is already cut, so move the release notes
to main.md and set the version badges to main (PX4 v1.19).

* docs(motor_failure_recovery): spell out the reversing requirements

Any of the six motors can fail, so every motor has to be reverse-capable,
not just one. List the settings that mode 2 needs, split the per-protocol
detail out (DroneCAN reverses from the signed RawCommand alone, DShot also
needs the ESCs in 3D mode and DSHOT_3D_ENABLE to match), and say what
happens when they are not set: a reverse command comes out as forward
thrust, and on PWM the recovery motor sits at around half throttle.

Also note that motors do not have to be marked bidirectional (CA_R_REV)
for this, since the recovery motor is made reversible at runtime.

* Apply suggestions from code review

Co-authored-by: Hamish Willee <hamishwillee@gmail.com>

* fix(uavcan_main): log final value in actuator_outputs, always run through reversible logic

We want worst case execution time all the time and as little control paths as possible. Also actuator_outputs is used to debug ESC behavior and should contain the final value that is sent out over the wire.

* refactor(DShot): directly calculate minimum dshot value in apply_min_values()

* refactor(control_allocator): static_cast, correct CA_FAILURE_MODE description

* fix(control_allocator): treat undefined parameter values as default, use matrix function to zero a collumn

---------

Co-authored-by: Hamish Willee <hamishwillee@gmail.com>
Co-authored-by: Matthias Grob <maetugr@gmail.com>
2026-08-07 09:46:17 +00:00
..