PX4 Firmware
PX4 Autopilot Software http://px4.io
mc_att_control_main.cpp
Go to the documentation of this file.
1 /****************************************************************************
2  *
3  * Copyright (c) 2013-2018 PX4 Development Team. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in
13  * the documentation and/or other materials provided with the
14  * distribution.
15  * 3. Neither the name PX4 nor the names of its contributors may be
16  * used to endorse or promote products derived from this software
17  * without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
26  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  *
32  ****************************************************************************/
33 
34 /**
35  * @file mc_att_control_main.cpp
36  * Multicopter attitude controller.
37  *
38  * @author Lorenz Meier <lorenz@px4.io>
39  * @author Anton Babushkin <anton.babushkin@me.com>
40  * @author Sander Smeets <sander@droneslab.com>
41  * @author Matthias Grob <maetugr@gmail.com>
42  * @author Beat Küng <beat-kueng@gmx.net>
43  *
44  */
45 
46 #include "mc_att_control.hpp"
47 
48 #include <drivers/drv_hrt.h>
49 #include <mathlib/math/Limits.hpp>
51 
52 using namespace matrix;
53 
55  ModuleParams(nullptr),
56  WorkItem(MODULE_NAME, px4::wq_configurations::att_pos_ctrl),
57  _loop_perf(perf_alloc(PC_ELAPSED, MODULE_NAME": cycle"))
58 {
59  _vehicle_status.vehicle_type = vehicle_status_s::VEHICLE_TYPE_ROTARY_WING;
60 
61  /* initialize quaternions in messages to be valid */
62  _v_att.q[0] = 1.f;
63  _v_att_sp.q_d[0] = 1.f;
64 
66 }
67 
69 {
71 }
72 
73 bool
75 {
77  PX4_ERR("vehicle_attitude callback registration failed!");
78  return false;
79  }
80 
81  return true;
82 }
83 
84 void
86 {
87  // Store some of the parameters in a more convenient way & precompute often-used values
88  _attitude_control.setProportionalGain(Vector3f(_param_mc_roll_p.get(), _param_mc_pitch_p.get(), _param_mc_yaw_p.get()));
89 
90  // angular rate limits
91  using math::radians;
92  _attitude_control.setRateLimit(Vector3f(radians(_param_mc_rollrate_max.get()), radians(_param_mc_pitchrate_max.get()),
93  radians(_param_mc_yawrate_max.get())));
94 
95  _man_tilt_max = math::radians(_param_mpc_man_tilt_max.get());
96 }
97 
98 void
100 {
101  /* check if there is new status information */
103  /* set correct uORB ID, depending on if vehicle is VTOL or not */
104  if (_attitude_sp_id == nullptr) {
105  if (_vehicle_status.is_vtol) {
106  _attitude_sp_id = ORB_ID(mc_virtual_attitude_setpoint);
107 
108  int32_t vt_type = -1;
109 
110  if (param_get(param_find("VT_TYPE"), &vt_type) == PX4_OK) {
111  _is_tailsitter = (static_cast<vtol_type>(vt_type) == vtol_type::TAILSITTER);
112  }
113 
114  } else {
115  _attitude_sp_id = ORB_ID(vehicle_attitude_setpoint);
116  }
117  }
118  }
119 }
120 
121 float
123 {
124  float throttle_min = _vehicle_land_detected.landed ? 0.0f : _param_mpc_manthr_min.get();
125 
126  // throttle_stick_input is in range [0, 1]
127  switch (_param_mpc_thr_curve.get()) {
128  case 1: // no rescaling to hover throttle
129  return throttle_min + throttle_stick_input * (_param_mpc_thr_max.get() - throttle_min);
130 
131  default: // 0 or other: rescale to hover throttle at 0.5 stick
132  if (throttle_stick_input < 0.5f) {
133  return (_param_mpc_thr_hover.get() - throttle_min) / 0.5f * throttle_stick_input +
134  throttle_min;
135 
136  } else {
137  return (_param_mpc_thr_max.get() - _param_mpc_thr_hover.get()) / 0.5f * (throttle_stick_input - 1.0f) +
138  _param_mpc_thr_max.get();
139  }
140  }
141 }
142 
143 void
145 {
146  vehicle_attitude_setpoint_s attitude_setpoint{};
147  const float yaw = Eulerf(Quatf(_v_att.q)).psi();
148 
149  /* reset yaw setpoint to current position if needed */
150  if (reset_yaw_sp) {
151  _man_yaw_sp = yaw;
152 
153  } else if (_manual_control_sp.z > 0.05f || _param_mc_airmode.get() == (int32_t)Mixer::Airmode::roll_pitch_yaw) {
154 
155  const float yaw_rate = math::radians(_param_mpc_man_y_max.get());
156  attitude_setpoint.yaw_sp_move_rate = _manual_control_sp.r * yaw_rate;
157  _man_yaw_sp = wrap_pi(_man_yaw_sp + attitude_setpoint.yaw_sp_move_rate * dt);
158  }
159 
160  /*
161  * Input mapping for roll & pitch setpoints
162  * ----------------------------------------
163  * We control the following 2 angles:
164  * - tilt angle, given by sqrt(x*x + y*y)
165  * - the direction of the maximum tilt in the XY-plane, which also defines the direction of the motion
166  *
167  * This allows a simple limitation of the tilt angle, the vehicle flies towards the direction that the stick
168  * points to, and changes of the stick input are linear.
169  */
170  const float x = _manual_control_sp.x * _man_tilt_max;
171  const float y = _manual_control_sp.y * _man_tilt_max;
172 
173  // we want to fly towards the direction of (x, y), so we use a perpendicular axis angle vector in the XY-plane
174  Vector2f v = Vector2f(y, -x);
175  float v_norm = v.norm(); // the norm of v defines the tilt angle
176 
177  if (v_norm > _man_tilt_max) { // limit to the configured maximum tilt angle
178  v *= _man_tilt_max / v_norm;
179  }
180 
181  Quatf q_sp_rpy = AxisAnglef(v(0), v(1), 0.f);
182  Eulerf euler_sp = q_sp_rpy;
183  attitude_setpoint.roll_body = euler_sp(0);
184  attitude_setpoint.pitch_body = euler_sp(1);
185  // The axis angle can change the yaw as well (noticeable at higher tilt angles).
186  // This is the formula by how much the yaw changes:
187  // let a := tilt angle, b := atan(y/x) (direction of maximum tilt)
188  // yaw = atan(-2 * sin(b) * cos(b) * sin^2(a/2) / (1 - 2 * cos^2(b) * sin^2(a/2))).
189  attitude_setpoint.yaw_body = _man_yaw_sp + euler_sp(2);
190 
191  /* modify roll/pitch only if we're a VTOL */
192  if (_vehicle_status.is_vtol) {
193  // Construct attitude setpoint rotation matrix. Modify the setpoints for roll
194  // and pitch such that they reflect the user's intention even if a large yaw error
195  // (yaw_sp - yaw) is present. In the presence of a yaw error constructing a rotation matrix
196  // from the pure euler angle setpoints will lead to unexpected attitude behaviour from
197  // the user's view as the euler angle sequence uses the yaw setpoint and not the current
198  // heading of the vehicle.
199  // However there's also a coupling effect that causes oscillations for fast roll/pitch changes
200  // at higher tilt angles, so we want to avoid using this on multicopters.
201  // The effect of that can be seen with:
202  // - roll/pitch into one direction, keep it fixed (at high angle)
203  // - apply a fast yaw rotation
204  // - look at the roll and pitch angles: they should stay pretty much the same as when not yawing
205 
206  // calculate our current yaw error
207  float yaw_error = wrap_pi(attitude_setpoint.yaw_body - yaw);
208 
209  // compute the vector obtained by rotating a z unit vector by the rotation
210  // given by the roll and pitch commands of the user
211  Vector3f zB = {0.0f, 0.0f, 1.0f};
212  Dcmf R_sp_roll_pitch = Eulerf(attitude_setpoint.roll_body, attitude_setpoint.pitch_body, 0.0f);
213  Vector3f z_roll_pitch_sp = R_sp_roll_pitch * zB;
214 
215  // transform the vector into a new frame which is rotated around the z axis
216  // by the current yaw error. this vector defines the desired tilt when we look
217  // into the direction of the desired heading
218  Dcmf R_yaw_correction = Eulerf(0.0f, 0.0f, -yaw_error);
219  z_roll_pitch_sp = R_yaw_correction * z_roll_pitch_sp;
220 
221  // use the formula z_roll_pitch_sp = R_tilt * [0;0;1]
222  // R_tilt is computed from_euler; only true if cos(roll) not equal zero
223  // -> valid if roll is not +-pi/2;
224  attitude_setpoint.roll_body = -asinf(z_roll_pitch_sp(1));
225  attitude_setpoint.pitch_body = atan2f(z_roll_pitch_sp(0), z_roll_pitch_sp(2));
226  }
227 
228  /* copy quaternion setpoint to attitude setpoint topic */
229  Quatf q_sp = Eulerf(attitude_setpoint.roll_body, attitude_setpoint.pitch_body, attitude_setpoint.yaw_body);
230  q_sp.copyTo(attitude_setpoint.q_d);
231  attitude_setpoint.q_d_valid = true;
232 
233  attitude_setpoint.thrust_body[2] = -throttle_curve(_manual_control_sp.z);
234  attitude_setpoint.timestamp = hrt_absolute_time();
235 
236  if (_attitude_sp_id != nullptr) {
238  }
239 }
240 
241 /**
242  * Attitude controller.
243  * Input: 'vehicle_attitude_setpoint' topics (depending on mode)
244  * Output: '_rates_sp' vector
245  */
246 void
248 {
250  _rates_sp = _attitude_control.update(Quatf(_v_att.q), Quatf(_v_att_sp.q_d), _v_att_sp.yaw_sp_move_rate);
251 }
252 
253 void
255 {
256  vehicle_rates_setpoint_s v_rates_sp{};
257 
258  v_rates_sp.roll = _rates_sp(0);
259  v_rates_sp.pitch = _rates_sp(1);
260  v_rates_sp.yaw = _rates_sp(2);
261  v_rates_sp.thrust_body[0] = _v_att_sp.thrust_body[0];
262  v_rates_sp.thrust_body[1] = _v_att_sp.thrust_body[1];
263  v_rates_sp.thrust_body[2] = _v_att_sp.thrust_body[2];
264  v_rates_sp.timestamp = hrt_absolute_time();
265 
266  _v_rates_sp_pub.publish(v_rates_sp);
267 }
268 
269 void
271 {
272  if (should_exit()) {
274  exit_and_cleanup();
275  return;
276  }
277 
279 
280  // Check if parameters have changed
281  if (_params_sub.updated()) {
282  // clear update
283  parameter_update_s param_update;
284  _params_sub.copy(&param_update);
285 
286  updateParams();
288  }
289 
290  // run controller on attitude updates
291  const uint8_t prev_quat_reset_counter = _v_att.quat_reset_counter;
292 
294 
295  // Check for a heading reset
296  if (prev_quat_reset_counter != _v_att.quat_reset_counter) {
297  // we only extract the heading change from the delta quaternion
298  _man_yaw_sp += Eulerf(Quatf(_v_att.delta_q_reset)).psi();
299  }
300 
301  const hrt_abstime now = hrt_absolute_time();
302 
303  // Guard against too small (< 0.2ms) and too large (> 20ms) dt's.
304  const float dt = math::constrain(((now - _last_run) / 1e6f), 0.0002f, 0.02f);
305  _last_run = now;
306 
307  /* check for updates in other topics */
312 
313  /* Check if we are in rattitude mode and the pilot is above the threshold on pitch
314  * or roll (yaw can rotate 360 in normal att control). If both are true don't
315  * even bother running the attitude controllers */
316  if (_v_control_mode.flag_control_rattitude_enabled) {
317  _v_control_mode.flag_control_attitude_enabled =
318  fabsf(_manual_control_sp.y) <= _param_mc_ratt_th.get() &&
319  fabsf(_manual_control_sp.x) <= _param_mc_ratt_th.get();
320  }
321 
322  bool attitude_setpoint_generated = false;
323 
324  const bool is_hovering = _vehicle_status.vehicle_type == vehicle_status_s::VEHICLE_TYPE_ROTARY_WING
325  && !_vehicle_status.in_transition_mode;
326 
327  // vehicle is a tailsitter in transition mode
328  const bool is_tailsitter_transition = _vehicle_status.in_transition_mode && _is_tailsitter;
329 
330  bool run_att_ctrl = _v_control_mode.flag_control_attitude_enabled && (is_hovering || is_tailsitter_transition);
331 
332  if (run_att_ctrl) {
333  // Generate the attitude setpoint from stick inputs if we are in Manual/Stabilized mode
334  if (_v_control_mode.flag_control_manual_enabled &&
335  !_v_control_mode.flag_control_altitude_enabled &&
336  !_v_control_mode.flag_control_velocity_enabled &&
337  !_v_control_mode.flag_control_position_enabled) {
338 
340  attitude_setpoint_generated = true;
341  }
342 
344 
345  if (_v_control_mode.flag_control_yawrate_override_enabled) {
346  /* Yaw rate override enabled, overwrite the yaw setpoint */
348  const auto yawrate_reference = _v_rates_sp.yaw;
349  _rates_sp(2) = yawrate_reference;
350  }
351 
353  }
354 
355  // reset yaw setpoint during transitions, tailsitter.cpp generates
356  // attitude setpoint for the transition
357  _reset_yaw_sp = (!attitude_setpoint_generated && !_v_control_mode.flag_control_rattitude_enabled) ||
358  _vehicle_land_detected.landed ||
359  (_vehicle_status.is_vtol && _vehicle_status.in_transition_mode);
360 
361  }
362 
364 }
365 
366 int MulticopterAttitudeControl::task_spawn(int argc, char *argv[])
367 {
369 
370  if (instance) {
371  _object.store(instance);
372  _task_id = task_id_is_work_queue;
373 
374  if (instance->init()) {
375  return PX4_OK;
376  }
377 
378  } else {
379  PX4_ERR("alloc failed");
380  }
381 
382  delete instance;
383  _object.store(nullptr);
384  _task_id = -1;
385 
386  return PX4_ERROR;
387 }
388 
390 {
391  PX4_INFO("Running");
392 
394 
395  return 0;
396 }
397 
399 {
400  return print_usage("unknown command");
401 }
402 
404 {
405  if (reason) {
406  PX4_WARN("%s\n", reason);
407  }
408 
409  PRINT_MODULE_DESCRIPTION(
410  R"DESCR_STR(
411 ### Description
412 This implements the multicopter attitude controller. It takes attitude
413 setpoints (`vehicle_attitude_setpoint`) as inputs and outputs a rate setpoint.
414 
415 The controller has a P loop for angular error
416 
417 Publication documenting the implemented Quaternion Attitude Control:
418 Nonlinear Quadrocopter Attitude Control (2013)
419 by Dario Brescianini, Markus Hehn and Raffaello D'Andrea
420 Institute for Dynamic Systems and Control (IDSC), ETH Zurich
421 
422 https://www.research-collection.ethz.ch/bitstream/handle/20.500.11850/154099/eth-7387-01.pdf
423 
424 )DESCR_STR");
425 
426  PRINT_MODULE_USAGE_NAME("mc_att_control", "controller");
427  PRINT_MODULE_USAGE_COMMAND("start");
428  PRINT_MODULE_USAGE_DEFAULT_COMMANDS();
429 
430  return 0;
431 }
432 
433 int mc_att_control_main(int argc, char *argv[])
434 {
435  return MulticopterAttitudeControl::main(argc, argv);
436 }
constexpr _Tp constrain(_Tp val, _Tp min_val, _Tp max_val)
Definition: Limits.hpp:66
float _man_tilt_max
maximum tilt allowed for manual flight [rad]
uORB::Subscription _v_att_sp_sub
vehicle attitude setpoint subscription
measure the time elapsed performing an event
Definition: perf_counter.h:56
bool update(void *dst)
Copy the struct if updated.
__EXPORT int param_get(param_t param, void *val)
Copy the value of a parameter.
Definition: parameters.cpp:589
matrix::Vector3f update(matrix::Quatf q, matrix::Quatf qd, float yawspeed_feedforward)
Run one control loop cycle calculation.
int main(int argc, char **argv)
Definition: main.cpp:3
orb_advert_t _vehicle_attitude_setpoint_pub
uORB::Subscription _vehicle_land_detected_sub
vehicle land detected subscription
Limiting / constrain helper functions.
vtol_type
Definition: vtol_type.h:83
LidarLite * instance
Definition: ll40ls.cpp:65
void copyTo(Type dst[M *N]) const
Definition: Matrix.hpp:115
Quaternion class.
Definition: Dcm.hpp:24
High-resolution timer with callouts and timekeeping.
uORB::Subscription _v_rates_sp_sub
vehicle rates setpoint subscription
uORB::SubscriptionCallbackWorkItem _vehicle_attitude_sub
collection of rather simple mathematical functions that get used over and over again ...
matrix::Vector3f _rates_sp
angular rates setpoint
float throttle_curve(float throttle_stick_input)
#define ORB_ID(_name)
Generates a pointer to the uORB metadata structure for a given topic.
Definition: uORB.h:87
uORB::Subscription _v_control_mode_sub
vehicle control mode subscription
void parameters_updated()
initialize some vectors/matrices from parameters
bool publish(const T &data)
Publish the struct.
Definition: Publication.hpp:68
void perf_free(perf_counter_t handle)
Free a counter.
#define perf_alloc(a, b)
Definition: px4io.h:59
orb_id_t _attitude_sp_id
pointer to correct attitude setpoint uORB metadata structure
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
Vector2< float > Vector2f
Definition: Vector2.hpp:69
Euler< float > Eulerf
Definition: Euler.hpp:156
void generate_attitude_setpoint(float dt, bool reset_yaw_sp)
Generate & publish an attitude setpoint from stick inputs.
void perf_end(perf_counter_t handle)
End a performance event.
Euler angles class.
Definition: AxisAngle.hpp:18
void setProportionalGain(const matrix::Vector3f &proportional_gain)
Set proportional attitude control gain.
bool updated()
Check if there is a new update.
__BEGIN_DECLS typedef uint64_t hrt_abstime
Absolute time, in microsecond units.
Definition: drv_hrt.h:58
uORB::Subscription _params_sub
parameter updates subscription
int mc_att_control_main(int argc, char *argv[])
Multicopter attitude control app start / stop handling function.
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
Vector3< float > Vector3f
Definition: Vector3.hpp:136
Quaternion< float > Quatf
Definition: Quaternion.hpp:544
float dt
Definition: px4io.c:73
float _man_yaw_sp
current yaw setpoint in manual mode
void perf_print_counter(perf_counter_t handle)
Print one performance counter to stdout.
void setRateLimit(const matrix::Vector3f &rate_limit)
Set hard limit for output rate setpoints.
Definition: bst.cpp:62
AxisAngle< float > AxisAnglef
Definition: AxisAngle.hpp:160
perf_counter_t _loop_perf
loop duration performance counter
uORB::Publication< vehicle_rates_setpoint_s > _v_rates_sp_pub
rate setpoint publication
bool update(void *dst)
Update the struct.
uORB::Subscription _vehicle_status_sub
vehicle status subscription
Type norm() const
Definition: Vector.hpp:73
static int print_usage(const char *reason=nullptr)
static int task_spawn(int argc, char *argv[])
static int custom_command(int argc, char *argv[])
AttitudeControl _attitude_control
class for attitude control calculations
uORB::Subscription _manual_control_sp_sub
manual control setpoint subscription
void control_attitude()
Attitude controller.
bool copy(void *dst)
Copy the struct.
void perf_begin(perf_counter_t handle)
Begin a performance event.
__EXPORT hrt_abstime hrt_absolute_time(void)
Get absolute time in [us] (does not wrap).
static int orb_publish_auto(const struct orb_metadata *meta, orb_advert_t *handle, const void *data, int *instance, int priority)
Advertise as the publisher of a topic.
Definition: uORB.h:177