dRonin  adbada4
dRonin firmware
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
vtol_follower_control.c
Go to the documentation of this file.
1 
16 /*
17  * This program is free software; you can redistribute it and/or modify
18  * it under the terms of the GNU General Public License as published by
19  * the Free Software Foundation; either version 3 of the License, or
20  * (at your option) any later version.
21  *
22  * This program is distributed in the hope that it will be useful, but
23  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
24  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
25  * for more details.
26  *
27  * You should have received a copy of the GNU General Public License along
28  * with this program; if not, see <http://www.gnu.org/licenses/>
29  *
30  * Additional note on redistribution: The copyright and license notices above
31  * must be maintained in each individual source file that is a derivative work
32  * of this source file; otherwise redistribution is prohibited.
33  */
34 
35 #include "openpilot.h"
36 
37 #include "coordinate_conversions.h"
38 #include "physical_constants.h"
39 #include "misc_math.h"
40 #include "paths.h"
41 #include "pid.h"
42 
43 #include "vtol_follower_priv.h"
44 
45 #include "acceldesired.h"
46 #include "altitudeholdsettings.h"
47 #include "altitudeholdstate.h"
48 #include "attitudeactual.h"
49 #include "loitercommand.h"
50 #include "pathdesired.h" // object that will be updated by the module
51 #include "positionactual.h"
52 #include "manualcontrolcommand.h"
53 #include "flightstatus.h"
54 #include "nedaccel.h"
55 #include "pathstatus.h"
56 #include "stabilizationdesired.h"
57 #include "stabilizationsettings.h"
58 #include "velocitydesired.h"
59 #include "velocityactual.h"
60 #include "vtolpathfollowersettings.h"
61 #include "systemsettings.h"
62 
63 // Private variables
64 static AltitudeHoldSettingsData altitudeHoldSettings;
66 
67 // Constants used in deadband calculation
68 static float vtol_path_m=0, vtol_path_r=0, vtol_end_m=0, vtol_end_r=0;
69 
70 // Time constants converted to IIR parameter
71 static float loiter_brakealpha=0.96f, loiter_errordecayalpha=0.88f;
72 
73 static int32_t vtol_follower_control_impl(
74  const float *hold_pos_ned, float alt_rate, bool update_status);
75 
85 int32_t vtol_follower_control_path(const PathDesiredData *pathDesired,
86  struct path_status *progress)
87 {
88  PositionActualData positionActual;
89  PositionActualGet(&positionActual);
90 
91  VelocityActualData velocityActual;
92  VelocityActualGet(&velocityActual);
93 
94  PathStatusData pathStatus;
95  PathStatusGet(&pathStatus);
96 
97  const float cur_pos_ned[3] = {
98  positionActual.North +
99  velocityActual.North * vtol_guidanceSettings.PositionFeedforward,
100  positionActual.East +
101  velocityActual.East * vtol_guidanceSettings.PositionFeedforward,
102  positionActual.Down };
103 
104  path_progress(pathDesired, cur_pos_ned, progress);
105 
106  // Check if we have already completed this leg
107  bool current_leg_completed =
108  (pathStatus.Status == PATHSTATUS_STATUS_COMPLETED) &&
109  (pathStatus.Waypoint == pathDesired->Waypoint);
110 
111  pathStatus.fractional_progress = progress->fractional_progress;
112  pathStatus.error = progress->error;
113  pathStatus.Waypoint = pathDesired->Waypoint;
114 
115  // Figure out how low (high) we should be and the error
116  const float altitudeSetpoint = interpolate_value(progress->fractional_progress,
117  pathDesired->Start[2], pathDesired->End[2]);
118 
119  const float downError = altitudeSetpoint - positionActual.Down;
120 
121  // If leg is completed signal this
122  if (current_leg_completed || pathStatus.fractional_progress > 1.0f) {
123  const bool criterion_altitude =
124  (downError > -vtol_guidanceSettings.WaypointAltitudeTol) ||
125  (!vtol_guidanceSettings.ThrottleControl);
126 
127  // Once we complete leg and hit altitude criterion signal this
128  // waypoint is done. Or if we're not controlling throttle,
129  // ignore height for completion.
130 
131  // Waypoint heights are thus treated as crossing restrictions-
132  // cross this point at or above...
133  if (criterion_altitude || current_leg_completed) {
134  pathStatus.Status = PATHSTATUS_STATUS_COMPLETED;
135  PathStatusSet(&pathStatus);
136  } else {
137  pathStatus.Status = PATHSTATUS_STATUS_INPROGRESS;
138  PathStatusSet(&pathStatus);
139  }
140 
141  // Wait here for new path segment
142  return vtol_follower_control_impl(pathDesired->End, 0, false);
143  }
144 
145  // Interpolate desired velocity and altitude along the path
147  pathDesired->StartingVelocity, pathDesired->EndingVelocity);
148 
149  float error_speed = cubic_deadband(progress->error,
150  vtol_guidanceSettings.PathDeadbandWidth,
151  vtol_guidanceSettings.PathDeadbandCenterGain,
153  vtol_guidanceSettings.HorizontalPosPI[VTOLPATHFOLLOWERSETTINGS_HORIZONTALPOSPI_KP];
154 
155  /* Sum the desired path movement vector with the correction vector */
156  float commands_ned[3];
157  commands_ned[0] = progress->path_direction[0] * groundspeed +
158  progress->correction_direction[0] * error_speed;
159 
160  commands_ned[1] = progress->path_direction[1] * groundspeed +
161  progress->correction_direction[1] * error_speed;
162 
163  /* Limit the total velocity based on the configured value. */
164  vector2_clip(commands_ned, vtol_guidanceSettings.HorizontalVelMax);
165 
166  commands_ned[2] = pid_apply_antiwindup(&vtol_pids[DOWN_POSITION], downError,
167  -altitudeHoldSettings.MaxClimbRate * 0.1f,
168  altitudeHoldSettings.MaxDescentRate * 0.1f,
169  0);
170 
171  VelocityDesiredData velocityDesired;
172  VelocityDesiredGet(&velocityDesired);
173  velocityDesired.North = commands_ned[0];
174  velocityDesired.East = commands_ned[1];
175  velocityDesired.Down = commands_ned[2];
176  VelocityDesiredSet(&velocityDesired);
177 
178  pathStatus.Status = PATHSTATUS_STATUS_INPROGRESS;
179  PathStatusSet(&pathStatus);
180 
181  return 0;
182 }
183 
192  const float *hold_pos_ned, float alt_rate, bool update_status)
193 {
194  PositionActualData positionActual;
195  VelocityDesiredData velocityDesired;
196 
197  PositionActualGet(&positionActual);
198 
199  VelocityActualData velocityActual;
200 
201  VelocityActualGet(&velocityActual);
202 
203  /* Where would we be in ___ second at current rates? */
204  const float cur_pos_ned[3] = {
205  positionActual.North +
206  velocityActual.North * vtol_guidanceSettings.PositionFeedforward,
207  positionActual.East +
208  velocityActual.East * vtol_guidanceSettings.PositionFeedforward,
209  positionActual.Down };
210 
211  float errors_ned[3];
212 
213  /* Calculate the difference between where we want to be and the
214  * above position */
215  vector3_distances(cur_pos_ned, hold_pos_ned, errors_ned, false);
216 
217  float horiz_error_mag = vectorn_magnitude(errors_ned, 2);
218  float scale_horiz_error_mag = 0;
219 
220  /* Apply a cubic deadband; if we are already really close don't work
221  * as hard to fix it as if we're far away. Prevents chasing high
222  * frequency noise in direction of correction.
223  *
224  * That is, when we're far, noise in estimated position mostly results
225  * in noise/dither in how fast we go towards the target. When we're
226  * close, there's a large directional / hunting component. This
227  * attenuates that.
228  */
229  if (horiz_error_mag > 0.00001f) {
230  scale_horiz_error_mag = cubic_deadband(horiz_error_mag,
231  vtol_guidanceSettings.EndpointDeadbandWidth,
232  vtol_guidanceSettings.EndpointDeadbandCenterGain,
233  vtol_end_m, vtol_end_r) / horiz_error_mag;
234  }
235 
236  float damped_ne[2] = { errors_ned[0] * scale_horiz_error_mag,
237  errors_ned[1] * scale_horiz_error_mag };
238 
239  float commands_ned[3];
240 
241  // Compute desired north command velocity from position error
242  commands_ned[0] = pid_apply_antiwindup(&vtol_pids[NORTH_POSITION], damped_ne[0],
243  -vtol_guidanceSettings.HorizontalVelMax, vtol_guidanceSettings.HorizontalVelMax, 0);
244 
245  // Compute desired east command velocity from position error
246  commands_ned[1] = pid_apply_antiwindup(&vtol_pids[EAST_POSITION], damped_ne[1],
247  -vtol_guidanceSettings.HorizontalVelMax, vtol_guidanceSettings.HorizontalVelMax, 0);
248 
249  if (fabsf(alt_rate) < 0.001f) {
250  // Compute desired down comand velocity from the position difference
251  commands_ned[2] = pid_apply_antiwindup(&vtol_pids[DOWN_POSITION], errors_ned[2],
252  -altitudeHoldSettings.MaxClimbRate * 0.1f,
253  altitudeHoldSettings.MaxDescentRate * 0.1f,
254  0);
255  } else {
256  // Just use the commanded rate
257  commands_ned[2] = alt_rate;
258  }
259 
260  // Limit the maximum horizontal velocity any direction (not north and east separately)
261  vector2_clip(commands_ned, vtol_guidanceSettings.HorizontalVelMax);
262 
263  velocityDesired.North = commands_ned[0];
264  velocityDesired.East = commands_ned[1];
265  velocityDesired.Down = commands_ned[2];
266 
267  VelocityDesiredSet(&velocityDesired);
268 
269  if (update_status) {
270  uint8_t path_status = PATHSTATUS_STATUS_INPROGRESS;
271 
272  if (fabsf(alt_rate) < 0.001f) {
273  const bool criterion_altitude =
274  (errors_ned[2]> -vtol_guidanceSettings.WaypointAltitudeTol) || (!vtol_guidanceSettings.ThrottleControl);
275 
276  // Indicate whether we are in radius of this endpoint
277  // And at/above the altitude requested
278  if ((vectorn_magnitude(errors_ned, 2) < vtol_guidanceSettings.EndpointRadius) && criterion_altitude) {
279  path_status = PATHSTATUS_STATUS_COMPLETED;
280  }
281  }
282  // Otherwise, we're not done-- we're in autoland or someone
283  // upstream is explicitly trimming our altitude
284 
285  PathStatusStatusSet(&path_status);
286  }
287 
288  return 0;
289 }
290 
301 int32_t vtol_follower_control_endpoint(const float *hold_pos_ned)
302 {
303  return vtol_follower_control_impl(hold_pos_ned, 0, true);
304 }
305 
314 int32_t vtol_follower_control_land(const float *hold_pos_ned,
315  bool *landed)
316 {
317  return vtol_follower_control_impl(hold_pos_ned,
318  vtol_guidanceSettings.LandingRate, true);
319 }
320 
327 int32_t vtol_follower_control_altrate(const float *hold_pos_ned,
328  float alt_adj)
329 {
330  return vtol_follower_control_impl(hold_pos_ned,
331  alt_adj, true);
332 }
333 
338 static int32_t vtol_follower_control_accel(float dT)
339 {
340  VelocityDesiredData velocityDesired;
341  VelocityActualData velocityActual;
342  AccelDesiredData accelDesired;
343  NedAccelData nedAccel;
344 
345  float north_error, north_acceleration;
346  float east_error, east_acceleration;
347  float down_error;
348 
349  static float last_north_velocity;
350  static float last_east_velocity;
351 
352  NedAccelGet(&nedAccel);
353  VelocityActualGet(&velocityActual);
354  VelocityDesiredGet(&velocityDesired);
355 
356  // Optionally compute the acceleration required component from a changing velocity desired
357  if (vtol_guidanceSettings.VelocityChangePrediction == VTOLPATHFOLLOWERSETTINGS_VELOCITYCHANGEPREDICTION_TRUE && dT > 0) {
358  north_acceleration = (velocityDesired.North - last_north_velocity) / dT;
359  east_acceleration = (velocityDesired.East - last_east_velocity) / dT;
360  last_north_velocity = velocityDesired.North;
361  last_east_velocity = velocityDesired.East;
362  } else {
363  north_acceleration = 0;
364  east_acceleration = 0;
365  }
366 
367  // Convert the max angles into the maximum angle that would be requested
368  const float MAX_ACCELERATION = GRAVITY * sinf(vtol_guidanceSettings.MaxRollPitch * DEG2RAD);
369 
370  // Compute desired north command from velocity error
371  north_error = velocityDesired.North - velocityActual.North;
372  north_acceleration += pid_apply_antiwindup(&vtol_pids[NORTH_VELOCITY], north_error,
373  -MAX_ACCELERATION, MAX_ACCELERATION, 0) +
374  velocityDesired.North * vtol_guidanceSettings.VelocityFeedforward +
375  -nedAccel.North * vtol_guidanceSettings.HorizontalVelPID[VTOLPATHFOLLOWERSETTINGS_HORIZONTALVELPID_KD];
376 
377  // Compute desired east command from velocity error
378  east_error = velocityDesired.East - velocityActual.East;
379  east_acceleration += pid_apply_antiwindup(&vtol_pids[EAST_VELOCITY], east_error,
380  -MAX_ACCELERATION, MAX_ACCELERATION, 0) +
381  velocityDesired.East * vtol_guidanceSettings.VelocityFeedforward +
382  -nedAccel.East * vtol_guidanceSettings.HorizontalVelPID[VTOLPATHFOLLOWERSETTINGS_HORIZONTALVELPID_KD];
383 
384  accelDesired.North = north_acceleration;
385  accelDesired.East = east_acceleration;
386 
387  // Note: vertical controller really isn't currently in units of acceleration and the anti-windup may
388  // not be appropriate. However, it is fine for now since it this output is just directly used on the
389  // output. To use this appropriately we need a model of throttle to acceleration.
390  // Compute desired down command. Using NED accel as the damping term
391  down_error = velocityDesired.Down - velocityActual.Down;
392  // Negative is critical here since throttle is negative with down
393  accelDesired.Down = -pid_apply_antiwindup(&vtol_pids[DOWN_VELOCITY], down_error, -1, 0, 0);
394 
395  // Store the desired acceleration
396  AccelDesiredSet(&accelDesired);
397 
398  return 0;
399 }
400 
401 
402 static float vtol_follower_control_altitude(float downCommand) {
403  AltitudeHoldStateData altitudeHoldState;
404  altitudeHoldState.VelocityDesired = downCommand;
405  altitudeHoldState.Integral = vtol_pids[DOWN_VELOCITY].iAccumulator;
406  altitudeHoldState.AngleGain = 1.0f;
407 
408  if (altitudeHoldSettings.AttitudeComp > 0) {
409  // Thrust desired is at this point the mount desired in the up direction, we can
410  // account for the attitude if desired
411  AttitudeActualData attitudeActual;
412  AttitudeActualGet(&attitudeActual);
413 
414  // Project a unit vector pointing up into the body frame and
415  // get the z component
416  float fraction = attitudeActual.q1 * attitudeActual.q1 -
417  attitudeActual.q2 * attitudeActual.q2 -
418  attitudeActual.q3 * attitudeActual.q3 +
419  attitudeActual.q4 * attitudeActual.q4;
420 
421  // Add ability to scale up the amount of compensation to achieve
422  // level forward flight
423  fraction = powf(fraction, (float) altitudeHoldSettings.AttitudeComp / 100.0f);
424 
425  // Dividing by the fraction remaining in the vertical projection will
426  // attempt to compensate for tilt. This acts like the thrust is linear
427  // with the output which isn't really true. If the fraction is starting
428  // to go negative we are inverted and should shut off throttle
429  downCommand = (fraction > 0.1f) ? (downCommand / fraction) : 0.0f;
430 
431  altitudeHoldState.AngleGain = 1.0f / fraction;
432  }
433 
434  altitudeHoldState.Thrust = downCommand;
435  AltitudeHoldStateSet(&altitudeHoldState);
436 
437  return downCommand;
438 }
439 
449 int32_t vtol_follower_control_attitude(float dT, const float *att_adj)
450 {
452 
453  float default_adj[2] = {0,0};
454 
455  if (!att_adj) {
456  att_adj = default_adj;
457  }
458 
459  AccelDesiredData accelDesired;
460  AccelDesiredGet(&accelDesired);
461 
462  StabilizationSettingsData stabSet;
463  StabilizationSettingsGet(&stabSet);
464 
465  SystemSettingsData system_settings;
466  SystemSettingsGet( &system_settings );
467 
468  ManualControlCommandData manual_control_command;
469  ManualControlCommandGet( &manual_control_command );
470 
471  float northCommand = accelDesired.North;
472  float eastCommand = accelDesired.East;
473 
474  // Project the north and east acceleration signals into body frame
475  float yaw;
476  AttitudeActualYawGet(&yaw);
477  float forward_accel_desired = -northCommand * cosf(yaw * DEG2RAD) + -eastCommand * sinf(yaw * DEG2RAD);
478  float right_accel_desired = -northCommand * sinf(yaw * DEG2RAD) + eastCommand * cosf(yaw * DEG2RAD);
479 
480  StabilizationDesiredData stabDesired = {};
481 
482  // Set the angle that would achieve the desired acceleration given the thrust is enough for a hover
483  stabDesired.Pitch = bound_sym(RAD2DEG * atanf(forward_accel_desired / GRAVITY), vtol_guidanceSettings.MaxRollPitch) + att_adj[1];
484  stabDesired.Roll = bound_sym(RAD2DEG * atanf(right_accel_desired / GRAVITY), vtol_guidanceSettings.MaxRollPitch) + att_adj[0];
485 
486  // Re-bound based on maximum attitude settings
487  stabDesired.Pitch = bound_sym(stabDesired.Pitch,
488  stabSet.MaxLevelAngle[STABILIZATIONSETTINGS_MAXLEVELANGLE_PITCH]);
489  stabDesired.Roll = bound_sym(stabDesired.Roll,
490  stabSet.MaxLevelAngle[STABILIZATIONSETTINGS_MAXLEVELANGLE_ROLL]);
491 
492  stabDesired.StabilizationMode[STABILIZATIONDESIRED_STABILIZATIONMODE_ROLL] = STABILIZATIONDESIRED_STABILIZATIONMODE_ATTITUDE;
493  stabDesired.StabilizationMode[STABILIZATIONDESIRED_STABILIZATIONMODE_PITCH] = STABILIZATIONDESIRED_STABILIZATIONMODE_ATTITUDE;
494 
495  stabDesired.ThrustMode = STABILIZATIONDESIRED_THRUSTMODE_DIRECT;
496  stabDesired.ReprojectionMode = STABILIZATIONDESIRED_REPROJECTIONMODE_NONE;
497 
498  // Calculate the throttle setting or use pass through from transmitter
499  if (vtol_guidanceSettings.ThrottleControl == VTOLPATHFOLLOWERSETTINGS_THROTTLECONTROL_FALSE) {
500  stabDesired.Thrust = (system_settings.AirframeType == SYSTEMSETTINGS_AIRFRAMETYPE_HELICP) ? manual_control_command.Collective : manual_control_command.Throttle;
501  } else {
502  float downCommand = vtol_follower_control_altitude(accelDesired.Down);
503 
504  stabDesired.Thrust = bound_min_max(downCommand, 0, 1);
505  }
506 
507  // Various ways to control the yaw that are essentially manual passthrough. However, because we do not have a fine
508  // grained mechanism of manual setting the yaw as it normally would we need to duplicate that code here
509  switch(vtol_guidanceSettings.YawMode) {
510  case VTOLPATHFOLLOWERSETTINGS_YAWMODE_RATE:
511  /* This is awkward. This allows the transmitter to control the yaw while flying navigation */
512  stabDesired.Yaw = stabSet.ManualRate[STABILIZATIONSETTINGS_MANUALRATE_YAW] * manual_control_command.Yaw;
513  stabDesired.StabilizationMode[STABILIZATIONDESIRED_STABILIZATIONMODE_YAW] = STABILIZATIONDESIRED_STABILIZATIONMODE_RATE;
514  break;
515  case VTOLPATHFOLLOWERSETTINGS_YAWMODE_AXISLOCK:
516  stabDesired.Yaw = stabSet.ManualRate[STABILIZATIONSETTINGS_MANUALRATE_YAW] * manual_control_command.Yaw;
517  stabDesired.StabilizationMode[STABILIZATIONDESIRED_STABILIZATIONMODE_YAW] = STABILIZATIONDESIRED_STABILIZATIONMODE_AXISLOCK;
518  break;
519  case VTOLPATHFOLLOWERSETTINGS_YAWMODE_PATH:
520  {
521  // Face forward on the path
522  VelocityDesiredData velocityDesired;
523  VelocityDesiredGet(&velocityDesired);
524  float total_vel2 = velocityDesired.East*velocityDesired.East + velocityDesired.North*velocityDesired.North;
525  float path_direction = atan2f(velocityDesired.East, velocityDesired.North) * RAD2DEG;
526  if (total_vel2 > 1) {
527  stabDesired.Yaw = path_direction;
528  stabDesired.StabilizationMode[STABILIZATIONDESIRED_STABILIZATIONMODE_YAW] = STABILIZATIONDESIRED_STABILIZATIONMODE_ATTITUDE;
529  } else {
530  stabDesired.Yaw = 0;
531  stabDesired.StabilizationMode[STABILIZATIONDESIRED_STABILIZATIONMODE_YAW] = STABILIZATIONDESIRED_STABILIZATIONMODE_RATE;
532  }
533  }
534  break;
535  case VTOLPATHFOLLOWERSETTINGS_YAWMODE_POI:
536  stabDesired.StabilizationMode[STABILIZATIONDESIRED_STABILIZATIONMODE_YAW] = STABILIZATIONDESIRED_STABILIZATIONMODE_POI;
537  break;
538  }
539 
540  StabilizationDesiredSet(&stabDesired);
541 
542  return 0;
543 }
544 
545 static float loiter_deadband(float input, float threshold, float expoPercent) {
546  if (input > threshold) {
547  input -= threshold;
548  } else if (input < -threshold) {
549  input += threshold;
550  } else {
551  input = 0;
552  }
553 
554  input /= (1 - threshold); // Normalize to -1 to 1 range.
555 
556  /* Confine to desired range */
557  if (input > 1.0f) {
558  input = 1.0f;
559  } else if (input < -1.0f) {
560  input = -1.0f;
561  }
562 
563  return expo3(input, expoPercent);
564 }
565 
576 bool vtol_follower_control_loiter(float dT, float *hold_pos, float *att_adj,
577  float *alt_adj) {
578  LoiterCommandData cmd;
579  LoiterCommandGet(&cmd);
580 
581  // XXX TODO reproject when we're not issuing body-centric commands
582  float commands_rp[2] = {
583  cmd.Roll,
584  cmd.Pitch
585  };
586 
587  const float CMD_THRESHOLD = 0.2f;
588 
589  float command_mag = vectorn_magnitude(commands_rp, 2);
590  float deadband_mag = loiter_deadband(command_mag, CMD_THRESHOLD, 40);
591 
592  float down_cmd = 0;
593 
594  if (vtol_guidanceSettings.ThrottleControl &&
595  vtol_guidanceSettings.LoiterAllowAltControl) {
596  // Inverted because we want units in "Down" frame
597  // loiter_deadband clips appropriately.
598  down_cmd = loiter_deadband(cmd.Thrust,
599  0, /* Deadbanded in manual control */
600  altitudeHoldSettings.Expo);
601  }
602 
603  // Peak detect and decay of the past command magnitude
604  static float historic_mag = 0.0f;
605 
606  // Initialize altitude command
607  *alt_adj = 0;
608 
609  // First reduce by decay constant
610  historic_mag *= loiter_brakealpha;
611 
612  // If our current magnitude is greater than the result, increase it.
613  if (deadband_mag > historic_mag) {
614  historic_mag = deadband_mag;
615  }
616 
617  // And if we haven't had any significant command lately, bug out and
618  // do nothing.
619  if ((historic_mag < 0.001f) && (fabsf(down_cmd) < 0.001f)) {
620  att_adj[0] = 0; att_adj[1] = 0;
621  return false;
622  }
623 
624  // Normalize our command magnitude. Command vectors from this
625  // point are normalized.
626  if (command_mag >= 0.001f) {
627  commands_rp[0] /= command_mag;
628  commands_rp[1] /= command_mag;
629  } else {
630  // Just pick a direction
631  commands_rp[0] = 0.0f;
632  commands_rp[1] = -1.0f;
633  }
634 
635  // Find our current position error
636  PositionActualData positionActual;
637  PositionActualGet(&positionActual);
638 
639  float cur_pos_ned[3] = { positionActual.North,
640  positionActual.East, positionActual.Down };
641 
642  float total_poserr_ned[3];
643  vector3_distances(cur_pos_ned, hold_pos, total_poserr_ned, false);
644 
645  if (deadband_mag >= 0.001f) {
646  float yaw;
647  AttitudeActualYawGet(&yaw);
648 
649  float commands_ne[2];
650  // 90 degrees here compensates for the above being in roll-pitch
651  // order vs. north-east (and where yaw is defined).
652  vector2_rotate(commands_rp, commands_ne, 90 + yaw);
653 
654  VelocityActualData velocityActual;
655  VelocityActualGet(&velocityActual);
656 
657  // Come up with a target velocity for us to fly the command
658  // at, considering our current momentum in that direction.
659  float target_vel = vtol_guidanceSettings.LoiterInitialMaxVel *
660  deadband_mag;
661 
662  // Plus whatever current velocity we're making good in
663  // that direction..
664  // find the portion of our current velocity vector parallel to
665  // cmd.
666  float parallel_sign =
667  velocityActual.North * commands_ne[0] +
668  velocityActual.East * commands_ne[1];
669 
670  if (parallel_sign > 0) {
671  float parallel_mag = sqrtf(
672  powf(velocityActual.North * commands_ne[0], 2) +
673  powf(velocityActual.East * commands_ne[1], 2));
674 
675  target_vel += deadband_mag * parallel_mag;
676  }
677 
678  // Feed the target velocity forward for our new desired position
679  hold_pos[0] = cur_pos_ned[0] +
680  commands_ne[0] * target_vel *
681  vtol_guidanceSettings.LoiterLookaheadTimeConstant;
682  hold_pos[1] = cur_pos_ned[1] +
683  commands_ne[1] * target_vel *
684  vtol_guidanceSettings.LoiterLookaheadTimeConstant;
685  }
686 
687  // Now put a portion of the error back in. At full stick
688  // deflection, decay error at specified time constant
689  float scaled_error_alpha = 1 - historic_mag * (1 - loiter_errordecayalpha);
690  hold_pos[0] -= scaled_error_alpha * total_poserr_ned[0];
691  hold_pos[1] -= scaled_error_alpha * total_poserr_ned[1];
692 
693  // Compute attitude feedforward
694  att_adj[0] = deadband_mag * commands_rp[0] *
695  vtol_guidanceSettings.LoiterAttitudeFeedthrough;
696  att_adj[1] = deadband_mag * commands_rp[1] *
697  vtol_guidanceSettings.LoiterAttitudeFeedthrough;
698 
699  // If we are being commanded to climb or descend...
700  if (fabsf(down_cmd) >= 0.001f) {
701  // Forgive all altitude error so when position controller comes
702  // back we do something sane
703 
704  hold_pos[2] = cur_pos_ned[2];
705 
706  // and output an adjustment for velocity control use
707  if (down_cmd < 0) { /* climbing*/
708  *alt_adj = down_cmd * altitudeHoldSettings.MaxClimbRate * 0.1f;
709  } else { /* descending */
710  *alt_adj = down_cmd * altitudeHoldSettings.MaxDescentRate * 0.1f;
711  }
712  }
713 
714  return true;
715 }
716 
718 {
719  VtolPathFollowerSettingsGet(&vtol_guidanceSettings);
720 
721  vtol_dT = vtol_guidanceSettings.UpdatePeriod / 1000.0f;
722 
723  // Configure the velocity control PID loops
725  vtol_guidanceSettings.HorizontalVelPID[VTOLPATHFOLLOWERSETTINGS_HORIZONTALVELPID_KP], // Kp
726  vtol_guidanceSettings.HorizontalVelPID[VTOLPATHFOLLOWERSETTINGS_HORIZONTALVELPID_KI], // Ki
727  0, // Kd
728  vtol_guidanceSettings.HorizontalVelPID[VTOLPATHFOLLOWERSETTINGS_HORIZONTALVELPID_ILIMIT],
729  vtol_dT);
731  vtol_guidanceSettings.HorizontalVelPID[VTOLPATHFOLLOWERSETTINGS_HORIZONTALVELPID_KP], // Kp
732  vtol_guidanceSettings.HorizontalVelPID[VTOLPATHFOLLOWERSETTINGS_HORIZONTALVELPID_KI], // Ki
733  0, // Kd
734  vtol_guidanceSettings.HorizontalVelPID[VTOLPATHFOLLOWERSETTINGS_HORIZONTALVELPID_ILIMIT],
735  vtol_dT);
736 
737  // Configure the position control (velocity output) PID loops
739  vtol_guidanceSettings.HorizontalPosPI[VTOLPATHFOLLOWERSETTINGS_HORIZONTALPOSPI_KP],
740  vtol_guidanceSettings.HorizontalPosPI[VTOLPATHFOLLOWERSETTINGS_HORIZONTALPOSPI_KI],
741  0,
742  vtol_guidanceSettings.HorizontalPosPI[VTOLPATHFOLLOWERSETTINGS_HORIZONTALPOSPI_ILIMIT],
743  vtol_dT);
745  vtol_guidanceSettings.HorizontalPosPI[VTOLPATHFOLLOWERSETTINGS_HORIZONTALPOSPI_KP],
746  vtol_guidanceSettings.HorizontalPosPI[VTOLPATHFOLLOWERSETTINGS_HORIZONTALPOSPI_KI],
747  0,
748  vtol_guidanceSettings.HorizontalPosPI[VTOLPATHFOLLOWERSETTINGS_HORIZONTALPOSPI_ILIMIT],
749  vtol_dT);
750 
751  // The parameters for vertical control are shared with Altitude Hold
752  AltitudeHoldSettingsGet(&altitudeHoldSettings);
755  altitudeHoldSettings.VelocityKp, altitudeHoldSettings.VelocityKi,
756  0, 1, vtol_dT); // Note the ILimit here is 1 because we use this offset to set the throttle offset
757 
758  // Calculate the constants used in the deadband calculation
759  cubic_deadband_setup(vtol_guidanceSettings.EndpointDeadbandWidth,
760  vtol_guidanceSettings.EndpointDeadbandCenterGain,
762 
764  vtol_guidanceSettings.PathDeadbandCenterGain,
766 
767  // calculate the loiter time constants.
768  loiter_brakealpha = expf(-(vtol_guidanceSettings.UpdatePeriod / 1000.0f) / vtol_guidanceSettings.LoiterBrakingTimeConstant);
769  loiter_errordecayalpha = expf(-(vtol_guidanceSettings.UpdatePeriod / 1000.0f) / vtol_guidanceSettings.LoiterErrorDecayConstant);
770 }
771 
float cubic_deadband(float in, float w, float b, float m, float r)
Definition: misc_math.c:246
static int32_t vtol_follower_control_accel(float dT)
float vtol_dT
static int32_t vtol_follower_control_impl(const float *hold_pos_ned, float alt_rate, bool update_status)
static PathDesiredData pathDesired
static float vtol_path_r
int32_t vtol_follower_control_attitude(float dT, const float *att_adj)
float vector3_distances(const float *actual, const float *desired, float *out, bool normalize)
Definition: misc_math.c:178
VtolPathFollowerSettingsData vtol_guidanceSettings
float iAccumulator
Definition: pid.h:50
int32_t vtol_follower_control_endpoint(const float *hold_pos_ned)
void vtol_follower_control_settings_updated()
float fractional_progress
Definition: paths.h:37
static PathStatusData pathStatus
struct pid vtol_pids[VTOL_PID_NUM]
int32_t vtol_follower_control_land(const float *hold_pos_ned, bool *landed)
float error
Definition: paths.h:38
float interpolate_value(const float fraction, const float beginVal, const float endVal)
Definition: misc_math.c:145
void pid_configure(struct pid *pid, float p, float i, float d, float iLim, float dT)
Definition: pid.c:280
static float vtol_end_m
Path calculation library with common API.
float path_direction[2]
Definition: paths.h:40
void path_progress(const PathDesiredData *pathDesired, const float *cur_point, struct path_status *status)
Compute progress along path and deviation from it.
Definition: paths.c:62
static float loiter_errordecayalpha
void vector2_clip(float *vels, float limit)
Definition: misc_math.c:208
uint16_t groundspeed
struct _msp_pid_item yaw
Definition: msp_messages.h:98
float bound_min_max(float val, float min, float max)
Bound input value between min and max.
Definition: misc_math.c:38
Includes for the internal methods.
float bound_sym(float val, float range)
Bound input value within range (plus or minus)
Definition: misc_math.c:50
bool vtol_follower_control_loiter(float dT, float *hold_pos, float *att_adj, float *alt_adj)
void cubic_deadband_setup(float w, float b, float *m, float *r)
Definition: misc_math.c:266
Header for Coordinate conversions library in coordinate_conversions.c.
static float loiter_deadband(float input, float threshold, float expoPercent)
tuple f
Definition: px_mkfw.py:81
static float vtol_end_r
int32_t vtol_follower_control_path(const PathDesiredData *pathDesired, struct path_status *progress)
Includes PiOS and core architecture components.
float vectorn_magnitude(const float *v, int n)
Definition: misc_math.c:159
float correction_direction[2]
Definition: paths.h:39
static float loiter_brakealpha
float pid_apply_antiwindup(struct pid *pid, const float err, float min_bound, float max_bound, float aw_bound)
Definition: pid.c:86
static float vtol_follower_control_altitude(float downCommand)
static ManualControlCommandData cmd
Definition: pid.h:42
float dT
Definition: pid.h:48
static AltitudeHoldSettingsData altitudeHoldSettings
static float vtol_path_m
float expo3(float x, int32_t g)
Approximation an exponential scale curve.
Definition: misc_math.c:111
void vector2_rotate(const float *original, float *out, float angle)
Definition: misc_math.c:225
PID Control algorithms.
int32_t vtol_follower_control_altrate(const float *hold_pos_ned, float alt_adj)