2 ******************************************************************************
3 * @addtogroup OpenPilotModules OpenPilot Modules
5 * @addtogroup StabilizationModule Stabilization Module
6 * @brief Relay tuning controller
7 * @note This object updates the @ref ActuatorDesired "Actuator Desired" based on the
8 * PID loops on the @ref AttitudeDesired "Attitude Desired" and @ref AttitudeState "Attitude State"
11 * @file stabilization.c
12 * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
13 * @brief Attitude stabilization module.
15 * @see The GNU Public License (GPL) Version 3
17 *****************************************************************************/
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 3 of the License, or
22 * (at your option) any later version.
24 * This program is distributed in the hope that it will be useful, but
25 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
26 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
29 * You should have received a copy of the GNU General Public License along
30 * with this program; if not, write to the Free Software Foundation, Inc.,
31 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
34 #include "openpilot.h"
35 #include "stabilization.h"
36 #include "relaytuning.h"
37 #include "relaytuningsettings.h"
38 #include "sin_lookup.h"
41 * Apply a step function for the stabilization controller and monitor the
44 * Used to Replace the rate PID with a relay to measure the critical properties of this axis
45 * i.e. period and gain
47 int stabilization_relay_rate(float error
, float *output
, int axis
, bool reinit
)
49 RelayTuningData relay
;
51 RelayTuningGet(&relay
);
53 static portTickType lastHighTime
;
54 static portTickType lastLowTime
;
56 static float accum_sin
, accum_cos
;
57 static uint32_t accumulated
= 0;
59 const uint16_t DEGLITCH_TIME
= 20; // ms
60 const float AMPLITUDE_ALPHA
= 0.95f
;
61 const float PERIOD_ALPHA
= 0.95f
;
63 portTickType thisTime
= xTaskGetTickCount();
65 static bool rateRelayRunning
[3];
67 // This indicates the current estimate of the smoothed error. So when it is high
68 // we are waiting for it to go low.
69 static bool high
= false;
71 // On first run initialize estimates to something reasonable
73 rateRelayRunning
[axis
] = false;
74 RelayTuningPeriodToArray(relay
.Period
)[axis
] = 200;
75 RelayTuningGainToArray(relay
.Gain
)[axis
] = 0;
81 // These should get reinitialized anyway
83 lastHighTime
= thisTime
;
84 lastLowTime
= thisTime
;
85 RelayTuningSet(&relay
);
89 RelayTuningSettingsData relaySettings
;
90 RelayTuningSettingsGet(&relaySettings
);
92 // Compute output, simple threshold on error
93 *output
= high
? relaySettings
.Amplitude
: -relaySettings
.Amplitude
;
95 /**** The code below here is to estimate the properties of the oscillation ****/
97 // Make sure the period can't go below limit
98 if (RelayTuningPeriodToArray(relay
.Period
)[axis
] < DEGLITCH_TIME
) {
99 RelayTuningPeriodToArray(relay
.Period
)[axis
] = DEGLITCH_TIME
;
102 // Project the error onto a sine and cosine of the same frequency
103 // to accumulate the average amplitude
104 int32_t dT
= thisTime
- lastHighTime
;
105 float phase
= ((float)360 * (float)dT
) / RelayTuningPeriodToArray(relay
.Period
)[axis
];
109 accum_sin
+= sin_lookup_deg(phase
) * error
;
110 accum_cos
+= cos_lookup_deg(phase
) * error
;
113 // Make sure we've had enough time since last transition then check for a change in the output
114 bool time_hysteresis
= (high
? (thisTime
- lastHighTime
) : (thisTime
- lastLowTime
)) > DEGLITCH_TIME
;
116 if (!high
&& time_hysteresis
&& error
> relaySettings
.HysteresisThresh
) {
117 /* POSITIVE CROSSING DETECTED */
119 float this_amplitude
= 2 * sqrtf(accum_sin
* accum_sin
+ accum_cos
* accum_cos
) / accumulated
;
120 float this_gain
= this_amplitude
/ relaySettings
.Amplitude
;
126 if (rateRelayRunning
[axis
] == false) {
127 rateRelayRunning
[axis
] = true;
128 RelayTuningPeriodToArray(relay
.Period
)[axis
] = 200;
129 RelayTuningGainToArray(relay
.Gain
)[axis
] = 0;
131 // Low pass filter each amplitude and period
132 RelayTuningGainToArray(relay
.Gain
)[axis
] =
133 RelayTuningGainToArray(relay
.Gain
)[axis
] *
134 AMPLITUDE_ALPHA
+ this_gain
* (1 - AMPLITUDE_ALPHA
);
135 RelayTuningPeriodToArray(relay
.Period
)[axis
] =
136 RelayTuningPeriodToArray(relay
.Period
)[axis
] *
137 PERIOD_ALPHA
+ dT
* (1 - PERIOD_ALPHA
);
139 lastHighTime
= thisTime
;
141 RelayTuningSet(&relay
);
142 } else if (high
&& time_hysteresis
&& error
< -relaySettings
.HysteresisThresh
) {
143 /* FALLING CROSSING DETECTED */
145 lastLowTime
= thisTime
;