2 ******************************************************************************
3 * @addtogroup OpenPilotLibraries OpenPilot Libraries
5 * @addtogroup Navigation
6 * @brief setups RTH/PH and other pathfollower/pathplanner status
10 * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2014.
12 * @see The GNU Public License (GPL) Version 3
14 ******************************************************************************/
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 3 of the License, or
19 * (at your option) any later version.
21 * This program is distributed in the hope that it will be useful, but
22 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
23 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
26 * You should have received a copy of the GNU General Public License along
27 * with this program; if not, write to the Free Software Foundation, Inc.,
28 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
32 #include <openpilot.h>
33 #include <attitudesettings.h>
34 #include <takeofflocation.h>
35 #include <pathdesired.h>
36 #include <positionstate.h>
37 #include <flightmodesettings.h>
38 #include <manualcontrolcommand.h>
39 #include <attitudestate.h>
40 #include <sin_lookup.h>
42 #define UPDATE_EXPECTED 0.02f
43 #define UPDATE_MIN 1.0e-6f
44 #define UPDATE_MAX 1.0f
45 #define UPDATE_ALPHA 1.0e-2f
48 * @brief initialize UAVOs and structs used by this library
50 void plan_initialize()
52 TakeOffLocationInitialize();
53 PositionStateInitialize();
54 PathDesiredInitialize();
55 FlightModeSettingsInitialize();
56 AttitudeStateInitialize();
57 ManualControlCommandInitialize();
61 * @brief setup pathplanner/pathfollower for positionhold
63 void plan_setup_positionHold()
65 PositionStateData positionState
;
67 PositionStateGet(&positionState
);
68 PathDesiredData pathDesired
;
69 PathDesiredGet(&pathDesired
);
71 FlightModeSettingsPositionHoldOffsetData offset
;
72 FlightModeSettingsPositionHoldOffsetGet(&offset
);
74 pathDesired
.End
.North
= positionState
.North
;
75 pathDesired
.End
.East
= positionState
.East
;
76 pathDesired
.End
.Down
= positionState
.Down
;
77 pathDesired
.Start
.North
= positionState
.North
+ offset
.Horizontal
; // in FlyEndPoint the direction of this vector does not matter
78 pathDesired
.Start
.East
= positionState
.East
;
79 pathDesired
.Start
.Down
= positionState
.Down
;
80 pathDesired
.StartingVelocity
= 0.0f
;
81 pathDesired
.EndingVelocity
= 0.0f
;
82 pathDesired
.Mode
= PATHDESIRED_MODE_FLYENDPOINT
;
84 PathDesiredSet(&pathDesired
);
88 * @brief setup pathplanner/pathfollower for return to base
90 void plan_setup_returnToBase()
92 // Simple Return To Base mode - keep altitude the same applying configured delta, fly to takeoff position
93 float positionStateDown
;
95 PositionStateDownGet(&positionStateDown
);
97 PathDesiredData pathDesired
;
98 PathDesiredGet(&pathDesired
);
100 TakeOffLocationData takeoffLocation
;
101 TakeOffLocationGet(&takeoffLocation
);
103 // TODO: right now VTOLPF does fly straight to destination altitude.
104 // For a safer RTB destination altitude will be the higher between takeofflocation and current position (corrected with safety margin)
107 FlightModeSettingsReturnToBaseAltitudeOffsetGet(&destDown
);
108 destDown
= MIN(positionStateDown
, takeoffLocation
.Down
) - destDown
;
109 FlightModeSettingsPositionHoldOffsetData offset
;
110 FlightModeSettingsPositionHoldOffsetGet(&offset
);
112 pathDesired
.End
.North
= takeoffLocation
.North
;
113 pathDesired
.End
.East
= takeoffLocation
.East
;
114 pathDesired
.End
.Down
= destDown
;
116 pathDesired
.Start
.North
= takeoffLocation
.North
+ offset
.Horizontal
; // in FlyEndPoint the direction of this vector does not matter
117 pathDesired
.Start
.East
= takeoffLocation
.East
;
118 pathDesired
.Start
.Down
= destDown
;
120 pathDesired
.StartingVelocity
= 0.0f
;
121 pathDesired
.EndingVelocity
= 0.0f
;
122 pathDesired
.Mode
= PATHDESIRED_MODE_FLYENDPOINT
;
124 PathDesiredSet(&pathDesired
);
127 static PiOSDeltatimeConfig landdT
;
128 void plan_setup_land()
132 plan_setup_positionHold();
134 FlightModeSettingsLandingVelocityGet(&descendspeed
);
135 PathDesiredData pathDesired
;
136 PathDesiredGet(&pathDesired
);
137 pathDesired
.StartingVelocity
= descendspeed
;
138 pathDesired
.EndingVelocity
= descendspeed
;
139 PathDesiredSet(&pathDesired
);
140 PIOS_DELTATIME_Init(&landdT
, UPDATE_EXPECTED
, UPDATE_MIN
, UPDATE_MAX
, UPDATE_ALPHA
);
144 * @brief execute land
148 float downPos
, descendspeed
;
149 PathDesiredEndData pathDesiredEnd
;
151 PositionStateDownGet(&downPos
); // current down position
152 PathDesiredEndGet(&pathDesiredEnd
); // desired position
153 PathDesiredEndingVelocityGet(&descendspeed
);
155 // desired position is updated to match the desired descend speed but don't run ahead
156 // too far if the current position can't keep up. This normaly means we have landed.
157 if (pathDesiredEnd
.Down
- downPos
< 10) {
158 pathDesiredEnd
.Down
+= descendspeed
* PIOS_DELTATIME_GetAverageSeconds(&landdT
);
161 PathDesiredEndSet(&pathDesiredEnd
);
166 * @brief positionvario functionality
168 static bool vario_hold
= true;
169 static float hold_position
[3];
170 static float vario_control_lowpass
[4];
171 static float vario_course
= 0.0f
;
173 static void plan_setup_PositionVario()
176 vario_control_lowpass
[0] = 0.0f
;
177 vario_control_lowpass
[1] = 0.0f
;
178 vario_control_lowpass
[2] = 0.0f
;
179 vario_control_lowpass
[3] = 0.0f
;
180 AttitudeStateYawGet(&vario_course
);
181 plan_setup_positionHold();
184 void plan_setup_CourseLock()
186 plan_setup_PositionVario();
189 void plan_setup_PositionRoam()
191 plan_setup_PositionVario();
194 void plan_setup_HomeLeash()
196 plan_setup_PositionVario();
199 void plan_setup_AbsolutePosition()
201 plan_setup_PositionVario();
205 #define DEADBAND 0.1f
206 static bool normalizeDeadband(float controlVector
[4])
210 // roll, pitch, yaw between -1 and +1
211 // thrust between 0 and 1 mapped to -1 to +1
212 controlVector
[3] = (2.0f
* controlVector
[3]) - 1.0f
;
215 for (t
= 0; t
< 4; t
++) {
216 if (controlVector
[t
] < -DEADBAND
) {
218 controlVector
[t
] += DEADBAND
;
219 } else if (controlVector
[t
] > DEADBAND
) {
221 controlVector
[t
] -= DEADBAND
;
223 controlVector
[t
] = 0.0f
;
225 // deadband has been cut out, scale value back to [-1,+1]
226 controlVector
[t
] *= (1.0f
/ (1.0f
- DEADBAND
));
227 controlVector
[t
] = boundf(controlVector
[t
], -1.0f
, 1.0f
);
233 typedef enum { COURSE
, FPV
, LOS
, NSEW
} vario_type
;
235 static void getVector(float controlVector
[4], vario_type type
)
237 FlightModeSettingsPositionHoldOffsetData offset
;
239 FlightModeSettingsPositionHoldOffsetGet(&offset
);
241 // scale controlVector[3] (thrust) by vertical/horizontal to have vertical plane less sensitive
242 controlVector
[3] *= offset
.Vertical
/ offset
.Horizontal
;
244 float length
= sqrtf(controlVector
[0] * controlVector
[0] + controlVector
[1] * controlVector
[1] + controlVector
[3] * controlVector
[3]);
246 if (length
<= 1e-9f
) {
247 length
= 1.0f
; // should never happen as getVector is not called if control within deadband
250 float direction
[3] = {
251 controlVector
[1] / length
, // pitch is north
252 controlVector
[0] / length
, // roll is east
253 controlVector
[3] / length
// thrust is down
255 controlVector
[0] = direction
[0];
256 controlVector
[1] = direction
[1];
257 controlVector
[2] = direction
[2];
259 controlVector
[3] = length
* offset
.Horizontal
;
261 // rotate north and east - rotation angle based on type
265 angle
= vario_course
;
269 // NSEW no rotation takes place
272 // local rotation, using current yaw
273 AttitudeStateYawGet(&angle
);
276 // determine location based on vector from takeoff to current location
278 PositionStateData positionState
;
279 PositionStateGet(&positionState
);
280 TakeOffLocationData takeoffLocation
;
281 TakeOffLocationGet(&takeoffLocation
);
282 angle
= RAD2DEG(atan2f(positionState
.East
- takeoffLocation
.East
, positionState
.North
- takeoffLocation
.North
));
286 // rotate horizontally by angle
289 controlVector
[0] * cos_lookup_deg(angle
) - controlVector
[1] * sin_lookup_deg(angle
),
290 controlVector
[0] * sin_lookup_deg(angle
) + controlVector
[1] * cos_lookup_deg(angle
)
292 controlVector
[0] = rotated
[0];
293 controlVector
[1] = rotated
[1];
298 static void plan_run_PositionVario(vario_type type
)
300 float controlVector
[4];
302 PathDesiredData pathDesired
;
304 PathDesiredGet(&pathDesired
);
305 FlightModeSettingsPositionHoldOffsetData offset
;
306 FlightModeSettingsPositionHoldOffsetGet(&offset
);
309 ManualControlCommandRollGet(&controlVector
[0]);
310 ManualControlCommandPitchGet(&controlVector
[1]);
311 ManualControlCommandYawGet(&controlVector
[2]);
312 ManualControlCommandThrustGet(&controlVector
[3]);
315 FlightModeSettingsVarioControlLowPassAlphaGet(&alpha
);
316 vario_control_lowpass
[0] = alpha
* vario_control_lowpass
[0] + (1.0f
- alpha
) * controlVector
[0];
317 vario_control_lowpass
[1] = alpha
* vario_control_lowpass
[1] + (1.0f
- alpha
) * controlVector
[1];
318 vario_control_lowpass
[2] = alpha
* vario_control_lowpass
[2] + (1.0f
- alpha
) * controlVector
[2];
319 vario_control_lowpass
[3] = alpha
* vario_control_lowpass
[3] + (1.0f
- alpha
) * controlVector
[3];
320 controlVector
[0] = vario_control_lowpass
[0];
321 controlVector
[1] = vario_control_lowpass
[1];
322 controlVector
[2] = vario_control_lowpass
[2];
323 controlVector
[3] = vario_control_lowpass
[3];
325 // check if movement is desired
326 if (normalizeDeadband(controlVector
) == false) {
327 // no movement desired, re-enter positionHold at current start-position
331 // new hold position is the position that was previously the start position
332 pathDesired
.End
.North
= hold_position
[0];
333 pathDesired
.End
.East
= hold_position
[1];
334 pathDesired
.End
.Down
= hold_position
[2];
335 // while the new start position has the same offset as in position hold
336 pathDesired
.Start
.North
= pathDesired
.End
.North
+ offset
.Horizontal
; // in FlyEndPoint the direction of this vector does not matter
337 pathDesired
.Start
.East
= pathDesired
.End
.East
;
338 pathDesired
.Start
.Down
= pathDesired
.End
.Down
;
339 PathDesiredSet(&pathDesired
);
342 PositionStateData positionState
;
343 PositionStateGet(&positionState
);
345 // flip pitch to have pitch down (away) point north
346 controlVector
[1] = -controlVector
[1];
347 getVector(controlVector
, type
);
349 // layout of control Vector : unitVector in movement direction {0,1,2} vector length {3} velocity {4}
351 // start position is the position that was previously the hold position
353 hold_position
[0] = pathDesired
.End
.North
;
354 hold_position
[1] = pathDesired
.End
.East
;
355 hold_position
[2] = pathDesired
.End
.Down
;
357 // start position is advanced according to movement - in the direction of ControlVector only
358 // projection using scalar product
359 float kp
= (positionState
.North
- hold_position
[0]) * controlVector
[0]
360 + (positionState
.East
- hold_position
[1]) * controlVector
[1]
361 + (positionState
.Down
- hold_position
[2]) * -controlVector
[2];
363 hold_position
[0] += kp
* controlVector
[0];
364 hold_position
[1] += kp
* controlVector
[1];
365 hold_position
[2] += kp
* -controlVector
[2];
368 // new destination position is advanced based on controlVector
369 pathDesired
.End
.North
= hold_position
[0] + controlVector
[0] * controlVector
[3];
370 pathDesired
.End
.East
= hold_position
[1] + controlVector
[1] * controlVector
[3];
371 pathDesired
.End
.Down
= hold_position
[2] - controlVector
[2] * controlVector
[3];
372 // the new start position has the same offset as in position hold
373 pathDesired
.Start
.North
= pathDesired
.End
.North
+ offset
.Horizontal
; // in FlyEndPoint the direction of this vector does not matter
374 pathDesired
.Start
.East
= pathDesired
.End
.East
;
375 pathDesired
.Start
.Down
= pathDesired
.End
.Down
;
376 PathDesiredSet(&pathDesired
);
380 void plan_run_CourseLock()
382 plan_run_PositionVario(COURSE
);
385 void plan_run_PositionRoam()
387 plan_run_PositionVario(FPV
);
390 void plan_run_HomeLeash()
392 plan_run_PositionVario(LOS
);
395 void plan_run_AbsolutePosition()
397 plan_run_PositionVario(NSEW
);
402 * @brief setup pathplanner/pathfollower for AutoCruise
404 static PiOSDeltatimeConfig actimeval
;
405 void plan_setup_AutoCruise()
407 PositionStateData positionState
;
409 PositionStateGet(&positionState
);
410 PathDesiredData pathDesired
;
411 PathDesiredGet(&pathDesired
);
413 FlightModeSettingsPositionHoldOffsetData offset
;
414 FlightModeSettingsPositionHoldOffsetGet(&offset
);
416 // initialization is flight in direction of the nose.
417 // the velocity is not relevant, as it will be reset by the run function even during first call
419 AttitudeStateYawGet(&angle
);
421 cos_lookup_deg(angle
),
422 sin_lookup_deg(angle
)
424 hold_position
[0] = positionState
.North
;
425 hold_position
[1] = positionState
.East
;
426 hold_position
[2] = positionState
.Down
;
427 pathDesired
.End
.North
= hold_position
[0] + vector
[0];
428 pathDesired
.End
.East
= hold_position
[1] + vector
[1];
429 pathDesired
.End
.Down
= hold_position
[2];
430 // start position has the same offset as in position hold
431 pathDesired
.Start
.North
= pathDesired
.End
.North
+ offset
.Horizontal
; // in FlyEndPoint the direction of this vector does not matter
432 pathDesired
.Start
.East
= pathDesired
.End
.East
;
433 pathDesired
.Start
.Down
= pathDesired
.End
.Down
;
434 pathDesired
.StartingVelocity
= 0.0f
;
435 pathDesired
.EndingVelocity
= 0.0f
;
436 pathDesired
.Mode
= PATHDESIRED_MODE_FLYENDPOINT
;
438 PathDesiredSet(&pathDesired
);
440 // re-iniztializing deltatime is valid and also good practice here since
441 // getAverageSeconds() has not been called/updated in a long time if we were in a different flightmode.
442 PIOS_DELTATIME_Init(&actimeval
, UPDATE_EXPECTED
, UPDATE_MIN
, UPDATE_MAX
, UPDATE_ALPHA
);
446 * @brief execute autocruise
448 void plan_run_AutoCruise()
450 PositionStateData positionState
;
452 PositionStateGet(&positionState
);
453 PathDesiredData pathDesired
;
454 PathDesiredGet(&pathDesired
);
455 FlightModeSettingsPositionHoldOffsetData offset
;
456 FlightModeSettingsPositionHoldOffsetGet(&offset
);
458 float controlVector
[4];
459 ManualControlCommandRollGet(&controlVector
[0]);
460 ManualControlCommandPitchGet(&controlVector
[1]);
461 ManualControlCommandYawGet(&controlVector
[2]);
462 controlVector
[3] = 0.5f
; // dummy, thrust is normalized separately
463 normalizeDeadband(controlVector
); // return value ignored
464 ManualControlCommandThrustGet(&controlVector
[3]); // no deadband as we are using thrust for velocity
465 controlVector
[3] = boundf(controlVector
[3], 1e-6f
, 1.0f
); // bound to above zero, to prevent loss of vector direction
467 // normalize old desired movement vector
468 float vector
[3] = { pathDesired
.End
.North
- hold_position
[0],
469 pathDesired
.End
.East
- hold_position
[1],
470 pathDesired
.End
.Down
- hold_position
[2] };
471 float length
= sqrtf(vector
[0] * vector
[0] + vector
[1] * vector
[1] + vector
[2] * vector
[2]);
472 if (length
< 1e-9f
) {
473 length
= 1.0f
; // should not happen since initialized properly in setup()
479 // start position is advanced according to actual movement - in the direction of desired vector only
480 // projection using scalar product
481 float kp
= (positionState
.North
- hold_position
[0]) * vector
[0]
482 + (positionState
.East
- hold_position
[1]) * vector
[1]
483 + (positionState
.Down
- hold_position
[2]) * vector
[2];
485 hold_position
[0] += kp
* vector
[0];
486 hold_position
[1] += kp
* vector
[1];
487 hold_position
[2] += kp
* vector
[2];
490 // new angle is equal to old angle plus offset depending on yaw input and time
491 // (controlVector is normalized with a deadband, change is zero within deadband)
492 float angle
= RAD2DEG(atan2f(vector
[1], vector
[0]));
493 float dT
= PIOS_DELTATIME_GetAverageSeconds(&actimeval
);
494 angle
+= 10.0f
* controlVector
[2] * dT
; // TODO magic value could eventually end up in a to be created settings
496 // resulting movement vector is scaled by velocity demand in controlvector[3] [0.0-1.0]
497 vector
[0] = cosf(DEG2RAD(angle
)) * offset
.Horizontal
* controlVector
[3];
498 vector
[1] = sinf(DEG2RAD(angle
)) * offset
.Horizontal
* controlVector
[3];
499 vector
[2] = -controlVector
[1] * offset
.Vertical
* controlVector
[3];
501 pathDesired
.End
.North
= hold_position
[0] + vector
[0];
502 pathDesired
.End
.East
= hold_position
[1] + vector
[1];
503 pathDesired
.End
.Down
= hold_position
[2] + vector
[2];
504 // start position has the same offset as in position hold
505 pathDesired
.Start
.North
= pathDesired
.End
.North
+ offset
.Horizontal
; // in FlyEndPoint the direction of this vector does not matter
506 pathDesired
.Start
.East
= pathDesired
.End
.East
;
507 pathDesired
.Start
.Down
= pathDesired
.End
.Down
;
508 PathDesiredSet(&pathDesired
);