APM:Libraries
AP_AutoTune.cpp
Go to the documentation of this file.
1 /*
2  This program is free software: you can redistribute it and/or modify
3  it under the terms of the GNU General Public License as published by
4  the Free Software Foundation, either version 3 of the License, or
5  (at your option) any later version.
6 
7  This program is distributed in the hope that it will be useful,
8  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  GNU General Public License for more details.
11 
12  You should have received a copy of the GNU General Public License
13  along with this program. If not, see <http://www.gnu.org/licenses/>.
14  */
15 
34 #include "AP_AutoTune.h"
35 
36 #include <AP_Common/AP_Common.h>
37 #include <AP_HAL/AP_HAL.h>
38 #include <AP_Math/AP_Math.h>
39 
40 extern const AP_HAL::HAL& hal;
41 
42 // time in milliseconds between autotune saves
43 #define AUTOTUNE_SAVE_PERIOD 10000U
44 
45 // how much time we have to overshoot for to reduce gains
46 #define AUTOTUNE_OVERSHOOT_TIME 100
47 
48 // how much time we have to undershoot for to increase gains
49 #define AUTOTUNE_UNDERSHOOT_TIME 200
50 
51 // step size for increasing gains, percentage
52 #define AUTOTUNE_INCREASE_STEP 5
53 
54 // step size for decreasing gains, percentage
55 #define AUTOTUNE_DECREASE_STEP 8
56 
57 // min/max P gains
58 #define AUTOTUNE_MAX_P 5.0f
59 #define AUTOTUNE_MIN_P 0.3f
60 
61 // tau ranges
62 #define AUTOTUNE_MAX_TAU 0.7f
63 #define AUTOTUNE_MIN_TAU 0.2f
64 
65 #define AUTOTUNE_MIN_IMAX 2000
66 #define AUTOTUNE_MAX_IMAX 4000
67 
68 // constructor
70  const AP_Vehicle::FixedWing &parms,
71  DataFlash_Class &_dataflash) :
72  running(false),
73  current(_gains),
74  type(_type),
75  aparm(parms),
76  dataflash(_dataflash),
77  saturated_surfaces(false)
78 {}
79 
80 #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
81 #include <stdio.h>
82 # define Debug(fmt, args ...) do {::printf("%s:%d: " fmt "\n", __FUNCTION__, __LINE__, ## args); } while(0)
83 #else
84  # define Debug(fmt, args ...)
85 #endif
86 
87 /*
88  auto-tuning table. This table gives the starting values for key
89  tuning parameters based on a user chosen AUTOTUNE_LEVEL parameter
90  from 1 to 10. Level 1 is a very soft tune. Level 10 is a very
91  aggressive tune.
92  */
93 static const struct {
94  float tau;
95  float Dratio;
96  float rmax;
97 } tuning_table[] = {
98  { 0.70f, 0.050f, 20 }, // level 1
99  { 0.65f, 0.055f, 30 }, // level 2
100  { 0.60f, 0.060f, 40 }, // level 3
101  { 0.55f, 0.065f, 50 }, // level 4
102  { 0.50f, 0.070f, 60 }, // level 5
103  { 0.45f, 0.075f, 75 }, // level 6
104  { 0.40f, 0.080f, 90 }, // level 7
105  { 0.30f, 0.085f, 120 }, // level 8
106  { 0.20f, 0.090f, 160 }, // level 9
107  { 0.10f, 0.095f, 210 }, // level 10
108  { 0.05f, 0.100f, 300 }, // (yes, it goes to 11)
109 };
110 
111 /*
112  start an autotune session
113 */
115 {
116  running = true;
118  uint32_t now = AP_HAL::millis();
119 
120  state_enter_ms = now;
121  last_save_ms = now;
122 
123  last_save = current;
124  restore = current;
125 
126  uint8_t level = aparm.autotune_level;
127  if (level > ARRAY_SIZE(tuning_table)) {
128  level = ARRAY_SIZE(tuning_table);
129  }
130  if (level < 1) {
131  level = 1;
132  }
133 
134  current.rmax.set(tuning_table[level-1].rmax);
135  // D gain is scaled to a fixed ratio of P gain
136  current.D.set(tuning_table[level-1].Dratio * current.P);
137  current.tau.set(tuning_table[level-1].tau);
138 
140 
141  // force a fixed ratio of I to D gain on the rate feedback path
142  current.I = 0.5f * current.D / current.tau;
143 
144  next_save = current;
145 
146  Debug("START P -> %.3f\n", current.P.get());
147 }
148 
149 /*
150  called when we change state to see if we should change gains
151  */
153 {
154  if (running) {
155  running = false;
157  }
158 }
159 
160 
161 /*
162  one update cycle of the autotuner
163  */
164 void AP_AutoTune::update(float desired_rate, float achieved_rate, float servo_out)
165 {
166  if (!running) {
167  return;
168  }
169  check_save();
170 
171  // see what state we are in
172  ATState new_state;
173  float abs_desired_rate = fabsf(desired_rate);
174  uint32_t now = AP_HAL::millis();
175 
176  if (fabsf(servo_out) >= 45) {
177  // we have saturated the servo demand (not including
178  // integrator), we cannot get any information that would allow
179  // us to increase the gain
180  saturated_surfaces = true;
181  }
182 
183  if (abs_desired_rate < 0.8f * current.rmax) {
184  // we are not demanding max rate
185  new_state = DEMAND_UNSATURATED;
186  } else if (fabsf(achieved_rate) > abs_desired_rate) {
187  new_state = desired_rate > 0 ? DEMAND_OVER_POS : DEMAND_OVER_NEG;
188  } else {
189  new_state = desired_rate > 0 ? DEMAND_UNDER_POS : DEMAND_UNDER_NEG;
190  }
191  if (new_state != state) {
193  state = new_state;
194  state_enter_ms = now;
195  saturated_surfaces = false;
196  }
197  if (state != DEMAND_UNSATURATED) {
198  write_log(servo_out, desired_rate, achieved_rate);
199  }
200 }
201 
202 /*
203  called when we change state to see if we should change gains
204  */
205 void AP_AutoTune::check_state_exit(uint32_t state_time_ms)
206 {
207  switch (state) {
208  case DEMAND_UNSATURATED:
209  break;
210  case DEMAND_UNDER_POS:
211  case DEMAND_UNDER_NEG:
212  // we increase P if we have not saturated the surfaces during
213  // this state, and we have
214  if (state_time_ms >= AUTOTUNE_UNDERSHOOT_TIME && !saturated_surfaces) {
215  current.P.set(current.P * (100+AUTOTUNE_INCREASE_STEP) * 0.01f);
216  if (current.P > AUTOTUNE_MAX_P) {
218  }
219  Debug("UNDER P -> %.3f\n", current.P.get());
220  }
222  break;
223  case DEMAND_OVER_POS:
224  case DEMAND_OVER_NEG:
225  if (state_time_ms >= AUTOTUNE_OVERSHOOT_TIME) {
226  current.P.set(current.P * (100-AUTOTUNE_DECREASE_STEP) * 0.01f);
227  if (current.P < AUTOTUNE_MIN_P) {
229  }
230  Debug("OVER P -> %.3f\n", current.P.get());
231  }
233  break;
234  }
235 }
236 
237 /*
238  see if we should save new values
239  */
241 {
243  return;
244  }
245 
246  // save the next_save values, which are the autotune value from
247  // the last save period. This means the pilot has
248  // AUTOTUNE_SAVE_PERIOD milliseconds to decide they don't like the
249  // gains and switch out of autotune
250  ATGains tmp = current;
251 
253  Debug("SAVE P -> %.3f\n", current.P.get());
254 
255  // restore our current gains
256  current = tmp;
257 
258  // if the pilot exits autotune they get these saved values
259  restore = next_save;
260 
261  // the next values to save will be the ones we are flying now
262  next_save = current;
264 }
265 
266 /*
267  log a parameter change from autotune
268  */
269 void AP_AutoTune::log_param_change(float v, const char *suffix)
270 {
271  if (!dataflash.logging_started()) {
272  return;
273  }
274  char key[AP_MAX_NAME_SIZE+1];
275  if (type == AUTOTUNE_ROLL) {
276  strncpy(key, "RLL2SRV_", 9);
277  strncpy(&key[8], suffix, AP_MAX_NAME_SIZE-8);
278  } else {
279  strncpy(key, "PTCH2SRV_", 10);
280  strncpy(&key[9], suffix, AP_MAX_NAME_SIZE-9);
281  }
282  key[AP_MAX_NAME_SIZE] = 0;
284 }
285 
286 /*
287  set a float and save a float if it has changed by more than
288  0.1%. This reduces the number of insignificant EEPROM writes
289  */
290 void AP_AutoTune::save_float_if_changed(AP_Float &v, float value, const char *suffix)
291 {
292  float old_value = v.get();
293  v.set(value);
294  if (value <= 0 || fabsf((value-old_value)/value) > 0.001f) {
295  v.save();
296  log_param_change(v.get(), suffix);
297  }
298 }
299 
300 /*
301  set a int16 and save if changed
302  */
303 void AP_AutoTune::save_int16_if_changed(AP_Int16 &v, int16_t value, const char *suffix)
304 {
305  int16_t old_value = v.get();
306  v.set(value);
307  if (old_value != v.get()) {
308  v.save();
309  log_param_change(v.get(), suffix);
310  }
311 }
312 
313 
314 /*
315  save a set of gains
316  */
318 {
319  current = last_save;
320  save_float_if_changed(current.tau, v.tau, "TCONST");
321  save_float_if_changed(current.P, v.P, "P");
322  save_float_if_changed(current.I, v.I, "I");
323  save_float_if_changed(current.D, v.D, "D");
326  last_save = current;
327 }
328 
329 void AP_AutoTune::write_log(float servo, float demanded, float achieved)
330 {
331  if (!dataflash.logging_started()) {
332  return;
333  }
334 
335  struct log_ATRP pkt = {
337  time_us : AP_HAL::micros64(),
338  type : static_cast<uint8_t>(type),
339  state : (uint8_t)state,
340  servo : (int16_t)(servo*100),
341  demanded : demanded,
342  achieved : achieved,
343  P : current.P.get()
344  };
345  dataflash.WriteBlock(&pkt, sizeof(pkt));
346 }
void check_save(void)
float tau
Definition: AP_AutoTune.cpp:94
#define AP_MAX_NAME_SIZE
Definition: AP_Param.h:32
const AP_Vehicle::FixedWing & aparm
Definition: AP_AutoTune.h:60
ATType type
Definition: AP_AutoTune.h:58
AP_AutoTune(ATGains &_gains, ATType type, const AP_Vehicle::FixedWing &parms, DataFlash_Class &_dataflash)
Definition: AP_AutoTune.cpp:69
void update(float desired_rate, float achieved_rate, float servo_out)
bool logging_started(void)
Definition: DataFlash.cpp:548
#define AUTOTUNE_UNDERSHOOT_TIME
Definition: AP_AutoTune.cpp:49
void Log_Write_Parameter(const char *name, float value)
Definition: DataFlash.cpp:603
void WriteBlock(const void *pBuffer, uint16_t size)
Definition: DataFlash.cpp:481
float Dratio
Definition: AP_AutoTune.cpp:95
#define AUTOTUNE_MIN_P
Definition: AP_AutoTune.cpp:59
void save_float_if_changed(AP_Float &v, float value, const char *suffix)
ATGains restore
Definition: AP_AutoTune.h:68
bool running
Definition: AP_AutoTune.h:51
void stop(void)
ATGains last_save
Definition: AP_AutoTune.h:71
uint32_t state_enter_ms
Definition: AP_AutoTune.h:87
float rmax
Definition: AP_AutoTune.cpp:96
#define f(i)
#define AUTOTUNE_SAVE_PERIOD
Definition: AP_AutoTune.cpp:43
uint32_t millis()
Definition: system.cpp:157
#define AUTOTUNE_OVERSHOOT_TIME
Definition: AP_AutoTune.cpp:46
void save_int16_if_changed(AP_Int16 &v, int16_t value, const char *suffix)
uint64_t micros64()
Definition: system.cpp:162
static const struct @193 tuning_table[]
#define AUTOTUNE_MIN_IMAX
Definition: AP_AutoTune.cpp:65
enum AP_AutoTune::ATState state
float v
Definition: Printf.cpp:15
ATGains & current
Definition: AP_AutoTune.h:55
#define ARRAY_SIZE(_arr)
Definition: AP_Common.h:80
void check_state_exit(uint32_t state_time_ms)
ATGains next_save
Definition: AP_AutoTune.h:74
#define Debug(fmt, args ...)
Definition: AP_AutoTune.cpp:82
#define AUTOTUNE_INCREASE_STEP
Definition: AP_AutoTune.cpp:52
Common definitions and utility routines for the ArduPilot libraries.
void write_log(float servo, float demanded, float achieved)
float constrain_float(const float amt, const float low, const float high)
Definition: AP_Math.h:142
#define AUTOTUNE_DECREASE_STEP
Definition: AP_AutoTune.cpp:55
#define AUTOTUNE_MAX_IMAX
Definition: AP_AutoTune.cpp:66
void log_param_change(float v, const char *suffix)
DataFlash_Class & dataflash
Definition: AP_AutoTune.h:62
uint32_t last_save_ms
Definition: AP_AutoTune.h:77
bool saturated_surfaces
Definition: AP_AutoTune.h:65
#define AUTOTUNE_MAX_P
Definition: AP_AutoTune.cpp:58
void save_gains(const ATGains &v)
float value
const AP_HAL::HAL & hal
-*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
Definition: AC_PID_test.cpp:14
void start(void)
#define LOG_PACKET_HEADER_INIT(id)
Definition: LogStructure.h:8