PX4 Firmware
PX4 Autopilot Software http://px4.io
FlightTaskAutoLineSmoothVel.cpp
Go to the documentation of this file.
1 /****************************************************************************
2  *
3  * Copyright (c) 2018 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 FlightAutoLine.cpp
36  */
37 
39 #include <mathlib/mathlib.h>
40 #include <float.h>
41 
42 using namespace matrix;
43 
45 {
46  bool ret = FlightTaskAutoMapper2::activate(last_setpoint);
47 
48  checkSetpoints(last_setpoint);
49  const Vector3f vel_prev(last_setpoint.vx, last_setpoint.vy, last_setpoint.vz);
50  const Vector3f pos_prev(last_setpoint.x, last_setpoint.y, last_setpoint.z);
51 
52  for (int i = 0; i < 3; ++i) {
53  _trajectory[i].reset(last_setpoint.acceleration[i], vel_prev(i), pos_prev(i));
54  }
55 
56  _yaw_sp_prev = last_setpoint.yaw;
57  _updateTrajConstraints();
58 
59  return ret;
60 }
61 
63 {
64  // On ground, reset acceleration and velocity to zero
65  for (int i = 0; i < 2; ++i) {
66  _trajectory[i].reset(0.f, 0.f, _position(i));
67  }
68 
69  _trajectory[2].reset(0.f, 0.7f, _position(2));
70 }
71 
73 {
74  // If the position setpoint is unknown, set to the current postion
75  if (!PX4_ISFINITE(setpoints.x)) { setpoints.x = _position(0); }
76 
77  if (!PX4_ISFINITE(setpoints.y)) { setpoints.y = _position(1); }
78 
79  if (!PX4_ISFINITE(setpoints.z)) { setpoints.z = _position(2); }
80 
81  // If the velocity setpoint is unknown, set to the current velocity
82  if (!PX4_ISFINITE(setpoints.vx)) { setpoints.vx = _velocity(0); }
83 
84  if (!PX4_ISFINITE(setpoints.vy)) { setpoints.vy = _velocity(1); }
85 
86  if (!PX4_ISFINITE(setpoints.vz)) { setpoints.vz = _velocity(2); }
87 
88  // No acceleration estimate available, set to zero if the setpoint is NAN
89  for (int i = 0; i < 3; i++) {
90  if (!PX4_ISFINITE(setpoints.acceleration[i])) { setpoints.acceleration[i] = 0.f; }
91  }
92 
93  if (!PX4_ISFINITE(setpoints.yaw)) { setpoints.yaw = _yaw; }
94 }
95 
96 /**
97  * EKF reset handling functions
98  * Those functions are called by the base FlightTask in
99  * case of an EKF reset event
100  */
102 {
103  _trajectory[0].setCurrentPosition(_position(0));
104  _trajectory[1].setCurrentPosition(_position(1));
105 }
106 
108 {
109  _trajectory[0].setCurrentVelocity(_velocity(0));
110  _trajectory[1].setCurrentVelocity(_velocity(1));
111 }
112 
114 {
115  _trajectory[2].setCurrentPosition(_position(2));
116 }
117 
119 {
120  _trajectory[2].setCurrentVelocity(_velocity(2));
121 }
122 
124 {
125  _yaw_sp_prev += delta_psi;
126 }
127 
129 {
130  _prepareSetpoints();
131  _generateTrajectory();
132 
133  if (!PX4_ISFINITE(_yaw_setpoint) && !PX4_ISFINITE(_yawspeed_setpoint)) {
134  // no valid heading -> generate heading in this flight task
135  _generateHeading();
136  }
137 }
138 
140 {
141  // Generate heading along trajectory if possible, otherwise hold the previous yaw setpoint
142  if (!_generateHeadingAlongTraj()) {
143  _yaw_setpoint = _yaw_sp_prev;
144  }
145 }
146 
148 {
149  bool res = false;
150  Vector2f vel_sp_xy(_velocity_setpoint);
151  Vector2f traj_to_target = Vector2f(_target) - Vector2f(_position);
152 
153  if ((vel_sp_xy.length() > .1f) &&
154  (traj_to_target.length() > _target_acceptance_radius)) {
155  // Generate heading from velocity vector, only if it is long enough
156  // and if the drone is far enough from the target
157  _compute_heading_from_2D_vector(_yaw_setpoint, vel_sp_xy);
158  res = true;
159  }
160 
161  return res;
162 }
163 
164 /* Constrain some value vith a constrain depending on the sign of the constraint
165  * Example: - if the constrain is -5, the value will be constrained between -5 and 0
166  * - if the constrain is 5, the value will be constrained between 0 and 5
167  */
168 float FlightTaskAutoLineSmoothVel::_constrainOneSide(float val, float constraint)
169 {
170  const float min = (constraint < FLT_EPSILON) ? constraint : 0.f;
171  const float max = (constraint > FLT_EPSILON) ? constraint : 0.f;
172 
173  return math::constrain(val, min, max);
174 }
175 
177 {
178  return math::sign(val) * math::min(fabsf(val), fabsf(max));
179 }
180 
181 float FlightTaskAutoLineSmoothVel::_getSpeedAtTarget(float next_target_speed) const
182 {
183  // Compute the maximum allowed speed at the waypoint assuming that we want to
184  // connect the two lines (prev-current and current-next)
185  // with a tangent circle with constant speed and desired centripetal acceleration: a_centripetal = speed^2 / radius
186  // The circle should in theory start and end at the intersection of the lines and the waypoint's acceptance radius.
187  // This is not exactly true in reality since Navigator switches the waypoint so we have to take in account that
188  // the real acceptance radius is smaller.
189  // It can be that the next waypoint is the last one or that the drone will have to stop for some other reason
190  // so we have to make sure that the speed at the current waypoint allows to stop at the next waypoint.
191  float speed_at_target = 0.0f;
192 
193  const float distance_current_next = (_target - _next_wp).xy().norm();
194  const bool waypoint_overlap = (_target - _prev_wp).xy().norm() < _target_acceptance_radius;
195  const bool yaw_align_check_pass = (_param_mpc_yaw_mode.get() != 4) || _yaw_sp_aligned;
196 
197 
198  if (distance_current_next > 0.001f &&
199  !waypoint_overlap &&
200  yaw_align_check_pass) {
201  Vector3f pos_traj;
202  pos_traj(0) = _trajectory[0].getCurrentPosition();
203  pos_traj(1) = _trajectory[1].getCurrentPosition();
204  pos_traj(2) = _trajectory[2].getCurrentPosition();
205  // Max speed between current and next
206  const float max_speed_current_next = _getMaxSpeedFromDistance(distance_current_next, next_target_speed);
207  const float alpha = acosf(Vector2f((_target - pos_traj).xy()).unit_or_zero().dot(
208  Vector2f((_target - _next_wp).xy()).unit_or_zero()));
209  // We choose a maximum centripetal acceleration of MPC_ACC_HOR * MPC_XY_TRAJ_P to take in account
210  // that there is a jerk limit (a direct transition from line to circle is not possible)
211  // MPC_XY_TRAJ_P should be between 0 and 1.
212  float accel_tmp = _param_mpc_xy_traj_p.get() * _param_mpc_acc_hor.get();
213  float max_speed_in_turn = math::trajectory::computeMaxSpeedInWaypoint(alpha,
214  accel_tmp,
215  _target_acceptance_radius);
216  speed_at_target = math::min(math::min(max_speed_in_turn, max_speed_current_next), _mc_cruise_speed);
217  }
218 
219  return speed_at_target;
220 }
221 
222 float FlightTaskAutoLineSmoothVel::_getMaxSpeedFromDistance(float braking_distance, float final_speed) const
223 {
224  float max_speed = math::trajectory::computeMaxSpeedFromDistance(_param_mpc_jerk_auto.get(),
225  _param_mpc_acc_hor.get(),
226  braking_distance,
227  final_speed);
228 
229  return max_speed;
230 }
231 
233 {
234  // Interface: A valid position setpoint generates a velocity target using a P controller. If a velocity is specified
235  // that one is used as a velocity limit.
236  // If the position setpoints are set to NAN, the values in the velocity setpoints are used as velocity targets: nothing to do here.
237 
238  _want_takeoff = false;
239 
240  if (_param_mpc_yaw_mode.get() == 4 && !_yaw_sp_aligned) {
241  // Wait for the yaw setpoint to be aligned
242  _velocity_setpoint.setAll(0.f);
243 
244  } else {
245  if (PX4_ISFINITE(_position_setpoint(0)) &&
246  PX4_ISFINITE(_position_setpoint(1))) {
247  // Use position setpoints to generate velocity setpoints
248 
249  // Get various path specific vectors
250  Vector3f pos_traj;
251  pos_traj(0) = _trajectory[0].getCurrentPosition();
252  pos_traj(1) = _trajectory[1].getCurrentPosition();
253  pos_traj(2) = _trajectory[2].getCurrentPosition();
254  Vector2f pos_traj_to_dest_xy = (_position_setpoint - pos_traj).xy();
255  Vector2f u_pos_traj_to_dest_xy(pos_traj_to_dest_xy.unit_or_zero());
256 
257  const bool has_reached_altitude = fabsf(_position_setpoint(2) - pos_traj(2)) < _param_nav_mc_alt_rad.get();
258 
259  // If the drone has to change altitude, stop at the waypoint, otherwise fly through
260  const float arrival_speed = has_reached_altitude ? _getSpeedAtTarget(0.f) : 0.f;
261  const Vector2f max_arrival_vel = u_pos_traj_to_dest_xy * arrival_speed;
262 
263  Vector2f vel_abs_max_xy(_getMaxSpeedFromDistance(fabsf(pos_traj_to_dest_xy(0)), max_arrival_vel(0)),
264  _getMaxSpeedFromDistance(fabsf(pos_traj_to_dest_xy(1)), max_arrival_vel(1)));
265 
266 
267  const Vector2f vel_sp_xy = u_pos_traj_to_dest_xy * _mc_cruise_speed;
268 
269  // Constrain the norm of each component using min and max values
270  Vector2f vel_sp_constrained_xy(_constrainAbs(vel_sp_xy(0), vel_abs_max_xy(0)),
271  _constrainAbs(vel_sp_xy(1), vel_abs_max_xy(1)));
272 
273  for (int i = 0; i < 2; i++) {
274  // If available, constrain the velocity using _velocity_setpoint(.)
275  if (PX4_ISFINITE(_velocity_setpoint(i))) {
276  _velocity_setpoint(i) = _constrainOneSide(vel_sp_constrained_xy(i), _velocity_setpoint(i));
277 
278  } else {
279  _velocity_setpoint(i) = vel_sp_constrained_xy(i);
280  }
281  }
282  }
283 
284  if (PX4_ISFINITE(_position_setpoint(2))) {
285  const float vel_sp_z = (_position_setpoint(2) - _trajectory[2].getCurrentPosition()) *
286  _param_mpc_z_traj_p.get(); // Generate a velocity target for the trajectory using a simple P loop
287 
288  // If available, constrain the velocity using _velocity_setpoint(.)
289  if (PX4_ISFINITE(_velocity_setpoint(2))) {
290  _velocity_setpoint(2) = _constrainOneSide(vel_sp_z, _velocity_setpoint(2));
291 
292  } else {
293  _velocity_setpoint(2) = vel_sp_z;
294  }
295 
296  _want_takeoff = _velocity_setpoint(2) < -0.3f;
297  }
298  }
299 }
300 
302 {
303  // Update the constraints of the trajectories
304  _trajectory[0].setMaxAccel(_param_mpc_acc_hor.get()); // TODO : Should be computed using heading
305  _trajectory[1].setMaxAccel(_param_mpc_acc_hor.get());
306  _trajectory[0].setMaxVel(_param_mpc_xy_vel_max.get());
307  _trajectory[1].setMaxVel(_param_mpc_xy_vel_max.get());
308  _trajectory[0].setMaxJerk(_param_mpc_jerk_auto.get()); // TODO : Should be computed using heading
309  _trajectory[1].setMaxJerk(_param_mpc_jerk_auto.get());
310  _trajectory[2].setMaxJerk(_param_mpc_jerk_auto.get());
311 
312  if (_velocity_setpoint(2) < 0.f) { // up
313  _trajectory[2].setMaxAccel(_param_mpc_acc_up_max.get());
314  _trajectory[2].setMaxVel(_param_mpc_z_vel_max_up.get());
315 
316  } else { // down
317  _trajectory[2].setMaxAccel(_param_mpc_acc_down_max.get());
318  _trajectory[2].setMaxVel(_param_mpc_z_vel_max_dn.get());
319  }
320 }
321 
323 {
324  if (!PX4_ISFINITE(_velocity_setpoint(0)) || !PX4_ISFINITE(_velocity_setpoint(1))
325  || !PX4_ISFINITE(_velocity_setpoint(2))) {
326  return;
327  }
328 
329  /* Slow down the trajectory by decreasing the integration time based on the position error.
330  * This is only performed when the drone is behind the trajectory
331  */
332  Vector2f position_trajectory_xy(_trajectory[0].getCurrentPosition(), _trajectory[1].getCurrentPosition());
333  Vector2f position_xy(_position);
334  Vector2f vel_traj_xy(_trajectory[0].getCurrentVelocity(), _trajectory[1].getCurrentVelocity());
335  Vector2f drone_to_trajectory_xy(position_trajectory_xy - position_xy);
336  float position_error = drone_to_trajectory_xy.length();
337 
338  float time_stretch = 1.f - math::constrain(position_error * 0.5f, 0.f, 1.f);
339 
340  // Don't stretch time if the drone is ahead of the position setpoint
341  if (drone_to_trajectory_xy.dot(vel_traj_xy) < 0.f) {
342  time_stretch = 1.f;
343  }
344 
345  Vector3f jerk_sp_smooth;
346  Vector3f accel_sp_smooth;
347  Vector3f vel_sp_smooth;
348  Vector3f pos_sp_smooth;
349 
350  for (int i = 0; i < 3; ++i) {
351  _trajectory[i].updateTraj(_deltatime, time_stretch);
352  jerk_sp_smooth(i) = _trajectory[i].getCurrentJerk();
353  accel_sp_smooth(i) = _trajectory[i].getCurrentAcceleration();
354  vel_sp_smooth(i) = _trajectory[i].getCurrentVelocity();
355  pos_sp_smooth(i) = _trajectory[i].getCurrentPosition();
356  }
357 
358  _updateTrajConstraints();
359 
360  for (int i = 0; i < 3; ++i) {
361  _trajectory[i].updateDurations(_velocity_setpoint(i));
362  }
363 
364  VelocitySmoothing::timeSynchronization(_trajectory, 2); // Synchronize x and y only
365 
366  _jerk_setpoint = jerk_sp_smooth;
367  _acceleration_setpoint = accel_sp_smooth;
368  _velocity_setpoint = vel_sp_smooth;
369  _position_setpoint = pos_sp_smooth;
370 }
constexpr _Tp constrain(_Tp val, _Tp min_val, _Tp max_val)
Definition: Limits.hpp:66
static float _constrainOneSide(float val, float constraint)
Constrain val between INF and constraint.
void checkSetpoints(vehicle_local_position_setpoint_s &setpoints)
float _getMaxSpeedFromDistance(float braking_distance, float final_speed) const
void reActivate() override
Call this to reset an active Flight Task.
void _prepareSetpoints()
Generate velocity target points for the trajectory generator.
void _ekfResetHandlerHeading(float delta_psi) override
static float _constrainAbs(float val, float max)
float computeMaxSpeedInWaypoint(const float alpha, const float accel, const float d)
Definition: TrajMath.hpp:86
bool _generateHeadingAlongTraj()
Generates heading along trajectory.
#define FLT_EPSILON
Flight task for autonomous, gps driven mode.
bool activate(vehicle_local_position_setpoint_s last_setpoint) override
Call once on the event where you switch to the task.
Type dot(const MatrixM1 &b) const
Definition: Vector.hpp:55
float _getSpeedAtTarget(float next_target_speed) const
Constrain the value -max <= val <= max.
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
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
constexpr _Tp min(_Tp a, _Tp b)
Definition: Limits.hpp:54
float computeMaxSpeedFromDistance(const float jerk, const float accel, const float braking_distance, const float final_speed)
Definition: TrajMath.hpp:61
int sign(T val)
Definition: Functions.hpp:49
bool activate(vehicle_local_position_setpoint_s last_setpoint) override
Call once on the event where you switch to the task.
static void timeSynchronization(VelocitySmoothing *traj, int n_traj)
Synchronize several trajectories to have the same total time.
Dual< Scalar, N > max(const Dual< Scalar, N > &a, const Dual< Scalar, N > &b)
Definition: Dual.hpp:224
void _generateSetpoints() override
Generate setpoints along line.
void _ekfResetHandlerPositionXY() override
Reset position or velocity setpoints in case of EKF reset event.
Vector unit_or_zero(const Type eps=Type(1e-5)) const
Definition: Vector.hpp:95