forked from ArduPilot/ardupilot
-
Notifications
You must be signed in to change notification settings - Fork 6
/
mode.cpp
339 lines (282 loc) · 11.3 KB
/
mode.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
#include "Plane.h"
Mode::Mode() :
ahrs(plane.ahrs)
#if HAL_QUADPLANE_ENABLED
, quadplane(plane.quadplane),
pos_control(plane.quadplane.pos_control),
attitude_control(plane.quadplane.attitude_control),
loiter_nav(plane.quadplane.loiter_nav),
poscontrol(plane.quadplane.poscontrol)
#endif
{
}
void Mode::exit()
{
// call sub-classes exit
_exit();
// stop autotuning if not AUTOTUNE mode
if (plane.control_mode != &plane.mode_autotune){
plane.autotune_restore();
}
}
bool Mode::enter()
{
#if AP_SCRIPTING_ENABLED
// reset nav_scripting.enabled
plane.nav_scripting.enabled = false;
#endif
// cancel inverted flight
plane.auto_state.inverted_flight = false;
// cancel waiting for rudder neutral
plane.takeoff_state.waiting_for_rudder_neutral = false;
// don't cross-track when starting a mission
plane.auto_state.next_wp_crosstrack = false;
// reset landing check
plane.auto_state.checked_for_autoland = false;
// zero locked course
plane.steer_state.locked_course_err = 0;
plane.steer_state.locked_course = false;
// reset crash detection
plane.crash_state.is_crashed = false;
plane.crash_state.impact_detected = false;
// reset external attitude guidance
plane.guided_state.last_forced_rpy_ms.zero();
plane.guided_state.last_forced_throttle_ms = 0;
#if OFFBOARD_GUIDED == ENABLED
plane.guided_state.target_heading = -4; // radians here are in range -3.14 to 3.14, so a default value needs to be outside that range
plane.guided_state.target_heading_type = GUIDED_HEADING_NONE;
plane.guided_state.target_airspeed_cm = -1; // same as above, although an airspeed of -1 is rare on plane.
plane.guided_state.target_alt = -1; // same as above, although a target alt of -1 is rare on plane.
plane.guided_state.target_alt_time_ms = 0;
plane.guided_state.last_target_alt = 0;
#endif
#if AP_CAMERA_ENABLED
plane.camera.set_is_auto_mode(this == &plane.mode_auto);
#endif
// zero initial pitch and highest airspeed on mode change
plane.auto_state.highest_airspeed = 0;
plane.auto_state.initial_pitch_cd = ahrs.pitch_sensor;
// disable taildrag takeoff on mode change
plane.auto_state.fbwa_tdrag_takeoff_mode = false;
// start with previous WP at current location
plane.prev_WP_loc = plane.current_loc;
// new mode means new loiter
plane.loiter.start_time_ms = 0;
// record time of mode change
plane.last_mode_change_ms = AP_HAL::millis();
// set VTOL auto state
plane.auto_state.vtol_mode = is_vtol_mode();
plane.auto_state.vtol_loiter = false;
// initialize speed variable used in AUTO and GUIDED for DO_CHANGE_SPEED commands
plane.new_airspeed_cm = -1;
// clear postponed long failsafe if mode change (from GCS) occurs before recall of long failsafe
plane.long_failsafe_pending = false;
#if HAL_QUADPLANE_ENABLED
quadplane.mode_enter();
#endif
#if AP_TERRAIN_AVAILABLE
plane.target_altitude.terrain_following_pending = false;
#endif
bool enter_result = _enter();
if (enter_result) {
// -------------------
// these must be done AFTER _enter() because they use the results to set more flags
// start with throttle suppressed in auto_throttle modes
plane.throttle_suppressed = does_auto_throttle();
#if HAL_ADSB_ENABLED
plane.adsb.set_is_auto_mode(does_auto_navigation());
#endif
// set the nav controller stale AFTER _enter() so that we can check if we're currently in a loiter during the mode change
plane.nav_controller->set_data_is_stale();
// reset steering integrator on mode change
plane.steerController.reset_I();
// update RC failsafe, as mode change may have necessitated changing the failsafe throttle
plane.control_failsafe();
#if AP_FENCE_ENABLED
// pilot requested flight mode change during a fence breach indicates pilot is attempting to manually recover
// this flight mode change could be automatic (i.e. fence, battery, GPS or GCS failsafe)
// but it should be harmless to disable the fence temporarily in these situations as well
plane.fence.manual_recovery_start();
#endif
//reset mission if in landing sequence, disarmed, not flying, and have changed to a non-autothrottle mode to clear prearm
if (plane.mission.get_in_landing_sequence_flag() &&
!plane.is_flying() && !plane.arming.is_armed_and_safety_off() &&
!plane.control_mode->does_auto_navigation()) {
GCS_SEND_TEXT(MAV_SEVERITY_INFO, "In landing sequence: mission reset");
plane.mission.reset();
}
// Make sure the flight stage is correct for the new mode
plane.update_flight_stage();
}
return enter_result;
}
bool Mode::is_vtol_man_throttle() const
{
#if HAL_QUADPLANE_ENABLED
if (plane.quadplane.tailsitter.is_in_fw_flight() &&
plane.quadplane.assisted_flight) {
// We are a tailsitter that has fully transitioned to Q-assisted forward flight.
// In this case the forward throttle directly drives the vertical throttle so
// set vertical throttle state to match the forward throttle state. Confusingly the booleans are inverted,
// forward throttle uses 'does_auto_throttle' whereas vertical uses 'is_vtol_man_throttle'.
return !does_auto_throttle();
}
#endif
return false;
}
void Mode::update_target_altitude()
{
Location target_location;
if (plane.landing.is_flaring()) {
// during a landing flare, use TECS_LAND_SINK as a target sink
// rate, and ignores the target altitude
plane.set_target_altitude_location(plane.next_WP_loc);
} else if (plane.landing.is_on_approach()) {
plane.landing.setup_landing_glide_slope(plane.prev_WP_loc, plane.next_WP_loc, plane.current_loc, plane.target_altitude.offset_cm);
plane.landing.adjust_landing_slope_for_rangefinder_bump(plane.rangefinder_state, plane.prev_WP_loc, plane.next_WP_loc, plane.current_loc, plane.auto_state.wp_distance, plane.target_altitude.offset_cm);
} else if (plane.landing.get_target_altitude_location(target_location)) {
plane.set_target_altitude_location(target_location);
#if HAL_SOARING_ENABLED
} else if (plane.g2.soaring_controller.is_active() && plane.g2.soaring_controller.get_throttle_suppressed()) {
// Reset target alt to current alt, to prevent large altitude errors when gliding.
plane.set_target_altitude_location(plane.current_loc);
plane.reset_offset_altitude();
#endif
} else if (plane.reached_loiter_target()) {
// once we reach a loiter target then lock to the final
// altitude target
plane.set_target_altitude_location(plane.next_WP_loc);
} else if (plane.target_altitude.offset_cm != 0 &&
!plane.current_loc.past_interval_finish_line(plane.prev_WP_loc, plane.next_WP_loc)) {
// control climb/descent rate
plane.set_target_altitude_proportion(plane.next_WP_loc, 1.0f-plane.auto_state.wp_proportion);
// stay within the range of the start and end locations in altitude
plane.constrain_target_altitude_location(plane.next_WP_loc, plane.prev_WP_loc);
} else {
plane.set_target_altitude_location(plane.next_WP_loc);
}
plane.altitude_error_cm = plane.calc_altitude_error_cm();
}
// returns true if the vehicle can be armed in this mode
bool Mode::pre_arm_checks(size_t buflen, char *buffer) const
{
if (!_pre_arm_checks(buflen, buffer)) {
if (strlen(buffer) == 0) {
// If no message is provided add a generic one
hal.util->snprintf(buffer, buflen, "mode not armable");
}
return false;
}
return true;
}
// Auto and Guided do not call this to bypass the q-mode check.
bool Mode::_pre_arm_checks(size_t buflen, char *buffer) const
{
#if HAL_QUADPLANE_ENABLED
if (plane.quadplane.enabled() && !is_vtol_mode() &&
plane.quadplane.option_is_set(QuadPlane::OPTION::ONLY_ARM_IN_QMODE_OR_AUTO)) {
hal.util->snprintf(buffer, buflen, "not Q mode");
return false;
}
#endif
return true;
}
void Mode::run()
{
// Direct stick mixing functionality has been removed, so as not to remove all stick mixing from the user completely
// the old direct option is now used to enable fbw mixing, this is easier than doing a param conversion.
if ((plane.g.stick_mixing == StickMixing::FBW) || (plane.g.stick_mixing == StickMixing::DIRECT_REMOVED)) {
plane.stabilize_stick_mixing_fbw();
}
plane.stabilize_roll();
plane.stabilize_pitch();
plane.stabilize_yaw();
}
// Reset rate and steering controllers
void Mode::reset_controllers()
{
// reset integrators
plane.rollController.reset_I();
plane.pitchController.reset_I();
plane.yawController.reset_I();
// reset steering controls
plane.steer_state.locked_course = false;
plane.steer_state.locked_course_err = 0;
}
bool Mode::is_taking_off() const
{
return (plane.flight_stage == AP_FixedWing::FlightStage::TAKEOFF);
}
// Helper to output to both k_rudder and k_steering servo functions
void Mode::output_rudder_and_steering(float val)
{
SRV_Channels::set_output_scaled(SRV_Channel::k_rudder, val);
SRV_Channels::set_output_scaled(SRV_Channel::k_steering, val);
}
// Output pilot throttle, this is used in stabilized modes without auto throttle control
// Direct mapping if THR_PASS_STAB is set
// Otherwise apply curve for trim correction if configured
void Mode::output_pilot_throttle()
{
if (plane.g.throttle_passthru_stabilize) {
// THR_PASS_STAB set, direct mapping
SRV_Channels::set_output_scaled(SRV_Channel::k_throttle, plane.get_throttle_input(true));
return;
}
// get throttle, but adjust center to output TRIM_THROTTLE if flight option set
SRV_Channels::set_output_scaled(SRV_Channel::k_throttle, plane.get_adjusted_throttle_input(true));
}
// true if throttle min/max limits should be applied
bool Mode::use_throttle_limits() const
{
#if AP_SCRIPTING_ENABLED
if (plane.nav_scripting_active()) {
return false;
}
#endif
if (this == &plane.mode_stabilize ||
this == &plane.mode_training ||
this == &plane.mode_acro ||
this == &plane.mode_fbwa ||
this == &plane.mode_autotune) {
// a manual throttle mode
return !plane.g.throttle_passthru_stabilize;
}
if (is_guided_mode() && plane.guided_throttle_passthru) {
// manual pass through of throttle while in GUIDED
return false;
}
#if HAL_QUADPLANE_ENABLED
if (quadplane.in_vtol_mode()) {
return quadplane.allow_forward_throttle_in_vtol_mode();
}
#endif
return true;
}
// true if voltage correction should be applied to throttle
bool Mode::use_battery_compensation() const
{
#if AP_SCRIPTING_ENABLED
if (plane.nav_scripting_active()) {
return false;
}
#endif
if (this == &plane.mode_stabilize ||
this == &plane.mode_training ||
this == &plane.mode_acro ||
this == &plane.mode_fbwa ||
this == &plane.mode_autotune) {
// a manual throttle mode
return false;
}
if (is_guided_mode() && plane.guided_throttle_passthru) {
// manual pass through of throttle while in GUIDED
return false;
}
#if HAL_QUADPLANE_ENABLED
if (quadplane.in_vtol_mode()) {
return false;
}
#endif
return true;
}