6-DoF Quadrotor Hover Recovery¶
Background¶
A small quadrotor displaced from hover must arrest translation and rotation while respecting both the finite speed and finite slew rate of each rotor. Because thrust direction depends on attitude, this is a coupled nonlinear optimal-control problem even without aerodynamics.
This example recovers a Crazyflie-class vehicle to the origin over a fixed five-second horizon. It uses three modified Rodrigues parameters (MRPs) for attitude rather than a four-component quaternion, so there is no redundant state or unit-norm equality constraint.
Problem formulation¶
Let \(\boldsymbol r,\boldsymbol v\in\mathbb R^3\) be inertial position and velocity, \(\boldsymbol p\in\mathbb R^3\) the MRP vector, and \(\boldsymbol\omega\in\mathbb R^3\) the body angular velocity. The actuator state for rotor \(i\) is the dimensionless squared-speed ratio
where \(\Omega_i\) is rotor speed and \(\Omega_h\) is the speed required for level hover. The control is its rate \(\nu_i=\dot c_i\). The corresponding body-frame thrust is
The four arm vectors and reaction-yaw-moment signs used in the model are
Here \(d_i\) is the sign of the reaction yaw moment exerted on the body about \(+\boldsymbol e_3\); it is not the rotor's spin-direction sign. Total body force and moment are
The body-to-inertial rotation \(R(\boldsymbol p)\) is obtained from the nonredundant MRP-to-quaternion map
and the dynamics are
Here \([\boldsymbol p]_\times\) is the cross-product matrix. The fixed horizon is \(T=5\ \mathrm s\), and the initial state is
All rotors start and finish at the balanced level-hover command,
Thus the terminal actuator state produces total thrust \(mg\) and zero net body moment. The terminal rigid-body state is free but strongly penalized.
Every term in the objective is nondimensionalized. Define
with \(r_s=1\ \mathrm m\), \(v_s=1\ \mathrm{m/s}\), \(\theta_s=1\ \mathrm{rad}\), \(\omega_s=1\ \mathrm{rad/s}\), and \(\nu_s=4\ \mathrm{s^{-1}}\). The smooth local attitude proxy \(4\boldsymbol p\) agrees with the principal rotation vector to first order near hover. The dimensionless objective is
Rotor and attitude-chart path constraints are
The final inequality limits the principal rotation angle to \(150^\circ\).
Variables, parameters, and units¶
| Symbol | Meaning | Value or unit |
|---|---|---|
| \(\boldsymbol r,\boldsymbol v\) | Inertial position and velocity | m, m/s |
| \(\boldsymbol p\) | Modified Rodrigues attitude vector | dimensionless |
| \(\boldsymbol\omega\) | Body angular velocity | rad/s |
| \(c_i\) | Squared rotor-speed ratio; actuator state | dimensionless |
| \(\nu_i=\dot c_i\) | Squared-speed-ratio slew; control | 1/s |
| \(\Omega_i\) | Physical rotor speed | rad/s |
| \(m\) | Vehicle mass | \(0.033\ \mathrm{kg}\) |
| \(I_b\) | Body inertia diagonal | \((1.395,1.395,2.173)\times10^{-5}\ \mathrm{kg\,m^2}\) |
| \(k_f\) | Thrust coefficient | \(2.3\times10^{-8}\ \mathrm{N/(rad/s)^2}\) |
| \(k_m\) | Drag-moment coefficient | \(7.8\times10^{-10}\ \mathrm{N\,m/(rad/s)^2}\) |
| \(l\) | Rotor-arm half-span | \(0.028\ \mathrm m\) |
| \(d_i\) | Body reaction-yaw-moment sign | \(+1,-1,+1,-1\) |
| \(r_s,v_s,\theta_s,\omega_s,\nu_s\) | Objective reference scales | \(1\ \mathrm m,\ 1\ \mathrm{m/s},\ 1\ \mathrm{rad},\ 1\ \mathrm{rad/s},\ 4\ \mathrm{s^{-1}}\) |
Modeling choices and limits¶
MRPs give a minimal, smooth local attitude coordinate and avoid the artificial quaternion norm constraint. They are not a global attitude representation: the chart is singular at a full \(360^\circ\) rotation, and this example does not implement shadow-set switching. The explicit \(150^\circ\) path bound therefore makes the model appropriate for local recovery maneuvers, not arbitrary tumbling trajectories.
The model includes rigid-body translation, gyroscopic rotation, arm moments,
rotor drag moments, and a continuous rate-limited actuator command. The state
\(c_i\) is a command-shaping model, not an electrical or electromechanical
motor model: the slew limit applies to squared speed ratio rather than directly
to shaft acceleration. Aerodynamic drag, ground effect, sensor noise, and
disturbances are also omitted. The default problem uses a \(44\times2\)
Lobatto mesh; --quick is available only as a coarse smoke test.
Run the example¶
From the repository root:
Save the common-style figure without opening a window:
Verified result¶
After solving, the script reconstructs all states and controls at 4,001 physical times. It converts the dimensionless rotor states back to physical speed, checks dense speed, slew-rate, and MRP-chart bounds, and verifies the optimized terminal state against the phase endpoint. It separately requires small terminal position, velocity, attitude, and angular-rate errors; all four commands equal to one; and small terminal linear and angular accelerations. The last checks distinguish a balanced hover from a trajectory that merely passes near zero state with an unbalanced terminal wrench.
| Quantity | Default-solve value |
|---|---|
| Objective \(J\) | \(8.54936175\) |
| Terminal position error | \(0.000001\ \mathrm m\) |
| Terminal velocity error | \(0.000002\ \mathrm{m/s}\) |
| Terminal principal-attitude error | \(0.000089^\circ\) |
| Terminal angular-rate error | \(0.000000\ \mathrm{rad/s}\) |
| Maximum terminal rotor-command error | \(0\) at reported precision |
| Terminal linear-acceleration norm | \(1.532\times10^{-5}\ \mathrm{m/s^2}\) |
| Terminal angular-acceleration norm | \(3.843\times10^{-19}\ \mathrm{rad/s^2}\) |
| Dense rotor-speed range | \(1400.89\) to \(2164.74\ \mathrm{rad/s}\) |
| Peak squared-speed-ratio slew | \(4.0000\ \mathrm{s^{-1}}\) |
| Peak total thrust | \(0.4258\ \mathrm N\) |
| Minimum MRP-chart radial margin | \(0.569544\) |
The optimizer therefore returns the vehicle close to rest with a balanced terminal rotor state, rather than only minimizing the kinematic state errors. The reconstructed rotor speeds and slew controls respect their dense limits. These figures describe a local optimum of the discretized problem, not a proof of a global optimum.

Source code¶
See the complete runnable example:
examples/drone_stabilization.py.