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_RollController.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 Riseborough17//1819#include <AP_HAL/AP_HAL.h>20#include "AP_RollController.h"21#include <AP_AHRS/AP_AHRS.h>22#include <AP_Scheduler/AP_Scheduler.h>23#include <GCS_MAVLink/GCS.h>2425extern const AP_HAL::HAL& hal;2627const AP_Param::GroupInfo AP_RollController::var_info[] = {28// @Param: 2SRV_TCONST29// @DisplayName: Roll Time Constant30// @Description: Time constant in seconds from demanded to achieved roll angle. Most models respond well to 0.5. May be reduced for faster responses, but setting lower than a model can achieve will not help.31// @Range: 0.4 1.032// @Units: s33// @Increment: 0.134// @User: Advanced35AP_GROUPINFO("2SRV_TCONST", 0, AP_RollController, gains.tau, 0.5f),3637// index 1 to 3 reserved for old PID values3839// @Param: 2SRV_RMAX40// @DisplayName: Maximum Roll Rate41// @Description: This sets the maximum roll rate that the attitude controller will demand (degrees/sec) in angle stabilized modes. Setting it to zero disables this limit.42// @Range: 0 18043// @Units: deg/s44// @Increment: 145// @User: Advanced46AP_GROUPINFO("2SRV_RMAX", 4, AP_RollController, gains.rmax_pos, 0),4748// index 5, 6 reserved for old IMAX, FF4950// @Param: _RATE_P51// @DisplayName: Roll axis rate controller P gain52// @Description: Roll axis rate controller P gain. Corrects in proportion to the difference between the desired roll rate vs actual roll rate53// @Range: 0.08 0.3554// @Increment: 0.00555// @User: Standard5657// @Param: _RATE_I58// @DisplayName: Roll axis rate controller I gain59// @Description: Roll axis rate controller I gain. Corrects long-term difference in desired roll rate vs actual roll rate60// @Range: 0.01 0.661// @Increment: 0.0162// @User: Standard6364// @Param: _RATE_IMAX65// @DisplayName: Roll axis rate controller I gain maximum66// @Description: Roll axis rate controller I gain maximum. Constrains the maximum that the I term will output67// @Range: 0 168// @Increment: 0.0169// @User: Standard7071// @Param: _RATE_D72// @DisplayName: Roll axis rate controller D gain73// @Description: Roll axis rate controller D gain. Compensates for short-term change in desired roll rate vs actual roll rate74// @Range: 0.001 0.0375// @Increment: 0.00176// @User: Standard7778// @Param: _RATE_FF79// @DisplayName: Roll axis rate controller feed forward80// @Description: Roll axis rate controller feed forward81// @Range: 0 3.082// @Increment: 0.00183// @User: Standard8485// @Param: _RATE_FLTT86// @DisplayName: Roll axis rate controller target frequency in Hz87// @Description: Roll axis rate controller target frequency in Hz88// @Range: 2 5089// @Increment: 190// @Units: Hz91// @User: Standard9293// @Param: _RATE_FLTE94// @DisplayName: Roll axis rate controller error frequency in Hz95// @Description: Roll axis rate controller error frequency in Hz96// @Range: 2 5097// @Increment: 198// @Units: Hz99// @User: Standard100101// @Param: _RATE_FLTD102// @DisplayName: Roll axis rate controller derivative frequency in Hz103// @Description: Roll axis rate controller derivative frequency in Hz104// @Range: 0 50105// @Increment: 1106// @Units: Hz107// @User: Standard108109// @Param: _RATE_SMAX110// @DisplayName: Roll slew rate limit111// @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.112// @Range: 0 200113// @Increment: 0.5114// @User: Advanced115116// @Param: _RATE_PDMX117// @DisplayName: Roll axis rate controller PD sum maximum118// @Description: Roll axis rate controller PD sum maximum. The maximum/minimum value that the sum of the P and D term can output119// @Range: 0 1120// @Increment: 0.01121122// @Param: _RATE_D_FF123// @DisplayName: Roll Derivative FeedForward Gain124// @Description: FF D Gain which produces an output that is proportional to the rate of change of the target125// @Range: 0 0.03126// @Increment: 0.001127// @User: Advanced128129// @Param: _RATE_NTF130// @DisplayName: Roll Target notch filter index131// @Description: Roll Target notch filter index132// @Range: 1 8133// @User: Advanced134135// @Param: _RATE_NEF136// @DisplayName: Roll Error notch filter index137// @Description: Roll Error notch filter index138// @Range: 1 8139// @User: Advanced140141AP_SUBGROUPINFO(rate_pid, "_RATE_", 9, AP_RollController, AC_PID),142143AP_GROUPEND144};145146// constructor147AP_RollController::AP_RollController(const AP_FixedWing &parms)148: aparm(parms)149{150AP_Param::setup_object_defaults(this, var_info);151rate_pid.set_slew_limit_scale(45);152}153154155/*156AC_PID based rate controller157*/158float AP_RollController::_get_rate_out(float desired_rate, float scaler, bool disable_integrator, bool ground_mode)159{160const AP_AHRS &_ahrs = AP::ahrs();161162const float dt = AP::scheduler().get_loop_period_s();163const float eas2tas = _ahrs.get_EAS2TAS();164bool limit_I = fabsf(_last_out) >= 45;165float rate_x = _ahrs.get_gyro().x;166float aspeed;167float old_I = rate_pid.get_i();168169if (!_ahrs.airspeed_estimate(aspeed)) {170aspeed = 0;171}172bool underspeed = aspeed <= float(aparm.airspeed_min);173if (underspeed) {174limit_I = true;175}176177// the P and I elements are scaled by sq(scaler). To use an178// unmodified AC_PID object we scale the inputs and calculate FF separately179//180// note that we run AC_PID in radians so that the normal scaling181// range for IMAX in AC_PID applies (usually an IMAX value less than 1.0)182rate_pid.update_all(radians(desired_rate) * scaler * scaler, rate_x * scaler * scaler, dt, limit_I);183184if (underspeed) {185// when underspeed we lock the integrator186rate_pid.set_integrator(old_I);187}188189// FF should be scaled by scaler/eas2tas, but since we have scaled190// the AC_PID target above by scaler*scaler we need to instead191// divide by scaler*eas2tas to get the right scaling192const float ff = degrees(ff_scale * rate_pid.get_ff() / (scaler * eas2tas));193ff_scale = 1.0;194195if (disable_integrator) {196rate_pid.reset_I();197}198199// convert AC_PID info object to same scale as old controller200_pid_info = rate_pid.get_pid_info();201auto &pinfo = _pid_info;202203const float deg_scale = degrees(1);204pinfo.FF = ff;205pinfo.P *= deg_scale;206pinfo.I *= deg_scale;207pinfo.D *= deg_scale;208pinfo.DFF *= deg_scale;209210// fix the logged target and actual values to not have the scalers applied211pinfo.target = desired_rate;212pinfo.actual = degrees(rate_x);213214// sum components215float out = pinfo.FF + pinfo.P + pinfo.I + pinfo.D + pinfo.DFF;216if (ground_mode) {217// when on ground suppress D term to prevent oscillations218out -= pinfo.D + 0.5*pinfo.P;219}220221// remember the last output to trigger the I limit222_last_out = out;223224if (autotune != nullptr && autotune->running && aspeed > aparm.airspeed_min) {225// let autotune have a go at the values226autotune->update(pinfo, scaler, angle_err_deg);227}228229// output is scaled to notional centidegrees of deflection230return constrain_float(out * 100, -4500, 4500);231}232233/*234Function returns an equivalent elevator deflection in centi-degrees in the range from -4500 to 4500235A positive demand is up236Inputs are:2371) desired roll rate in degrees/sec2382) control gain scaler = scaling_speed / aspeed239*/240float AP_RollController::get_rate_out(float desired_rate, float scaler)241{242return _get_rate_out(desired_rate, scaler, false, false);243}244245/*246Function returns an equivalent aileron deflection in centi-degrees in the range from -4500 to 4500247A positive demand is up248Inputs are:2491) demanded bank angle in centi-degrees2502) control gain scaler = scaling_speed / aspeed2513) boolean which is true when stabilise mode is active2524) minimum FBW airspeed (metres/sec)253*/254float AP_RollController::get_servo_out(int32_t angle_err, float scaler, bool disable_integrator, bool ground_mode)255{256if (gains.tau < 0.05f) {257gains.tau.set(0.05f);258}259260// Calculate the desired roll rate (deg/sec) from the angle error261angle_err_deg = angle_err * 0.01;262float desired_rate = angle_err_deg/ gains.tau;263264// Limit the demanded roll rate265if (gains.rmax_pos && desired_rate < -gains.rmax_pos) {266desired_rate = - gains.rmax_pos;267} else if (gains.rmax_pos && desired_rate > gains.rmax_pos) {268desired_rate = gains.rmax_pos;269}270271return _get_rate_out(desired_rate, scaler, disable_integrator, ground_mode);272}273274void AP_RollController::reset_I()275{276rate_pid.reset_I();277}278279/*280convert from old to new PIDs281this is a temporary conversion function during development282*/283void AP_RollController::convert_pid()284{285AP_Float &ff = rate_pid.ff();286if (ff.configured()) {287return;288}289float old_ff=0, old_p=1.0, old_i=0.3, old_d=0.08;290int16_t old_imax=3000;291bool have_old = AP_Param::get_param_by_index(this, 1, AP_PARAM_FLOAT, &old_p);292have_old |= AP_Param::get_param_by_index(this, 3, AP_PARAM_FLOAT, &old_i);293have_old |= AP_Param::get_param_by_index(this, 2, AP_PARAM_FLOAT, &old_d);294have_old |= AP_Param::get_param_by_index(this, 6, AP_PARAM_FLOAT, &old_ff);295have_old |= AP_Param::get_param_by_index(this, 5, AP_PARAM_INT16, &old_imax);296if (!have_old) {297// none of the old gains were set298return;299}300301const float kp_ff = MAX((old_p - old_i * gains.tau) * gains.tau - old_d, 0);302rate_pid.ff().set_and_save(old_ff + kp_ff);303rate_pid.kI().set_and_save_ifchanged(old_i * gains.tau);304rate_pid.kP().set_and_save_ifchanged(old_d);305rate_pid.kD().set_and_save_ifchanged(0);306rate_pid.kIMAX().set_and_save_ifchanged(old_imax/4500.0);307}308309/*310start an autotune311*/312void AP_RollController::autotune_start(void)313{314if (autotune == nullptr) {315autotune = NEW_NOTHROW AP_AutoTune(gains, AP_AutoTune::AUTOTUNE_ROLL, aparm, rate_pid);316if (autotune == nullptr) {317if (!failed_autotune_alloc) {318GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "AutoTune: failed roll allocation");319}320failed_autotune_alloc = true;321}322}323if (autotune != nullptr) {324autotune->start();325}326}327328/*329restore autotune gains330*/331void AP_RollController::autotune_restore(void)332{333if (autotune != nullptr) {334autotune->stop();335}336}337338339