PX4 Firmware
PX4 Autopilot Software http://px4.io
FixedwingPositionControl.cpp
Go to the documentation of this file.
1 /****************************************************************************
2  *
3  * Copyright (c) 2013-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 
35 
37  ModuleParams(nullptr),
38  WorkItem(MODULE_NAME, px4::wq_configurations::att_pos_ctrl),
39  _loop_perf(perf_alloc(PC_ELAPSED, "fw_pos_control_l1: cycle")),
40  _launchDetector(this),
41  _runway_takeoff(this)
42 {
43  // limit to 50 Hz
45 
46  _parameter_handles.l1_period = param_find("FW_L1_PERIOD");
47  _parameter_handles.l1_damping = param_find("FW_L1_DAMPING");
48  _parameter_handles.roll_slew_deg_sec = param_find("FW_L1_R_SLEW_MAX");
49 
50  _parameter_handles.airspeed_min = param_find("FW_AIRSPD_MIN");
51  _parameter_handles.airspeed_trim = param_find("FW_AIRSPD_TRIM");
52  _parameter_handles.airspeed_max = param_find("FW_AIRSPD_MAX");
53  _parameter_handles.airspeed_disabled = param_find("FW_ARSP_MODE");
54 
55  _parameter_handles.pitch_limit_min = param_find("FW_P_LIM_MIN");
56  _parameter_handles.pitch_limit_max = param_find("FW_P_LIM_MAX");
57  _parameter_handles.roll_limit = param_find("FW_R_LIM");
58  _parameter_handles.throttle_min = param_find("FW_THR_MIN");
59  _parameter_handles.throttle_max = param_find("FW_THR_MAX");
60  _parameter_handles.throttle_idle = param_find("FW_THR_IDLE");
61  _parameter_handles.throttle_slew_max = param_find("FW_THR_SLEW_MAX");
62  _parameter_handles.throttle_cruise = param_find("FW_THR_CRUISE");
63  _parameter_handles.throttle_alt_scale = param_find("FW_THR_ALT_SCL");
64  _parameter_handles.throttle_land_max = param_find("FW_THR_LND_MAX");
65  _parameter_handles.man_roll_max_deg = param_find("FW_MAN_R_MAX");
66  _parameter_handles.man_pitch_max_deg = param_find("FW_MAN_P_MAX");
67  _parameter_handles.rollsp_offset_deg = param_find("FW_RSP_OFF");
68  _parameter_handles.pitchsp_offset_deg = param_find("FW_PSP_OFF");
69 
70  _parameter_handles.land_slope_angle = param_find("FW_LND_ANG");
71  _parameter_handles.land_H1_virt = param_find("FW_LND_HVIRT");
72  _parameter_handles.land_flare_alt_relative = param_find("FW_LND_FLALT");
73  _parameter_handles.land_flare_pitch_min_deg = param_find("FW_LND_FL_PMIN");
74  _parameter_handles.land_flare_pitch_max_deg = param_find("FW_LND_FL_PMAX");
75  _parameter_handles.land_thrust_lim_alt_relative = param_find("FW_LND_TLALT");
76  _parameter_handles.land_heading_hold_horizontal_distance = param_find("FW_LND_HHDIST");
77  _parameter_handles.land_use_terrain_estimate = param_find("FW_LND_USETER");
78  _parameter_handles.land_early_config_change = param_find("FW_LND_EARLYCFG");
79  _parameter_handles.land_airspeed_scale = param_find("FW_LND_AIRSPD_SC");
80  _parameter_handles.land_throtTC_scale = param_find("FW_LND_THRTC_SC");
81 
82  _parameter_handles.time_const = param_find("FW_T_TIME_CONST");
83  _parameter_handles.time_const_throt = param_find("FW_T_THRO_CONST");
84  _parameter_handles.min_sink_rate = param_find("FW_T_SINK_MIN");
85  _parameter_handles.max_sink_rate = param_find("FW_T_SINK_MAX");
86  _parameter_handles.max_climb_rate = param_find("FW_T_CLMB_MAX");
87  _parameter_handles.climbout_diff = param_find("FW_CLMBOUT_DIFF");
88  _parameter_handles.throttle_damp = param_find("FW_T_THR_DAMP");
89  _parameter_handles.integrator_gain = param_find("FW_T_INTEG_GAIN");
90  _parameter_handles.vertical_accel_limit = param_find("FW_T_VERT_ACC");
91  _parameter_handles.height_comp_filter_omega = param_find("FW_T_HGT_OMEGA");
92  _parameter_handles.speed_comp_filter_omega = param_find("FW_T_SPD_OMEGA");
93  _parameter_handles.roll_throttle_compensation = param_find("FW_T_RLL2THR");
94  _parameter_handles.speed_weight = param_find("FW_T_SPDWEIGHT");
95  _parameter_handles.pitch_damping = param_find("FW_T_PTCH_DAMP");
96  _parameter_handles.heightrate_p = param_find("FW_T_HRATE_P");
97  _parameter_handles.heightrate_ff = param_find("FW_T_HRATE_FF");
98  _parameter_handles.speedrate_p = param_find("FW_T_SRATE_P");
99  _parameter_handles.loiter_radius = param_find("NAV_LOITER_RAD");
100 
101  // if vehicle is vtol these handles will be set when we get the vehicle status
102  _parameter_handles.airspeed_trans = PARAM_INVALID;
103  _parameter_handles.vtol_type = PARAM_INVALID;
104 
105  // initialize to invalid vtol type
106  _parameters.vtol_type = -1;
107 
108  /* fetch initial parameter values */
110 }
111 
113 {
115 }
116 
117 bool
119 {
121  PX4_ERR("vehicle global position callback registration failed!");
122  return false;
123  }
124 
125  return true;
126 }
127 
128 int
130 {
131  updateParams();
132 
133  param_get(_parameter_handles.airspeed_min, &(_parameters.airspeed_min));
134  param_get(_parameter_handles.airspeed_trim, &(_parameters.airspeed_trim));
135  param_get(_parameter_handles.airspeed_max, &(_parameters.airspeed_max));
136  param_get(_parameter_handles.airspeed_disabled, &(_parameters.airspeed_disabled));
137 
138  param_get(_parameter_handles.pitch_limit_min, &(_parameters.pitch_limit_min));
139  param_get(_parameter_handles.pitch_limit_max, &(_parameters.pitch_limit_max));
140  param_get(_parameter_handles.throttle_min, &(_parameters.throttle_min));
141  param_get(_parameter_handles.throttle_max, &(_parameters.throttle_max));
142  param_get(_parameter_handles.throttle_idle, &(_parameters.throttle_idle));
143  param_get(_parameter_handles.throttle_cruise, &(_parameters.throttle_cruise));
144  param_get(_parameter_handles.throttle_alt_scale, &(_parameters.throttle_alt_scale));
145  param_get(_parameter_handles.throttle_land_max, &(_parameters.throttle_land_max));
146 
147  param_get(_parameter_handles.man_roll_max_deg, &_parameters.man_roll_max_rad);
148  _parameters.man_roll_max_rad = radians(_parameters.man_roll_max_rad);
149  param_get(_parameter_handles.man_pitch_max_deg, &_parameters.man_pitch_max_rad);
150  _parameters.man_pitch_max_rad = radians(_parameters.man_pitch_max_rad);
151 
152  param_get(_parameter_handles.rollsp_offset_deg, &_parameters.rollsp_offset_rad);
153  _parameters.rollsp_offset_rad = radians(_parameters.rollsp_offset_rad);
154  param_get(_parameter_handles.pitchsp_offset_deg, &_parameters.pitchsp_offset_rad);
155  _parameters.pitchsp_offset_rad = radians(_parameters.pitchsp_offset_rad);
156 
157  param_get(_parameter_handles.climbout_diff, &(_parameters.climbout_diff));
158 
159  param_get(_parameter_handles.land_heading_hold_horizontal_distance,
160  &(_parameters.land_heading_hold_horizontal_distance));
161  param_get(_parameter_handles.land_flare_pitch_min_deg, &(_parameters.land_flare_pitch_min_deg));
162  param_get(_parameter_handles.land_flare_pitch_max_deg, &(_parameters.land_flare_pitch_max_deg));
163  param_get(_parameter_handles.land_use_terrain_estimate, &(_parameters.land_use_terrain_estimate));
164  param_get(_parameter_handles.land_early_config_change, &(_parameters.land_early_config_change));
165  param_get(_parameter_handles.land_airspeed_scale, &(_parameters.land_airspeed_scale));
166  param_get(_parameter_handles.land_throtTC_scale, &(_parameters.land_throtTC_scale));
167  param_get(_parameter_handles.loiter_radius, &(_parameters.loiter_radius));
168 
169  // VTOL parameter VTOL_TYPE
170  if (_parameter_handles.vtol_type != PARAM_INVALID) {
171  param_get(_parameter_handles.vtol_type, &_parameters.vtol_type);
172  }
173 
174  // VTOL parameter VT_ARSP_TRANS
175  if (_parameter_handles.airspeed_trans != PARAM_INVALID) {
176  param_get(_parameter_handles.airspeed_trans, &_parameters.airspeed_trans);
177  }
178 
179 
180  float v = 0.0f;
181 
182 
183  // L1 control parameters
184 
185  if (param_get(_parameter_handles.l1_damping, &v) == PX4_OK) {
187  }
188 
189  if (param_get(_parameter_handles.l1_period, &v) == PX4_OK) {
191  }
192 
193  if (param_get(_parameter_handles.roll_limit, &v) == PX4_OK) {
195  }
196 
197  if (param_get(_parameter_handles.roll_slew_deg_sec, &v) == PX4_OK) {
199  }
200 
201  // TECS parameters
202 
203  param_get(_parameter_handles.max_climb_rate, &(_parameters.max_climb_rate));
204  _tecs.set_max_climb_rate(_parameters.max_climb_rate);
205 
206  param_get(_parameter_handles.max_sink_rate, &(_parameters.max_sink_rate));
207  _tecs.set_max_sink_rate(_parameters.max_sink_rate);
208 
209  param_get(_parameter_handles.speed_weight, &(_parameters.speed_weight));
210  _tecs.set_speed_weight(_parameters.speed_weight);
211 
214 
215  if (param_get(_parameter_handles.time_const_throt, &(_parameters.time_const_throt)) == PX4_OK) {
216  _tecs.set_time_const_throt(_parameters.time_const_throt);
217  }
218 
219  if (param_get(_parameter_handles.time_const, &v) == PX4_OK) {
221  }
222 
223  if (param_get(_parameter_handles.min_sink_rate, &v) == PX4_OK) {
225  }
226 
227  if (param_get(_parameter_handles.throttle_damp, &v) == PX4_OK) {
229  }
230 
231  if (param_get(_parameter_handles.integrator_gain, &v) == PX4_OK) {
233  }
234 
235  if (param_get(_parameter_handles.throttle_slew_max, &v) == PX4_OK) {
237  }
238 
239  if (param_get(_parameter_handles.vertical_accel_limit, &v) == PX4_OK) {
241  }
242 
243  if (param_get(_parameter_handles.height_comp_filter_omega, &v) == PX4_OK) {
245  }
246 
247  if (param_get(_parameter_handles.speed_comp_filter_omega, &v) == PX4_OK) {
249  }
250 
251  if (param_get(_parameter_handles.roll_throttle_compensation, &v) == PX4_OK) {
253  }
254 
255  if (param_get(_parameter_handles.pitch_damping, &v) == PX4_OK) {
257  }
258 
259  if (param_get(_parameter_handles.heightrate_p, &v) == PX4_OK) {
261  }
262 
263  if (param_get(_parameter_handles.heightrate_ff, &v) == PX4_OK) {
265  }
266 
267  if (param_get(_parameter_handles.speedrate_p, &v) == PX4_OK) {
269  }
270 
271 
272  // Landing slope
273 
274  float land_slope_angle = 0.0f;
275  param_get(_parameter_handles.land_slope_angle, &land_slope_angle);
276 
277  float land_flare_alt_relative = 0.0f;
278  param_get(_parameter_handles.land_flare_alt_relative, &land_flare_alt_relative);
279 
280  float land_thrust_lim_alt_relative = 0.0f;
281  param_get(_parameter_handles.land_thrust_lim_alt_relative, &land_thrust_lim_alt_relative);
282 
283  float land_H1_virt = 0.0f;
284  param_get(_parameter_handles.land_H1_virt, &land_H1_virt);
285 
286  /* check if negative value for 2/3 of flare altitude is set for throttle cut */
287  if (land_thrust_lim_alt_relative < 0.0f) {
288  land_thrust_lim_alt_relative = 0.66f * land_flare_alt_relative;
289  }
290 
291  _landingslope.update(radians(land_slope_angle), land_flare_alt_relative, land_thrust_lim_alt_relative, land_H1_virt);
292 
294 
295  // sanity check parameters
296  if ((_parameters.airspeed_max < _parameters.airspeed_min) ||
297  (_parameters.airspeed_max < 5.0f) ||
298  (_parameters.airspeed_min > 100.0f) ||
299  (_parameters.airspeed_trim < _parameters.airspeed_min) ||
300  (_parameters.airspeed_trim > _parameters.airspeed_max)) {
301 
302  mavlink_log_critical(&_mavlink_log_pub, "Airspeed parameters invalid");
303 
304  return PX4_ERROR;
305  }
306 
307  return PX4_OK;
308 }
309 
310 void
312 {
313  if (_control_mode_sub.updated()) {
314  const bool was_armed = _control_mode.flag_armed;
315 
317 
318  // reset state when arming
319  if (!was_armed && _control_mode.flag_armed) {
320  reset_takeoff_state(true);
322  }
323  }
324  }
325 }
326 
327 void
329 {
332  handle_command();
333  }
334 }
335 
336 void
338 {
340  /* set correct uORB ID, depending on if vehicle is VTOL or not */
341  if (_attitude_setpoint_id == nullptr) {
342  if (_vehicle_status.is_vtol) {
343  _attitude_setpoint_id = ORB_ID(fw_virtual_attitude_setpoint);
344 
345  _parameter_handles.airspeed_trans = param_find("VT_ARSP_TRANS");
346  _parameter_handles.vtol_type = param_find("VT_TYPE");
347 
349 
350  } else {
351  _attitude_setpoint_id = ORB_ID(vehicle_attitude_setpoint);
352  }
353  }
354  }
355 }
356 
357 void
359 {
360  bool airspeed_valid = _airspeed_valid;
361 
362  if (!_parameters.airspeed_disabled && _airspeed_validated_sub.update()) {
363 
364  const airspeed_validated_s &airspeed_validated = _airspeed_validated_sub.get();
365  _eas2tas = 1.0f; //this is the default value, taken in case of invalid airspeed
366 
367  if (PX4_ISFINITE(airspeed_validated.equivalent_airspeed_m_s)
368  && PX4_ISFINITE(airspeed_validated.true_airspeed_m_s)
369  && (airspeed_validated.equivalent_airspeed_m_s > 0.0f)) {
370 
371  airspeed_valid = true;
372 
373  _airspeed_last_valid = airspeed_validated.timestamp;
374  _airspeed = airspeed_validated.equivalent_airspeed_m_s;
375 
376  _eas2tas = constrain(airspeed_validated.true_airspeed_m_s / airspeed_validated.equivalent_airspeed_m_s, 0.9f, 2.0f);
377  }
378 
379  } else {
380  // no airspeed updates for one second
381  if (airspeed_valid && (hrt_elapsed_time(&_airspeed_last_valid) > 1_s)) {
382  airspeed_valid = false;
383  }
384  }
385 
386  // update TECS if validity changed
387  if (airspeed_valid != _airspeed_valid) {
388  _tecs.enable_airspeed(airspeed_valid);
389  _airspeed_valid = airspeed_valid;
390  }
391 }
392 
393 void
395 {
397  /* set rotation matrix and euler angles */
398  _R_nb = Quatf(_att.q);
399 
400  // if the vehicle is a tailsitter we have to rotate the attitude by the pitch offset
401  // between multirotor and fixed wing flight
402  if (static_cast<vtol_type>(_parameters.vtol_type) == vtol_type::TAILSITTER && _vehicle_status.is_vtol) {
403  Dcmf R_offset = Eulerf(0, M_PI_2_F, 0);
404  _R_nb = _R_nb * R_offset;
405  }
406 
407  Eulerf euler_angles(_R_nb);
408  _roll = euler_angles(0);
409  _pitch = euler_angles(1);
410  _yaw = euler_angles(2);
411  }
412 }
413 
414 float
416 {
417  float altctrl_airspeed = 0;
418 
419  // neutral throttle corresponds to trim airspeed
420  if (_manual.z < 0.5f) {
421  // lower half of throttle is min to trim airspeed
422  altctrl_airspeed = _parameters.airspeed_min +
423  (_parameters.airspeed_trim - _parameters.airspeed_min) *
424  _manual.z * 2;
425 
426  } else {
427  // upper half of throttle is trim to max airspeed
428  altctrl_airspeed = _parameters.airspeed_trim +
429  (_parameters.airspeed_max - _parameters.airspeed_trim) *
430  (_manual.z * 2 - 1);
431  }
432 
433  return altctrl_airspeed;
434 }
435 
436 float
437 FixedwingPositionControl::calculate_target_airspeed(float airspeed_demand, const Vector2f &ground_speed)
438 {
439  /*
440  * Calculate accelerated stall airspeed factor from commanded bank angle and use it to increase minimum airspeed.
441  *
442  * We don't know the stall speed of the aircraft, but assuming user defined
443  * minimum airspeed (FW_AIRSPD_MIN) is slightly larger than stall speed
444  * this is close enough.
445  *
446  * increase lift vector to balance additional weight in bank
447  * cos(bank angle) = W/L = 1/n
448  * n is the load factor
449  *
450  * lift is proportional to airspeed^2 so the increase in stall speed is
451  * Vsacc = Vs * sqrt(n)
452  *
453  */
454  float adjusted_min_airspeed = _parameters.airspeed_min;
455 
456  if (_airspeed_valid && PX4_ISFINITE(_att_sp.roll_body)) {
457 
458  adjusted_min_airspeed = constrain(_parameters.airspeed_min / sqrtf(cosf(_att_sp.roll_body)),
459  _parameters.airspeed_min, _parameters.airspeed_max);
460  }
461 
462  // groundspeed undershoot
463  if (!_l1_control.circle_mode()) {
464 
465  // rotate ground speed vector with current attitude
466  Vector2f yaw_vector(_R_nb(0, 0), _R_nb(1, 0));
467  yaw_vector.normalize();
468 
469  const float ground_speed_body = yaw_vector * ground_speed;
470 
471  /*
472  * This error value ensures that a plane (as long as its throttle capability is
473  * not exceeded) travels towards a waypoint (and is not pushed more and more away
474  * by wind). Not countering this would lead to a fly-away.
475  */
476  if (ground_speed_body < _groundspeed_min.get()) {
477  airspeed_demand += max(_groundspeed_min.get() - ground_speed_body, 0.0f);
478  }
479  }
480 
481  // add minimum ground speed undershoot (only non-zero in presence of sufficient wind)
482  // sanity check: limit to range
483  return constrain(airspeed_demand, adjusted_min_airspeed, _parameters.airspeed_max);
484 }
485 
486 void
488 {
489  tecs_status_s t = {};
490 
491  switch (_tecs.tecs_mode()) {
493  t.mode = tecs_status_s::TECS_MODE_NORMAL;
494  break;
495 
497  t.mode = tecs_status_s::TECS_MODE_UNDERSPEED;
498  break;
499 
501  t.mode = tecs_status_s::TECS_MODE_BAD_DESCENT;
502  break;
503 
505  t.mode = tecs_status_s::TECS_MODE_CLIMBOUT;
506  break;
507  }
508 
511 
514 
517 
520 
523 
526 
529 
531 
533 }
534 
535 void
537 {
538  position_controller_status_s pos_ctrl_status = {};
539 
540  pos_ctrl_status.nav_roll = _att_sp.roll_body;
541  pos_ctrl_status.nav_pitch = _att_sp.pitch_body;
542  pos_ctrl_status.nav_bearing = _l1_control.nav_bearing();
543 
544  pos_ctrl_status.target_bearing = _l1_control.target_bearing();
545  pos_ctrl_status.xtrack_error = _l1_control.crosstrack_error();
546 
549 
550  pos_ctrl_status.acceptance_radius = _l1_control.switch_distance(500.0f);
551 
552  pos_ctrl_status.yaw_acceptance = NAN;
553 
554  pos_ctrl_status.timestamp = hrt_absolute_time();
555 
556  _pos_ctrl_status_pub.publish(pos_ctrl_status);
557 }
558 
559 void
561 {
562  position_controller_landing_status_s pos_ctrl_landing_status = {};
563 
564  pos_ctrl_landing_status.slope_angle_rad = _landingslope.landing_slope_angle_rad();
566  pos_ctrl_landing_status.flare_length = _landingslope.flare_length();
567 
568  pos_ctrl_landing_status.abort_landing = _land_abort;
569 
570  pos_ctrl_landing_status.timestamp = hrt_absolute_time();
571 
572  _pos_ctrl_landing_status_pub.publish(pos_ctrl_landing_status);
573 }
574 
575 void
577 {
578  // only announce changes
579  if (abort && !_land_abort) {
580  mavlink_log_critical(&_mavlink_log_pub, "Landing aborted");
581  }
582 
583  _land_abort = abort;
585 }
586 
587 void
589  position_setpoint_s &waypoint_next, bool flag_init)
590 {
591  position_setpoint_s temp_prev = waypoint_prev;
592  position_setpoint_s temp_next = waypoint_next;
593 
594  if (flag_init) {
595  // previous waypoint: HDG_HOLD_SET_BACK_DIST meters behind us
597  HDG_HOLD_SET_BACK_DIST, &temp_prev.lat, &temp_prev.lon);
598 
599  // next waypoint: HDG_HOLD_DIST_NEXT meters in front of us
601  HDG_HOLD_DIST_NEXT, &temp_next.lat, &temp_next.lon);
602 
603  } else {
604  // use the existing flight path from prev to next
605 
606  // previous waypoint: shifted HDG_HOLD_REACHED_DIST + HDG_HOLD_SET_BACK_DIST
607  create_waypoint_from_line_and_dist(waypoint_next.lat, waypoint_next.lon, waypoint_prev.lat, waypoint_prev.lon,
608  HDG_HOLD_REACHED_DIST + HDG_HOLD_SET_BACK_DIST, &temp_prev.lat, &temp_prev.lon);
609 
610  // next waypoint: shifted -(HDG_HOLD_DIST_NEXT + HDG_HOLD_REACHED_DIST)
611  create_waypoint_from_line_and_dist(waypoint_next.lat, waypoint_next.lon, waypoint_prev.lat, waypoint_prev.lon,
612  -(HDG_HOLD_REACHED_DIST + HDG_HOLD_DIST_NEXT), &temp_next.lat, &temp_next.lon);
613  }
614 
615  waypoint_prev = temp_prev;
616  waypoint_prev.alt = _hold_alt;
617  waypoint_prev.valid = true;
618 
619  waypoint_next = temp_next;
620  waypoint_next.alt = _hold_alt;
621  waypoint_next.valid = true;
622 }
623 
624 float
626  const vehicle_global_position_s &global_pos)
627 {
628  if (PX4_ISFINITE(global_pos.terrain_alt) && global_pos.terrain_alt_valid) {
629  return global_pos.terrain_alt;
630  }
631 
632  return takeoff_alt;
633 }
634 
635 bool
637 {
638  /*
639  * The complete range is -1..+1, so this is 6%
640  * of the up or down range or 3% of the total range.
641  */
642  const float deadBand = 0.06f;
643 
644  /*
645  * The correct scaling of the complete range needs
646  * to account for the missing part of the slope
647  * due to the deadband
648  */
649  const float factor = 1.0f - deadBand;
650 
651  /* Climbout mode sets maximum throttle and pitch up */
652  bool climbout_mode = false;
653 
654  /*
655  * Reset the hold altitude to the current altitude if the uncertainty
656  * changes significantly.
657  * This is to guard against uncommanded altitude changes
658  * when the altitude certainty increases or decreases.
659  */
660 
664  }
665 
666  /*
667  * Manual control has as convention the rotation around
668  * an axis. Positive X means to rotate positively around
669  * the X axis in NED frame, which is pitching down
670  */
671  if (_manual.x > deadBand) {
672  /* pitching down */
673  float pitch = -(_manual.x - deadBand) / factor;
674  _hold_alt += (_parameters.max_sink_rate * dt) * pitch;
675  _was_in_deadband = false;
676 
677  } else if (_manual.x < - deadBand) {
678  /* pitching up */
679  float pitch = -(_manual.x + deadBand) / factor;
680  _hold_alt += (_parameters.max_climb_rate * dt) * pitch;
681  _was_in_deadband = false;
682  climbout_mode = (pitch > MANUAL_THROTTLE_CLIMBOUT_THRESH);
683 
684  } else if (!_was_in_deadband) {
685  /* store altitude at which manual.x was inside deadBand
686  * The aircraft should immediately try to fly at this altitude
687  * as this is what the pilot expects when he moves the stick to the center */
690  _was_in_deadband = true;
691  }
692 
693  if (_vehicle_status.is_vtol) {
694  if (_vehicle_status.vehicle_type == vehicle_status_s::VEHICLE_TYPE_ROTARY_WING || _vehicle_status.in_transition_mode) {
696  }
697  }
698 
699  return climbout_mode;
700 }
701 
702 bool
704 {
705  // a VTOL does not need special takeoff handling
706  if (_vehicle_status.is_vtol) {
707  return false;
708  }
709 
710  // in air for < 10s
711  const hrt_abstime delta_takeoff = 10_s;
712 
713  return (hrt_elapsed_time(&_time_went_in_air) < delta_takeoff)
714  && (_global_pos.alt <= _takeoff_ground_alt + _parameters.climbout_diff);
715 }
716 
717 void
719 {
720  /* demand "climbout_diff" m above ground if user switched into this mode during takeoff */
721  if (in_takeoff_situation()) {
722  *hold_altitude = _takeoff_ground_alt + _parameters.climbout_diff;
723  *pitch_limit_min = radians(10.0f);
724  }
725 }
726 
727 bool
728 FixedwingPositionControl::control_position(const Vector2f &curr_pos, const Vector2f &ground_speed,
729  const position_setpoint_s &pos_sp_prev, const position_setpoint_s &pos_sp_curr, const position_setpoint_s &pos_sp_next)
730 {
731  float dt = 0.01f;
732 
735  }
736 
738 
739  _l1_control.set_dt(dt);
740 
741  /* only run position controller in fixed-wing mode and during transitions for VTOL */
742  if (_vehicle_status.vehicle_type == vehicle_status_s::VEHICLE_TYPE_ROTARY_WING && !_vehicle_status.in_transition_mode) {
743  _control_mode_current = FW_POSCTRL_MODE_OTHER;
744  return false;
745  }
746 
747  bool setpoint = true;
748 
749  _att_sp.fw_control_yaw = false; // by default we don't want yaw to be contoller directly with rudder
750  _att_sp.apply_flaps = vehicle_attitude_setpoint_s::FLAPS_OFF; // by default we don't use flaps
751 
752  Vector2f nav_speed_2d{ground_speed};
753 
754  if (_airspeed_valid) {
755  // l1 navigation logic breaks down when wind speed exceeds max airspeed
756  // compute 2D groundspeed from airspeed-heading projection
757  const Vector2f air_speed_2d{_airspeed * cosf(_yaw), _airspeed * sinf(_yaw)};
758 
759  // angle between air_speed_2d and ground_speed
760  const float air_gnd_angle = acosf((air_speed_2d * ground_speed) / (air_speed_2d.length() * ground_speed.length()));
761 
762  // if angle > 90 degrees or groundspeed is less than threshold, replace groundspeed with airspeed projection
763  if ((fabsf(air_gnd_angle) > M_PI_2_F) || (ground_speed.length() < 3.0f)) {
764  nav_speed_2d = air_speed_2d;
765  }
766  }
767 
768  /* no throttle limit as default */
769  float throttle_max = 1.0f;
770 
771  /* save time when airplane is in air */
773  _was_in_air = true;
776  }
777 
778  /* reset flag when airplane landed */
780  _was_in_air = false;
781  }
782 
783  /* Reset integrators if switching to this mode from a other mode in which posctl was not active */
784  if (_control_mode_current == FW_POSCTRL_MODE_OTHER) {
785  /* reset integrators */
786  _tecs.reset_state();
787  }
788 
790  /* AUTONOMOUS FLIGHT */
791 
792  _control_mode_current = FW_POSCTRL_MODE_AUTO;
793 
794  /* reset hold altitude */
796 
797  /* reset hold yaw */
799 
800  /* get circle mode */
801  bool was_circle_mode = _l1_control.circle_mode();
802 
803  /* restore TECS parameters, in case changed intermittently (e.g. in landing handling) */
804  _tecs.set_speed_weight(_parameters.speed_weight);
805  _tecs.set_time_const_throt(_parameters.time_const_throt);
806 
807  /* current waypoint (the one currently heading for) */
808  Vector2f curr_wp((float)pos_sp_curr.lat, (float)pos_sp_curr.lon);
809 
810  /* Initialize attitude controller integrator reset flags to 0 */
813  _att_sp.yaw_reset_integral = false;
814 
815  /* previous waypoint */
816  Vector2f prev_wp{0.0f, 0.0f};
817 
818  if (pos_sp_prev.valid) {
819  prev_wp(0) = (float)pos_sp_prev.lat;
820  prev_wp(1) = (float)pos_sp_prev.lon;
821 
822  } else {
823  /*
824  * No valid previous waypoint, go for the current wp.
825  * This is automatically handled by the L1 library.
826  */
827  prev_wp(0) = (float)pos_sp_curr.lat;
828  prev_wp(1) = (float)pos_sp_curr.lon;
829  }
830 
831  float mission_airspeed = _parameters.airspeed_trim;
832 
833  if (PX4_ISFINITE(pos_sp_curr.cruising_speed) &&
834  pos_sp_curr.cruising_speed > 0.1f) {
835 
836  mission_airspeed = pos_sp_curr.cruising_speed;
837  }
838 
839  float mission_throttle = _parameters.throttle_cruise;
840 
841  if (PX4_ISFINITE(pos_sp_curr.cruising_throttle) &&
842  pos_sp_curr.cruising_throttle > 0.01f) {
843 
844  mission_throttle = pos_sp_curr.cruising_throttle;
845  }
846 
847  if (pos_sp_curr.type == position_setpoint_s::SETPOINT_TYPE_IDLE) {
848  _att_sp.thrust_body[0] = 0.0f;
849  _att_sp.roll_body = 0.0f;
850  _att_sp.pitch_body = 0.0f;
851 
852  } else if (pos_sp_curr.type == position_setpoint_s::SETPOINT_TYPE_POSITION) {
853  /* waypoint is a plain navigation waypoint */
854  _l1_control.navigate_waypoints(prev_wp, curr_wp, curr_pos, nav_speed_2d);
857 
858  tecs_update_pitch_throttle(pos_sp_curr.alt,
859  calculate_target_airspeed(mission_airspeed, ground_speed),
860  radians(_parameters.pitch_limit_min) - _parameters.pitchsp_offset_rad,
861  radians(_parameters.pitch_limit_max) - _parameters.pitchsp_offset_rad,
862  _parameters.throttle_min,
863  _parameters.throttle_max,
864  mission_throttle,
865  false,
866  radians(_parameters.pitch_limit_min));
867 
868  } else if (pos_sp_curr.type == position_setpoint_s::SETPOINT_TYPE_LOITER) {
869 
870  /* waypoint is a loiter waypoint */
871  float loiter_radius = pos_sp_curr.loiter_radius;
872  uint8_t loiter_direction = pos_sp_curr.loiter_direction;
873 
874  if (pos_sp_curr.loiter_radius < 0.01f || pos_sp_curr.loiter_radius > -0.01f) {
875  loiter_radius = _parameters.loiter_radius;
876  loiter_direction = (loiter_radius > 0) ? 1 : -1;
877 
878  }
879 
880  _l1_control.navigate_loiter(curr_wp, curr_pos, loiter_radius, loiter_direction, nav_speed_2d);
881 
884 
885  float alt_sp = pos_sp_curr.alt;
886 
887  if (pos_sp_next.type == position_setpoint_s::SETPOINT_TYPE_LAND && pos_sp_next.valid
888  && _l1_control.circle_mode() && _parameters.land_early_config_change == 1) {
889  // We're in a loiter directly before a landing WP. Enable our landing configuration (flaps,
890  // landing airspeed and potentially tighter throttle control) already such that we don't
891  // have to do this switch (which can cause significant altitude errors) close to the ground.
892  _tecs.set_time_const_throt(_parameters.land_throtTC_scale * _parameters.time_const_throt);
893  mission_airspeed = _parameters.land_airspeed_scale * _parameters.airspeed_min;
894  _att_sp.apply_flaps = true;
895  }
896 
897  if (in_takeoff_situation()) {
898  alt_sp = max(alt_sp, _takeoff_ground_alt + _parameters.climbout_diff);
900  }
901 
902  if (_land_abort) {
903  if (pos_sp_curr.alt - _global_pos.alt < _parameters.climbout_diff) {
904  // aborted landing complete, normal loiter over landing point
905  abort_landing(false);
906 
907  } else {
908  // continue straight until vehicle has sufficient altitude
909  _att_sp.roll_body = 0.0f;
910  }
911 
912  _tecs.set_time_const_throt(_parameters.land_throtTC_scale * _parameters.time_const_throt);
913  }
914 
916  calculate_target_airspeed(mission_airspeed, ground_speed),
917  radians(_parameters.pitch_limit_min) - _parameters.pitchsp_offset_rad,
918  radians(_parameters.pitch_limit_max) - _parameters.pitchsp_offset_rad,
919  _parameters.throttle_min,
920  _parameters.throttle_max,
921  _parameters.throttle_cruise,
922  false,
923  radians(_parameters.pitch_limit_min));
924 
925  } else if (pos_sp_curr.type == position_setpoint_s::SETPOINT_TYPE_LAND) {
926  control_landing(curr_pos, ground_speed, pos_sp_prev, pos_sp_curr);
927 
928  } else if (pos_sp_curr.type == position_setpoint_s::SETPOINT_TYPE_TAKEOFF) {
929  control_takeoff(curr_pos, ground_speed, pos_sp_prev, pos_sp_curr);
930  }
931 
932  /* reset landing state */
933  if (pos_sp_curr.type != position_setpoint_s::SETPOINT_TYPE_LAND) {
935  }
936 
937  /* reset takeoff/launch state */
938  if (pos_sp_curr.type != position_setpoint_s::SETPOINT_TYPE_TAKEOFF) {
940  }
941 
942  if (was_circle_mode && !_l1_control.circle_mode()) {
943  /* just kicked out of loiter, reset roll integrals */
945  }
946 
949  /* POSITION CONTROL: pitch stick moves altitude setpoint, throttle stick sets airspeed,
950  heading is set to a distant waypoint */
951 
952  if (_control_mode_current != FW_POSCTRL_MODE_POSITION) {
953  /* Need to init because last loop iteration was in a different mode */
956  _hdg_hold_enabled = false; // this makes sure the waypoints are reset below
957  _yaw_lock_engaged = false;
958 
959  /* reset setpoints from other modes (auto) otherwise we won't
960  * level out without new manual input */
961  _att_sp.roll_body = _manual.y * _parameters.man_roll_max_rad;
962  _att_sp.yaw_body = 0;
963  }
964 
965  _control_mode_current = FW_POSCTRL_MODE_POSITION;
966 
967  float altctrl_airspeed = get_demanded_airspeed();
968 
969  /* update desired altitude based on user pitch stick input */
970  bool climbout_requested = update_desired_altitude(dt);
971 
972  // if we assume that user is taking off then help by demanding altitude setpoint well above ground
973  // and set limit to pitch angle to prevent steering into ground
974  // this will only affect planes and not VTOL
975  float pitch_limit_min = _parameters.pitch_limit_min;
976  do_takeoff_help(&_hold_alt, &pitch_limit_min);
977 
978  /* throttle limiting */
979  throttle_max = _parameters.throttle_max;
980 
982  throttle_max = 0.0f;
983  }
984 
986  altctrl_airspeed,
987  radians(_parameters.pitch_limit_min),
988  radians(_parameters.pitch_limit_max),
989  _parameters.throttle_min,
990  throttle_max,
991  _parameters.throttle_cruise,
992  climbout_requested,
993  climbout_requested ? radians(10.0f) : pitch_limit_min,
994  tecs_status_s::TECS_MODE_NORMAL);
995 
996  /* heading control */
997  if (fabsf(_manual.y) < HDG_HOLD_MAN_INPUT_THRESH &&
999 
1000  /* heading / roll is zero, lock onto current heading */
1002  // little yaw movement, lock to current heading
1003  _yaw_lock_engaged = true;
1004 
1005  }
1006 
1007  /* user tries to do a takeoff in heading hold mode, reset the yaw setpoint on every iteration
1008  to make sure the plane does not start rolling
1009  */
1010  if (in_takeoff_situation()) {
1011  _hdg_hold_enabled = false;
1012  _yaw_lock_engaged = true;
1013  }
1014 
1015  if (_yaw_lock_engaged) {
1016 
1017  /* just switched back from non heading-hold to heading hold */
1018  if (!_hdg_hold_enabled) {
1019  _hdg_hold_enabled = true;
1020  _hdg_hold_yaw = _yaw;
1021 
1023  }
1024 
1025  /* we have a valid heading hold position, are we too close? */
1028 
1029  if (dist < HDG_HOLD_REACHED_DIST) {
1031  }
1032 
1033  Vector2f prev_wp{(float)_hdg_hold_prev_wp.lat, (float)_hdg_hold_prev_wp.lon};
1034  Vector2f curr_wp{(float)_hdg_hold_curr_wp.lat, (float)_hdg_hold_curr_wp.lon};
1035 
1036  /* populate l1 control setpoint */
1037  _l1_control.navigate_waypoints(prev_wp, curr_wp, curr_pos, ground_speed);
1038 
1041 
1042  if (in_takeoff_situation()) {
1043  /* limit roll motion to ensure enough lift */
1045  }
1046  }
1047  }
1048 
1050  fabsf(_manual.r) >= HDG_HOLD_MAN_INPUT_THRESH) {
1051 
1052  _hdg_hold_enabled = false;
1053  _yaw_lock_engaged = false;
1054  _att_sp.roll_body = _manual.y * _parameters.man_roll_max_rad;
1055  _att_sp.yaw_body = 0;
1056  }
1057 
1059  /* ALTITUDE CONTROL: pitch stick moves altitude setpoint, throttle stick sets airspeed */
1060 
1061  if (_control_mode_current != FW_POSCTRL_MODE_POSITION && _control_mode_current != FW_POSCTRL_MODE_ALTITUDE) {
1062  /* Need to init because last loop iteration was in a different mode */
1064  }
1065 
1066  _control_mode_current = FW_POSCTRL_MODE_ALTITUDE;
1067 
1068  /* Get demanded airspeed */
1069  float altctrl_airspeed = get_demanded_airspeed();
1070 
1071  /* update desired altitude based on user pitch stick input */
1072  bool climbout_requested = update_desired_altitude(dt);
1073 
1074  // if we assume that user is taking off then help by demanding altitude setpoint well above ground
1075  // and set limit to pitch angle to prevent steering into ground
1076  // this will only affect planes and not VTOL
1077  float pitch_limit_min = _parameters.pitch_limit_min;
1078  do_takeoff_help(&_hold_alt, &pitch_limit_min);
1079 
1080  /* throttle limiting */
1081  throttle_max = _parameters.throttle_max;
1082 
1084  throttle_max = 0.0f;
1085  }
1086 
1088  altctrl_airspeed,
1089  radians(_parameters.pitch_limit_min),
1090  radians(_parameters.pitch_limit_max),
1091  _parameters.throttle_min,
1092  throttle_max,
1093  _parameters.throttle_cruise,
1094  climbout_requested,
1095  climbout_requested ? radians(10.0f) : pitch_limit_min,
1096  tecs_status_s::TECS_MODE_NORMAL);
1097 
1098  _att_sp.roll_body = _manual.y * _parameters.man_roll_max_rad;
1099  _att_sp.yaw_body = 0;
1100 
1101  } else {
1102  _control_mode_current = FW_POSCTRL_MODE_OTHER;
1103 
1104  /* do not publish the setpoint */
1105  setpoint = false;
1106 
1107  // reset hold altitude
1109 
1110  /* reset landing and takeoff state */
1111  if (!_last_manual) {
1114  }
1115  }
1116 
1117  /* Copy thrust output for publication */
1118  if (_control_mode_current == FW_POSCTRL_MODE_AUTO && // launchdetector only available in auto
1119  pos_sp_curr.type == position_setpoint_s::SETPOINT_TYPE_TAKEOFF &&
1122 
1123  /* making sure again that the correct thrust is used,
1124  * without depending on library calls for safety reasons.
1125  the pre-takeoff throttle and the idle throttle normally map to the same parameter. */
1126  _att_sp.thrust_body[0] = _parameters.throttle_idle;
1127 
1128  } else if (_control_mode_current == FW_POSCTRL_MODE_AUTO &&
1129  pos_sp_curr.type == position_setpoint_s::SETPOINT_TYPE_TAKEOFF &&
1131 
1133 
1134  } else if (_control_mode_current == FW_POSCTRL_MODE_AUTO &&
1135  pos_sp_curr.type == position_setpoint_s::SETPOINT_TYPE_IDLE) {
1136 
1137  _att_sp.thrust_body[0] = 0.0f;
1138 
1139  } else if (_control_mode_current == FW_POSCTRL_MODE_OTHER) {
1140  _att_sp.thrust_body[0] = min(_att_sp.thrust_body[0], _parameters.throttle_max);
1141 
1142  } else {
1143  /* Copy thrust and pitch values from tecs */
1145  // when we are landed state we want the motor to spin at idle speed
1146  _att_sp.thrust_body[0] = min(_parameters.throttle_idle, throttle_max);
1147 
1148  } else {
1149  _att_sp.thrust_body[0] = min(get_tecs_thrust(), throttle_max);
1150  }
1151  }
1152 
1153  // decide when to use pitch setpoint from TECS because in some cases pitch
1154  // setpoint is generated by other means
1155  bool use_tecs_pitch = true;
1156 
1157  // auto runway takeoff
1158  use_tecs_pitch &= !(_control_mode_current == FW_POSCTRL_MODE_AUTO &&
1159  pos_sp_curr.type == position_setpoint_s::SETPOINT_TYPE_TAKEOFF &&
1161 
1162  // flaring during landing
1163  use_tecs_pitch &= !(pos_sp_curr.type == position_setpoint_s::SETPOINT_TYPE_LAND && _land_noreturn_vertical);
1164 
1165  // manual attitude control
1166  use_tecs_pitch &= !(_control_mode_current == FW_POSCTRL_MODE_OTHER);
1167 
1168  if (use_tecs_pitch) {
1170  }
1171 
1173  _last_manual = false;
1174 
1175  } else {
1176  _last_manual = true;
1177  }
1178 
1179  return setpoint;
1180 }
1181 
1182 void
1183 FixedwingPositionControl::control_takeoff(const Vector2f &curr_pos, const Vector2f &ground_speed,
1184  const position_setpoint_s &pos_sp_prev, const position_setpoint_s &pos_sp_curr)
1185 {
1186  /* current waypoint (the one currently heading for) */
1187  Vector2f curr_wp((float)pos_sp_curr.lat, (float)pos_sp_curr.lon);
1188  Vector2f prev_wp{0.0f, 0.0f}; /* previous waypoint */
1189 
1190  if (pos_sp_prev.valid) {
1191  prev_wp(0) = (float)pos_sp_prev.lat;
1192  prev_wp(1) = (float)pos_sp_prev.lon;
1193 
1194  } else {
1195  /*
1196  * No valid previous waypoint, go for the current wp.
1197  * This is automatically handled by the L1 library.
1198  */
1199  prev_wp(0) = (float)pos_sp_curr.lat;
1200  prev_wp(1) = (float)pos_sp_curr.lon;
1201  }
1202 
1203  // apply flaps for takeoff according to the corresponding scale factor set
1204  // via FW_FLAPS_TO_SCL
1205  _att_sp.apply_flaps = vehicle_attitude_setpoint_s::FLAPS_TAKEOFF;
1206 
1207  // continuously reset launch detection and runway takeoff until armed
1208  if (!_control_mode.flag_armed) {
1212  }
1213 
1215  if (!_runway_takeoff.isInitialized()) {
1216  Eulerf euler(Quatf(_att.q));
1218 
1219  /* need this already before takeoff is detected
1220  * doesn't matter if it gets reset when takeoff is detected eventually */
1222 
1223  mavlink_log_info(&_mavlink_log_pub, "Takeoff on runway");
1224  }
1225 
1227 
1228  // update runway takeoff helper
1231 
1232  /*
1233  * Update navigation: _runway_takeoff returns the start WP according to mode and phase.
1234  * If we use the navigator heading or not is decided later.
1235  */
1236  _l1_control.navigate_waypoints(_runway_takeoff.getStartWP(), curr_wp, curr_pos, ground_speed);
1237 
1238  // update tecs
1239  const float takeoff_pitch_max_deg = _runway_takeoff.getMaxPitch(_parameters.pitch_limit_max);
1240 
1241  tecs_update_pitch_throttle(pos_sp_curr.alt,
1243  radians(_parameters.pitch_limit_min),
1244  radians(takeoff_pitch_max_deg),
1245  _parameters.throttle_min,
1246  _parameters.throttle_max, // XXX should we also set runway_takeoff_throttle here?
1247  _parameters.throttle_cruise,
1249  radians(_runway_takeoff.getMinPitch(pos_sp_curr.pitch_min, 10.0f, _parameters.pitch_limit_min)),
1250  tecs_status_s::TECS_MODE_TAKEOFF);
1251 
1252  // assign values
1257 
1258  // reset integrals except yaw (which also counts for the wheel controller)
1261 
1262  } else {
1263  /* Perform launch detection */
1266 
1267  if (_control_mode.flag_armed) {
1268  /* Perform launch detection */
1269 
1270  /* Inform user that launchdetection is running every 4s */
1272  mavlink_log_critical(&_mavlink_log_pub, "Launch detection running");
1274  }
1275 
1276  /* Detect launch using body X (forward) acceleration */
1278 
1279  /* update our copy of the launch detection state */
1281  }
1282 
1283  } else {
1284  /* no takeoff detection --> fly */
1286  }
1287 
1288  /* Set control values depending on the detection state */
1290  /* Launch has been detected, hence we have to control the plane. */
1291 
1292  _l1_control.navigate_waypoints(prev_wp, curr_wp, curr_pos, ground_speed);
1295 
1296  /* Select throttle: only in LAUNCHDETECTION_RES_DETECTED_ENABLEMOTORS we want to use
1297  * full throttle, otherwise we use idle throttle */
1298  float takeoff_throttle = _parameters.throttle_max;
1299 
1301  takeoff_throttle = _parameters.throttle_idle;
1302  }
1303 
1304  /* select maximum pitch: the launchdetector may impose another limit for the pitch
1305  * depending on the state of the launch */
1306  const float takeoff_pitch_max_deg = _launchDetector.getPitchMax(_parameters.pitch_limit_max);
1307  const float altitude_error = pos_sp_curr.alt - _global_pos.alt;
1308 
1309  /* apply minimum pitch and limit roll if target altitude is not within climbout_diff meters */
1310  if (_parameters.climbout_diff > 0.0f && altitude_error > _parameters.climbout_diff) {
1311  /* enforce a minimum of 10 degrees pitch up on takeoff, or take parameter */
1312  tecs_update_pitch_throttle(pos_sp_curr.alt,
1313  _parameters.airspeed_trim,
1314  radians(_parameters.pitch_limit_min),
1315  radians(takeoff_pitch_max_deg),
1316  _parameters.throttle_min,
1317  takeoff_throttle,
1318  _parameters.throttle_cruise,
1319  true,
1320  max(radians(pos_sp_curr.pitch_min), radians(10.0f)),
1321  tecs_status_s::TECS_MODE_TAKEOFF);
1322 
1323  /* limit roll motion to ensure enough lift */
1325 
1326  } else {
1327  tecs_update_pitch_throttle(pos_sp_curr.alt,
1328  calculate_target_airspeed(_parameters.airspeed_trim, ground_speed),
1329  radians(_parameters.pitch_limit_min),
1330  radians(_parameters.pitch_limit_max),
1331  _parameters.throttle_min,
1332  takeoff_throttle,
1333  _parameters.throttle_cruise,
1334  false,
1335  radians(_parameters.pitch_limit_min));
1336  }
1337 
1338  } else {
1339  /* Tell the attitude controller to stop integrating while we are waiting
1340  * for the launch */
1343  _att_sp.yaw_reset_integral = true;
1344 
1345  /* Set default roll and pitch setpoints during detection phase */
1346  _att_sp.roll_body = 0.0f;
1347  _att_sp.pitch_body = max(radians(pos_sp_curr.pitch_min), radians(10.0f));
1348  }
1349  }
1350 }
1351 
1352 void
1353 FixedwingPositionControl::control_landing(const Vector2f &curr_pos, const Vector2f &ground_speed,
1354  const position_setpoint_s &pos_sp_prev, const position_setpoint_s &pos_sp_curr)
1355 {
1356  /* current waypoint (the one currently heading for) */
1357  Vector2f curr_wp((float)pos_sp_curr.lat, (float)pos_sp_curr.lon);
1358  Vector2f prev_wp{0.0f, 0.0f}; /* previous waypoint */
1359 
1360  if (pos_sp_prev.valid) {
1361  prev_wp(0) = (float)pos_sp_prev.lat;
1362  prev_wp(1) = (float)pos_sp_prev.lon;
1363 
1364  } else {
1365  /*
1366  * No valid previous waypoint, go for the current wp.
1367  * This is automatically handled by the L1 library.
1368  */
1369  prev_wp(0) = (float)pos_sp_curr.lat;
1370  prev_wp(1) = (float)pos_sp_curr.lon;
1371  }
1372 
1373  // apply full flaps for landings. this flag will also trigger the use of flaperons
1374  // if they have been enabled using the corresponding parameter
1375  _att_sp.apply_flaps = vehicle_attitude_setpoint_s::FLAPS_LAND;
1376 
1377  // Enable tighter throttle control for landings
1378  _tecs.set_time_const_throt(_parameters.land_throtTC_scale * _parameters.time_const_throt);
1379 
1380  // save time at which we started landing and reset abort_landing
1381  if (_time_started_landing == 0) {
1384  }
1385 
1386  const float bearing_airplane_currwp = get_bearing_to_next_waypoint((double)curr_pos(0), (double)curr_pos(1),
1387  (double)curr_wp(0), (double)curr_wp(1));
1388 
1389  float bearing_lastwp_currwp = bearing_airplane_currwp;
1390 
1391  if (pos_sp_prev.valid) {
1392  bearing_lastwp_currwp = get_bearing_to_next_waypoint((double)prev_wp(0), (double)prev_wp(1), (double)curr_wp(0),
1393  (double)curr_wp(1));
1394  }
1395 
1396  /* Horizontal landing control */
1397  /* switch to heading hold for the last meters, continue heading hold after */
1398  float wp_distance = get_distance_to_next_waypoint((double)curr_pos(0), (double)curr_pos(1), (double)curr_wp(0),
1399  (double)curr_wp(1));
1400 
1401  /* calculate a waypoint distance value which is 0 when the aircraft is behind the waypoint */
1402  float wp_distance_save = wp_distance;
1403 
1404  if (fabsf(wrap_pi(bearing_airplane_currwp - bearing_lastwp_currwp)) >= radians(90.0f)) {
1405  wp_distance_save = 0.0f;
1406  }
1407 
1408  // create virtual waypoint which is on the desired flight path but
1409  // some distance behind landing waypoint. This will make sure that the plane
1410  // will always follow the desired flight path even if we get close or past
1411  // the landing waypoint
1412  if (pos_sp_prev.valid) {
1413  double lat = pos_sp_curr.lat;
1414  double lon = pos_sp_curr.lon;
1415 
1416  create_waypoint_from_line_and_dist(pos_sp_curr.lat, pos_sp_curr.lon,
1417  pos_sp_prev.lat, pos_sp_prev.lon, -1000.0f, &lat, &lon);
1418 
1419  curr_wp(0) = (float)lat;
1420  curr_wp(1) = (float)lon;
1421  }
1422 
1423  // we want the plane to keep tracking the desired flight path until we start flaring
1424  // if we go into heading hold mode earlier then we risk to be pushed away from the runway by cross winds
1425  if ((_parameters.land_heading_hold_horizontal_distance > 0.0f) && !_land_noreturn_horizontal &&
1426  ((wp_distance < _parameters.land_heading_hold_horizontal_distance) || _land_noreturn_vertical)) {
1427 
1428  if (pos_sp_prev.valid) {
1429  /* heading hold, along the line connecting this and the last waypoint */
1430  _target_bearing = bearing_lastwp_currwp;
1431 
1432  } else {
1434  }
1435 
1437  mavlink_log_info(&_mavlink_log_pub, "Landing, heading hold");
1438  }
1439 
1441  // heading hold
1443 
1444  } else {
1445  // normal navigation
1446  _l1_control.navigate_waypoints(prev_wp, curr_wp, curr_pos, ground_speed);
1447  }
1448 
1451 
1453  /* limit roll motion to prevent wings from touching the ground first */
1455  }
1456 
1457  /* Vertical landing control */
1458  /* apply minimum pitch (flare) and limit roll if close to touch down, altitude error is negative (going down) */
1459 
1460  // default to no terrain estimation, just use landing waypoint altitude
1461  float terrain_alt = pos_sp_curr.alt;
1462 
1463  if (_parameters.land_use_terrain_estimate == 1) {
1465  // all good, have valid terrain altitude
1466  terrain_alt = _global_pos.terrain_alt;
1467  _t_alt_prev_valid = terrain_alt;
1469 
1470  } else if (_time_last_t_alt == 0) {
1471  // we have started landing phase but don't have valid terrain
1472  // wait for some time, maybe we will soon get a valid estimate
1473  // until then just use the altitude of the landing waypoint
1474  if (hrt_elapsed_time(&_time_started_landing) < 10_s) {
1475  terrain_alt = pos_sp_curr.alt;
1476 
1477  } else {
1478  // still no valid terrain, abort landing
1479  terrain_alt = pos_sp_curr.alt;
1480  abort_landing(true);
1481  }
1482 
1485  // use previous terrain estimate for some time and hope to recover
1486  // if we are already flaring (land_noreturn_vertical) then just
1487  // go with the old estimate
1488  terrain_alt = _t_alt_prev_valid;
1489 
1490  } else {
1491  // terrain alt was not valid for long time, abort landing
1492  terrain_alt = _t_alt_prev_valid;
1493  abort_landing(true);
1494  }
1495  }
1496 
1497  /* Check if we should start flaring with a vertical and a
1498  * horizontal limit (with some tolerance)
1499  * The horizontal limit is only applied when we are in front of the wp
1500  */
1501  if ((_global_pos.alt < terrain_alt + _landingslope.flare_relative_alt()) ||
1502  _land_noreturn_vertical) { //checking for land_noreturn to avoid unwanted climb out
1503 
1504  /* land with minimal speed */
1505 
1506  /* force TECS to only control speed with pitch, altitude is only implicitly controlled now */
1507  // _tecs.set_speed_weight(2.0f);
1508 
1509  /* kill the throttle if param requests it */
1510  float throttle_max = _parameters.throttle_max;
1511 
1512  /* enable direct yaw control using rudder/wheel */
1515  _att_sp.fw_control_yaw = true;
1516  }
1517 
1518  if (((_global_pos.alt < terrain_alt + _landingslope.motor_lim_relative_alt()) &&
1519  (wp_distance_save < _landingslope.flare_length() + 5.0f)) || // Only kill throttle when close to WP
1520  _land_motor_lim) {
1521  throttle_max = min(throttle_max, _parameters.throttle_land_max);
1522 
1523  if (!_land_motor_lim) {
1524  _land_motor_lim = true;
1525  mavlink_log_info(&_mavlink_log_pub, "Landing, limiting throttle");
1526  }
1527  }
1528 
1529  float flare_curve_alt_rel = _landingslope.getFlareCurveRelativeAltitudeSave(wp_distance, bearing_lastwp_currwp,
1530  bearing_airplane_currwp);
1531 
1532  /* avoid climbout */
1533  if ((_flare_curve_alt_rel_last < flare_curve_alt_rel && _land_noreturn_vertical) || _land_stayonground) {
1534  flare_curve_alt_rel = 0.0f; // stay on ground
1535  _land_stayonground = true;
1536  }
1537 
1538  const float airspeed_land = _parameters.land_airspeed_scale * _parameters.airspeed_min;
1539  const float throttle_land = _parameters.throttle_min + (_parameters.throttle_max - _parameters.throttle_min) * 0.1f;
1540 
1541  tecs_update_pitch_throttle(terrain_alt + flare_curve_alt_rel,
1542  calculate_target_airspeed(airspeed_land, ground_speed),
1543  radians(_parameters.land_flare_pitch_min_deg),
1544  radians(_parameters.land_flare_pitch_max_deg),
1545  0.0f,
1546  throttle_max,
1547  throttle_land,
1548  false,
1549  _land_motor_lim ? radians(_parameters.land_flare_pitch_min_deg) : radians(_parameters.pitch_limit_min),
1550  _land_motor_lim ? tecs_status_s::TECS_MODE_LAND_THROTTLELIM : tecs_status_s::TECS_MODE_LAND);
1551 
1552  if (!_land_noreturn_vertical) {
1553  // just started with the flaring phase
1554  _flare_pitch_sp = 0.0f;
1555  _flare_height = _global_pos.alt - terrain_alt;
1556  mavlink_log_info(&_mavlink_log_pub, "Landing, flaring");
1557  _land_noreturn_vertical = true;
1558 
1559  } else {
1560  if (_global_pos.vel_d > 0.1f) {
1561  _flare_pitch_sp = radians(_parameters.land_flare_pitch_min_deg) *
1562  constrain((_flare_height - (_global_pos.alt - terrain_alt)) / _flare_height, 0.0f, 1.0f);
1563  }
1564 
1565  // otherwise continue using previous _flare_pitch_sp
1566  }
1567 
1569  _flare_curve_alt_rel_last = flare_curve_alt_rel;
1570 
1571  } else {
1572 
1573  /* intersect glide slope:
1574  * minimize speed to approach speed
1575  * if current position is higher than the slope follow the glide slope (sink to the
1576  * glide slope)
1577  * also if the system captures the slope it should stay
1578  * on the slope (bool land_onslope)
1579  * if current position is below the slope continue at previous wp altitude
1580  * until the intersection with slope
1581  * */
1582 
1583  float altitude_desired = terrain_alt;
1584 
1585  const float landing_slope_alt_rel_desired = _landingslope.getLandingSlopeRelativeAltitudeSave(wp_distance,
1586  bearing_lastwp_currwp, bearing_airplane_currwp);
1587 
1588  if (_global_pos.alt > terrain_alt + landing_slope_alt_rel_desired || _land_onslope) {
1589  /* stay on slope */
1590  altitude_desired = terrain_alt + landing_slope_alt_rel_desired;
1591 
1592  if (!_land_onslope) {
1593  mavlink_log_info(&_mavlink_log_pub, "Landing, on slope");
1594  _land_onslope = true;
1595  }
1596 
1597  } else {
1598  /* continue horizontally */
1599  if (pos_sp_prev.valid) {
1600  altitude_desired = pos_sp_prev.alt;
1601 
1602  } else {
1603  altitude_desired = _global_pos.alt;
1604  }
1605  }
1606 
1607  const float airspeed_approach = _parameters.land_airspeed_scale * _parameters.airspeed_min;
1608 
1609  tecs_update_pitch_throttle(altitude_desired,
1610  calculate_target_airspeed(airspeed_approach, ground_speed),
1611  radians(_parameters.pitch_limit_min),
1612  radians(_parameters.pitch_limit_max),
1613  _parameters.throttle_min,
1614  _parameters.throttle_max,
1615  _parameters.throttle_cruise,
1616  false,
1617  radians(_parameters.pitch_limit_min));
1618  }
1619 }
1620 
1621 float
1623 {
1624  if (_is_tecs_running) {
1625  return _tecs.get_pitch_setpoint();
1626  }
1627 
1628  // return 0 to prevent stale tecs state when it's not running
1629  return 0.0f;
1630 }
1631 
1632 float
1634 {
1635  if (_is_tecs_running) {
1636  return _tecs.get_throttle_setpoint();
1637  }
1638 
1639  // return 0 to prevent stale tecs state when it's not running
1640  return 0.0f;
1641 }
1642 
1643 void
1645 {
1646  if (_vehicle_command.command == vehicle_command_s::VEHICLE_CMD_DO_GO_AROUND) {
1647  // only abort landing before point of no return (horizontal and vertical)
1650  _pos_sp_triplet.current.type == position_setpoint_s::SETPOINT_TYPE_LAND) {
1651 
1652  abort_landing(true);
1653  }
1654  }
1655 }
1656 
1657 void
1659 {
1660  if (should_exit()) {
1662  exit_and_cleanup();
1663  return;
1664  }
1665 
1667 
1668  /* only run controller if position changed */
1670 
1671  // check for parameter updates
1673  // clear update
1674  parameter_update_s pupdate;
1675  _parameter_update_sub.copy(&pupdate);
1676 
1677  // update parameters from storage
1679  }
1680 
1682 
1683  // handle estimator reset events. we only adjust setpoins for manual modes
1687  // make TECS accept step in altitude and demanded altitude
1689  }
1690 
1691  // adjust navigation waypoints in position control mode
1694 
1695  // reset heading hold flag, which will re-initialise position control
1696  _hdg_hold_enabled = false;
1697  }
1698  }
1699 
1700  // update the reset counters in any case
1703 
1704  airspeed_poll();
1714 
1715  Vector2f curr_pos((float)_global_pos.lat, (float)_global_pos.lon);
1716  Vector2f ground_speed(_global_pos.vel_n, _global_pos.vel_e);
1717 
1718  //Convert Local setpoints to global setpoints
1723 
1724  } else {
1727  }
1728  }
1729 
1730  /*
1731  * Attempt to control position, on success (= sensors present and not in manual mode),
1732  * publish setpoint.
1733  */
1736 
1737  // add attitude setpoint offsets
1738  _att_sp.roll_body += _parameters.rollsp_offset_rad;
1739  _att_sp.pitch_body += _parameters.pitchsp_offset_rad;
1740 
1742  _att_sp.roll_body = constrain(_att_sp.roll_body, -_parameters.man_roll_max_rad, _parameters.man_roll_max_rad);
1743  _att_sp.pitch_body = constrain(_att_sp.pitch_body, -_parameters.man_pitch_max_rad, _parameters.man_pitch_max_rad);
1744  }
1745 
1747  q.copyTo(_att_sp.q_d);
1748  _att_sp.q_d_valid = true;
1749 
1755 
1756  /* lazily publish the setpoint only once available */
1757  if (_attitude_sp_pub != nullptr) {
1758  /* publish the attitude setpoint */
1760 
1761  } else if (_attitude_setpoint_id != nullptr) {
1762  /* advertise and publish */
1764  }
1765 
1766  // only publish status in full FW mode
1769  status_publish();
1770  }
1771  }
1772  }
1773 
1775  }
1776 }
1777 
1778 void
1780 {
1781  // only reset takeoff if !armed or just landed
1783 
1785 
1789 
1790  } else {
1792  }
1793 }
1794 
1795 void
1797 {
1799 
1800  // reset terrain estimation relevant values
1801  _time_last_t_alt = 0;
1802 
1803  _land_noreturn_horizontal = false;
1804  _land_noreturn_vertical = false;
1805  _land_stayonground = false;
1806  _land_motor_lim = false;
1807  _land_onslope = false;
1808 
1809  // reset abort land, unless loitering after an abort
1810  if (_land_abort && (_pos_sp_triplet.current.type != position_setpoint_s::SETPOINT_TYPE_LOITER)) {
1811 
1812  abort_landing(false);
1813  }
1814 }
1815 
1816 void
1818  float pitch_min_rad, float pitch_max_rad,
1819  float throttle_min, float throttle_max, float throttle_cruise,
1820  bool climbout_mode, float climbout_pitch_min_rad,
1821  uint8_t mode)
1822 {
1823  float dt = 0.01f; // prevent division with 0
1824 
1825  if (_last_tecs_update > 0) {
1826  dt = hrt_elapsed_time(&_last_tecs_update) * 1e-6;
1827  }
1828 
1830 
1831  // do not run TECS if we are not in air
1832  bool run_tecs = !_vehicle_land_detected.landed;
1833 
1834  // do not run TECS if vehicle is a VTOL and we are in rotary wing mode or in transition
1835  // (it should also not run during VTOL blending because airspeed is too low still)
1836  if (_vehicle_status.is_vtol) {
1837  if (_vehicle_status.vehicle_type == vehicle_status_s::VEHICLE_TYPE_ROTARY_WING || _vehicle_status.in_transition_mode) {
1838  run_tecs = false;
1839  }
1840 
1842  // we're in transition
1843  _was_in_transition = true;
1844 
1845  // set this to transition airspeed to init tecs correctly
1846  if (_parameters.airspeed_disabled) {
1847  // some vtols fly without airspeed sensor
1848  _asp_after_transition = _parameters.airspeed_trans;
1849 
1850  } else {
1852  }
1853 
1855 
1856  } else if (_was_in_transition) {
1857  // after transition we ramp up desired airspeed from the speed we had coming out of the transition
1858  _asp_after_transition += dt * 2; // increase 2m/s
1859 
1860  if (_asp_after_transition < airspeed_sp && _airspeed < airspeed_sp) {
1861  airspeed_sp = max(_asp_after_transition, _airspeed);
1862 
1863  } else {
1864  _was_in_transition = false;
1866  }
1867  }
1868  }
1869 
1870  _is_tecs_running = run_tecs;
1871 
1872  if (!run_tecs) {
1873  // next time we run TECS we should reinitialize states
1874  _reinitialize_tecs = true;
1875  return;
1876  }
1877 
1878  if (_reinitialize_tecs) {
1879  _tecs.reset_state();
1880  _reinitialize_tecs = false;
1881  }
1882 
1884  /* Force the slow downwards spiral */
1885  pitch_min_rad = M_DEG_TO_RAD_F * -1.0f;
1886  pitch_max_rad = M_DEG_TO_RAD_F * 5.0f;
1887  }
1888 
1889  /* No underspeed protection in landing mode */
1890  _tecs.set_detect_underspeed_enabled(!(mode == tecs_status_s::TECS_MODE_LAND
1891  || mode == tecs_status_s::TECS_MODE_LAND_THROTTLELIM));
1892 
1893  /* Using tecs library */
1894  float pitch_for_tecs = _pitch - _parameters.pitchsp_offset_rad;
1895 
1896  /* filter speed and altitude for controller */
1897  Vector3f accel_body(_vehicle_acceleration_sub.get().xyz);
1898 
1899  // tailsitters use the multicopter frame as reference, in fixed wing
1900  // we need to use the fixed wing frame
1901  if (static_cast<vtol_type>(_parameters.vtol_type) == vtol_type::TAILSITTER && _vehicle_status.is_vtol) {
1902  float tmp = accel_body(0);
1903  accel_body(0) = -accel_body(2);
1904  accel_body(2) = tmp;
1905  }
1906 
1907  /* tell TECS to update its state, but let it know when it cannot actually control the plane */
1908  bool in_air_alt_control = (!_vehicle_land_detected.landed &&
1913 
1914  /* update TECS vehicle state estimates */
1916  accel_body, (_global_pos.timestamp > 0), in_air_alt_control,
1918 
1919  /* scale throttle cruise by baro pressure */
1920  if (_parameters.throttle_alt_scale > FLT_EPSILON) {
1921  sensor_baro_s baro{};
1922 
1923  if (_sensor_baro_sub.update(&baro)) {
1924  if (PX4_ISFINITE(baro.pressure) && PX4_ISFINITE(_parameters.throttle_alt_scale)) {
1925  // scale throttle as a function of sqrt(p0/p) (~ EAS -> TAS at low speeds and altitudes ignoring temperature)
1926  const float eas2tas = sqrtf(CONSTANTS_STD_PRESSURE_MBAR / baro.pressure);
1927  const float scale = constrain((eas2tas - 1.0f) * _parameters.throttle_alt_scale + 1.0f, 1.0f, 2.0f);
1928 
1929  throttle_max = constrain(throttle_max * scale, throttle_min, 1.0f);
1930  throttle_cruise = constrain(throttle_cruise * scale, throttle_min + 0.01f, throttle_max - 0.01f);
1931  }
1932  }
1933  }
1934 
1935  _tecs.update_pitch_throttle(_R_nb, pitch_for_tecs,
1936  _global_pos.alt, alt_sp,
1937  airspeed_sp, _airspeed, _eas2tas,
1938  climbout_mode, climbout_pitch_min_rad,
1939  throttle_min, throttle_max, throttle_cruise,
1940  pitch_min_rad, pitch_max_rad);
1941 
1943 }
1944 
1945 int FixedwingPositionControl::task_spawn(int argc, char *argv[])
1946 {
1948 
1949  if (instance) {
1950  _object.store(instance);
1951  _task_id = task_id_is_work_queue;
1952 
1953  if (instance->init()) {
1954  return PX4_OK;
1955  }
1956 
1957  } else {
1958  PX4_ERR("alloc failed");
1959  }
1960 
1961  delete instance;
1962  _object.store(nullptr);
1963  _task_id = -1;
1964 
1965  return PX4_ERROR;
1966 }
1967 
1968 int FixedwingPositionControl::custom_command(int argc, char *argv[])
1969 {
1970  return print_usage("unknown command");
1971 }
1972 
1974 {
1975  PX4_INFO("Running");
1977  return 0;
1978 }
1979 
1981 {
1982  if (reason) {
1983  PX4_WARN("%s\n", reason);
1984  }
1985 
1986  PRINT_MODULE_DESCRIPTION(
1987  R"DESCR_STR(
1988 ### Description
1989 fw_pos_control_l1 is the fixed wing position controller.
1990 
1991 )DESCR_STR");
1992 
1993 
1994  PRINT_MODULE_USAGE_NAME("fw_pos_control_l1", "controller");
1995  PRINT_MODULE_USAGE_COMMAND("start");
1996  PRINT_MODULE_USAGE_DEFAULT_COMMANDS();
1997 
1998  return 0;
1999 }
2000 
2001 extern "C" __EXPORT int fw_pos_control_l1_main(int argc, char *argv[])
2002 {
2003  return FixedwingPositionControl::main(argc, argv);
2004 }
#define VEHICLE_TYPE_FIXED_WING
ECL_TECS_MODE tecs_mode()
Definition: tecs.h:133
#define PARAM_INVALID
Handle returned when a parameter cannot be found.
Definition: param.h:103
constexpr _Tp constrain(_Tp val, _Tp min_val, _Tp max_val)
Definition: Limits.hpp:66
float STE_rate_error()
Definition: tecs.h:148
uORB::SubscriptionData< vehicle_angular_velocity_s > _vehicle_rates_sub
int globallocalconverter_init(double lat_0, double lon_0, float alt_0, uint64_t timestamp)
Initialize the global mapping between global position (spherical) and local position (NED)...
Definition: geo.cpp:211
float SEB_error()
Definition: tecs.h:150
void set_min_sink_rate(float rate)
Definition: tecs.h:107
uORB::Subscription _vehicle_command_sub
vehicle command subscription
void set_heightrate_ff(float heightrate_ff)
Definition: tecs.h:112
void update(float landing_slope_angle_rad_new, float flare_relative_alt_new, float motor_lim_relative_alt_new, float H1_virt_new)
float nav_bearing()
The current target bearing.
void set_speed_weight(float weight)
Definition: tecs.h:122
bool circle_mode()
Returns true if following a circle (loiter)
float total_energy_rate_error
Definition: tecs_status.h:71
Launch has been detected, the controller should control attitude and also throttle up the motors...
Definition: LaunchMethod.h:53
uORB::Publication< position_controller_status_s > _pos_ctrl_status_pub
navigation capabilities publication
measure the time elapsed performing an event
Definition: perf_counter.h:56
struct FixedwingPositionControl::@97 _parameter_handles
handles for interesting parameters
void set_max_climb_rate(float climb_rate)
Definition: tecs.h:109
void set_l1_damping(float damping)
Set the L1 damping factor.
float getPitchMax(float pitchMaxDefault)
bool update(void *dst)
Copy the struct if updated.
float getLandingSlopeRelativeAltitudeSave(float wp_landing_distance, float bearing_lastwp_currwp, float bearing_airplane_currwp)
float height_rate
Definition: tecs_status.h:64
void get_waypoint_heading_distance(float heading, position_setpoint_s &waypoint_prev, position_setpoint_s &waypoint_next, bool flag_init)
Get a new waypoint based on heading and distance from current position.
__EXPORT int param_get(param_t param, void *val)
Copy the value of a parameter.
Definition: parameters.cpp:589
uORB::SubscriptionCallbackWorkItem _global_pos_sub
void set_height_comp_filter_omega(float omega)
Definition: tecs.h:111
static constexpr float THROTTLE_THRESH
max throttle from user which will not lead to motors spinning up in altitude controlled modes ...
Dcm< float > Dcmf
Definition: Dcm.hpp:185
void set_max_sink_rate(float sink_rate)
Definition: tecs.h:108
float get_distance_to_next_waypoint(double lat_now, double lon_now, double lat_next, double lon_next)
Returns the distance to the next waypoint in meters.
Definition: geo.cpp:270
SubscriptionData< vehicle_acceleration_s > _vehicle_acceleration_sub
uORB::Subscription _vehicle_land_detected_sub
vehicle land detected subscription
int main(int argc, char **argv)
Definition: main.cpp:3
struct position_setpoint_s next
float switch_distance(float waypoint_switch_radius)
Get the switch distance.
float getYaw(float navigatorYaw)
No launch has been detected.
Definition: LaunchMethod.h:48
float airspeed_filtered
Definition: tecs_status.h:66
bool _was_in_deadband
wether the last stick input was in althold deadband
hrt_abstime _time_went_in_air
time at which the plane went in the air
Definition: I2C.hpp:51
uint8_t mode
Definition: tecs_status.h:75
float throttle_integ
Definition: tecs_status.h:73
float _hdg_hold_yaw
hold heading for velocity mode
perf_counter_t _loop_perf
loop performance counter
static constexpr float HDG_HOLD_REACHED_DIST
uORB::Subscription _control_mode_sub
control mode subscription
void enable_airspeed(bool enabled)
Set the airspeed enable state.
Definition: tecs.h:67
float speed_derivative()
Definition: tecs.h:145
void set_time_const(float time_const)
Definition: tecs.h:104
orb_advert_t orb_advertise(const struct orb_metadata *meta, const void *data)
Definition: uORB.cpp:43
void tecs_update_pitch_throttle(float alt_sp, float airspeed_sp, float pitch_min_rad, float pitch_max_rad, float throttle_min, float throttle_max, float throttle_cruise, bool climbout_mode, float climbout_pitch_min_rad, uint8_t mode=tecs_status_s::TECS_MODE_NORMAL)
float get_pitch_setpoint()
Definition: tecs.h:89
bool _reinitialize_tecs
indicates if the TECS states should be reinitialized (used for VTOL)
static constexpr float MANUAL_THROTTLE_CLIMBOUT_THRESH
a throttle / pitch input above this value leads to the system switching to climbout mode ...
float get_terrain_altitude_takeoff(float takeoff_alt, const vehicle_global_position_s &global_pos)
Return the terrain estimate during takeoff or takeoff_alt if terrain estimate is not available...
void navigate_loiter(const matrix::Vector2f &vector_A, const matrix::Vector2f &vector_curr_position, float radius, int8_t loiter_direction, const matrix::Vector2f &ground_speed_vector)
Navigate on an orbit around a loiter waypoint.
struct position_setpoint_s previous
static constexpr float HDG_HOLD_SET_BACK_DIST
float altitude_sp
Definition: tecs_status.h:61
void set_indicated_airspeed_min(float airspeed)
Definition: tecs.h:116
float _takeoff_ground_alt
ground altitude at which plane was launched
void set_roll_slew_rate(float roll_slew_rate)
Set roll angle slew rate.
float pitch_integ
Definition: tecs_status.h:74
uORB::Subscription _vehicle_status_sub
vehicle status subscription
hrt_abstime _airspeed_last_valid
last time airspeed was received. Used to detect timeouts.
LidarLite * instance
Definition: ll40ls.cpp:65
bool _hdg_hold_enabled
heading hold enabled
float STE_error()
Definition: tecs.h:147
float pitch_integ_state()
Definition: tecs.h:154
enum FixedwingPositionControl::FW_POSCTRL_MODE FW_POSCTRL_MODE_OTHER
used to check the mode in the last control loop iteration. Use to check if the last iteration was in ...
float TAS_rate_setpoint()
Definition: tecs.h:144
#define FLT_EPSILON
bool update_desired_altitude(float dt)
Update desired altitude base on user pitch stick input.
void set_interval_ms(uint32_t interval)
Set the interval in milliseconds.
float crosstrack_error()
Get the current crosstrack error.
uORB::Publication< position_controller_landing_status_s > _pos_ctrl_landing_status_pub
landing status publication
float getPitch(float tecsPitch)
void set_integrator_gain(float gain)
Definition: tecs.h:105
vehicle_land_detected_s _vehicle_land_detected
vehicle land detected
bool _airspeed_valid
flag if a valid airspeed estimate exists
#define ORB_ID(_name)
Generates a pointer to the uORB metadata structure for a given topic.
Definition: uORB.h:87
struct position_setpoint_s current
void reset_takeoff_state(bool force=false)
float TAS_setpoint_adj()
Definition: tecs.h:138
struct FixedwingPositionControl::@96 _parameters
local copies of interesting parameters
bool publish(const T &data)
Publish the struct.
Definition: Publication.hpp:68
position_setpoint_triplet_s _pos_sp_triplet
triplet of mission items
float calculate_target_airspeed(float airspeed_demand, const Vector2f &ground_speed)
static void parameters_update()
update all parameters
void perf_free(perf_counter_t handle)
Free a counter.
uORB::Subscription _vehicle_attitude_sub
vehicle attitude subscription
uint8_t _pos_reset_counter
captures the number of times the estimator has reset the horizontal position
manual_control_setpoint_s _manual
r/c channel data
float _althold_epv
the position estimate accuracy when engaging alt hold
float target_bearing()
Bearing from aircraft to current target.
void control_landing(const Vector2f &curr_pos, const Vector2f &ground_speed, const position_setpoint_s &pos_sp_prev, const position_setpoint_s &pos_sp_curr)
#define perf_alloc(a, b)
Definition: px4io.h:59
static constexpr float ALTHOLD_EPV_RESET_THRESH
static constexpr float HDG_HOLD_YAWRATE_THRESH
float _target_bearing
estimated height to ground at which flare started
void waypoint_from_heading_and_distance(double lat_start, double lon_start, float bearing, float dist, double *lat_target, double *lon_target)
Creates a waypoint from given waypoint, distance and bearing see http://www.movable-type.co.uk/scripts/latlong.html.
Definition: geo.cpp:303
orb_advert_t _attitude_sp_pub
attitude setpoint
void set_speedrate_p(float speedrate_p)
Definition: tecs.h:123
float landing_slope_angle_rad()
constexpr T radians(T degrees)
Definition: Limits.hpp:85
Vector< float, 6 > f(float t, const Matrix< float, 6, 1 > &, const Matrix< float, 3, 1 > &)
Definition: integration.cpp:8
void set_throttle_damp(float throttle_damp)
Definition: tecs.h:126
Vector2< float > Vector2f
Definition: Vector2.hpp:69
SubscriptionData< airspeed_validated_s > _airspeed_validated_sub
vehicle_control_mode_s _control_mode
control mode
static hrt_abstime hrt_elapsed_time(const hrt_abstime *then)
Compute the delta between a timestamp taken in the past and now.
Definition: drv_hrt.h:102
float getThrottle(float tecsThrottle)
Euler< float > Eulerf
Definition: Euler.hpp:156
float _flare_pitch_sp
Current forced (i.e. not determined using TECS) flare pitch setpoint.
vehicle_status_s _vehicle_status
vehicle status
LaunchDetectionResult getLaunchDetected()
float tas_state()
Definition: tecs.h:139
float get_bearing_to_next_waypoint(double lat_now, double lon_now, double lat_next, double lon_next)
Returns the bearing to the next waypoint in radians.
Definition: geo.cpp:320
LaunchDetectionResult _launch_detection_state
float motor_lim_relative_alt()
void perf_end(perf_counter_t handle)
End a performance event.
void handle_alt_step(float delta_alt, float altitude)
Handle the altitude reset.
Definition: tecs.h:162
float hgt_rate_setpoint()
Definition: tecs.h:141
vehicle_local_position_s _local_pos
vehicle local position
float _flare_height
estimated height to ground at which flare started
bool updated()
Check if there is a new update.
void set_indicated_airspeed_max(float airspeed)
Definition: tecs.h:115
void set_l1_period(float period)
Set the L1 period.
void set_roll_throttle_compensation(float compensation)
Definition: tecs.h:129
__BEGIN_DECLS typedef uint64_t hrt_abstime
Absolute time, in microsecond units.
Definition: drv_hrt.h:58
bool in_takeoff_situation()
Check if we are in a takeoff situation.
int orb_publish(const struct orb_metadata *meta, orb_advert_t handle, const void *data)
Definition: uORB.cpp:70
void set_dt(float dt)
Set control loop dt.
void do_takeoff_help(float *hold_altitude, float *pitch_limit_min)
Do takeoff help when in altitude controlled modes.
static int print_usage(const char *reason=nullptr)
bool _last_manual
true if the last iteration was in manual mode (used to determine when a reset is needed) ...
float throttle_integ_state()
Definition: tecs.h:153
constexpr _Tp min(_Tp a, _Tp b)
Definition: Limits.hpp:54
vehicle_attitude_setpoint_s _att_sp
vehicle attitude setpoint
const T & get() const
float _hold_alt
hold altitude for altitude mode
void set_pitch_damping(float damping)
Definition: tecs.h:118
Type wrap_pi(Type x)
Wrap value in range [-π, π)
__EXPORT param_t param_find(const char *name)
Look up a parameter by name.
Definition: parameters.cpp:370
float getFlareCurveRelativeAltitudeSave(float wp_landing_distance, float bearing_lastwp_currwp, float bearing_airplane_currwp)
void handle_command()
Handle incoming vehicle commands.
position_setpoint_s _hdg_hold_curr_wp
position to which heading hold flies
float altitude_filtered
Definition: tecs_status.h:62
uORB::Subscription _manual_control_sub
notification of manual control updates
float vert_pos_state()
Definition: tecs.h:136
void update_pitch_throttle(const matrix::Dcmf &rotMat, float pitch, float baro_altitude, float hgt_setpoint, float EAS_setpoint, float indicated_airspeed, float eas_to_tas, bool climb_out_setpoint, float pitch_min_climbout, float throttle_min, float throttle_setpoint_max, float throttle_cruise, float pitch_limit_min, float pitch_limit_max)
Update the control loop calculations.
Definition: tecs.cpp:582
float energy_distribution_error
Definition: tecs_status.h:70
Vector3< float > Vector3f
Definition: Vector3.hpp:136
void create_waypoint_from_line_and_dist(double lat_A, double lon_A, double lat_B, double lon_B, float dist, double *lat_target, double *lon_target)
Creates a new waypoint C on the line of two given waypoints (A, B) at certain distance from waypoint ...
Definition: geo.cpp:285
void navigate_waypoints(const matrix::Vector2f &vector_A, const matrix::Vector2f &vector_B, const matrix::Vector2f &vector_curr_position, const matrix::Vector2f &ground_speed)
Navigate between two waypoints.
static constexpr float HDG_HOLD_DIST_NEXT
int globallocalconverter_toglobal(float x, float y, float z, double *lat, double *lon, float *alt)
Convert from local position coordinates to global position coordinates using the global reference...
Definition: geo.cpp:241
float airspeed_sp
Definition: tecs_status.h:65
void init(float yaw, double current_lat, double current_lon)
void set_detect_underspeed_enabled(bool enabled)
Definition: tecs.h:101
constexpr _Tp max(_Tp a, _Tp b)
Definition: Limits.hpp:60
uint8_t _alt_reset_counter
captures the number of times the estimator has reset the altitude state
void set_l1_roll_limit(float roll_lim_rad)
Set the maximum roll angle output in radians.
uint64_t timestamp
Definition: tecs_status.h:60
float SEB_rate_error()
Definition: tecs.h:151
uORB::Subscription _parameter_update_sub
notification of parameter updates
void update_vehicle_state_estimates(float airspeed, const matrix::Dcmf &rotMat, const matrix::Vector3f &accel_body, bool altitude_lock, bool in_air, float altitude, bool vz_valid, float vz, float az)
Updates the following vehicle kineamtic state estimates: Vertical position, velocity and acceleration...
Definition: tecs.cpp:57
void reset_state()
Definition: tecs.h:92
Quaternion< float > Quatf
Definition: Quaternion.hpp:544
vehicle_attitude_s _att
vehicle attitude setpoint
float dt
Definition: px4io.c:73
float getMinPitch(float sp_min, float climbout_min, float min)
void set_time_const_throt(float time_const_throt)
Definition: tecs.h:125
__EXPORT int fw_pos_control_l1_main(int argc, char *argv[])
float energy_distribution_rate_error
Definition: tecs_status.h:72
float airspeed_derivative
Definition: tecs_status.h:68
void perf_print_counter(perf_counter_t handle)
Print one performance counter to stdout.
float getRoll(float navigatorRoll)
matrix::Vector2f getStartWP()
bool control_position(const Vector2f &curr_pos, const Vector2f &ground_speed, const position_setpoint_s &pos_sp_prev, const position_setpoint_s &pos_sp_curr, const position_setpoint_s &pos_sp_next)
float get_throttle_setpoint(void)
Definition: tecs.h:88
float height_rate_setpoint
Definition: tecs_status.h:63
void control_takeoff(const Vector2f &curr_pos, const Vector2f &ground_speed, const position_setpoint_s &pos_sp_prev, const position_setpoint_s &pos_sp_curr)
float airspeed_derivative_sp
Definition: tecs_status.h:67
float flare_length()
static constexpr hrt_abstime T_ALT_TIMEOUT
void set_vertical_accel_limit(float limit)
Definition: tecs.h:119
Definition: bst.cpp:62
bool _was_in_air
indicated wether the plane was in the air in the previous interation*/
float get_roll_setpoint()
Get roll angle setpoint for fixed wing.
float _t_alt_prev_valid
last terrain estimate which was valid
bool globallocalconverter_initialized()
Checks if globallocalconverter was initialized.
Definition: geo.cpp:224
vehicle_global_position_s _global_pos
global vehicle position
void set_heightrate_p(float heightrate_p)
Definition: tecs.h:113
float horizontal_slope_displacement()
static constexpr float CONSTANTS_STD_PRESSURE_MBAR
Definition: geo.h:55
hrt_abstime _time_last_t_alt
time at which we had last valid terrain alt
static int custom_command(int argc, char *argv[])
vehicle_command_s _vehicle_command
vehicle commands
bool update(void *dst)
Update the struct.
hrt_abstime _control_position_last_called
last call of control_position
bool _yaw_lock_engaged
yaw is locked for heading hold
static constexpr float HDG_HOLD_MAN_INPUT_THRESH
float flare_relative_alt()
hrt_abstime _time_started_landing
time at which landing started
void update(float airspeed, float alt_agl, double current_lat, double current_lon, orb_advert_t *mavlink_log_pub)
float total_energy_error
Definition: tecs_status.h:69
void set_speed_comp_filter_omega(float omega)
Definition: tecs.h:121
mode
Definition: vtol_type.h:76
bool copy(void *dst)
Copy the struct.
void perf_begin(perf_counter_t handle)
Begin a performance event.
float hgt_setpoint_adj()
Definition: tecs.h:135
position_setpoint_s _hdg_hold_prev_wp
position where heading hold started
__EXPORT hrt_abstime hrt_absolute_time(void)
Get absolute time in [us] (does not wrap).
void set_throttle_slewrate(float slewrate)
Definition: tecs.h:127
void navigate_heading(float navigation_heading, float current_heading, const matrix::Vector2f &ground_speed)
Navigate on a fixed bearing.
float vert_vel_state()
Definition: tecs.h:142
uORB::Publication< tecs_status_s > _tecs_status_pub
TECS status publication.
static int task_spawn(int argc, char *argv[])