CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
Ardupilot

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.

GitHub Repository: Ardupilot/ardupilot
Path: blob/master/ArduPlane/Attitude.cpp
Views: 1798
1
#include "Plane.h"
2
3
/*
4
calculate speed scaling number for control surfaces. This is applied
5
to PIDs to change the scaling of the PID with speed. At high speed
6
we move the surfaces less, and at low speeds we move them more.
7
*/
8
float Plane::calc_speed_scaler(void)
9
{
10
float aspeed, speed_scaler;
11
if (ahrs.airspeed_estimate(aspeed)) {
12
if (aspeed > auto_state.highest_airspeed && arming.is_armed_and_safety_off()) {
13
auto_state.highest_airspeed = aspeed;
14
}
15
// ensure we have scaling over the full configured airspeed
16
const float airspeed_min = MAX(aparm.airspeed_min, MIN_AIRSPEED_MIN);
17
const float scale_min = MIN(0.5, g.scaling_speed / (2.0 * aparm.airspeed_max));
18
const float scale_max = MAX(2.0, g.scaling_speed / (0.7 * airspeed_min));
19
if (aspeed > 0.0001f) {
20
speed_scaler = g.scaling_speed / aspeed;
21
} else {
22
speed_scaler = scale_max;
23
}
24
speed_scaler = constrain_float(speed_scaler, scale_min, scale_max);
25
26
#if HAL_QUADPLANE_ENABLED
27
if (quadplane.in_vtol_mode() && arming.is_armed_and_safety_off()) {
28
// when in VTOL modes limit surface movement at low speed to prevent instability
29
float threshold = airspeed_min * 0.5;
30
if (aspeed < threshold) {
31
float new_scaler = linear_interpolate(0.001, g.scaling_speed / threshold, aspeed, 0, threshold);
32
speed_scaler = MIN(speed_scaler, new_scaler);
33
34
// we also decay the integrator to prevent an integrator from before
35
// we were at low speed persistent at high speed
36
rollController.decay_I();
37
pitchController.decay_I();
38
yawController.decay_I();
39
}
40
}
41
#endif
42
} else if (arming.is_armed_and_safety_off()) {
43
// scale assumed surface movement using throttle output
44
float throttle_out = MAX(SRV_Channels::get_output_scaled(SRV_Channel::k_throttle), 1);
45
speed_scaler = sqrtf(THROTTLE_CRUISE / throttle_out);
46
// This case is constrained tighter as we don't have real speed info
47
speed_scaler = constrain_float(speed_scaler, 0.6f, 1.67f);
48
} else {
49
// no speed estimate and not armed, use a unit scaling
50
speed_scaler = 1;
51
}
52
if (!plane.ahrs.using_airspeed_sensor() &&
53
(plane.flight_option_enabled(FlightOptions::SURPRESS_TKOFF_SCALING)) &&
54
(plane.flight_stage == AP_FixedWing::FlightStage::TAKEOFF)) { //scaling is suppressed during climb phase of automatic takeoffs with no airspeed sensor being used due to problems with inaccurate airspeed estimates
55
return MIN(speed_scaler, 1.0f) ;
56
}
57
return speed_scaler;
58
}
59
60
/*
61
return true if the current settings and mode should allow for stick mixing
62
*/
63
bool Plane::stick_mixing_enabled(void)
64
{
65
if (!rc().has_valid_input()) {
66
// never stick mix without valid RC
67
return false;
68
}
69
#if AP_FENCE_ENABLED
70
const bool stickmixing = fence_stickmixing();
71
#else
72
const bool stickmixing = true;
73
#endif
74
#if HAL_QUADPLANE_ENABLED
75
if (control_mode == &mode_qrtl &&
76
quadplane.poscontrol.get_state() >= QuadPlane::QPOS_POSITION1) {
77
// user may be repositioning
78
return false;
79
}
80
if (quadplane.in_vtol_land_poscontrol()) {
81
// user may be repositioning
82
return false;
83
}
84
#endif
85
if (control_mode->does_auto_throttle() && plane.control_mode->does_auto_navigation()) {
86
// we're in an auto mode. Check the stick mixing flag
87
if (g.stick_mixing != StickMixing::NONE &&
88
g.stick_mixing != StickMixing::VTOL_YAW &&
89
stickmixing) {
90
return true;
91
} else {
92
return false;
93
}
94
}
95
96
if (failsafe.rc_failsafe && g.fs_action_short == FS_ACTION_SHORT_FBWA) {
97
// don't do stick mixing in FBWA glide mode
98
return false;
99
}
100
101
// non-auto mode. Always do stick mixing
102
return true;
103
}
104
105
106
/*
107
this is the main roll stabilization function. It takes the
108
previously set nav_roll calculates roll servo_out to try to
109
stabilize the plane at the given roll
110
*/
111
void Plane::stabilize_roll()
112
{
113
if (fly_inverted()) {
114
// we want to fly upside down. We need to cope with wrap of
115
// the roll_sensor interfering with wrap of nav_roll, which
116
// would really confuse the PID code. The easiest way to
117
// handle this is to ensure both go in the same direction from
118
// zero
119
nav_roll_cd += 18000;
120
if (ahrs.roll_sensor < 0) nav_roll_cd -= 36000;
121
}
122
123
const float roll_out = stabilize_roll_get_roll_out();
124
SRV_Channels::set_output_scaled(SRV_Channel::k_aileron, roll_out);
125
}
126
127
float Plane::stabilize_roll_get_roll_out()
128
{
129
const float speed_scaler = get_speed_scaler();
130
#if HAL_QUADPLANE_ENABLED
131
if (!quadplane.use_fw_attitude_controllers()) {
132
// use the VTOL rate for control, to ensure consistency
133
const auto &pid_info = quadplane.attitude_control->get_rate_roll_pid().get_pid_info();
134
135
// scale FF to angle P
136
if (quadplane.option_is_set(QuadPlane::OPTION::SCALE_FF_ANGLE_P)) {
137
const float mc_angR = quadplane.attitude_control->get_angle_roll_p().kP()
138
* quadplane.attitude_control->get_last_angle_P_scale().x;
139
if (is_positive(mc_angR)) {
140
rollController.set_ff_scale(MIN(1.0, 1.0 / (mc_angR * rollController.tau())));
141
}
142
}
143
144
const float roll_out = rollController.get_rate_out(degrees(pid_info.target), speed_scaler);
145
/* when slaving fixed wing control to VTOL control we need to decay the integrator to prevent
146
opposing integrators balancing between the two controllers
147
*/
148
rollController.decay_I();
149
return roll_out;
150
}
151
#endif
152
153
bool disable_integrator = false;
154
if (control_mode == &mode_stabilize && channel_roll->get_control_in() != 0) {
155
disable_integrator = true;
156
}
157
return rollController.get_servo_out(nav_roll_cd - ahrs.roll_sensor, speed_scaler, disable_integrator,
158
ground_mode && !(plane.flight_option_enabled(FlightOptions::DISABLE_GROUND_PID_SUPPRESSION)));
159
}
160
161
/*
162
this is the main pitch stabilization function. It takes the
163
previously set nav_pitch and calculates servo_out values to try to
164
stabilize the plane at the given attitude.
165
*/
166
void Plane::stabilize_pitch()
167
{
168
int8_t force_elevator = takeoff_tail_hold();
169
if (force_elevator != 0) {
170
// we are holding the tail down during takeoff. Just convert
171
// from a percentage to a -4500..4500 centidegree angle
172
SRV_Channels::set_output_scaled(SRV_Channel::k_elevator, 45*force_elevator);
173
return;
174
}
175
176
const float pitch_out = stabilize_pitch_get_pitch_out();
177
SRV_Channels::set_output_scaled(SRV_Channel::k_elevator, pitch_out);
178
}
179
180
float Plane::stabilize_pitch_get_pitch_out()
181
{
182
const float speed_scaler = get_speed_scaler();
183
#if HAL_QUADPLANE_ENABLED
184
if (!quadplane.use_fw_attitude_controllers()) {
185
// use the VTOL rate for control, to ensure consistency
186
const auto &pid_info = quadplane.attitude_control->get_rate_pitch_pid().get_pid_info();
187
188
// scale FF to angle P
189
if (quadplane.option_is_set(QuadPlane::OPTION::SCALE_FF_ANGLE_P)) {
190
const float mc_angP = quadplane.attitude_control->get_angle_pitch_p().kP()
191
* quadplane.attitude_control->get_last_angle_P_scale().y;
192
if (is_positive(mc_angP)) {
193
pitchController.set_ff_scale(MIN(1.0, 1.0 / (mc_angP * pitchController.tau())));
194
}
195
}
196
197
const int32_t pitch_out = pitchController.get_rate_out(degrees(pid_info.target), speed_scaler);
198
/* when slaving fixed wing control to VTOL control we need to decay the integrator to prevent
199
opposing integrators balancing between the two controllers
200
*/
201
pitchController.decay_I();
202
return pitch_out;
203
}
204
#endif
205
// if LANDING_FLARE RCx_OPTION switch is set and in FW mode, manual throttle,throttle idle then set pitch to LAND_PITCH_DEG if flight option FORCE_FLARE_ATTITUDE is set
206
#if HAL_QUADPLANE_ENABLED
207
const bool quadplane_in_frwd_transition = quadplane.in_frwd_transition();
208
#else
209
const bool quadplane_in_frwd_transition = false;
210
#endif
211
212
int32_t demanded_pitch = nav_pitch_cd + int32_t(g.pitch_trim * 100.0) + SRV_Channels::get_output_scaled(SRV_Channel::k_throttle) * g.kff_throttle_to_pitch;
213
bool disable_integrator = false;
214
if (control_mode == &mode_stabilize && channel_pitch->get_control_in() != 0) {
215
disable_integrator = true;
216
}
217
/* force landing pitch if:
218
- flare switch high
219
- throttle stick at zero thrust
220
- in fixed wing non auto-throttle mode
221
*/
222
if (!quadplane_in_frwd_transition &&
223
!control_mode->is_vtol_mode() &&
224
!control_mode->does_auto_throttle() &&
225
flare_mode == FlareMode::ENABLED_PITCH_TARGET &&
226
throttle_at_zero()) {
227
demanded_pitch = landing.get_pitch_cd();
228
}
229
230
return pitchController.get_servo_out(demanded_pitch - ahrs.pitch_sensor, speed_scaler, disable_integrator,
231
ground_mode && !(plane.flight_option_enabled(FlightOptions::DISABLE_GROUND_PID_SUPPRESSION)));
232
}
233
234
/*
235
this gives the user control of the aircraft in stabilization modes, only used in Stabilize Mode
236
to be moved to mode_stabilize.cpp in future
237
*/
238
void ModeStabilize::stabilize_stick_mixing_direct()
239
{
240
if (!plane.stick_mixing_enabled()) {
241
return;
242
}
243
#if HAL_QUADPLANE_ENABLED
244
if (!plane.quadplane.allow_stick_mixing()) {
245
return;
246
}
247
#endif
248
float aileron = SRV_Channels::get_output_scaled(SRV_Channel::k_aileron);
249
aileron = plane.channel_roll->stick_mixing(aileron);
250
SRV_Channels::set_output_scaled(SRV_Channel::k_aileron, aileron);
251
252
float elevator = SRV_Channels::get_output_scaled(SRV_Channel::k_elevator);
253
elevator = plane.channel_pitch->stick_mixing(elevator);
254
SRV_Channels::set_output_scaled(SRV_Channel::k_elevator, elevator);
255
}
256
257
/*
258
this gives the user control of the aircraft in stabilization modes
259
using FBW style controls
260
*/
261
void Plane::stabilize_stick_mixing_fbw()
262
{
263
if (!stick_mixing_enabled() ||
264
control_mode == &mode_acro ||
265
control_mode == &mode_fbwa ||
266
control_mode == &mode_autotune ||
267
control_mode == &mode_fbwb ||
268
control_mode == &mode_cruise ||
269
#if HAL_QUADPLANE_ENABLED
270
control_mode == &mode_qstabilize ||
271
control_mode == &mode_qhover ||
272
control_mode == &mode_qloiter ||
273
control_mode == &mode_qland ||
274
control_mode == &mode_qacro ||
275
#if QAUTOTUNE_ENABLED
276
control_mode == &mode_qautotune ||
277
#endif
278
!quadplane.allow_stick_mixing() ||
279
#endif // HAL_QUADPLANE_ENABLED
280
control_mode == &mode_training) {
281
return;
282
}
283
// do FBW style stick mixing. We don't treat it linearly
284
// however. For inputs up to half the maximum, we use linear
285
// addition to the nav_roll and nav_pitch. Above that it goes
286
// non-linear and ends up as 2x the maximum, to ensure that
287
// the user can direct the plane in any direction with stick
288
// mixing.
289
float roll_input = channel_roll->norm_input_dz();
290
if (roll_input > 0.5f) {
291
roll_input = (3*roll_input - 1);
292
} else if (roll_input < -0.5f) {
293
roll_input = (3*roll_input + 1);
294
}
295
nav_roll_cd += roll_input * roll_limit_cd;
296
nav_roll_cd = constrain_int32(nav_roll_cd, -roll_limit_cd, roll_limit_cd);
297
298
if ((control_mode == &mode_loiter) && (plane.flight_option_enabled(FlightOptions::ENABLE_LOITER_ALT_CONTROL))) {
299
// loiter is using altitude control based on the pitch stick, don't use it again here
300
return;
301
}
302
303
float pitch_input = channel_pitch->norm_input_dz();
304
if (pitch_input > 0.5f) {
305
pitch_input = (3*pitch_input - 1);
306
} else if (pitch_input < -0.5f) {
307
pitch_input = (3*pitch_input + 1);
308
}
309
if (fly_inverted()) {
310
pitch_input = -pitch_input;
311
}
312
if (pitch_input > 0) {
313
nav_pitch_cd += pitch_input * aparm.pitch_limit_max*100;
314
} else {
315
nav_pitch_cd += -(pitch_input * pitch_limit_min*100);
316
}
317
nav_pitch_cd = constrain_int32(nav_pitch_cd, pitch_limit_min*100, aparm.pitch_limit_max.get()*100);
318
}
319
320
321
/*
322
stabilize the yaw axis. There are 3 modes of operation:
323
324
- hold a specific heading with ground steering
325
- rate controlled with ground steering
326
- yaw control for coordinated flight
327
*/
328
void Plane::stabilize_yaw()
329
{
330
bool ground_steering = false;
331
if (landing.is_flaring()) {
332
// in flaring then enable ground steering
333
ground_steering = true;
334
} else {
335
// otherwise use ground steering when no input control and we
336
// are below the GROUND_STEER_ALT
337
ground_steering = (channel_roll->get_control_in() == 0 &&
338
fabsf(relative_altitude) < g.ground_steer_alt);
339
if (!landing.is_ground_steering_allowed()) {
340
// don't use ground steering on landing approach
341
ground_steering = false;
342
}
343
}
344
345
346
/*
347
first calculate steering for a nose or tail
348
wheel. We use "course hold" mode for the rudder when either performing
349
a flare (when the wings are held level) or when in course hold in
350
FBWA mode (when we are below GROUND_STEER_ALT)
351
*/
352
float steering_output = 0.0;
353
if (landing.is_flaring() ||
354
(steer_state.hold_course_cd != -1 && ground_steering)) {
355
steering_output = calc_nav_yaw_course();
356
} else if (ground_steering) {
357
steering_output = calc_nav_yaw_ground();
358
}
359
360
/*
361
now calculate rudder for the rudder
362
*/
363
const float rudder_output = calc_nav_yaw_coordinated();
364
365
if (!ground_steering) {
366
// Not doing ground steering, output rudder on steering channel
367
SRV_Channels::set_output_scaled(SRV_Channel::k_rudder, rudder_output);
368
SRV_Channels::set_output_scaled(SRV_Channel::k_steering, rudder_output);
369
370
} else if (!SRV_Channels::function_assigned(SRV_Channel::k_steering)) {
371
// Ground steering active but no steering output configured, output steering on rudder channel
372
SRV_Channels::set_output_scaled(SRV_Channel::k_rudder, steering_output);
373
SRV_Channels::set_output_scaled(SRV_Channel::k_steering, steering_output);
374
375
} else {
376
// Ground steering with both steering and rudder channels
377
SRV_Channels::set_output_scaled(SRV_Channel::k_rudder, rudder_output);
378
SRV_Channels::set_output_scaled(SRV_Channel::k_steering, steering_output);
379
}
380
381
}
382
383
/*
384
main stabilization function for all 3 axes
385
*/
386
void Plane::stabilize()
387
{
388
uint32_t now = AP_HAL::millis();
389
#if HAL_QUADPLANE_ENABLED
390
if (quadplane.available()) {
391
quadplane.transition->set_FW_roll_pitch(nav_pitch_cd, nav_roll_cd);
392
}
393
#endif
394
395
if (now - last_stabilize_ms > 2000) {
396
// if we haven't run the rate controllers for 2 seconds then reset
397
control_mode->reset_controllers();
398
}
399
last_stabilize_ms = now;
400
401
if (control_mode == &mode_training ||
402
control_mode == &mode_manual) {
403
plane.control_mode->run();
404
#if AP_SCRIPTING_ENABLED
405
} else if (nav_scripting_active()) {
406
// scripting is in control of roll and pitch rates and throttle
407
const float speed_scaler = get_speed_scaler();
408
const float aileron = rollController.get_rate_out(nav_scripting.roll_rate_dps, speed_scaler);
409
const float elevator = pitchController.get_rate_out(nav_scripting.pitch_rate_dps, speed_scaler);
410
SRV_Channels::set_output_scaled(SRV_Channel::k_aileron, aileron);
411
SRV_Channels::set_output_scaled(SRV_Channel::k_elevator, elevator);
412
float rudder = 0;
413
if (yawController.rate_control_enabled()) {
414
rudder = nav_scripting.rudder_offset_pct * 45;
415
if (nav_scripting.run_yaw_rate_controller) {
416
rudder += yawController.get_rate_out(nav_scripting.yaw_rate_dps, speed_scaler, false);
417
} else {
418
yawController.reset_I();
419
}
420
}
421
SRV_Channels::set_output_scaled(SRV_Channel::k_rudder, rudder);
422
SRV_Channels::set_output_scaled(SRV_Channel::k_steering, rudder);
423
SRV_Channels::set_output_scaled(SRV_Channel::k_throttle, plane.nav_scripting.throttle_pct);
424
#endif
425
} else {
426
plane.control_mode->run();
427
}
428
429
/*
430
see if we should zero the attitude controller integrators.
431
*/
432
if (is_zero(get_throttle_input()) &&
433
fabsf(relative_altitude) < 5.0f &&
434
fabsf(barometer.get_climb_rate()) < 0.5f &&
435
ahrs.groundspeed() < 3) {
436
// we are low, with no climb rate, and zero throttle, and very
437
// low ground speed. Zero the attitude controller
438
// integrators. This prevents integrator buildup pre-takeoff.
439
rollController.reset_I();
440
pitchController.reset_I();
441
yawController.reset_I();
442
443
// if moving very slowly also zero the steering integrator
444
if (ahrs.groundspeed() < 1) {
445
steerController.reset_I();
446
}
447
}
448
}
449
450
451
/*
452
* Set the throttle output.
453
* This is called by TECS-enabled flight modes, e.g. AUTO, GUIDED, etc.
454
*/
455
void Plane::calc_throttle()
456
{
457
if (aparm.throttle_cruise <= 1) {
458
// user has asked for zero throttle - this may be done by a
459
// mission which wants to turn off the engine for a parachute
460
// landing
461
SRV_Channels::set_output_scaled(SRV_Channel::k_throttle, 0.0);
462
return;
463
}
464
465
// Read the TECS throttle output and set it to the throttle channel.
466
float commanded_throttle = TECS_controller.get_throttle_demand();
467
SRV_Channels::set_output_scaled(SRV_Channel::k_throttle, commanded_throttle);
468
}
469
470
/*****************************************
471
* Calculate desired roll/pitch/yaw angles (in medium freq loop)
472
*****************************************/
473
474
/*
475
calculate yaw control for coordinated flight
476
*/
477
int16_t Plane::calc_nav_yaw_coordinated()
478
{
479
const float speed_scaler = get_speed_scaler();
480
bool disable_integrator = false;
481
int16_t rudder_in = rudder_input();
482
483
int16_t commanded_rudder;
484
bool using_rate_controller = false;
485
486
// Received an external msg that guides yaw in the last 3 seconds?
487
if (control_mode->is_guided_mode() &&
488
plane.guided_state.last_forced_rpy_ms.z > 0 &&
489
millis() - plane.guided_state.last_forced_rpy_ms.z < 3000) {
490
commanded_rudder = plane.guided_state.forced_rpy_cd.z;
491
} else if (autotuning && g.acro_yaw_rate > 0 && yawController.rate_control_enabled()) {
492
// user is doing an AUTOTUNE with yaw rate control
493
const float rudd_expo = rudder_in_expo(true);
494
const float yaw_rate = (rudd_expo/SERVO_MAX) * g.acro_yaw_rate;
495
// add in the coordinated turn yaw rate to make it easier to fly while tuning the yaw rate controller
496
const float coordination_yaw_rate = degrees(GRAVITY_MSS * tanf(radians(nav_roll_cd*0.01f))/MAX(aparm.airspeed_min,smoothed_airspeed));
497
commanded_rudder = yawController.get_rate_out(yaw_rate+coordination_yaw_rate, speed_scaler, false);
498
using_rate_controller = true;
499
} else {
500
if (control_mode == &mode_stabilize && rudder_in != 0) {
501
disable_integrator = true;
502
}
503
504
commanded_rudder = yawController.get_servo_out(speed_scaler, disable_integrator);
505
506
// add in rudder mixing from roll
507
commanded_rudder += SRV_Channels::get_output_scaled(SRV_Channel::k_aileron) * g.kff_rudder_mix;
508
commanded_rudder += rudder_in;
509
}
510
511
if (!using_rate_controller) {
512
/*
513
When not running the yaw rate controller, we need to reset the rate
514
*/
515
yawController.reset_rate_PID();
516
}
517
518
return constrain_int16(commanded_rudder, -4500, 4500);
519
}
520
521
/*
522
calculate yaw control for ground steering with specific course
523
*/
524
int16_t Plane::calc_nav_yaw_course(void)
525
{
526
// holding a specific navigation course on the ground. Used in
527
// auto-takeoff and landing
528
int32_t bearing_error_cd = nav_controller->bearing_error_cd();
529
int16_t steering = steerController.get_steering_out_angle_error(bearing_error_cd);
530
if (stick_mixing_enabled()) {
531
steering = channel_rudder->stick_mixing(steering);
532
}
533
return constrain_int16(steering, -4500, 4500);
534
}
535
536
/*
537
calculate yaw control for ground steering
538
*/
539
int16_t Plane::calc_nav_yaw_ground(void)
540
{
541
if (gps.ground_speed() < 1 &&
542
is_zero(get_throttle_input()) &&
543
flight_stage != AP_FixedWing::FlightStage::TAKEOFF &&
544
flight_stage != AP_FixedWing::FlightStage::ABORT_LANDING) {
545
// manual rudder control while still
546
steer_state.locked_course = false;
547
steer_state.locked_course_err = 0;
548
return rudder_input();
549
}
550
551
// if we haven't been steering for 1s then clear locked course
552
const uint32_t now_ms = AP_HAL::millis();
553
if (now_ms - steer_state.last_steer_ms > 1000) {
554
steer_state.locked_course = false;
555
}
556
steer_state.last_steer_ms = now_ms;
557
558
float steer_rate = (rudder_input()/4500.0f) * g.ground_steer_dps;
559
if (flight_stage == AP_FixedWing::FlightStage::TAKEOFF ||
560
flight_stage == AP_FixedWing::FlightStage::ABORT_LANDING) {
561
steer_rate = 0;
562
}
563
if (!is_zero(steer_rate)) {
564
// pilot is giving rudder input
565
steer_state.locked_course = false;
566
} else if (!steer_state.locked_course) {
567
// pilot has released the rudder stick or we are still - lock the course
568
steer_state.locked_course = true;
569
if (flight_stage != AP_FixedWing::FlightStage::TAKEOFF &&
570
flight_stage != AP_FixedWing::FlightStage::ABORT_LANDING) {
571
steer_state.locked_course_err = 0;
572
}
573
}
574
575
int16_t steering;
576
if (!steer_state.locked_course) {
577
// use a rate controller at the pilot specified rate
578
steering = steerController.get_steering_out_rate(steer_rate);
579
} else {
580
// use a error controller on the summed error
581
int32_t yaw_error_cd = -ToDeg(steer_state.locked_course_err)*100;
582
steering = steerController.get_steering_out_angle_error(yaw_error_cd);
583
}
584
return constrain_int16(steering, -4500, 4500);
585
}
586
587
588
/*
589
calculate a new nav_pitch_cd from the speed height controller
590
*/
591
void Plane::calc_nav_pitch()
592
{
593
int32_t commanded_pitch = TECS_controller.get_pitch_demand();
594
nav_pitch_cd = constrain_int32(commanded_pitch, pitch_limit_min*100, aparm.pitch_limit_max.get()*100);
595
}
596
597
598
/*
599
calculate a new nav_roll_cd from the navigation controller
600
*/
601
void Plane::calc_nav_roll()
602
{
603
int32_t commanded_roll = nav_controller->nav_roll_cd();
604
nav_roll_cd = constrain_int32(commanded_roll, -roll_limit_cd, roll_limit_cd);
605
update_load_factor();
606
}
607
608
/*
609
adjust nav_pitch_cd for STAB_PITCH_DOWN_CD. This is used to make
610
keeping up good airspeed in FBWA mode easier, as the plane will
611
automatically pitch down a little when at low throttle. It makes
612
FBWA landings without stalling much easier.
613
*/
614
void Plane::adjust_nav_pitch_throttle(void)
615
{
616
int8_t throttle = throttle_percentage();
617
if (throttle >= 0 && throttle < aparm.throttle_cruise && flight_stage != AP_FixedWing::FlightStage::VTOL) {
618
float p = (aparm.throttle_cruise - throttle) / (float)aparm.throttle_cruise;
619
nav_pitch_cd -= g.stab_pitch_down * 100.0f * p;
620
}
621
}
622
623
624
/*
625
calculate a new aerodynamic_load_factor and limit nav_roll_cd to
626
ensure that the load factor does not take us below the sustainable
627
airspeed
628
*/
629
void Plane::update_load_factor(void)
630
{
631
float demanded_roll = fabsf(nav_roll_cd*0.01f);
632
if (demanded_roll > 85) {
633
// limit to 85 degrees to prevent numerical errors
634
demanded_roll = 85;
635
}
636
aerodynamic_load_factor = 1.0f / safe_sqrt(cosf(radians(demanded_roll)));
637
638
#if HAL_QUADPLANE_ENABLED
639
if (quadplane.available() && quadplane.transition->set_FW_roll_limit(roll_limit_cd)) {
640
nav_roll_cd = constrain_int32(nav_roll_cd, -roll_limit_cd, roll_limit_cd);
641
return;
642
}
643
#endif
644
645
if (!aparm.stall_prevention) {
646
// stall prevention is disabled
647
return;
648
}
649
if (fly_inverted()) {
650
// no roll limits when inverted
651
return;
652
}
653
#if HAL_QUADPLANE_ENABLED
654
if (quadplane.tailsitter.active()) {
655
// no limits while hovering
656
return;
657
}
658
#endif
659
660
float max_load_factor = smoothed_airspeed / MAX(aparm.airspeed_min, 1);
661
if (max_load_factor <= 1) {
662
// our airspeed is below the minimum airspeed. Limit roll to
663
// 25 degrees
664
nav_roll_cd = constrain_int32(nav_roll_cd, -2500, 2500);
665
roll_limit_cd = MIN(roll_limit_cd, 2500);
666
} else if (max_load_factor < aerodynamic_load_factor) {
667
// the demanded nav_roll would take us past the aerodynamic
668
// load limit. Limit our roll to a bank angle that will keep
669
// the load within what the airframe can handle. We always
670
// allow at least 25 degrees of roll however, to ensure the
671
// aircraft can be manoeuvred with a bad airspeed estimate. At
672
// 25 degrees the load factor is 1.1 (10%)
673
int32_t roll_limit = degrees(acosf(sq(1.0f / max_load_factor)))*100;
674
if (roll_limit < 2500) {
675
roll_limit = 2500;
676
}
677
nav_roll_cd = constrain_int32(nav_roll_cd, -roll_limit, roll_limit);
678
roll_limit_cd = MIN(roll_limit_cd, roll_limit);
679
}
680
}
681
682