/**************************************************************************** * * Copyright (c) 2019 PX4 Development Team. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * 3. Neither the name PX4 nor the names of its contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * ****************************************************************************/ /** * @file AttitudeControl.cpp */ #include #include #include using namespace matrix; void AttitudeControl::setProportionalGain(const matrix::Vector3f &proportional_gain) { _proportional_gain = proportional_gain; // prepare yaw weight from the ratio between roll/pitch and yaw gains const float roll_pitch_gain = (proportional_gain(0) + proportional_gain(1)) / 2.f; _yaw_w = math::constrain(proportional_gain(2) / roll_pitch_gain, 0.f, 1.f); _proportional_gain(2) = roll_pitch_gain; } matrix::Vector3f AttitudeControl::update(matrix::Quatf q, matrix::Quatf qd, const float yawspeed_feedforward) { // ensure input quaternions are exactly normalized because acosf(1.00001) == NaN q.normalize(); qd.normalize(); // calculate reduced desired attitude neglecting vehicle's yaw to prioritize roll and pitch const Vector3f e_z = q.dcm_z(); const Vector3f e_z_d = qd.dcm_z(); Quatf qd_red(e_z, e_z_d); if (fabsf(qd_red(1)) > (1.f - 1e-5f) || fabsf(qd_red(2)) > (1.f - 1e-5f)) { // In the infinitesimal corner case where the vehicle and thrust have the completely opposite direction, // full attitude control anyways generates no yaw input and directly takes the combination of // roll and pitch leading to the correct desired yaw. Ignoring this case would still be totally safe and stable. qd_red = qd; } else { // transform rotation from current to desired thrust vector into a world frame reduced desired attitude qd_red *= q; } // mix full and reduced desired attitude Quatf q_mix = qd_red.inversed() * qd; q_mix *= math::signNoZero(q_mix(0)); // catch numerical problems with the domain of acosf and asinf q_mix(0) = math::constrain(q_mix(0), -1.f, 1.f); q_mix(3) = math::constrain(q_mix(3), -1.f, 1.f); qd = qd_red * Quatf(cosf(_yaw_w * acosf(q_mix(0))), 0, 0, sinf(_yaw_w * asinf(q_mix(3)))); // quaternion attitude control law, qe is rotation from q to qd const Quatf qe = q.inversed() * qd; // using sin(alpha/2) scaled rotation axis as attitude error (see quaternion definition by axis angle) // also taking care of the antipodal unit quaternion ambiguity const Vector3f eq = 2.f * math::signNoZero(qe(0)) * qe.imag(); // calculate angular rates setpoint matrix::Vector3f rate_setpoint = eq.emult(_proportional_gain); // Feed forward the yaw setpoint rate. // yaw_sp_move_rate is the feed forward commanded rotation around the world z-axis, // but we need to apply it in the body frame (because _rates_sp is expressed in the body frame). // Therefore we infer the world z-axis (expressed in the body frame) by taking the last column of R.transposed (== q.inversed) // and multiply it by the yaw setpoint rate (yaw_sp_move_rate). // This yields a vector representing the commanded rotatation around the world z-axis expressed in the body frame // such that it can be added to the rates setpoint. rate_setpoint += q.inversed().dcm_z() * yawspeed_feedforward; // limit rates for (int i = 0; i < 3; i++) { rate_setpoint(i) = math::constrain(rate_setpoint(i), -_rate_limit(i), _rate_limit(i)); } return rate_setpoint; }