PX4 Firmware
PX4 Autopilot Software http://px4.io
PositionControl.cpp
Go to the documentation of this file.
1 /****************************************************************************
2  *
3  * Copyright (c) 2018 - 2019 PX4 Development Team. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in
13  * the documentation and/or other materials provided with the
14  * distribution.
15  * 3. Neither the name PX4 nor the names of its contributors may be
16  * used to endorse or promote products derived from this software
17  * without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
26  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  *
32  ****************************************************************************/
33 
34 /**
35  * @file PositionControl.cpp
36  */
37 
38 #include "PositionControl.hpp"
39 #include <float.h>
40 #include <mathlib/mathlib.h>
41 #include <ControlMath.hpp>
42 #include <px4_platform_common/defines.h>
43 
44 using namespace matrix;
45 
47 {
48  _gain_vel_p = P;
49  _gain_vel_i = I;
50  _gain_vel_d = D;
51 }
52 
53 void PositionControl::setVelocityLimits(const float vel_horizontal, const float vel_up, const float vel_down)
54 {
55  _lim_vel_horizontal = vel_horizontal;
56  _lim_vel_up = vel_up;
57  _lim_vel_down = vel_down;
58 }
59 
60 void PositionControl::setThrustLimits(const float min, const float max)
61 {
62  _lim_thr_min = min;
63  _lim_thr_max = max;
64 }
65 
67 {
68  _pos = states.position;
69  _vel = states.velocity;
70  _yaw = states.yaw;
71  _vel_dot = states.acceleration;
72 }
73 
75 {
76  for (int i = 0; i <= 2; i++) {
77  _ctrl_pos[i] = _ctrl_vel[i] = value;
78  }
79 }
80 
82 {
83  // by default we use the entire position-velocity control-loop pipeline (flag only for logging purpose)
84  _setCtrlFlag(true);
85 
86  _pos_sp = Vector3f(setpoint.x, setpoint.y, setpoint.z);
87  _vel_sp = Vector3f(setpoint.vx, setpoint.vy, setpoint.vz);
88  _acc_sp = Vector3f(setpoint.acceleration);
89  _thr_sp = Vector3f(setpoint.thrust);
90  _yaw_sp = setpoint.yaw;
91  _yawspeed_sp = setpoint.yawspeed;
92  bool mapping_succeeded = _interfaceMapping();
93 
94  // If full manual is required (thrust already generated), don't run position/velocity
95  // controller and just return thrust.
96  _skip_controller = PX4_ISFINITE(_thr_sp(0)) && PX4_ISFINITE(_thr_sp(1))
97  && PX4_ISFINITE(_thr_sp(2));
98 
99  return mapping_succeeded;
100 }
101 
103 {
104  if (_skip_controller) {
105 
106  // Already received a valid thrust set-point.
107  // Limit the thrust vector.
108  float thr_mag = _thr_sp.length();
109 
110  if (thr_mag > _lim_thr_max) {
111  _thr_sp = _thr_sp.normalized() * _lim_thr_max;
112 
113  } else if (thr_mag < _lim_thr_min && thr_mag > FLT_EPSILON) {
114  _thr_sp = _thr_sp.normalized() * _lim_thr_min;
115  }
116 
117  // Just set the set-points equal to the current vehicle state.
118  _pos_sp = _pos;
119  _vel_sp = _vel;
120  _acc_sp = _vel_dot;
121 
122  } else {
123  _positionController();
124  _velocityController(dt);
125  }
126 }
127 
129 {
130  // if nothing is valid, then apply failsafe landing
131  bool failsafe = false;
132 
133  // Respects FlightTask interface, where NAN-set-points are of no interest
134  // and do not require control. A valid position and velocity setpoint will
135  // be mapped to a desired position setpoint with a feed-forward term.
136  // States and setpoints which are integrals of the reference setpoint are set to 0.
137  // For instance: reference is velocity-setpoint -> position and position-setpoint = 0
138  // reference is thrust-setpoint -> position, velocity, position-/velocity-setpoint = 0
139  for (int i = 0; i <= 2; i++) {
140 
141  if (PX4_ISFINITE(_pos_sp(i))) {
142  // Position control is required
143 
144  if (!PX4_ISFINITE(_vel_sp(i))) {
145  // Velocity is not used as feedforward term.
146  _vel_sp(i) = 0.0f;
147  }
148 
149  // thrust setpoint is not supported in position control
150  _thr_sp(i) = NAN;
151 
152  // to run position control, we require valid position and velocity
153  if (!PX4_ISFINITE(_pos(i)) || !PX4_ISFINITE(_vel(i))) {
154  failsafe = true;
155  }
156 
157  } else if (PX4_ISFINITE(_vel_sp(i))) {
158 
159  // Velocity controller is active without position control.
160  // Set integral states and setpoints to 0
161  _pos_sp(i) = _pos(i) = 0.0f;
162  _ctrl_pos[i] = false; // position control-loop is not used
163 
164  // thrust setpoint is not supported in velocity control
165  _thr_sp(i) = NAN;
166 
167  // to run velocity control, we require valid velocity
168  if (!PX4_ISFINITE(_vel(i))) {
169  failsafe = true;
170  }
171 
172  } else if (PX4_ISFINITE(_thr_sp(i))) {
173 
174  // Thrust setpoint was generated from sticks directly.
175  // Set all integral states and setpoints to 0
176  _pos_sp(i) = _pos(i) = 0.0f;
177  _vel_sp(i) = _vel(i) = 0.0f;
178  _ctrl_pos[i] = _ctrl_vel[i] = false; // position/velocity control loop is not used
179 
180  // Reset the Integral term.
181  _thr_int(i) = 0.0f;
182  // Don't require velocity derivative.
183  _vel_dot(i) = 0.0f;
184 
185  } else {
186  // nothing is valid. do failsafe
187  failsafe = true;
188  }
189  }
190 
191  // ensure that vel_dot is finite, otherwise set to 0
192  if (!PX4_ISFINITE(_vel_dot(0)) || !PX4_ISFINITE(_vel_dot(1))) {
193  _vel_dot(0) = _vel_dot(1) = 0.0f;
194  }
195 
196  if (!PX4_ISFINITE(_vel_dot(2))) {
197  _vel_dot(2) = 0.0f;
198  }
199 
200  if (!PX4_ISFINITE(_yawspeed_sp)) {
201  // Set the yawspeed to 0 since not used.
202  _yawspeed_sp = 0.0f;
203  }
204 
205  if (!PX4_ISFINITE(_yaw_sp)) {
206  // Set the yaw-sp equal the current yaw.
207  // That is the best we can do and it also
208  // agrees with FlightTask-interface definition.
209  if (PX4_ISFINITE(_yaw)) {
210  _yaw_sp = _yaw;
211 
212  } else {
213  failsafe = true;
214  }
215  }
216 
217  // check failsafe
218  if (failsafe) {
219  // point the thrust upwards
220  _thr_sp(0) = _thr_sp(1) = 0.0f;
221  // throttle down such that vehicle goes down with
222  // 70% of throttle range between min and hover
223  _thr_sp(2) = -(_lim_thr_min + (_hover_thrust - _lim_thr_min) * 0.7f);
224  // position and velocity control-loop is currently unused (flag only for logging purpose)
225  _setCtrlFlag(false);
226  }
227 
228  return !(failsafe);
229 }
230 
232 {
233  // P-position controller
234  const Vector3f vel_sp_position = (_pos_sp - _pos).emult(_gain_pos_p);
235  _vel_sp = vel_sp_position + _vel_sp;
236 
237  // Constrain horizontal velocity by prioritizing the velocity component along the
238  // the desired position setpoint over the feed-forward term.
239  const Vector2f vel_sp_xy = ControlMath::constrainXY(Vector2f(vel_sp_position),
240  Vector2f(_vel_sp - vel_sp_position), _lim_vel_horizontal);
241  _vel_sp(0) = vel_sp_xy(0);
242  _vel_sp(1) = vel_sp_xy(1);
243  // Constrain velocity in z-direction.
244  _vel_sp(2) = math::constrain(_vel_sp(2), -_constraints.speed_up, _constraints.speed_down);
245 }
246 
248 {
249  // Generate desired thrust setpoint.
250  // PID
251  // u_des = P(vel_err) + D(vel_err_dot) + I(vel_integral)
252  // Umin <= u_des <= Umax
253  //
254  // Anti-Windup:
255  // u_des = _thr_sp; r = _vel_sp; y = _vel
256  // u_des >= Umax and r - y >= 0 => Saturation = true
257  // u_des >= Umax and r - y <= 0 => Saturation = false
258  // u_des <= Umin and r - y <= 0 => Saturation = true
259  // u_des <= Umin and r - y >= 0 => Saturation = false
260  //
261  // Notes:
262  // - PID implementation is in NED-frame
263  // - control output in D-direction has priority over NE-direction
264  // - the equilibrium point for the PID is at hover-thrust
265  // - the maximum tilt cannot exceed 90 degrees. This means that it is
266  // not possible to have a desired thrust direction pointing in the positive
267  // D-direction (= downward)
268  // - the desired thrust in D-direction is limited by the thrust limits
269  // - the desired thrust in NE-direction is limited by the thrust excess after
270  // consideration of the desired thrust in D-direction. In addition, the thrust in
271  // NE-direction is also limited by the maximum tilt.
272 
273  const Vector3f vel_err = _vel_sp - _vel;
274 
275  // Consider thrust in D-direction.
276  float thrust_desired_D = _gain_vel_p(2) * vel_err(2) + _gain_vel_d(2) * _vel_dot(2) + _thr_int(
277  2) - _hover_thrust;
278 
279  // The Thrust limits are negated and swapped due to NED-frame.
280  float uMax = -_lim_thr_min;
281  float uMin = -_lim_thr_max;
282 
283  // make sure there's always enough thrust vector length to infer the attitude
284  uMax = math::min(uMax, -10e-4f);
285 
286  // Apply Anti-Windup in D-direction.
287  bool stop_integral_D = (thrust_desired_D >= uMax && vel_err(2) >= 0.0f) ||
288  (thrust_desired_D <= uMin && vel_err(2) <= 0.0f);
289 
290  if (!stop_integral_D) {
291  _thr_int(2) += vel_err(2) * _gain_vel_i(2) * dt;
292 
293  // limit thrust integral
294  _thr_int(2) = math::min(fabsf(_thr_int(2)), _lim_thr_max) * math::sign(_thr_int(2));
295  }
296 
297  // Saturate thrust setpoint in D-direction.
298  _thr_sp(2) = math::constrain(thrust_desired_D, uMin, uMax);
299 
300  if (PX4_ISFINITE(_thr_sp(0)) && PX4_ISFINITE(_thr_sp(1))) {
301  // Thrust set-point in NE-direction is already provided. Only
302  // scaling by the maximum tilt is required.
303  float thr_xy_max = fabsf(_thr_sp(2)) * tanf(_constraints.tilt);
304  _thr_sp(0) *= thr_xy_max;
305  _thr_sp(1) *= thr_xy_max;
306 
307  } else {
308  // PID-velocity controller for NE-direction.
309  Vector2f thrust_desired_NE;
310  thrust_desired_NE(0) = _gain_vel_p(0) * vel_err(0) + _gain_vel_d(0) * _vel_dot(0) + _thr_int(0);
311  thrust_desired_NE(1) = _gain_vel_p(1) * vel_err(1) + _gain_vel_d(1) * _vel_dot(1) + _thr_int(1);
312 
313  // Get maximum allowed thrust in NE based on tilt and excess thrust.
314  float thrust_max_NE_tilt = fabsf(_thr_sp(2)) * tanf(_constraints.tilt);
315  float thrust_max_NE = sqrtf(_lim_thr_max * _lim_thr_max - _thr_sp(2) * _thr_sp(2));
316  thrust_max_NE = math::min(thrust_max_NE_tilt, thrust_max_NE);
317 
318  // Saturate thrust in NE-direction.
319  _thr_sp(0) = thrust_desired_NE(0);
320  _thr_sp(1) = thrust_desired_NE(1);
321 
322  if (thrust_desired_NE * thrust_desired_NE > thrust_max_NE * thrust_max_NE) {
323  float mag = thrust_desired_NE.length();
324  _thr_sp(0) = thrust_desired_NE(0) / mag * thrust_max_NE;
325  _thr_sp(1) = thrust_desired_NE(1) / mag * thrust_max_NE;
326  }
327 
328  // Use tracking Anti-Windup for NE-direction: during saturation, the integrator is used to unsaturate the output
329  // see Anti-Reset Windup for PID controllers, L.Rundqwist, 1990
330  float arw_gain = 2.f / _gain_vel_p(0);
331 
332  Vector2f vel_err_lim;
333  vel_err_lim(0) = vel_err(0) - (thrust_desired_NE(0) - _thr_sp(0)) * arw_gain;
334  vel_err_lim(1) = vel_err(1) - (thrust_desired_NE(1) - _thr_sp(1)) * arw_gain;
335 
336  // Update integral
337  _thr_int(0) += _gain_vel_i(0) * vel_err_lim(0) * dt;
338  _thr_int(1) += _gain_vel_i(1) * vel_err_lim(1) * dt;
339  }
340 }
341 
343 {
344  _constraints = constraints;
345 
346  // For safety check if adjustable constraints are below global constraints. If they are not stricter than global
347  // constraints, then just use global constraints for the limits.
348 
349  if (!PX4_ISFINITE(constraints.tilt)
350  || !(constraints.tilt < _lim_tilt)) {
351  _constraints.tilt = _lim_tilt;
352  }
353 
354  if (!PX4_ISFINITE(constraints.speed_up) || !(constraints.speed_up < _lim_vel_up)) {
355  _constraints.speed_up = _lim_vel_up;
356  }
357 
358  if (!PX4_ISFINITE(constraints.speed_down) || !(constraints.speed_down < _lim_vel_down)) {
359  _constraints.speed_down = _lim_vel_down;
360  }
361 
362  if (!PX4_ISFINITE(constraints.speed_xy) || !(constraints.speed_xy < _lim_vel_horizontal)) {
363  _constraints.speed_xy = _lim_vel_horizontal;
364  }
365 }
366 
368 {
369  local_position_setpoint.x = _pos_sp(0);
370  local_position_setpoint.y = _pos_sp(1);
371  local_position_setpoint.z = _pos_sp(2);
372  local_position_setpoint.yaw = _yaw_sp;
373  local_position_setpoint.yawspeed = _yawspeed_sp;
374  local_position_setpoint.vx = _vel_sp(0);
375  local_position_setpoint.vy = _vel_sp(1);
376  local_position_setpoint.vz = _vel_sp(2);
377  _acc_sp.copyTo(local_position_setpoint.acceleration);
378  _thr_sp.copyTo(local_position_setpoint.thrust);
379 }
380 
382 {
383  ControlMath::thrustToAttitude(_thr_sp, _yaw_sp, attitude_setpoint);
384  attitude_setpoint.yaw_sp_move_rate = _yawspeed_sp;
385 }
constexpr _Tp constrain(_Tp val, _Tp min_val, _Tp max_val)
Definition: Limits.hpp:66
void updateConstraints(const vehicle_constraints_s &constraints)
Set constraints that are stricter than the global limits.
matrix::Vector3f acceleration
void generateThrustYawSetpoint(const float dt)
Apply P-position and PID-velocity controller that updates the member thrust, yaw- and yawspeed-setpoi...
A cascaded position controller for position/velocity control only.
matrix::Vector3f position
void getAttitudeSetpoint(vehicle_attitude_setpoint_s &attitude_setpoint) const
Get the controllers output attitude setpoint This attitude setpoint was generated from the resulting ...
#define FLT_EPSILON
void updateState(const PositionControlStates &states)
Update the current vehicle state.
void setThrustLimits(const float min, const float max)
Set the minimum and maximum collective normalized thrust [0,1] that can be output by the controller...
void setVelocityLimits(const float vel_horizontal, const float vel_up, float vel_down)
Set the maximum velocity to execute with feed forward and position control.
void _velocityController(const float &dt)
applies the P-position-controller
Vector< float, 6 > f(float t, const Matrix< float, 6, 1 > &, const Matrix< float, 3, 1 > &)
Definition: integration.cpp:8
Vector2< float > Vector2f
Definition: Vector2.hpp:69
matrix::Vector3f velocity
void setVelocityGains(const matrix::Vector3f &P, const matrix::Vector3f &I, const matrix::Vector3f &D)
Set the velocity control gains.
Type length() const
Definition: Vector.hpp:83
Dual< Scalar, N > min(const Dual< Scalar, N > &a, const Dual< Scalar, N > &b)
Definition: Dual.hpp:231
void _setCtrlFlag(bool value)
applies the PID-velocity-controller
constexpr _Tp min(_Tp a, _Tp b)
Definition: Limits.hpp:54
void thrustToAttitude(const Vector3f &thr_sp, const float yaw_sp, vehicle_attitude_setpoint_s &att_sp)
Converts thrust vector and yaw set-point to a desired attitude.
Definition: ControlMath.cpp:47
int sign(T val)
Definition: Functions.hpp:49
Vector3< float > Vector3f
Definition: Vector3.hpp:136
Vector2f constrainXY(const Vector2f &v0, const Vector2f &v1, const float &max)
Outputs the sum of two vectors but respecting the limits and priority.
bool _interfaceMapping()
Maps setpoints to internal-setpoints.
void getLocalPositionSetpoint(vehicle_local_position_setpoint_s &local_position_setpoint) const
Get the controllers output local position setpoint These setpoints are the ones which were executed o...
float dt
Definition: px4io.c:73
bool updateSetpoint(const vehicle_local_position_setpoint_s &setpoint)
Update the desired setpoints.
Dual< Scalar, N > max(const Dual< Scalar, N > &a, const Dual< Scalar, N > &b)
Definition: Dual.hpp:224
Simple functions for vector manipulation that do not fit into matrix lib.
P[0][0]
Definition: quatCovMat.c:44