Hi all, it appears that my blog post “Yaw Control, ESC Linearization, and Hover” has been lost. I hope that it will be saveable but I am not holding my breath. If it can’t be saved I will attempt to reconstruct it but while I have most people’s replies on my emails but I don’t have my own. If anybody has a copy of my replies I would be very grateful if you could send them to me.

The discussion that followed that blog post focused on the "stability patch" in AP_MotorsMatrix.cpp.

Both myself and R_Lefebvre attempted to explain this in detail in the blog post above so I won’t do it again now in a hope that we will get that work back (fingers crossed). But I will attempt to summarise the issues that we would like to address.

- Near maximum and minimum throttle the control is compromised because we can’t decrease propeller lift much further at minimum throttle or increase it much further maximum throttle.
- When multiple Roll, Pitch, and Yaw manoeuvres are happening, how do we combine these with the requested Throttle setting to maintain stability.
- Yaw control is very limited in most of the multi rotor geometries and therefore results in large RPM variation in motors compared to Roll and Pitch to create an equivalent Yaw response. The lift force generated tends to be non-linear creating an increase in lifting force when Yaw is applied. How can we correct this behaviour?

While addressing these points we need to ensure that:

- We maintain stability and preferably good control over Roll, Pitch, and Yaw.
- Throttle response and altitude control is not compromised any more than absolutely necessary.
- That we don’t do anything that can prevent an overpowered multi rotor from coming down!

# Yaw Curve

Ok, easy bit first.

Yaw is achieved by increasing the RPM of half the motors and decreasing it in the other half of the motors. However, because the lift of a given propeller tends to increase by the square of the change in RPM, this results in an increased total lifting force.

Reference is page 16 here: http://dc-rc.org/pdf/Model%20Propellers%20Article.pdf

So what we want to do is increase the RPM of half the motors less than we decrease the RPM of the other half and keep it the same as when all motors are at the same RPM.

Ok, some maths.

x = RPM of all motors at stationary hover.

y = RPM reduction of half the motors during Yaw.

z = RPM increase of half the motors during Yaw.

Total lifting force at hover for a quad is:

Total Force = 4*x^2

When inducing a Yaw it is:

Total Force = 2*(x - y)^2 + 2*(x + z)^2

We want these two equations to be equal if we don’t want the quad to start accelerating vertically therefore:

4*x^2 = 2*(x - y)^2 + 2*(x + z)^2

2*x^2 = (x - y)^2 + (x + z)^2

2*x^2 = x^2 + y^2 -2*x*y + x^2 + z^2 + 2*x*z

2*x^2 = 2*x^2 + y^2 -2*x*y + z^2 + 2*x*z

0 = y^2 -2*x*y + z^2 + 2*x*z

0 = z^2 + 2*x*z + y^2 -2*x*y

if we solve this for z we get:

z = -x + sqrt(x^2 – y^2 + 2*x*y)

If we plot this we get:

The excel file I used is here if you would like to play with it.

The current ratio line is the ratio we are currently considering using, ie 0.7 up and 1.42 down. As you can see, the ratio starts off 1:1 then reduces to sqrt(2)-1 or 0.414. We could get pretty close to the ideal line using a straight line approximation:

z = 1 - (2-SQRT(2)) * x/y

This will give us a small increase in lift half way along but it is pretty close. Looking at the added complexity with this approach I think it is probably better to go with the constant ratio approach that is currently commented out in the code. This makes all calculations much quicker and easier.

# Stability Patch

Ok, now the difficult bit.

The approach I suggested in the lost blog was

- calculate all motor settings for Roll, Pitch and Throttle.
- calculate the remaining headroom between these throttle settings and the max_throttle and min_throttle.
- if the headroom is not enough to contain the requested Yaw input the we reduce the requested Yaw to fit the available headroom or the minimum reserved yaw headroom.
- We then go through and add the Yaw request to each motor output with the throttle offset to ensure the outputs remain between the max_throttle setting and the min_throttle setting.

We do need to be very careful that Pitch or Yaw oscillations can’t make a high powered quad fly away though. I have some ideas on this but it may not be needed.

In the lost post the question was raised by Randy. “For example, I think it's much better to abandon a request from an autopilot for a 45 degree pitch forward in favour of maintaining yaw.” We could do this by:

- reducing the stick input travel at low or high throttle,
- reducing the limit on roll and pitch rate outputs at low or high throttle,
- or scaling the rate outputs in the MotorMatix function.

I don’t like any of these options because they either directly interfere with the users inputs or change the ability of the Rate_I (or Stab_I) term to do it job and correct for CG and motor output offsets.

The good news is that I don’t think this is a problem if we leave the Roll and Pitch components un touched. At near minimum throttle we are descending and we can get a large Roll or Pitch output from the Rate PID’s from two things:

- a large stick input from the user,
- a large exterior force.

Point 1. If the user enters a command to change attitude from horizontal to 45 degree pitch for example. With full Pitch control this will happen in less than a second (for my quad anyway). For this short time we may lose almost all our Yaw authority leaving just a little more than is required to maintain a heading. However, as the Pitch angle increases the throttle boost kicks in. This will dramatically increase the throttle setting restoring Yaw authority.

On the other hand, If the airframe is at 45 degrees and I let go of the sticks in stabilise mode. I want to see my copter go level as fast as possible. I don’t care if I lose all Yaw authority for a second provided my copter is level afterwards. Then with the small amount of Yaw authority that we keep in reserve the copter can change the heading as requested.

Point 2. If the copter experiences a large outside force… say clips a branch while descending to land, all I care about is that it gets up the right way again. As before, Throttle boost will help us here for a portion of the rotation, but Yaw is my absolute lowest priority.

Remember I am not advocating removing all Yaw authority, I am suggesting reserving only enough to maintain a heading and apply small Yaw accelerations.

# Code Example

To quote R_Lefebvre from the lost blog post, “this isn't finished working code, just kind of a back-of-the-envelope sketch”

// capture desired roll, pitch, yaw and throttle from receiver

_rc_roll->calc_pwm();

_rc_pitch->calc_pwm();

_rc_throttle->calc_pwm();

_rc_yaw->calc_pwm();

// mix roll, pitch, throttle into output for each motor

**for**( i=0; i<AP_MOTORS_MAX_NUM_MOTORS; i++ ) {

**if**( motor_enabled[i] ) {

motor_out[i] = _rc_throttle->radio_out +

_rc_roll->pwm_out * _roll_factor[i] +

_rc_pitch->pwm_out * _pitch_factor[i];

}

}

// calulate the minium headroom left for Yaw.

__high_yaw_pos__ = 1000;

__high_yaw_neg__ = 1000;

__low_yaw_pos__ = 1000;

__low_yaw_neg__ = 1000;

__high_throttle__ = 0;

__low_throttle__ = 2000;

// the signs I have used might be wrong

**for**( i=0; i<AP_MOTORS_MAX_NUM_MOTORS; i++ ) {

**if**( motor_enabled[i]) {

**if**( _yaw_factor[i] > 0 ) {

**if**( __high_yaw_pos > (out_max - motor_out[i])/__ ) {

__high_yaw_pos__ = out_max - motor_out[i];

}

**if**( __low_yaw_neg__ > motor_out[i] - out_min ) {

__low_yaw_neg__ = motor_out[i] - out_min;

}

} **else** **if**( _yaw_factor[i] < 0 ) {

**if**( __high_yaw_neg__ > out_max - motor_out[i] ) {

__high_yaw_neg__ = out_max - motor_out[i];

}

**if**( __low_yaw_pos__ > motor_out[i] - out_min ) {

__low_yaw_pos__ = motor_out[i] - out_min;

}

}

**if**( __high_throttle__ < motor_out[i] ) {

__high_throttle__ = motor_out[i];

}

**if**( __low_throttle__ > motor_out[i] ) {

__low_throttle__ = motor_out[i];

}

}

}

__yaw_ratio__ = 0.7/1.42;

__high_yaw_pos__ = max(__high_yaw_pos__, __min_yaw_headroom__);

__high_yaw_neg__ = max(__high_yaw_neg__, __min_yaw_headroom__);

__low_yaw_pos__ = max(__low_yaw_pos__, __min_yaw_headroom__);

__low_yaw_neg__ = max(__low_yaw_neg__, __min_yaw_headroom__);

__yaw_pos__ = min(__high_yaw_pos__/__yaw_ratio__, __low_yaw_pos__);

__yaw_neg__ = min(__high_yaw_neg__/__yaw_ratio__, __low_yaw_neg__);

__rc_yaw_pwm_out__ = constrain(_rc_yaw->pwm_out, -__yaw_neg__, __yaw_pos__);

__min_required_yaw_headroom__ = min(__reserved_yaw_headroom__, __rc_yaw__->__pwm_out__);

**if**( (out_max-__high_throttle__)/__yaw_ratio__ < (__low_throttle__ - out_min) ) {

__throttle_change__ = min((out_max-__high_throttle__) - __min_yaw_headroom__*__yaw_ratio__,0);

}**else**{

__throttle_change__ = max((out_min-__low_throttle__) + __min_yaw_headroom__,0);

}

**for**( i=0; i<AP_MOTORS_MAX_NUM_MOTORS; i++ ) {

**if**( motor_enabled[i] ) {

__yaw_contribution__ = __rc_yaw_pwm_out__*_yaw_factor[i];

**if** (__yaw_contribution__ > 0 ){

__yaw_contribution__ *= __yaw_ratio__;

}

motor_out[i] = motor_out[i] +

__throttle_change__ +

__yaw_contribution__;

}

}

// ensure motors are not outside alowable region

**for**( i=0; i<AP_MOTORS_MAX_NUM_MOTORS; i++ ) {

**if**( motor_enabled[i] ) {

motor_out[i] = constrain(motor_out[i], out_min, out_max);

}

}

## Comments

Leonard, check your gmail please.

Lefebvre,

Yeh, the code I wrote above was written for communication, not for speed but it is still all in the 200 Hz loop.

I did check your numbers and compare them in the graph above. I make the RPM reduction equal to _rc_yaw->pwm_out and the up going RPM equal to _rc_yaw->pwm_out * 0.7/1.42. That way the down moving throttle that is causing the problem doesn't change in peek magnitude.

Marooned,

Thanks for that mate, If they can't get the Blog post back I will attempt to reconstruct it with the comments that everybody did. I will give them a couple of days though because I haven't had any message yet.

Did you run through the calculation for the yaw equation, and find that my WAG of 0.7/1.42 was pretty close?

I had actually planned to make the 0.7 a parameter that the user could tune. It only needs one parameter because the upside should be 1/0.7=1.42, or whatever it works out for on it's parameter.

This yaw differential was deprecated because Jason found that on his high powered quad, the slowing motors slowed too much, and hit throttle-min, leaving him with no attitude control. I hadn't experienced this because my moderately powered quad hovers at 50%, so I have lots of room to go down. But simply making it a parameter would allow users to tune it to their liking. If they want to prevent rise on yaw, then use 0.7. If they need to have no differential, just set it to 1.

This is getting into "parameter creep" but, I think if it was an advanced param, and set to something like 0.9 as a default, it should work out OK.

If we get to the full attitude control hierarchy going, then having this a parameter won't matter so much. The problem is, determining which control gets priority is a really complicated problem! It's also possible that we don't have enough processor overhead left to do a bunch more complicated stuff. It may have to wait for ARM.

I do think we should try to at least get "negative stability patch" going. The stability patch makes sense, but but it only works on the top of the PWM range. Many quads these days are operating closer to the bottom of the range.

I've sent your 5 messages I had in my inbox via PM here.

I was also surprised to find that blog post gone. One of the most interesting lately.