PX4 Firmware
PX4 Autopilot Software http://px4.io
FixedwingAttitudeControl.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 
38 using namespace time_literals;
39 using math::constrain;
40 using math::gradual;
41 using math::radians;
42 
44  ModuleParams(nullptr),
45  WorkItem(MODULE_NAME, px4::wq_configurations::att_pos_ctrl),
46  _loop_perf(perf_alloc(PC_ELAPSED, MODULE_NAME": cycle"))
47 {
48  // check if VTOL first
50 
51  /* fetch initial parameter values */
53 
54  // set initial maximum body rate setpoints
55  _roll_ctrl.set_max_rate(radians(_param_fw_acro_x_max.get()));
56  _pitch_ctrl.set_max_rate_pos(radians(_param_fw_acro_y_max.get()));
57  _pitch_ctrl.set_max_rate_neg(radians(_param_fw_acro_y_max.get()));
58  _yaw_ctrl.set_max_rate(radians(_param_fw_acro_z_max.get()));
59 }
60 
62 {
64 }
65 
66 bool
68 {
69  if (!_att_sub.registerCallback()) {
70  PX4_ERR("vehicle attitude callback registration failed!");
71  return false;
72  }
73 
74  return true;
75 }
76 
77 int
79 {
80  /* pitch control parameters */
81  _pitch_ctrl.set_time_constant(_param_fw_p_tc.get());
82  _pitch_ctrl.set_k_p(_param_fw_pr_p.get());
83  _pitch_ctrl.set_k_i(_param_fw_pr_i.get());
84  _pitch_ctrl.set_k_ff(_param_fw_pr_ff.get());
85  _pitch_ctrl.set_integrator_max(_param_fw_pr_imax.get());
86 
87  /* roll control parameters */
88  _roll_ctrl.set_time_constant(_param_fw_r_tc.get());
89  _roll_ctrl.set_k_p(_param_fw_rr_p.get());
90  _roll_ctrl.set_k_i(_param_fw_rr_i.get());
91  _roll_ctrl.set_k_ff(_param_fw_rr_ff.get());
92  _roll_ctrl.set_integrator_max(_param_fw_rr_imax.get());
93 
94  /* yaw control parameters */
95  _yaw_ctrl.set_k_p(_param_fw_yr_p.get());
96  _yaw_ctrl.set_k_i(_param_fw_yr_i.get());
97  _yaw_ctrl.set_k_ff(_param_fw_yr_ff.get());
98  _yaw_ctrl.set_integrator_max(_param_fw_yr_imax.get());
99 
100  /* wheel control parameters */
101  _wheel_ctrl.set_k_p(_param_fw_wr_p.get());
102  _wheel_ctrl.set_k_i(_param_fw_wr_i.get());
103  _wheel_ctrl.set_k_ff(_param_fw_wr_ff.get());
104  _wheel_ctrl.set_integrator_max(_param_fw_wr_imax.get());
105  _wheel_ctrl.set_max_rate(radians(_param_fw_w_rmax.get()));
106 
107  return PX4_OK;
108 }
109 
110 void
112 {
114 
115  if (_vehicle_status.is_vtol) {
116  const bool is_hovering = _vehicle_status.vehicle_type == vehicle_status_s::VEHICLE_TYPE_ROTARY_WING
118  const bool is_tailsitter_transition = _vehicle_status.in_transition_mode && _is_tailsitter;
119 
120  if (is_hovering || is_tailsitter_transition) {
123  }
124  }
125 }
126 
127 void
129 {
130  const bool is_tailsitter_transition = _is_tailsitter && _vehicle_status.in_transition_mode;
132 
133  if (_vcontrol_mode.flag_control_manual_enabled && (!is_tailsitter_transition || is_fixed_wing)) {
134 
135  // Always copy the new manual setpoint, even if it wasn't updated, to fill the _actuators with valid values
136  if (_manual_sub.copy(&_manual)) {
137 
138  // Check if we are in rattitude mode and the pilot is above the threshold on pitch
140  if (fabsf(_manual.y) > _param_fw_ratt_th.get() || fabsf(_manual.x) > _param_fw_ratt_th.get()) {
142  }
143  }
144 
147 
149  // STABILIZED mode generate the attitude setpoint from manual user inputs
150 
151  _att_sp.roll_body = _manual.y * radians(_param_fw_man_r_max.get()) + radians(_param_fw_rsp_off.get());
153  -radians(_param_fw_man_r_max.get()), radians(_param_fw_man_r_max.get()));
154 
155  _att_sp.pitch_body = -_manual.x * radians(_param_fw_man_p_max.get()) + radians(_param_fw_psp_off.get());
157  -radians(_param_fw_man_p_max.get()), radians(_param_fw_man_p_max.get()));
158 
159  _att_sp.yaw_body = 0.0f;
161 
163  q.copyTo(_att_sp.q_d);
164  _att_sp.q_d_valid = true;
165 
167 
168  if (_attitude_sp_pub != nullptr) {
169  /* publish the attitude rates setpoint */
171 
172  } else if (_attitude_setpoint_id) {
173  /* advertise the attitude rates setpoint */
175  }
176 
179 
180  // RATE mode we need to generate the rate setpoint from manual user inputs
182  _rates_sp.roll = _manual.y * radians(_param_fw_acro_x_max.get());
183  _rates_sp.pitch = -_manual.x * radians(_param_fw_acro_y_max.get());
184  _rates_sp.yaw = _manual.r * radians(_param_fw_acro_z_max.get());
186 
188 
189  } else {
190  /* manual/direct control */
191  _actuators.control[actuator_controls_s::INDEX_ROLL] = _manual.y * _param_fw_man_r_sc.get() + _param_trim_roll.get();
192  _actuators.control[actuator_controls_s::INDEX_PITCH] = -_manual.x * _param_fw_man_p_sc.get() + _param_trim_pitch.get();
193  _actuators.control[actuator_controls_s::INDEX_YAW] = _manual.r * _param_fw_man_y_sc.get() + _param_trim_yaw.get();
194  _actuators.control[actuator_controls_s::INDEX_THROTTLE] = _manual.z;
195  }
196  }
197  }
198  }
199 }
200 
201 void
203 {
204  if (_att_sp_sub.update(&_att_sp)) {
208  }
209 }
210 
211 void
213 {
215  if (_is_tailsitter) {
216  float tmp = _rates_sp.roll;
218  _rates_sp.yaw = tmp;
219  }
220  }
221 }
222 
223 void
225 {
227  /* set correct uORB ID, depending on if vehicle is VTOL or not */
228  if (!_actuators_id) {
229  if (_vehicle_status.is_vtol) {
230  _actuators_id = ORB_ID(actuator_controls_virtual_fw);
231  _attitude_setpoint_id = ORB_ID(fw_virtual_attitude_setpoint);
232 
233  int32_t vt_type = -1;
234 
235  if (param_get(param_find("VT_TYPE"), &vt_type) == PX4_OK) {
236  _is_tailsitter = (static_cast<vtol_type>(vt_type) == vtol_type::TAILSITTER);
237  }
238 
239  } else {
240  _actuators_id = ORB_ID(actuator_controls_0);
241  _attitude_setpoint_id = ORB_ID(vehicle_attitude_setpoint);
242  }
243  }
244  }
245 }
246 
247 void
249 {
251  vehicle_land_detected_s vehicle_land_detected {};
252 
253  if (_vehicle_land_detected_sub.copy(&vehicle_land_detected)) {
254  _landed = vehicle_land_detected.landed;
255  }
256  }
257 }
258 
260 {
262  const bool airspeed_valid = PX4_ISFINITE(_airspeed_validated_sub.get().indicated_airspeed_m_s)
264 
265  // if no airspeed measurement is available out best guess is to use the trim airspeed
266  float airspeed = _param_fw_airspd_trim.get();
267 
268  if ((_param_fw_arsp_mode.get() == 0) && airspeed_valid) {
269  /* prevent numerical drama by requiring 0.5 m/s minimal speed */
271 
272  } else {
273  // VTOL: if we have no airspeed available and we are in hover mode then assume the lowest airspeed possible
274  // this assumption is good as long as the vehicle is not hovering in a headwind which is much larger
275  // than the minimum airspeed
276  if (_vehicle_status.is_vtol && _vehicle_status.vehicle_type == vehicle_status_s::VEHICLE_TYPE_ROTARY_WING
278  airspeed = _param_fw_airspd_min.get();
279  }
280  }
281 
282  /*
283  * For scaling our actuators using anything less than the min (close to stall)
284  * speed doesn't make any sense - its the strongest reasonable deflection we
285  * want to do in flight and its the baseline a human pilot would choose.
286  *
287  * Forcing the scaling to this value allows reasonable handheld tests.
288  */
289  const float airspeed_constrained = constrain(airspeed, _param_fw_airspd_min.get(), _param_fw_airspd_max.get());
290  _airspeed_scaling = _param_fw_airspd_trim.get() / airspeed_constrained;
291 
292  return airspeed;
293 }
294 
296 {
297  if (should_exit()) {
299  exit_and_cleanup();
300  return;
301  }
302 
304 
305  if (_att_sub.update(&_att)) {
306 
307  // only update parameters if they changed
308  bool params_updated = _parameter_update_sub.updated();
309 
310  // check for parameter updates
311  if (params_updated) {
312  // clear update
313  parameter_update_s pupdate;
314  _parameter_update_sub.copy(&pupdate);
315 
316  // update parameters from storage
317  updateParams();
319  }
320 
321  /* only run controller if attitude changed */
322  static uint64_t last_run = 0;
323  float deltaT = constrain((hrt_elapsed_time(&last_run) / 1e6f), 0.01f, 0.1f);
324  last_run = hrt_absolute_time();
325 
326  /* get current rotation matrix and euler angles from control state quaternions */
328 
329  vehicle_angular_velocity_s angular_velocity{};
330  _vehicle_rates_sub.copy(&angular_velocity);
331  float rollspeed = angular_velocity.xyz[0];
332  float pitchspeed = angular_velocity.xyz[1];
333  float yawspeed = angular_velocity.xyz[2];
334 
335  if (_is_tailsitter) {
336  /* vehicle is a tailsitter, we need to modify the estimated attitude for fw mode
337  *
338  * Since the VTOL airframe is initialized as a multicopter we need to
339  * modify the estimated attitude for the fixed wing operation.
340  * Since the neutral position of the vehicle in fixed wing mode is -90 degrees rotated around
341  * the pitch axis compared to the neutral position of the vehicle in multicopter mode
342  * we need to swap the roll and the yaw axis (1st and 3rd column) in the rotation matrix.
343  * Additionally, in order to get the correct sign of the pitch, we need to multiply
344  * the new x axis of the rotation matrix with -1
345  *
346  * original: modified:
347  *
348  * Rxx Ryx Rzx -Rzx Ryx Rxx
349  * Rxy Ryy Rzy -Rzy Ryy Rxy
350  * Rxz Ryz Rzz -Rzz Ryz Rxz
351  * */
352  matrix::Dcmf R_adapted = R; //modified rotation matrix
353 
354  /* move z to x */
355  R_adapted(0, 0) = R(0, 2);
356  R_adapted(1, 0) = R(1, 2);
357  R_adapted(2, 0) = R(2, 2);
358 
359  /* move x to z */
360  R_adapted(0, 2) = R(0, 0);
361  R_adapted(1, 2) = R(1, 0);
362  R_adapted(2, 2) = R(2, 0);
363 
364  /* change direction of pitch (convert to right handed system) */
365  R_adapted(0, 0) = -R_adapted(0, 0);
366  R_adapted(1, 0) = -R_adapted(1, 0);
367  R_adapted(2, 0) = -R_adapted(2, 0);
368 
369  /* fill in new attitude data */
370  R = R_adapted;
371 
372  /* lastly, roll- and yawspeed have to be swaped */
373  float helper = rollspeed;
374  rollspeed = -yawspeed;
375  yawspeed = helper;
376  }
377 
378  const matrix::Eulerf euler_angles(R);
379 
381  vehicle_status_poll(); // this poll has to be before the control_mode_poll, otherwise rate sp are not published during whole transition
386 
387  // the position controller will not emit attitude setpoints in some modes
388  // we need to make sure that this flag is reset
390 
391  /* lock integrator until control is started */
392  bool lock_integrator = !_vcontrol_mode.flag_control_rates_enabled
393  || (_vehicle_status.vehicle_type == vehicle_status_s::VEHICLE_TYPE_ROTARY_WING && ! _vehicle_status.in_transition_mode);
394 
395  /* Simple handling of failsafe: deploy parachute if failsafe is on */
397  _actuators_airframe.control[7] = 1.0f;
398 
399  } else {
400  _actuators_airframe.control[7] = 0.0f;
401  }
402 
403  /* if we are in rotary wing mode, do nothing */
404  if (_vehicle_status.vehicle_type == vehicle_status_s::VEHICLE_TYPE_ROTARY_WING && !_vehicle_status.is_vtol) {
406  return;
407  }
408 
409  control_flaps(deltaT);
410 
411  /* decide if in stabilized or full manual control */
413 
414  const float airspeed = get_airspeed_and_update_scaling();
415 
416  /* Use min airspeed to calculate ground speed scaling region.
417  * Don't scale below gspd_scaling_trim
418  */
419  float groundspeed = sqrtf(_global_pos.vel_n * _global_pos.vel_n +
421  float gspd_scaling_trim = (_param_fw_airspd_min.get() * 0.6f);
422  float groundspeed_scaler = gspd_scaling_trim / ((groundspeed < gspd_scaling_trim) ? gspd_scaling_trim : groundspeed);
423 
424  /* reset integrals where needed */
426  _roll_ctrl.reset_integrator();
427  }
428 
431  }
432 
436  }
437 
438  /* Reset integrators if the aircraft is on ground
439  * or a multicopter (but not transitioning VTOL)
440  */
441  if (_landed
442  || (_vehicle_status.vehicle_type == vehicle_status_s::VEHICLE_TYPE_ROTARY_WING
444 
445  _roll_ctrl.reset_integrator();
449  }
450 
451  /* Prepare data for attitude controllers */
452  struct ECL_ControlData control_input = {};
453  control_input.roll = euler_angles.phi();
454  control_input.pitch = euler_angles.theta();
455  control_input.yaw = euler_angles.psi();
456  control_input.body_x_rate = rollspeed;
457  control_input.body_y_rate = pitchspeed;
458  control_input.body_z_rate = yawspeed;
459  control_input.roll_setpoint = _att_sp.roll_body;
460  control_input.pitch_setpoint = _att_sp.pitch_body;
461  control_input.yaw_setpoint = _att_sp.yaw_body;
462  control_input.airspeed_min = _param_fw_airspd_min.get();
463  control_input.airspeed_max = _param_fw_airspd_max.get();
464  control_input.airspeed = airspeed;
465  control_input.scaler = _airspeed_scaling;
466  control_input.lock_integrator = lock_integrator;
467  control_input.groundspeed = groundspeed;
468  control_input.groundspeed_scaler = groundspeed_scaler;
469 
470  /* reset body angular rate limits on mode change */
473  || _vehicle_status.vehicle_type == vehicle_status_s::VEHICLE_TYPE_ROTARY_WING) {
474  _roll_ctrl.set_max_rate(radians(_param_fw_r_rmax.get()));
475  _pitch_ctrl.set_max_rate_pos(radians(_param_fw_p_rmax_pos.get()));
476  _pitch_ctrl.set_max_rate_neg(radians(_param_fw_p_rmax_neg.get()));
477  _yaw_ctrl.set_max_rate(radians(_param_fw_y_rmax.get()));
478 
479  } else {
480  _roll_ctrl.set_max_rate(radians(_param_fw_acro_x_max.get()));
481  _pitch_ctrl.set_max_rate_pos(radians(_param_fw_acro_y_max.get()));
482  _pitch_ctrl.set_max_rate_neg(radians(_param_fw_acro_y_max.get()));
483  _yaw_ctrl.set_max_rate(radians(_param_fw_acro_z_max.get()));
484  }
485  }
486 
488 
489  /* bi-linear interpolation over airspeed for actuator trim scheduling */
490  float trim_roll = _param_trim_roll.get();
491  float trim_pitch = _param_trim_pitch.get();
492  float trim_yaw = _param_trim_yaw.get();
493 
494  if (airspeed < _param_fw_airspd_trim.get()) {
495  trim_roll += gradual(airspeed, _param_fw_airspd_min.get(), _param_fw_airspd_trim.get(), _param_fw_dtrim_r_vmin.get(),
496  0.0f);
497  trim_pitch += gradual(airspeed, _param_fw_airspd_min.get(), _param_fw_airspd_trim.get(), _param_fw_dtrim_p_vmin.get(),
498  0.0f);
499  trim_yaw += gradual(airspeed, _param_fw_airspd_min.get(), _param_fw_airspd_trim.get(), _param_fw_dtrim_y_vmin.get(),
500  0.0f);
501 
502  } else {
503  trim_roll += gradual(airspeed, _param_fw_airspd_trim.get(), _param_fw_airspd_max.get(), 0.0f,
504  _param_fw_dtrim_r_vmax.get());
505  trim_pitch += gradual(airspeed, _param_fw_airspd_trim.get(), _param_fw_airspd_max.get(), 0.0f,
506  _param_fw_dtrim_p_vmax.get());
507  trim_yaw += gradual(airspeed, _param_fw_airspd_trim.get(), _param_fw_airspd_max.get(), 0.0f,
508  _param_fw_dtrim_y_vmax.get());
509  }
510 
511  /* add trim increment if flaps are deployed */
512  trim_roll += _flaps_applied * _param_fw_dtrim_r_flps.get();
513  trim_pitch += _flaps_applied * _param_fw_dtrim_p_flps.get();
514 
515  /* Run attitude controllers */
517  if (PX4_ISFINITE(_att_sp.roll_body) && PX4_ISFINITE(_att_sp.pitch_body)) {
518  _roll_ctrl.control_attitude(control_input);
519  _pitch_ctrl.control_attitude(control_input);
520  _yaw_ctrl.control_attitude(control_input); //runs last, because is depending on output of roll and pitch attitude
521  _wheel_ctrl.control_attitude(control_input);
522 
523  /* Update input data for rate controllers */
524  control_input.roll_rate_setpoint = _roll_ctrl.get_desired_rate();
526  control_input.yaw_rate_setpoint = _yaw_ctrl.get_desired_rate();
527 
528  /* Run attitude RATE controllers which need the desired attitudes from above, add trim */
529  float roll_u = _roll_ctrl.control_euler_rate(control_input);
530  _actuators.control[actuator_controls_s::INDEX_ROLL] = (PX4_ISFINITE(roll_u)) ? roll_u + trim_roll : trim_roll;
531 
532  if (!PX4_ISFINITE(roll_u)) {
533  _roll_ctrl.reset_integrator();
534  }
535 
536  float pitch_u = _pitch_ctrl.control_euler_rate(control_input);
537  _actuators.control[actuator_controls_s::INDEX_PITCH] = (PX4_ISFINITE(pitch_u)) ? pitch_u + trim_pitch : trim_pitch;
538 
539  if (!PX4_ISFINITE(pitch_u)) {
541  }
542 
543  float yaw_u = 0.0f;
544 
545  if (_param_fw_w_en.get() && _att_sp.fw_control_yaw) {
546  yaw_u = _wheel_ctrl.control_bodyrate(control_input);
547 
548  } else {
549  yaw_u = _yaw_ctrl.control_euler_rate(control_input);
550  }
551 
552  _actuators.control[actuator_controls_s::INDEX_YAW] = (PX4_ISFINITE(yaw_u)) ? yaw_u + trim_yaw : trim_yaw;
553 
554  /* add in manual rudder control in manual modes */
556  _actuators.control[actuator_controls_s::INDEX_YAW] += _manual.r;
557  }
558 
559  if (!PX4_ISFINITE(yaw_u)) {
562  }
563 
564  /* throttle passed through if it is finite and if no engine failure was detected */
565  _actuators.control[actuator_controls_s::INDEX_THROTTLE] = (PX4_ISFINITE(_att_sp.thrust_body[0])
567 
568  /* scale effort by battery status */
569  if (_param_fw_bat_scale_en.get() &&
570  _actuators.control[actuator_controls_s::INDEX_THROTTLE] > 0.1f) {
571 
574 
576  if (battery_status.scale > 0.0f) {
578  }
579  }
580  }
581 
582  _actuators.control[actuator_controls_s::INDEX_THROTTLE] *= _battery_scale;
583  }
584  }
585 
586  /*
587  * Lazily publish the rate setpoint (for analysis, the actuators are published below)
588  * only once available
589  */
590  _rates_sp.roll = _roll_ctrl.get_desired_bodyrate();
593 
595 
597 
598  } else {
600 
601  _roll_ctrl.set_bodyrate_setpoint(_rates_sp.roll);
604 
605  float roll_u = _roll_ctrl.control_bodyrate(control_input);
606  _actuators.control[actuator_controls_s::INDEX_ROLL] = (PX4_ISFINITE(roll_u)) ? roll_u + trim_roll : trim_roll;
607 
608  float pitch_u = _pitch_ctrl.control_bodyrate(control_input);
609  _actuators.control[actuator_controls_s::INDEX_PITCH] = (PX4_ISFINITE(pitch_u)) ? pitch_u + trim_pitch : trim_pitch;
610 
611  float yaw_u = _yaw_ctrl.control_bodyrate(control_input);
612  _actuators.control[actuator_controls_s::INDEX_YAW] = (PX4_ISFINITE(yaw_u)) ? yaw_u + trim_yaw : trim_yaw;
613 
614  _actuators.control[actuator_controls_s::INDEX_THROTTLE] = PX4_ISFINITE(_rates_sp.thrust_body[0]) ?
615  _rates_sp.thrust_body[0] : 0.0f;
616  }
617 
618  rate_ctrl_status_s rate_ctrl_status;
619  rate_ctrl_status.timestamp = hrt_absolute_time();
620  rate_ctrl_status.rollspeed_integ = _roll_ctrl.get_integrator();
621  rate_ctrl_status.pitchspeed_integ = _pitch_ctrl.get_integrator();
622  rate_ctrl_status.yawspeed_integ = _yaw_ctrl.get_integrator();
623  rate_ctrl_status.additional_integ1 = _wheel_ctrl.get_integrator();
624 
625  _rate_ctrl_status_pub.publish(rate_ctrl_status);
626  }
627 
628  // Add feed-forward from roll control output to yaw control output
629  // This can be used to counteract the adverse yaw effect when rolling the plane
630  _actuators.control[actuator_controls_s::INDEX_YAW] += _param_fw_rll_to_yaw_ff.get()
631  * constrain(_actuators.control[actuator_controls_s::INDEX_ROLL], -1.0f, 1.0f);
632 
633  _actuators.control[actuator_controls_s::INDEX_FLAPS] = _flaps_applied;
635  _actuators.control[actuator_controls_s::INDEX_AIRBRAKES] = _flaperons_applied;
636  // FIXME: this should use _vcontrol_mode.landing_gear_pos in the future
638 
639  /* lazily publish the setpoint only once available */
644 
645  /* Only publish if any of the proper modes are enabled */
649  /* publish the actuator controls */
650  if (_actuators_0_pub != nullptr) {
652 
653  } else if (_actuators_id) {
655  }
656 
658  }
659  }
660 
662 }
663 
665 {
666  /* default flaps to center */
667  float flap_control = 0.0f;
668 
669  /* map flaps by default to manual if valid */
671  && fabsf(_param_fw_flaps_scl.get()) > 0.01f) {
672  flap_control = 0.5f * (_manual.flaps + 1.0f) * _param_fw_flaps_scl.get();
673 
675  && fabsf(_param_fw_flaps_scl.get()) > 0.01f) {
676 
677  switch (_att_sp.apply_flaps) {
678  case vehicle_attitude_setpoint_s::FLAPS_OFF:
679  flap_control = 0.0f; // no flaps
680  break;
681 
682  case vehicle_attitude_setpoint_s::FLAPS_LAND:
683  flap_control = 1.0f * _param_fw_flaps_scl.get() * _param_fw_flaps_lnd_scl.get();
684  break;
685 
686  case vehicle_attitude_setpoint_s::FLAPS_TAKEOFF:
687  flap_control = 1.0f * _param_fw_flaps_scl.get() * _param_fw_flaps_to_scl.get();
688  break;
689  }
690  }
691 
692  // move the actual control value continuous with time, full flap travel in 1sec
693  if (fabsf(_flaps_applied - flap_control) > 0.01f) {
694  _flaps_applied += (_flaps_applied - flap_control) < 0 ? dt : -dt;
695 
696  } else {
697  _flaps_applied = flap_control;
698  }
699 
700  /* default flaperon to center */
701  float flaperon_control = 0.0f;
702 
703  /* map flaperons by default to manual if valid */
705  && fabsf(_param_fw_flaperon_scl.get()) > 0.01f) {
706 
707  flaperon_control = 0.5f * (_manual.aux2 + 1.0f) * _param_fw_flaperon_scl.get();
708 
710  && fabsf(_param_fw_flaperon_scl.get()) > 0.01f) {
711 
712  if (_att_sp.apply_flaps == vehicle_attitude_setpoint_s::FLAPS_LAND) {
713  flaperon_control = _param_fw_flaperon_scl.get();
714 
715  } else {
716  flaperon_control = 0.0f;
717  }
718  }
719 
720  // move the actual control value continuous with time, full flap travel in 1sec
721  if (fabsf(_flaperons_applied - flaperon_control) > 0.01f) {
722  _flaperons_applied += (_flaperons_applied - flaperon_control) < 0 ? dt : -dt;
723 
724  } else {
725  _flaperons_applied = flaperon_control;
726  }
727 }
728 
729 int FixedwingAttitudeControl::task_spawn(int argc, char *argv[])
730 {
732 
733  if (instance) {
734  _object.store(instance);
735  _task_id = task_id_is_work_queue;
736 
737  if (instance->init()) {
738  return PX4_OK;
739  }
740 
741  } else {
742  PX4_ERR("alloc failed");
743  }
744 
745  delete instance;
746  _object.store(nullptr);
747  _task_id = -1;
748 
749  return PX4_ERROR;
750 }
751 
752 int FixedwingAttitudeControl::custom_command(int argc, char *argv[])
753 {
754  return print_usage("unknown command");
755 }
756 
758 {
759  PX4_INFO("Running");
761  return 0;
762 }
763 
765 {
766  if (reason) {
767  PX4_WARN("%s\n", reason);
768  }
769 
770  PRINT_MODULE_DESCRIPTION(
771  R"DESCR_STR(
772 ### Description
773 fw_att_control is the fixed wing attitude controller.
774 
775 )DESCR_STR");
776 
777  PRINT_MODULE_USAGE_COMMAND("start");
778  PRINT_MODULE_USAGE_NAME("fw_att_control", "controller");
779  PRINT_MODULE_USAGE_DEFAULT_COMMANDS();
780 
781  return 0;
782 }
783 
784 extern "C" __EXPORT int fw_att_control_main(int argc, char *argv[])
785 {
786  return FixedwingAttitudeControl::main(argc, argv);
787 }
#define VEHICLE_TYPE_FIXED_WING
constexpr _Tp constrain(_Tp val, _Tp min_val, _Tp max_val)
Definition: Limits.hpp:66
static int task_spawn(int argc, char *argv[])
vehicle_attitude_s _att
vehicle attitude setpoint
Type theta() const
Definition: Euler.hpp:132
void set_time_constant(float time_constant)
measure the time elapsed performing an event
Definition: perf_counter.h:56
uORB::Subscription _vehicle_status_sub
vehicle status subscription
bool update(void *dst)
Copy the struct if updated.
float pitch_rate_setpoint
__EXPORT int param_get(param_t param, void *val)
Copy the value of a parameter.
Definition: parameters.cpp:589
bool is_fixed_wing(const struct vehicle_status_s *current_status)
orb_id_t _actuators_id
pointer to correct actuator controls0 uORB metadata structure
int main(int argc, char **argv)
Definition: main.cpp:3
manual_control_setpoint_s _manual
r/c channel data
uORB::Subscription _battery_status_sub
battery status subscription
Definition: I2C.hpp:51
actuator_controls_s _actuators
actuator control inputs
vehicle_control_mode_s _vcontrol_mode
vehicle control mode
orb_advert_t orb_advertise(const struct orb_metadata *meta, const void *data)
Definition: uORB.cpp:43
void set_max_rate(float max_rate)
perf_counter_t _loop_perf
loop performance counter
float control_bodyrate(const struct ECL_ControlData &ctl_data) override
float get_desired_rate()
vehicle_attitude_setpoint_s _att_sp
vehicle attitude setpoint
vtol_type
Definition: vtol_type.h:83
void set_k_i(float k_i)
uORB::SubscriptionData< airspeed_validated_s > _airspeed_validated_sub
float control_attitude(const struct ECL_ControlData &ctl_data) override
LidarLite * instance
Definition: ll40ls.cpp:65
static int custom_command(int argc, char *argv[])
void set_max_rate_pos(float max_rate_pos)
uORB::Subscription _vcontrol_mode_sub
vehicle status subscription
Type phi() const
Definition: Euler.hpp:128
Type psi() const
Definition: Euler.hpp:136
const T gradual(const T &value, const T &x_low, const T &x_high, const T &y_low, const T &y_high)
Definition: Functions.hpp:139
uORB::PublicationMulti< rate_ctrl_status_s > _rate_ctrl_status_pub
rate controller status publication
#define ORB_ID(_name)
Generates a pointer to the uORB metadata structure for a given topic.
Definition: uORB.h:87
uORB::Subscription _rates_sp_sub
vehicle rates setpoint
bool publish(const T &data)
Publish the struct.
Definition: Publication.hpp:68
uORB::Publication< actuator_controls_s > _actuators_2_pub
actuator control group 1 setpoint (Airframe)
void perf_free(perf_counter_t handle)
Free a counter.
uORB::Subscription _global_pos_sub
global position subscription
#define perf_alloc(a, b)
Definition: px4io.h:59
orb_advert_t _attitude_sp_pub
attitude setpoint point
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
float control_attitude(const struct ECL_ControlData &ctl_data) override
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
void set_k_ff(float k_ff)
void set_max_rate_neg(float max_rate_neg)
Euler< float > Eulerf
Definition: Euler.hpp:156
orb_advert_t _actuators_0_pub
actuator control group 0 setpoint
vehicle_status_s _vehicle_status
vehicle status
void perf_end(perf_counter_t handle)
End a performance event.
Euler angles class.
Definition: AxisAngle.hpp:18
bool updated()
Check if there is a new update.
float control_attitude(const struct ECL_ControlData &ctl_data) override
float get_desired_bodyrate()
uORB::Publication< vehicle_rates_setpoint_s > _rate_sp_pub
rate setpoint publication
int orb_publish(const struct orb_metadata *meta, orb_advert_t handle, const void *data)
Definition: uORB.cpp:70
uORB::SubscriptionCallbackWorkItem _att_sub
vehicle attitude
const T & get() const
__EXPORT param_t param_find(const char *name)
Look up a parameter by name.
Definition: parameters.cpp:370
actuator_controls_s _actuators_airframe
actuator control inputs
float control_euler_rate(const struct ECL_ControlData &ctl_data) override
void set_bodyrate_setpoint(float rate)
void set_k_p(float k_p)
constexpr _Tp max(_Tp a, _Tp b)
Definition: Limits.hpp:60
int parameters_update()
Update our local parameter cache.
vehicle_global_position_s _global_pos
global position
Quaternion< float > Quatf
Definition: Quaternion.hpp:544
float dt
Definition: px4io.c:73
void perf_print_counter(perf_counter_t handle)
Print one performance counter to stdout.
Definition: bst.cpp:62
uORB::Subscription _vehicle_land_detected_sub
vehicle land detected subscription
float roll_rate_setpoint
float control_bodyrate(const struct ECL_ControlData &ctl_data) override
uORB::Subscription _att_sp_sub
vehicle attitude setpoint
bool update(void *dst)
Update the struct.
uORB::Subscription _manual_sub
notification of manual control updates
__EXPORT int fw_att_control_main(int argc, char *argv[])
vehicle_rates_setpoint_s _rates_sp
static int print_usage(const char *reason=nullptr)
uORB::Subscription _parameter_update_sub
notification of parameter updates
bool publish(const T &data)
Publish the struct.
float control_euler_rate(const struct ECL_ControlData &ctl_data) override
float control_bodyrate(const struct ECL_ControlData &ctl_data) override
bool copy(void *dst)
Copy the struct.
void perf_begin(perf_counter_t handle)
Begin a performance event.
void set_integrator_max(float max)
__EXPORT hrt_abstime hrt_absolute_time(void)
Get absolute time in [us] (does not wrap).
float groundspeed_scaler
void set_bodyrate_setpoint(float rate)