Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Path: blob/master/libraries/APM_Control/AP_YawController.cpp
Views: 1798
/*1This program is free software: you can redistribute it and/or modify2it under the terms of the GNU General Public License as published by3the Free Software Foundation, either version 3 of the License, or4(at your option) any later version.56This program is distributed in the hope that it will be useful,7but WITHOUT ANY WARRANTY; without even the implied warranty of8MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the9GNU General Public License for more details.1011You should have received a copy of the GNU General Public License12along with this program. If not, see <http://www.gnu.org/licenses/>.13*/1415// Code by Jon Challinger16// Modified by Paul Riseborough to implement a three loop autopilot17// topology18//19#include <AP_HAL/AP_HAL.h>20#include "AP_YawController.h"21#include <AP_AHRS/AP_AHRS.h>22#include <GCS_MAVLink/GCS.h>23#include <AP_Scheduler/AP_Scheduler.h>2425extern const AP_HAL::HAL& hal;2627const AP_Param::GroupInfo AP_YawController::var_info[] = {2829// @Param: 2SRV_SLIP30// @DisplayName: Sideslip control gain31// @Description: Gain from lateral acceleration to demanded yaw rate for aircraft with enough fuselage area to detect lateral acceleration and sideslips. Do not enable for flying wings and gliders. Actively coordinates flight more than just yaw damping. Set after YAW2SRV_DAMP and YAW2SRV_INT are tuned.32// @Range: 0 433// @Increment: 0.2534// @User: Advanced35AP_GROUPINFO("2SRV_SLIP", 0, AP_YawController, _K_A, 0),3637// @Param: 2SRV_INT38// @DisplayName: Sideslip control integrator39// @Description: Integral gain from lateral acceleration error. Effectively trims rudder to eliminate long-term sideslip.40// @Range: 0 241// @Increment: 0.2542// @User: Advanced43AP_GROUPINFO("2SRV_INT", 1, AP_YawController, _K_I, 0),4445// @Param: 2SRV_DAMP46// @DisplayName: Yaw damping47// @Description: Gain from yaw rate to rudder. Most effective at yaw damping and should be tuned after KFF_RDDRMIX. Also disables YAW2SRV_INT if set to 0.48// @Range: 0 249// @Increment: 0.2550// @User: Advanced51AP_GROUPINFO("2SRV_DAMP", 2, AP_YawController, _K_D, 0),5253// @Param: 2SRV_RLL54// @DisplayName: Yaw coordination gain55// @Description: Gain to the yaw rate required to keep it consistent with the turn rate in a coordinated turn. Corrects for yaw tendencies after the turn is established. Increase yaw into the turn by raising. Increase yaw out of the turn by decreasing. Values outside of 0.9-1.1 range indicate airspeed calibration problems.56// @Range: 0.8 1.257// @Increment: 0.0558// @User: Advanced59AP_GROUPINFO("2SRV_RLL", 3, AP_YawController, _K_FF, 1),6061/*62Note: index 4 should not be used - it was used for an incorrect63AP_Int8 version of the IMAX in the 2.74 release64*/6566// @Param: 2SRV_IMAX67// @DisplayName: Integrator limit68// @Description: Limit of yaw integrator gain in centi-degrees of servo travel. Servos are assumed to have +/- 4500 centi-degrees of travel, so a value of 1500 allows trim of up to 1/3 of servo travel range.69// @Range: 0 450070// @Increment: 171// @User: Advanced72AP_GROUPINFO("2SRV_IMAX", 5, AP_YawController, _imax, 1500),7374// @Param: _RATE_ENABLE75// @DisplayName: Yaw rate enable76// @Description: Enable yaw rate controller for aerobatic flight77// @Values: 0:Disable,1:Enable78// @User: Advanced79AP_GROUPINFO_FLAGS("_RATE_ENABLE", 6, AP_YawController, _rate_enable, 0, AP_PARAM_FLAG_ENABLE),8081// @Param: _RATE_P82// @DisplayName: Yaw axis rate controller P gain83// @Description: Yaw axis rate controller P gain. Corrects in proportion to the difference between the desired yaw rate vs actual yaw rate84// @Range: 0.08 0.3585// @Increment: 0.00586// @User: Standard8788// @Param: _RATE_I89// @DisplayName: Yaw axis rate controller I gain90// @Description: Yaw axis rate controller I gain. Corrects long-term difference in desired yaw rate vs actual yaw rate91// @Range: 0.01 0.692// @Increment: 0.0193// @User: Standard9495// @Param: _RATE_IMAX96// @DisplayName: Yaw axis rate controller I gain maximum97// @Description: Yaw axis rate controller I gain maximum. Constrains the maximum that the I term will output98// @Range: 0 199// @Increment: 0.01100// @User: Standard101102// @Param: _RATE_D103// @DisplayName: Yaw axis rate controller D gain104// @Description: Yaw axis rate controller D gain. Compensates for short-term change in desired yaw rate vs actual yaw rate105// @Range: 0.001 0.03106// @Increment: 0.001107// @User: Standard108109// @Param: _RATE_FF110// @DisplayName: Yaw axis rate controller feed forward111// @Description: Yaw axis rate controller feed forward112// @Range: 0 3.0113// @Increment: 0.001114// @User: Standard115116// @Param: _RATE_FLTT117// @DisplayName: Yaw axis rate controller target frequency in Hz118// @Description: Yaw axis rate controller target frequency in Hz119// @Range: 2 50120// @Increment: 1121// @Units: Hz122// @User: Standard123124// @Param: _RATE_FLTE125// @DisplayName: Yaw axis rate controller error frequency in Hz126// @Description: Yaw axis rate controller error frequency in Hz127// @Range: 2 50128// @Increment: 1129// @Units: Hz130// @User: Standard131132// @Param: _RATE_FLTD133// @DisplayName: Yaw axis rate controller derivative frequency in Hz134// @Description: Yaw axis rate controller derivative frequency in Hz135// @Range: 0 50136// @Increment: 1137// @Units: Hz138// @User: Standard139140// @Param: _RATE_SMAX141// @DisplayName: Yaw slew rate limit142// @Description: Sets an upper limit on the slew rate produced by the combined P and D gains. If the amplitude of the control action produced by the rate feedback exceeds this value, then the D+P gain is reduced to respect the limit. This limits the amplitude of high frequency oscillations caused by an excessive gain. The limit should be set to no more than 25% of the actuators maximum slew rate to allow for load effects. Note: The gain will not be reduced to less than 10% of the nominal value. A value of zero will disable this feature.143// @Range: 0 200144// @Increment: 0.5145// @User: Advanced146147// @Param: _RATE_PDMX148// @DisplayName: Yaw axis rate controller PD sum maximum149// @Description: Yaw axis rate controller PD sum maximum. The maximum/minimum value that the sum of the P and D term can output150// @Range: 0 1151// @Increment: 0.01152153// @Param: _RATE_D_FF154// @DisplayName: Yaw Derivative FeedForward Gain155// @Description: FF D Gain which produces an output that is proportional to the rate of change of the target156// @Range: 0 0.03157// @Increment: 0.001158// @User: Advanced159160// @Param: _RATE_NTF161// @DisplayName: Yaw Target notch filter index162// @Description: Yaw Target notch filter index163// @Range: 1 8164// @User: Advanced165166// @Param: _RATE_NEF167// @DisplayName: Yaw Error notch filter index168// @Description: Yaw Error notch filter index169// @Range: 1 8170// @User: Advanced171172AP_SUBGROUPINFO(rate_pid, "_RATE_", 9, AP_YawController, AC_PID),173174AP_GROUPEND175};176177AP_YawController::AP_YawController(const AP_FixedWing &parms)178: aparm(parms)179{180AP_Param::setup_object_defaults(this, var_info);181_pid_info.target = 0;182_pid_info.FF = 0;183_pid_info.P = 0;184rate_pid.set_slew_limit_scale(45);185}186187int32_t AP_YawController::get_servo_out(float scaler, bool disable_integrator)188{189uint32_t tnow = AP_HAL::millis();190uint32_t dt = tnow - _last_t;191if (_last_t == 0 || dt > 1000) {192dt = 0;193_pid_info.I = 0;194}195_last_t = tnow;196197198int16_t aspd_min = aparm.airspeed_min;199if (aspd_min < 1) {200aspd_min = 1;201}202203float delta_time = (float) dt * 0.001f;204205// Calculate yaw rate required to keep up with a constant height coordinated turn206float aspeed;207float rate_offset;208float bank_angle = AP::ahrs().get_roll();209// limit bank angle between +- 80 deg if right way up210if (fabsf(bank_angle) < 1.5707964f) {211bank_angle = constrain_float(bank_angle,-1.3962634f,1.3962634f);212}213const AP_AHRS &_ahrs = AP::ahrs();214if (!_ahrs.airspeed_estimate(aspeed)) {215// If no airspeed available use average of min and max216aspeed = 0.5f*(float(aspd_min) + float(aparm.airspeed_max));217}218rate_offset = (GRAVITY_MSS / MAX(aspeed, float(aspd_min))) * sinf(bank_angle) * _K_FF;219220// Get body rate vector (radians/sec)221float omega_z = _ahrs.get_gyro().z;222223// Get the accln vector (m/s^2)224float accel_y = AP::ins().get_accel().y;225226// subtract current bias estimate from EKF227const Vector3f &abias = _ahrs.get_accel_bias();228accel_y -= abias.y;229230// Subtract the steady turn component of rate from the measured rate231// to calculate the rate relative to the turn requirement in degrees/sec232float rate_hp_in = ToDeg(omega_z - rate_offset);233234// Apply a high-pass filter to the rate to washout any steady state error235// due to bias errors in rate_offset236// Use a cut-off frequency of omega = 0.2 rad/sec237// Could make this adjustable by replacing 0.9960080 with (1 - omega * dt)238float rate_hp_out = 0.9960080f * _last_rate_hp_out + rate_hp_in - _last_rate_hp_in;239_last_rate_hp_out = rate_hp_out;240_last_rate_hp_in = rate_hp_in;241242//Calculate input to integrator243float integ_in = - _K_I * (_K_A * accel_y + rate_hp_out);244245// Apply integrator, but clamp input to prevent control saturation and freeze integrator below min FBW speed246// Don't integrate if in stabilise mode as the integrator will wind up against the pilots inputs247// Don't integrate if _K_D is zero as integrator will keep winding up248if (!disable_integrator && _K_D > 0) {249//only integrate if airspeed above min value250if (aspeed > float(aspd_min)) {251// prevent the integrator from increasing if surface defln demand is above the upper limit252if (_last_out < -45) {253_integrator += MAX(integ_in * delta_time, 0);254} else if (_last_out > 45) {255// prevent the integrator from decreasing if surface defln demand is below the lower limit256_integrator += MIN(integ_in * delta_time, 0);257} else {258_integrator += integ_in * delta_time;259}260}261} else {262_integrator = 0;263}264265if (_K_D < 0.0001f) {266// yaw damping is disabled, and the integrator is scaled by damping, so return 0267return 0;268}269270// Scale the integration limit271float intLimScaled = _imax * 0.01f / (_K_D * scaler * scaler);272273// Constrain the integrator state274_integrator = constrain_float(_integrator, -intLimScaled, intLimScaled);275276// Protect against increases to _K_D during in-flight tuning from creating large control transients277// due to stored integrator values278if (_K_D > _K_D_last && _K_D > 0) {279_integrator = _K_D_last/_K_D * _integrator;280}281_K_D_last = _K_D;282283// Calculate demanded rudder deflection, +Ve deflection yaws nose right284// Save to last value before application of limiter so that integrator limiting285// can detect exceedance next frame286// Scale using inverse dynamic pressure (1/V^2)287_pid_info.I = _K_D * _integrator * scaler * scaler;288_pid_info.D = _K_D * (-rate_hp_out) * scaler * scaler;289_last_out = _pid_info.I + _pid_info.D;290291// Convert to centi-degrees and constrain292return constrain_float(_last_out * 100, -4500, 4500);293}294295// get actuator output for direct rate control296// desired_rate is in deg/sec. scaler is the surface speed scaler297float AP_YawController::get_rate_out(float desired_rate, float scaler, bool disable_integrator)298{299const AP_AHRS &_ahrs = AP::ahrs();300301const float dt = AP::scheduler().get_loop_period_s();302const float eas2tas = _ahrs.get_EAS2TAS();303bool limit_I = fabsf(_last_out) >= 45 || disable_integrator;304float rate_z = _ahrs.get_gyro().z;305float aspeed;306float old_I = rate_pid.get_i();307308if (!_ahrs.airspeed_estimate(aspeed)) {309aspeed = 0;310}311bool underspeed = aspeed <= float(aparm.airspeed_min);312if (underspeed) {313limit_I = true;314}315316// the P and I elements are scaled by sq(scaler). To use an317// unmodified AC_PID object we scale the inputs and calculate FF separately318//319// note that we run AC_PID in radians so that the normal scaling320// range for IMAX in AC_PID applies (usually an IMAX value less than 1.0)321rate_pid.update_all(radians(desired_rate) * scaler * scaler, rate_z * scaler * scaler, dt, limit_I);322323if (underspeed) {324// when underspeed we lock the integrator325rate_pid.set_integrator(old_I);326}327328// FF should be scaled by scaler/eas2tas, but since we have scaled329// the AC_PID target above by scaler*scaler we need to instead330// divide by scaler*eas2tas to get the right scaling331const float ff = degrees(rate_pid.get_ff() / (scaler * eas2tas));332333if (disable_integrator) {334rate_pid.reset_I();335}336337// convert AC_PID info object to same scale as old controller338_pid_info = rate_pid.get_pid_info();339auto &pinfo = _pid_info;340341const float deg_scale = degrees(1);342pinfo.FF = ff;343pinfo.P *= deg_scale;344pinfo.I *= deg_scale;345pinfo.D *= deg_scale;346pinfo.DFF *= deg_scale;347pinfo.limit = limit_I;348349// fix the logged target and actual values to not have the scalers applied350pinfo.target = desired_rate;351pinfo.actual = degrees(rate_z);352353// sum components354float out = pinfo.FF + pinfo.P + pinfo.I + pinfo.D + pinfo.DFF;355356// remember the last output to trigger the I limit357_last_out = out;358359if (autotune != nullptr && autotune->running && aspeed > aparm.airspeed_min) {360// fake up an angular error based on a notional time constant of 0.5s361const float angle_err_deg = desired_rate * gains.tau;362// let autotune have a go at the values363autotune->update(pinfo, scaler, angle_err_deg);364}365366// output is scaled to notional centidegrees of deflection367return constrain_float(out * 100, -4500, 4500);368}369370void AP_YawController::reset_I()371{372_pid_info.I = 0;373rate_pid.reset_I();374_integrator = 0;375}376377void AP_YawController::reset_rate_PID()378{379rate_pid.reset_I();380rate_pid.reset_filter();381}382383/*384start an autotune385*/386void AP_YawController::autotune_start(void)387{388if (autotune == nullptr && rate_control_enabled()) {389// the autotuner needs a time constant. Use an assumed tconst of 0.5390gains.tau.set(0.5);391gains.rmax_pos.set(90);392393autotune = NEW_NOTHROW AP_AutoTune(gains, AP_AutoTune::AUTOTUNE_YAW, aparm, rate_pid);394if (autotune == nullptr) {395if (!failed_autotune_alloc) {396GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "AutoTune: failed yaw allocation");397}398failed_autotune_alloc = true;399}400}401if (autotune != nullptr) {402autotune->start();403}404}405406/*407restore autotune gains408*/409void AP_YawController::autotune_restore(void)410{411if (autotune != nullptr) {412autotune->stop();413}414}415416417