update credits
[librepilot.git] / flight / modules / CameraStab / camerastab.c
blob43b57427807888fe1edc45c155f5c5682ab6e171
1 /**
2 ******************************************************************************
3 * @addtogroup OpenPilotModules OpenPilot Modules
4 * @{
5 * @addtogroup CameraStab Camera Stabilization Module
6 * @brief Camera stabilization module
7 * Updates accessory outputs with values appropriate for camera stabilization
8 * @{
10 * @file camerastab.c
11 * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
12 * @brief Stabilize camera against the roll pitch and yaw of aircraft
14 * @see The GNU Public License (GPL) Version 3
16 *****************************************************************************/
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 3 of the License, or
21 * (at your option) any later version.
23 * This program is distributed in the hope that it will be useful, but
24 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
25 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
26 * for more details.
28 * You should have received a copy of the GNU General Public License along
29 * with this program; if not, write to the Free Software Foundation, Inc.,
30 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
33 /**
34 * Output object: Accessory
36 * This module will periodically calculate the output values for stabilizing the camera
38 * UAVObjects are automatically generated by the UAVObjectGenerator from
39 * the object definition XML file.
41 * Modules have no API, all communication to other modules is done through UAVObjects.
42 * However modules may use the API exposed by shared libraries.
43 * See the OpenPilot wiki for more details.
44 * http://www.openpilot.org/OpenPilot_Application_Architecture
48 #include "openpilot.h"
50 #include "accessorydesired.h"
51 #include "attitudestate.h"
52 #include "mixersettings.h"
53 #include "actuatorcommand.h"
54 #include "camerastabsettings.h"
55 #include "cameradesired.h"
56 #include "hwsettings.h"
59 // Configuration
61 #define SAMPLE_PERIOD_MS 10
63 // Private types
65 // Private variables
66 static bool gimbalOutputEnabled = false;
68 static struct CameraStab_data {
69 portTickType lastSysTime;
70 float inputs[CAMERASTABSETTINGS_INPUT_NUMELEM];
72 #ifdef USE_GIMBAL_LPF
73 float attitudeFiltered[CAMERASTABSETTINGS_INPUT_NUMELEM];
74 #endif
76 #ifdef USE_GIMBAL_FF
77 float ffLastAttitude[CAMERASTABSETTINGS_INPUT_NUMELEM];
78 float ffLastAttitudeFiltered[CAMERASTABSETTINGS_INPUT_NUMELEM];
79 float ffFilterAccumulator[CAMERASTABSETTINGS_INPUT_NUMELEM];
80 #endif
81 } *csd;
83 // Private functions
84 static void attitudeUpdated(UAVObjEvent *ev);
86 #ifdef USE_GIMBAL_FF
87 static void applyFeedForward(uint8_t index, float dT, float *attitude, CameraStabSettingsData *cameraStab);
88 #endif
90 // this structure is equivalent to the UAVObjects for one mixer.
91 typedef struct {
92 uint8_t type;
93 int8_t matrix[5];
94 } __attribute__((packed)) Mixer_t;
97 /**
98 * Initialise the module, called on startup
99 * \returns 0 on success or -1 if initialisation failed
101 int32_t CameraStabInitialize(void)
103 bool cameraStabEnabled;
105 HwSettingsOptionalModulesData optionalModules;
107 HwSettingsOptionalModulesGet(&optionalModules);
109 #ifdef MODULE_CAMERASTAB_BUILTIN
110 cameraStabEnabled = true;
111 optionalModules.CameraStab = HWSETTINGS_OPTIONALMODULES_ENABLED;
112 HwSettingsOptionalModulesSet(&optionalModules);
113 #else
114 if (optionalModules.CameraStab == HWSETTINGS_OPTIONALMODULES_ENABLED) {
115 cameraStabEnabled = true;
116 } else {
117 cameraStabEnabled = false;
119 #endif
121 if (cameraStabEnabled) {
122 // allocate and initialize the static data storage only if module is enabled
123 csd = (struct CameraStab_data *)pios_malloc(sizeof(struct CameraStab_data));
124 if (!csd) {
125 return -1;
128 // initialize camera state variables
129 memset(csd, 0, sizeof(struct CameraStab_data));
130 csd->lastSysTime = xTaskGetTickCount();
132 AttitudeStateInitialize();
133 CameraDesiredInitialize();
135 UAVObjEvent ev = {
136 .obj = AttitudeStateHandle(),
137 .instId = 0,
138 .event = 0,
139 .lowPriority = false,
141 EventPeriodicCallbackCreate(&ev, attitudeUpdated, SAMPLE_PERIOD_MS / portTICK_RATE_MS);
143 return 0;
146 return -1;
149 /* stub: module has no module thread */
150 int32_t CameraStabStart(void)
152 return 0;
155 MODULE_INITCALL(CameraStabInitialize, CameraStabStart);
157 static void attitudeUpdated(UAVObjEvent *ev)
159 if (ev->obj != AttitudeStateHandle()) {
160 return;
163 if (!gimbalOutputEnabled) {
164 MixerSettingsData mixerSettings;
165 MixerSettingsGet(&mixerSettings);
166 Mixer_t *mixers = (Mixer_t *)&mixerSettings.Mixer1Type;
167 for (int ct = 0; ct < ACTUATORCOMMAND_CHANNEL_NUMELEM; ct++) {
168 uint8_t mixer_type = mixers[ct].type;
169 if ((mixer_type >= MIXERSETTINGS_MIXER1TYPE_CAMERAROLLORSERVO1) &&
170 (mixer_type <= MIXERSETTINGS_MIXER1TYPE_CAMERAYAW)) {
171 gimbalOutputEnabled = true;
174 return;
177 AccessoryDesiredData accessory;
179 CameraStabSettingsData cameraStab;
180 CameraStabSettingsGet(&cameraStab);
182 // check how long since last update, time delta between calls in ms
183 portTickType thisSysTime = xTaskGetTickCount();
184 float dT_millis = (thisSysTime > csd->lastSysTime) ?
185 (float)((thisSysTime - csd->lastSysTime) * portTICK_RATE_MS) :
186 (float)SAMPLE_PERIOD_MS;
187 csd->lastSysTime = thisSysTime;
189 // storage for elevon roll component before the pitch component has been generated
190 // we are guaranteed that the iteration order of i is roll pitch yaw
191 // that guarnteees this won't be used uninited, but the compiler doesn't know that
192 // so we init it or turn the warning/error off for each compiler
193 float elevon_roll = 0.0f;
195 // process axes
196 for (uint8_t i = 0; i < CAMERASTABSETTINGS_INPUT_NUMELEM; i++) {
197 // read and process control input
198 if (CameraStabSettingsInputToArray(cameraStab.Input)[i] != CAMERASTABSETTINGS_INPUT_NONE) {
199 if (AccessoryDesiredInstGet(CameraStabSettingsInputToArray(cameraStab.Input)[i] -
200 CAMERASTABSETTINGS_INPUT_ACCESSORY0, &accessory) == 0) {
201 float input_rate;
202 switch (CameraStabSettingsStabilizationModeToArray(cameraStab.StabilizationMode)[i]) {
203 case CAMERASTABSETTINGS_STABILIZATIONMODE_ATTITUDE:
204 csd->inputs[i] = accessory.AccessoryVal *
205 CameraStabSettingsInputRangeToArray(cameraStab.InputRange)[i];
206 break;
207 case CAMERASTABSETTINGS_STABILIZATIONMODE_AXISLOCK:
208 input_rate = accessory.AccessoryVal *
209 CameraStabSettingsInputRateToArray(cameraStab.InputRate)[i];
210 if (fabsf(input_rate) > cameraStab.MaxAxisLockRate) {
211 csd->inputs[i] = boundf(csd->inputs[i] + input_rate * 0.001f * dT_millis,
212 -CameraStabSettingsInputRangeToArray(cameraStab.InputRange)[i],
213 CameraStabSettingsInputRangeToArray(cameraStab.InputRange)[i]);
215 break;
216 default:
217 PIOS_Assert(0);
222 // calculate servo output
223 float attitude;
225 switch (i) {
226 case CAMERASTABSETTINGS_INPUT_ROLL:
227 AttitudeStateRollGet(&attitude);
228 break;
229 case CAMERASTABSETTINGS_INPUT_PITCH:
230 AttitudeStatePitchGet(&attitude);
231 break;
232 case CAMERASTABSETTINGS_INPUT_YAW:
233 AttitudeStateYawGet(&attitude);
234 break;
235 default:
236 PIOS_Assert(0);
239 #ifdef USE_GIMBAL_LPF
240 if (CameraStabSettingsResponseTimeToArray(cameraStab.ResponseTime)[i]) {
241 float rt = (float)CameraStabSettingsResponseTimeToArray(cameraStab.ResponseTime)[i];
242 attitude = csd->attitudeFiltered[i] = ((rt * csd->attitudeFiltered[i]) + (dT_millis * attitude)) / (rt + dT_millis);
244 #endif
246 #ifdef USE_GIMBAL_FF
247 if (CameraStabSettingsFeedForwardToArray(cameraStab.FeedForward)[i]) {
248 applyFeedForward(i, dT_millis, &attitude, &cameraStab);
250 #endif
252 // bounding for elevon mixing occurs on the unmixed output
253 // to limit the range of the mixed output you must limit the range
254 // of both the unmixed pitch and unmixed roll
255 float output = boundf((attitude + csd->inputs[i]) / CameraStabSettingsOutputRangeToArray(cameraStab.OutputRange)[i], -1.0f, 1.0f);
257 // set output channels
258 switch (i) {
259 case CAMERASTABSETTINGS_INPUT_ROLL:
260 // we are guaranteed that the iteration order of i is roll pitch yaw
261 // for elevon mixing we simply grab the value for later use
262 if (cameraStab.GimbalType == CAMERASTABSETTINGS_GIMBALTYPE_ROLLPITCHMIXED) {
263 elevon_roll = output;
264 } else {
265 CameraDesiredRollOrServo1Set(&output);
267 break;
268 case CAMERASTABSETTINGS_INPUT_PITCH:
269 // we are guaranteed that the iteration order of i is roll pitch yaw
270 // for elevon mixing we use the value we previously grabbed and set both s1 and s2
271 if (cameraStab.GimbalType == CAMERASTABSETTINGS_GIMBALTYPE_ROLLPITCHMIXED) {
272 float elevon_pitch = output;
273 // elevon reversing works like this:
274 // first use the normal reversing facilities to get servo 1 roll working in the correct direction
275 // then use the normal reversing facilities to get servo 2 roll working in the correct direction
276 // then use these new reversing switches to reverse servo 1 and/or 2 pitch as needed
277 // if servo 1 pitch is reversed
278 if (cameraStab.Servo1PitchReverse == CAMERASTABSETTINGS_SERVO1PITCHREVERSE_TRUE) {
279 // use (reversed pitch) + roll
280 output = ((1.0f - elevon_pitch) + elevon_roll) / 2.0f;
281 } else {
282 // use pitch + roll
283 output = (elevon_pitch + elevon_roll) / 2.0f;
285 CameraDesiredRollOrServo1Set(&output);
286 // if servo 2 pitch is reversed
287 if (cameraStab.Servo2PitchReverse == CAMERASTABSETTINGS_SERVO2PITCHREVERSE_TRUE) {
288 // use (reversed pitch) - roll
289 output = ((1.0f - elevon_pitch) - elevon_roll) / 2.0f;
290 } else {
291 // use pitch - roll
292 output = (elevon_pitch - elevon_roll) / 2.0f;
294 CameraDesiredPitchOrServo2Set(&output);
295 } else {
296 CameraDesiredPitchOrServo2Set(&output);
298 break;
299 case CAMERASTABSETTINGS_INPUT_YAW:
300 CameraDesiredYawSet(&output);
301 break;
302 default:
303 PIOS_Assert(0);
308 #ifdef USE_GIMBAL_FF
309 void applyFeedForward(uint8_t index, float dT_millis, float *attitude, CameraStabSettingsData *cameraStab)
311 // compensate high feed forward values depending on gimbal type
312 float gimbalTypeCorrection = 1.0f;
314 switch (cameraStab->GimbalType) {
315 case CAMERASTABSETTINGS_GIMBALTYPE_GENERIC:
316 case CAMERASTABSETTINGS_GIMBALTYPE_ROLLPITCHMIXED:
317 // no correction
318 break;
319 case CAMERASTABSETTINGS_GIMBALTYPE_YAWROLLPITCH:
320 if (index == CAMERASTABSETTINGS_INPUT_ROLL) {
321 float pitch;
322 AttitudeStatePitchGet(&pitch);
323 gimbalTypeCorrection = (cameraStab->OutputRange.Pitch - fabsf(pitch))
324 / cameraStab->OutputRange.Pitch;
326 break;
327 case CAMERASTABSETTINGS_GIMBALTYPE_YAWPITCHROLL:
328 if (index == CAMERASTABSETTINGS_INPUT_PITCH) {
329 float roll;
330 AttitudeStateRollGet(&roll);
331 gimbalTypeCorrection = (cameraStab->OutputRange.Roll - fabsf(roll))
332 / cameraStab->OutputRange.Roll;
334 break;
335 default:
336 PIOS_Assert(0);
339 // apply feed forward
340 float accumulator = csd->ffFilterAccumulator[index];
341 accumulator += (*attitude - csd->ffLastAttitude[index]) *
342 (float)CameraStabSettingsFeedForwardToArray(cameraStab->FeedForward)[index] * gimbalTypeCorrection;
343 csd->ffLastAttitude[index] = *attitude;
344 *attitude += accumulator;
346 float filter = (float)((accumulator > 0.0f) ? CameraStabSettingsAccelTimeToArray(cameraStab->AccelTime)[index] :
347 CameraStabSettingsDecelTimeToArray(cameraStab->DecelTime)[index]) / dT_millis;
348 if (filter < 1.0f) {
349 filter = 1.0f;
351 accumulator -= accumulator / filter;
352 csd->ffFilterAccumulator[index] = accumulator;
353 *attitude += accumulator;
355 // apply acceleration limit
356 float delta = *attitude - csd->ffLastAttitudeFiltered[index];
357 float maxDelta = (float)cameraStab->MaxAccel * 0.001f * dT_millis;
359 if (fabsf(delta) > maxDelta) {
360 // we are accelerating too hard
361 *attitude = csd->ffLastAttitudeFiltered[index] + ((delta > 0.0f) ? maxDelta : -maxDelta);
363 csd->ffLastAttitudeFiltered[index] = *attitude;
365 #endif // USE_GIMBAL_FF
368 * @}
372 * @}