2012-08-21 23:08:14 -03:00
|
|
|
// -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: t -*-
|
|
|
|
|
2013-04-23 08:02:18 -03:00
|
|
|
// Initial Code by Jon Challinger
|
|
|
|
// Modified by Paul Riseborough
|
2012-08-21 23:08:14 -03:00
|
|
|
// This library is free software; you can redistribute it and / or
|
|
|
|
// modify it under the terms of the GNU Lesser General Public
|
|
|
|
// License as published by the Free Software Foundation; either
|
|
|
|
// version 2.1 of the License, or (at your option) any later version.
|
|
|
|
|
2013-01-10 15:22:37 -04:00
|
|
|
#include <AP_Math.h>
|
2012-10-12 14:53:42 -03:00
|
|
|
#include <AP_HAL.h>
|
2013-01-01 23:44:02 -04:00
|
|
|
#include <AP_Common.h>
|
2012-08-21 23:08:14 -03:00
|
|
|
#include "AP_PitchController.h"
|
|
|
|
|
2012-10-12 14:53:42 -03:00
|
|
|
extern const AP_HAL::HAL& hal;
|
|
|
|
|
2012-08-21 23:08:14 -03:00
|
|
|
const AP_Param::GroupInfo AP_PitchController::var_info[] PROGMEM = {
|
2013-04-23 08:02:18 -03:00
|
|
|
AP_GROUPINFO("OMEGA", 0, AP_PitchController, _kp_angle, 1.0),
|
|
|
|
AP_GROUPINFO("K_P", 1, AP_PitchController, _kp_ff, 0.4),
|
|
|
|
AP_GROUPINFO("K_D", 2, AP_PitchController, _kp_rate, 0.0),
|
|
|
|
AP_GROUPINFO("K_I", 3, AP_PitchController, _ki_rate, 0.0),
|
2012-08-21 23:08:14 -03:00
|
|
|
AP_GROUPINFO("RMAX_U", 4, AP_PitchController, _max_rate_pos, 0.0),
|
|
|
|
AP_GROUPINFO("RMAX_D", 5, AP_PitchController, _max_rate_neg, 0.0),
|
2013-04-23 08:02:18 -03:00
|
|
|
AP_GROUPINFO("K_RLL", 6, AP_PitchController, _roll_ff, 1.0),
|
2012-08-21 23:08:14 -03:00
|
|
|
AP_GROUPEND
|
|
|
|
};
|
|
|
|
|
2013-04-23 08:02:18 -03:00
|
|
|
// Function returns an equivalent elevator deflection in centi-degrees in the range from -4500 to 4500
|
|
|
|
// A positive demand is up
|
|
|
|
// Inputs are:
|
|
|
|
// 1) demanded pitch angle in centi-degrees
|
|
|
|
// 2) control gain scaler = scaling_speed / aspeed
|
|
|
|
// 3) boolean which is true when stabilise mode is active
|
|
|
|
// 4) minimum FBW airspeed (metres/sec)
|
|
|
|
// 5) maximum FBW airspeed (metres/sec)
|
|
|
|
//
|
|
|
|
int32_t AP_PitchController::get_servo_out(int32_t angle, float scaler, bool stabilize, int16_t aspd_min, int16_t aspd_max)
|
2012-08-21 23:08:14 -03:00
|
|
|
{
|
2012-10-12 14:53:42 -03:00
|
|
|
uint32_t tnow = hal.scheduler->millis();
|
2012-08-21 23:08:14 -03:00
|
|
|
uint32_t dt = tnow - _last_t;
|
|
|
|
|
|
|
|
if (_last_t == 0 || dt > 1000) {
|
|
|
|
dt = 0;
|
|
|
|
}
|
|
|
|
_last_t = tnow;
|
|
|
|
|
|
|
|
if(_ahrs == NULL) return 0;
|
2013-01-10 14:42:24 -04:00
|
|
|
float delta_time = (float)dt / 1000.0f;
|
2012-08-21 23:08:14 -03:00
|
|
|
|
2013-04-29 04:37:34 -03:00
|
|
|
// Calculate offset to pitch rate demand required to maintain pitch angle whilst banking
|
|
|
|
// Calculate ideal turn rate from bank angle and airspeed assuming a level coordinated turn
|
|
|
|
// Pitch rate offset is the component of turn rate about the pitch axis
|
|
|
|
float aspeed;
|
|
|
|
float rate_offset;
|
|
|
|
float bank_angle = _ahrs->roll;
|
|
|
|
// limit bank angle between +- 80 deg if right way up
|
|
|
|
if (fabsf(bank_angle) < 1.5707964f) {
|
|
|
|
bank_angle = constrain(bank_angle,-1.3962634,1.3962634f);
|
|
|
|
}
|
|
|
|
if (!_ahrs->airspeed_estimate(&aspeed)) {
|
|
|
|
// If no airspeed available use average of min and max
|
|
|
|
aspeed = 0.5f*(float(aspd_min) + float(aspd_max));
|
|
|
|
}
|
2013-04-23 08:02:18 -03:00
|
|
|
rate_offset = fabsf(ToDeg((9.807f / constrain(aspeed , float(aspd_min), float(aspd_max))) * tanf(bank_angle) * sinf(bank_angle))) * _roll_ff;
|
|
|
|
|
2013-04-29 04:37:34 -03:00
|
|
|
//Calculate pitch angle error in centi-degrees
|
2012-08-21 23:08:14 -03:00
|
|
|
int32_t angle_err = angle - _ahrs->pitch_sensor;
|
2013-04-23 08:02:18 -03:00
|
|
|
|
|
|
|
// Calculate the desired pitch rate (deg/sec) from the angle error
|
|
|
|
float desired_rate = angle_err * 0.01f * _kp_angle;
|
2012-08-21 23:08:14 -03:00
|
|
|
|
2013-04-23 08:02:18 -03:00
|
|
|
// limit the maximum pitch rate demand
|
2012-10-12 14:53:42 -03:00
|
|
|
if (_max_rate_neg && desired_rate < -_max_rate_neg) {
|
|
|
|
desired_rate = -_max_rate_neg;
|
|
|
|
} else if (_max_rate_pos && desired_rate > _max_rate_pos) {
|
|
|
|
desired_rate = _max_rate_pos;
|
|
|
|
}
|
2012-08-21 23:08:14 -03:00
|
|
|
|
2013-04-23 16:22:17 -03:00
|
|
|
// Apply the turn correction offset
|
|
|
|
desired_rate = desired_rate + rate_offset;
|
|
|
|
|
|
|
|
// Get body rate vector (radians/sec)
|
|
|
|
float omega_y = _ahrs->get_gyro().y;
|
2012-08-21 23:08:14 -03:00
|
|
|
|
2013-04-23 08:02:18 -03:00
|
|
|
// Apply a first order lowpass filter with a 20Hz cut-off
|
|
|
|
// Coefficients derived using a first order hold discretisation method
|
|
|
|
// Use of FOH discretisation increases high frequency noise rejection
|
|
|
|
// and reduces phase loss compared to other methods
|
|
|
|
float rate = 0.0810026f * _last_rate_out + 0.6343426f * omega_y + 0.2846549f * _last_rate_in;
|
|
|
|
_last_rate_out = rate;
|
|
|
|
_last_rate_in = omega_y;
|
|
|
|
|
2013-04-23 16:22:17 -03:00
|
|
|
// Calculate the pitch rate error (deg/sec) and scale
|
2013-04-23 08:02:18 -03:00
|
|
|
float rate_error = (desired_rate - ToDeg(rate)) * scaler;
|
2012-08-21 23:08:14 -03:00
|
|
|
|
2013-04-23 16:22:17 -03:00
|
|
|
// Multiply pitch rate error by _ki_rate and integrate
|
|
|
|
// Don't integrate if in stabilise mode as the integrator will wind up against the pilots inputs
|
2012-08-21 23:08:14 -03:00
|
|
|
if (!stabilize) {
|
2013-04-23 08:02:18 -03:00
|
|
|
//only integrate if gain and time step are positive and airspeed above min value.
|
|
|
|
if ((fabsf(_ki_rate) > 0) && (dt > 0) && (aspeed > float(aspd_min)))
|
2012-08-21 23:08:14 -03:00
|
|
|
{
|
2013-04-23 08:02:18 -03:00
|
|
|
float integrator_delta = rate_error * _ki_rate * scaler * delta_time;
|
|
|
|
// prevent the integrator from increasing if surface defln demand is above the upper limit
|
|
|
|
if (_last_out < -45) integrator_delta = max(integrator_delta , 0);
|
|
|
|
// prevent the integrator from decreasing if surface defln demand is below the lower limit
|
|
|
|
else if (_last_out > 45) integrator_delta = min(integrator_delta , 0);
|
|
|
|
_integrator += integrator_delta;
|
2012-08-21 23:08:14 -03:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
_integrator = 0;
|
|
|
|
}
|
|
|
|
|
2013-04-23 08:02:18 -03:00
|
|
|
// Calculate the demanded control surface deflection
|
|
|
|
// Note the scaler is applied again. We want a 1/speed scaler applied to the feed-forward
|
|
|
|
// path, but want a 1/speed^2 scaler applied to the rate error path.
|
|
|
|
// This is because acceleration scales with speed^2, but rate scales with speed.
|
|
|
|
float _last_out = ( (rate_error * _kp_rate) + _integrator + (desired_rate * _kp_ff) ) * scaler;
|
|
|
|
|
|
|
|
// Convert to centi-degrees and constrain
|
|
|
|
float out = constrain(_last_out * 100, -4500, 4500);
|
|
|
|
return out;
|
2012-08-21 23:08:14 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
void AP_PitchController::reset_I()
|
|
|
|
{
|
|
|
|
_integrator = 0;
|
|
|
|
}
|