Dangerous pitch oscillation at high speed

I was doing some high speed testing yesterday on a custom “H” quad. There were several instances where at high speed (>20m/s) a violent oscillation on pitch occurred. Initially I suspected a tuning problem and so tried to crank up the pitch gains, but this did not offer any improvement. After reviewing the log files I can see that the oscillation is in fact being induced by the control system.

check out the pitch chart in this log at times 5:48-5:52, 7:24-7:29, 10:50-10:55, 13:21-13:24


It seems that the controller is commanding oscillating pitch values when throttle = 100%. While it does make sense to back off on pitch at high throttle values, the oscillation it creates is frightening. I suppose it is possible that phase delay on the pitch response of my platform is to blame, but that the fact the the pitch controller is allowing this oscillation to get set up is disturbing.

Is there a way to fix this problem or is it simply a matter of I need to ease back on the speed?

Similar problem occurs in this log at times 12:45-12:52,


My guess is your MPC_PITCH_P is tuned to high. Your pitch rate or MPC_Pitchrate_P tracking is doing okay and doing what the angle setpoint is telling it to do. Your Pitch angle is leading the instability so start by lowering MPC_Pitch_P.

Capping your pitch angle with MPC_TILTMAX_AIR to 20 deg could also help put a bandage over the problem but wont solve the root cause.

In the course of testing this problem I tried several different pitch tunings and none of them made any real difference. Pitch angle changes are being commanded by the control system and the pitch controller is attempting to follow them. I suspect if I could reduce the phase delay on the pitch axis all would be well. I think the problem is here, from around line 280 in PositionControl.cpp:

` float thrust_max_NE_tilt = fabsf(_thr_sp(2)) * tanf(_constraints.tilt);
float thrust_max_NE = sqrtf(MPC_THR_MAX.get() * MPC_THR_MAX.get() - _thr_sp(2) * _thr_sp(2));
thrust_max_NE = math::min(thrust_max_NE_tilt, thrust_max_NE);

	// Saturate thrust in NE-direction.
	_thr_sp(0) = thrust_desired_NE(0);
	_thr_sp(1) = thrust_desired_NE(1);

	if (thrust_desired_NE * thrust_desired_NE > thrust_max_NE * thrust_max_NE) {
		float mag = thrust_desired_NE.length();
		_thr_sp(0) = thrust_desired_NE(0) / mag * thrust_max_NE;
		_thr_sp(1) = thrust_desired_NE(1) / mag * thrust_max_NE;

This limits the maximum lateral thrust to whatever is “left over” after throttle control takes what it needs. Trouble is there is no rate limiting here so very high bandwidth changes are possible. In my opinion _vel_sp (further up) should be slowly reduced to mitigate this problem. All that being said, limiting MPC_TILTMAX_AIR will at least be a bandaid for now.