APM:Libraries
AP_PitchController.cpp
Go to the documentation of this file.
1 /*
2  This program is free software: you can redistribute it and/or modify
3  it under the terms of the GNU General Public License as published by
4  the Free Software Foundation, either version 3 of the License, or
5  (at your option) any later version.
6 
7  This program is distributed in the hope that it will be useful,
8  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  GNU General Public License for more details.
11 
12  You should have received a copy of the GNU General Public License
13  along with this program. If not, see <http://www.gnu.org/licenses/>.
14  */
15 
16 // Initial Code by Jon Challinger
17 // Modified by Paul Riseborough
18 
19 #include <AP_HAL/AP_HAL.h>
20 #include "AP_PitchController.h"
21 
22 extern const AP_HAL::HAL& hal;
23 
25 
26  // @Param: TCONST
27  // @DisplayName: Pitch Time Constant
28  // @Description: This controls the time constant in seconds from demanded to achieved pitch angle. A value of 0.5 is a good default and will work with nearly all models. Advanced users may want to reduce this time to obtain a faster response but there is no point setting a time less than the aircraft can achieve.
29  // @Range: 0.4 1.0
30  // @Units: s
31  // @Increment: 0.1
32  // @User: Advanced
33  AP_GROUPINFO("TCONST", 0, AP_PitchController, gains.tau, 0.5f),
34 
35  // @Param: P
36  // @DisplayName: Proportional Gain
37  // @Description: This is the gain from pitch angle to elevator. This gain works the same way as PTCH2SRV_P in the old PID controller and can be set to the same value.
38  // @Range: 0.1 3.0
39  // @Increment: 0.1
40  // @User: User
41  AP_GROUPINFO("P", 1, AP_PitchController, gains.P, 0.6f),
42 
43  // @Param: D
44  // @DisplayName: Damping Gain
45  // @Description: This is the gain from pitch rate to elevator. This adjusts the damping of the pitch control loop. It has the same effect as PTCH2SRV_D in the old PID controller and can be set to the same value, but without the spikes in servo demands. This gain helps to reduce pitching in turbulence. Some airframes such as flying wings that have poor pitch damping can benefit from increasing this gain term. This should be increased in 0.01 increments as too high a value can lead to a high frequency pitch oscillation that could overstress the airframe.
46  // @Range: 0 0.1
47  // @Increment: 0.01
48  // @User: User
49  AP_GROUPINFO("D", 2, AP_PitchController, gains.D, 0.02f),
50 
51  // @Param: I
52  // @DisplayName: Integrator Gain
53  // @Description: This is the gain applied to the integral of pitch angle. It has the same effect as PTCH2SRV_I in the old PID controller and can be set to the same value. Increasing this gain causes the controller to trim out constant offsets between demanded and measured pitch angle.
54  // @Range: 0 0.5
55  // @Increment: 0.05
56  // @User: User
57  AP_GROUPINFO("I", 3, AP_PitchController, gains.I, 0.15f),
58 
59  // @Param: RMAX_UP
60  // @DisplayName: Pitch up max rate
61  // @Description: This sets the maximum nose up pitch rate that the controller will demand (degrees/sec). Setting it to zero disables the limit.
62  // @Range: 0 100
63  // @Units: deg/s
64  // @Increment: 1
65  // @User: Advanced
66  AP_GROUPINFO("RMAX_UP", 4, AP_PitchController, gains.rmax, 0.0f),
67 
68  // @Param: RMAX_DN
69  // @DisplayName: Pitch down max rate
70  // @Description: This sets the maximum nose down pitch rate that the controller will demand (degrees/sec). Setting it to zero disables the limit.
71  // @Range: 0 100
72  // @Units: deg/s
73  // @Increment: 1
74  // @User: Advanced
75  AP_GROUPINFO("RMAX_DN", 5, AP_PitchController, _max_rate_neg, 0.0f),
76 
77  // @Param: RLL
78  // @DisplayName: Roll compensation
79  // @Description: This is the gain term that is applied to the pitch rate offset calculated as required to keep the nose level during turns. The default value is 1 which will work for all models. Advanced users can use it to correct for height variation in turns. If height is lost initially in turns this can be increased in small increments of 0.05 to compensate. If height is gained initially in turns then it can be decreased.
80  // @Range: 0.7 1.5
81  // @Increment: 0.05
82  // @User: User
83  AP_GROUPINFO("RLL", 6, AP_PitchController, _roll_ff, 1.0f),
84 
85  // @Param: IMAX
86  // @DisplayName: Integrator limit
87  // @Description: This limits the number of centi-degrees of elevator over which the integrator will operate. At the default setting of 3000 centi-degrees, the integrator will be limited to +- 30 degrees of servo travel. The maximum servo deflection is +- 45 degrees, so the default value represents a 2/3rd of the total control throw which is adequate for most aircraft unless they are severely out of trim or have very limited elevator control effectiveness.
88  // @Range: 0 4500
89  // @Increment: 1
90  // @User: Advanced
91  AP_GROUPINFO("IMAX", 7, AP_PitchController, gains.imax, 3000),
92 
93  // @Param: FF
94  // @DisplayName: Feed forward Gain
95  // @Description: This is the gain from demanded rate to elevator output.
96  // @Range: 0.1 4.0
97  // @Increment: 0.1
98  // @User: User
99  AP_GROUPINFO("FF", 8, AP_PitchController, gains.FF, 0.0f),
100 
102 };
103 
104 /*
105  Function returns an equivalent elevator deflection in centi-degrees in the range from -4500 to 4500
106  A positive demand is up
107  Inputs are:
108  1) demanded pitch rate in degrees/second
109  2) control gain scaler = scaling_speed / aspeed
110  3) boolean which is true when stabilise mode is active
111  4) minimum FBW airspeed (metres/sec)
112  5) maximum FBW airspeed (metres/sec)
113 */
114 int32_t AP_PitchController::_get_rate_out(float desired_rate, float scaler, bool disable_integrator, float aspeed)
115 {
116  uint32_t tnow = AP_HAL::millis();
117  uint32_t dt = tnow - _last_t;
118 
119  if (_last_t == 0 || dt > 1000) {
120  dt = 0;
121  }
122  _last_t = tnow;
123 
124  float delta_time = (float)dt * 0.001f;
125 
126  // Get body rate vector (radians/sec)
127  float omega_y = _ahrs.get_gyro().y;
128 
129  // Calculate the pitch rate error (deg/sec) and scale
130  float achieved_rate = ToDeg(omega_y);
131  float rate_error = (desired_rate - achieved_rate) * scaler;
132 
133  // Multiply pitch rate error by _ki_rate and integrate
134  // Scaler is applied before integrator so that integrator state relates directly to elevator deflection
135  // This means elevator trim offset doesn't change as the value of scaler changes with airspeed
136  // Don't integrate if in stabilise mode as the integrator will wind up against the pilots inputs
137  if (!disable_integrator && gains.I > 0) {
138  float k_I = gains.I;
139  if (is_zero(gains.FF)) {
140  /*
141  if the user hasn't set a direct FF then assume they are
142  not doing sophisticated tuning. Set a minimum I value of
143  0.15 to ensure that the time constant for trimming in
144  pitch is not too long. We have had a lot of user issues
145  with very small I value leading to very slow pitch
146  trimming, which causes a lot of problems for TECS. A
147  value of 0.15 is still quite small, but a lot better
148  than what many users are running.
149  */
150  k_I = MAX(k_I, 0.15f);
151  }
152  float ki_rate = k_I * gains.tau;
153  //only integrate if gain and time step are positive and airspeed above min value.
154  if (dt > 0 && aspeed > 0.5f*float(aparm.airspeed_min)) {
155  float integrator_delta = rate_error * ki_rate * delta_time * scaler;
156  if (_last_out < -45) {
157  // prevent the integrator from increasing if surface defln demand is above the upper limit
158  integrator_delta = MAX(integrator_delta , 0);
159  } else if (_last_out > 45) {
160  // prevent the integrator from decreasing if surface defln demand is below the lower limit
161  integrator_delta = MIN(integrator_delta , 0);
162  }
163  _pid_info.I += integrator_delta;
164  }
165  } else {
166  _pid_info.I = 0;
167  }
168 
169  // Scale the integration limit
170  float intLimScaled = gains.imax * 0.01f;
171 
172  // Constrain the integrator state
173  _pid_info.I = constrain_float(_pid_info.I, -intLimScaled, intLimScaled);
174 
175  // Calculate equivalent gains so that values for K_P and K_I can be taken across from the old PID law
176  // No conversion is required for K_D
177  float eas2tas = _ahrs.get_EAS2TAS();
178  float kp_ff = MAX((gains.P - gains.I * gains.tau) * gains.tau - gains.D , 0) / eas2tas;
179  float k_ff = gains.FF / eas2tas;
180 
181  // Calculate the demanded control surface deflection
182  // Note the scaler is applied again. We want a 1/speed scaler applied to the feed-forward
183  // path, but want a 1/speed^2 scaler applied to the rate error path.
184  // This is because acceleration scales with speed^2, but rate scales with speed.
185  _pid_info.P = desired_rate * kp_ff * scaler;
186  _pid_info.FF = desired_rate * k_ff * scaler;
187  _pid_info.D = rate_error * gains.D * scaler;
189  _pid_info.desired = desired_rate;
190 
191  if (autotune.running && aspeed > aparm.airspeed_min) {
192  // let autotune have a go at the values
193  // Note that we don't pass the integrator component so we get
194  // a better idea of how much the base PD controller
195  // contributed
196  autotune.update(desired_rate, achieved_rate, _last_out);
197 
198  // set down rate to rate up when auto-tuning
199  _max_rate_neg.set_and_save_ifchanged(gains.rmax);
200  }
201 
202  _last_out += _pid_info.I;
203 
204  /*
205  when we are past the users defined roll limit for the
206  aircraft our priority should be to bring the aircraft back
207  within the roll limit. Using elevator for pitch control at
208  large roll angles is ineffective, and can be counter
209  productive as it induces earth-frame yaw which can reduce
210  the ability to roll. We linearly reduce elevator input when
211  beyond the configured roll limit, reducing to zero at 90
212  degrees
213  */
214  float roll_wrapped = fabsf(_ahrs.roll_sensor);
215  if (roll_wrapped > 9000) {
216  roll_wrapped = 18000 - roll_wrapped;
217  }
218  if (roll_wrapped > aparm.roll_limit_cd + 500 && aparm.roll_limit_cd < 8500 &&
219  labs(_ahrs.pitch_sensor) < 7000) {
220  float roll_prop = (roll_wrapped - (aparm.roll_limit_cd+500)) / (float)(9000 - aparm.roll_limit_cd);
221  _last_out *= (1 - roll_prop);
222  }
223 
224  // Convert to centi-degrees and constrain
225  return constrain_float(_last_out * 100, -4500, 4500);
226 }
227 
228 /*
229  Function returns an equivalent elevator deflection in centi-degrees in the range from -4500 to 4500
230  A positive demand is up
231  Inputs are:
232  1) demanded pitch rate in degrees/second
233  2) control gain scaler = scaling_speed / aspeed
234  3) boolean which is true when stabilise mode is active
235  4) minimum FBW airspeed (metres/sec)
236  5) maximum FBW airspeed (metres/sec)
237 */
238 int32_t AP_PitchController::get_rate_out(float desired_rate, float scaler)
239 {
240  float aspeed;
241  if (!_ahrs.airspeed_estimate(&aspeed)) {
242  // If no airspeed available use average of min and max
243  aspeed = 0.5f*(float(aparm.airspeed_min) + float(aparm.airspeed_max));
244  }
245  return _get_rate_out(desired_rate, scaler, false, aspeed);
246 }
247 
248 /*
249  get the rate offset in degrees/second needed for pitch in body frame
250  to maintain height in a coordinated turn.
251 
252  Also returns the inverted flag and the estimated airspeed in m/s for
253  use by the rest of the pitch controller
254  */
255 float AP_PitchController::_get_coordination_rate_offset(float &aspeed, bool &inverted) const
256 {
257  float rate_offset;
258  float bank_angle = _ahrs.roll;
259 
260  // limit bank angle between +- 80 deg if right way up
261  if (fabsf(bank_angle) < radians(90)) {
262  bank_angle = constrain_float(bank_angle,-radians(80),radians(80));
263  inverted = false;
264  } else {
265  inverted = true;
266  if (bank_angle > 0.0f) {
267  bank_angle = constrain_float(bank_angle,radians(100),radians(180));
268  } else {
269  bank_angle = constrain_float(bank_angle,-radians(180),-radians(100));
270  }
271  }
272  if (!_ahrs.airspeed_estimate(&aspeed)) {
273  // If no airspeed available use average of min and max
274  aspeed = 0.5f*(float(aparm.airspeed_min) + float(aparm.airspeed_max));
275  }
276  if (abs(_ahrs.pitch_sensor) > 7000) {
277  // don't do turn coordination handling when at very high pitch angles
278  rate_offset = 0;
279  } else {
280  rate_offset = cosf(_ahrs.pitch)*fabsf(ToDeg((GRAVITY_MSS / MAX((aspeed * _ahrs.get_EAS2TAS()) , float(aparm.airspeed_min))) * tanf(bank_angle) * sinf(bank_angle))) * _roll_ff;
281  }
282  if (inverted) {
283  rate_offset = -rate_offset;
284  }
285  return rate_offset;
286 }
287 
288 // Function returns an equivalent elevator deflection in centi-degrees in the range from -4500 to 4500
289 // A positive demand is up
290 // Inputs are:
291 // 1) demanded pitch angle in centi-degrees
292 // 2) control gain scaler = scaling_speed / aspeed
293 // 3) boolean which is true when stabilise mode is active
294 // 4) minimum FBW airspeed (metres/sec)
295 // 5) maximum FBW airspeed (metres/sec)
296 //
297 int32_t AP_PitchController::get_servo_out(int32_t angle_err, float scaler, bool disable_integrator)
298 {
299  // Calculate offset to pitch rate demand required to maintain pitch angle whilst banking
300  // Calculate ideal turn rate from bank angle and airspeed assuming a level coordinated turn
301  // Pitch rate offset is the component of turn rate about the pitch axis
302  float aspeed;
303  float rate_offset;
304  bool inverted;
305 
306  if (gains.tau < 0.1f) {
307  gains.tau.set(0.1f);
308  }
309 
310  rate_offset = _get_coordination_rate_offset(aspeed, inverted);
311 
312  // Calculate the desired pitch rate (deg/sec) from the angle error
313  float desired_rate = angle_err * 0.01f / gains.tau;
314 
315  // limit the maximum pitch rate demand. Don't apply when inverted
316  // as the rates will be tuned when upright, and it is common that
317  // much higher rates are needed inverted
318  if (!inverted) {
319  if (_max_rate_neg && desired_rate < -_max_rate_neg) {
320  desired_rate = -_max_rate_neg;
321  } else if (gains.rmax && desired_rate > gains.rmax) {
322  desired_rate = gains.rmax;
323  }
324  }
325 
326  if (inverted) {
327  desired_rate = -desired_rate;
328  }
329 
330  // Apply the turn correction offset
331  desired_rate = desired_rate + rate_offset;
332 
333  return _get_rate_out(desired_rate, scaler, disable_integrator, aspeed);
334 }
335 
337 {
338  _pid_info.I = 0;
339 }
float _get_coordination_rate_offset(float &aspeed, bool &inverted) const
int32_t get_rate_out(float desired_rate, float scaler)
float roll
Definition: AP_AHRS.h:224
virtual const Vector3f & get_gyro(void) const =0
static const struct AP_Param::GroupInfo var_info[]
void update(float desired_rate, float achieved_rate, float servo_out)
#define AP_GROUPINFO(name, idx, clazz, element, def)
Definition: AP_Param.h:102
float pitch
Definition: AP_AHRS.h:225
virtual bool airspeed_estimate(float *airspeed_ret) const
Definition: AP_AHRS.cpp:170
bool running
Definition: AP_AutoTune.h:51
#define MIN(a, b)
Definition: usb_conf.h:215
int32_t roll_sensor
Definition: AP_AHRS.h:229
#define ToDeg(x)
Definition: AP_Common.h:54
static auto MAX(const A &one, const B &two) -> decltype(one > two ? one :two)
Definition: AP_Math.h:202
DataFlash_Class::PID_Info _pid_info
AP_AutoTune::ATGains gains
int32_t pitch_sensor
Definition: AP_AHRS.h:230
#define GRAVITY_MSS
Definition: definitions.h:47
bool is_zero(const T fVal1)
Definition: AP_Math.h:40
#define f(i)
T y
Definition: vector3.h:67
uint32_t millis()
Definition: system.cpp:157
const AP_HAL::HAL & hal
-*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
Definition: AC_PID_test.cpp:14
float constrain_float(const float amt, const float low, const float high)
Definition: AP_Math.h:142
AP_Int16 roll_limit_cd
Definition: AP_Vehicle.h:41
static constexpr float radians(float deg)
Definition: AP_Math.h:158
const AP_Vehicle::FixedWing & aparm
int32_t get_servo_out(int32_t angle_err, float scaler, bool disable_integrator)
float get_EAS2TAS(void) const
Definition: AP_AHRS.h:290
int32_t _get_rate_out(float desired_rate, float scaler, bool disable_integrator, float aspeed)
#define AP_GROUPEND
Definition: AP_Param.h:121