Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Ardupilot
GitHub Repository: Ardupilot/ardupilot
Path: blob/master/libraries/AP_AccelCal/AP_AccelCal.cpp
9378 views
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
This program is distributed in the hope that it will be useful,
7
but WITHOUT ANY WARRANTY; without even the implied warranty of
8
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9
GNU General Public License for more details.
10
You should have received a copy of the GNU General Public License
11
along with this program. If not, see <http://www.gnu.org/licenses/>.
12
*/
13
14
#include "AP_AccelCal.h"
15
16
#if HAL_INS_ACCELCAL_ENABLED
17
18
#include <stdarg.h>
19
#include <AP_HAL/AP_HAL.h>
20
#include <GCS_MAVLink/GCS.h>
21
22
#define AP_ACCELCAL_POSITION_REQUEST_INTERVAL_MS 1000
23
24
#define _printf(fmt, args ...) do { \
25
if (_gcs != nullptr) { \
26
_gcs->send_text(MAV_SEVERITY_CRITICAL, fmt, ## args); \
27
} \
28
} while (0)
29
30
31
const extern AP_HAL::HAL& hal;
32
static bool _start_collect_sample;
33
34
uint8_t AP_AccelCal::_num_clients = 0;
35
AP_AccelCal_Client* AP_AccelCal::_clients[AP_ACCELCAL_MAX_NUM_CLIENTS] {};
36
37
void AP_AccelCal::update()
38
{
39
if (!get_calibrator(0)) {
40
// no calibrators
41
return;
42
}
43
44
if (_started) {
45
update_status();
46
47
uint8_t num_active_calibrators = 0;
48
for(uint8_t i=0; get_calibrator(i) != nullptr; i++) {
49
num_active_calibrators++;
50
}
51
if (num_active_calibrators != _num_active_calibrators) {
52
fail();
53
return;
54
}
55
if(_start_collect_sample) {
56
collect_sample();
57
}
58
AccelCalibrator *cal;
59
switch(_status) {
60
case ACCEL_CAL_NOT_STARTED:
61
fail();
62
return;
63
case ACCEL_CAL_WAITING_FOR_ORIENTATION: {
64
// if we're waiting for orientation, first ensure that all calibrators are on the same step
65
uint8_t step;
66
if ((cal = get_calibrator(0)) == nullptr) {
67
fail();
68
return;
69
}
70
step = cal->get_num_samples_collected()+1;
71
72
for(uint8_t i=1 ; (cal = get_calibrator(i)) ; i++) {
73
if (step != cal->get_num_samples_collected()+1) {
74
fail();
75
return;
76
}
77
}
78
// if we're on a new step, print a message describing the step
79
if (step != _step) {
80
_step = step;
81
82
if(_use_gcs_snoop) {
83
const char *msg;
84
switch (step) {
85
case ACCELCAL_VEHICLE_POS_LEVEL:
86
msg = "level";
87
break;
88
case ACCELCAL_VEHICLE_POS_LEFT:
89
msg = "on its LEFT side";
90
break;
91
case ACCELCAL_VEHICLE_POS_RIGHT:
92
msg = "on its RIGHT side";
93
break;
94
case ACCELCAL_VEHICLE_POS_NOSEDOWN:
95
msg = "nose DOWN";
96
break;
97
case ACCELCAL_VEHICLE_POS_NOSEUP:
98
msg = "nose UP";
99
break;
100
case ACCELCAL_VEHICLE_POS_BACK:
101
msg = "on its BACK";
102
break;
103
default:
104
fail();
105
return;
106
}
107
_printf("Place vehicle %s and press any key.", msg);
108
_waiting_for_mavlink_ack = true;
109
}
110
}
111
112
uint32_t now = AP_HAL::millis();
113
if (now - _last_position_request_ms > AP_ACCELCAL_POSITION_REQUEST_INTERVAL_MS) {
114
_last_position_request_ms = now;
115
_gcs->send_accelcal_vehicle_position(step);
116
}
117
break;
118
}
119
case ACCEL_CAL_COLLECTING_SAMPLE:
120
// check for timeout
121
122
for(uint8_t i=0; (cal = get_calibrator(i)); i++) {
123
cal->check_for_timeout();
124
}
125
126
update_status();
127
128
if (_status == ACCEL_CAL_FAILED) {
129
fail();
130
}
131
return;
132
case ACCEL_CAL_SUCCESS:
133
// save
134
if (_saving) {
135
bool done = true;
136
for(uint8_t i=0; i<_num_clients; i++) {
137
if (client_active(i) && _clients[i]->_acal_get_saving()) {
138
done = false;
139
break;
140
}
141
}
142
if (done) {
143
success();
144
}
145
return;
146
} else {
147
for(uint8_t i=0; i<_num_clients; i++) {
148
if(client_active(i) && _clients[i]->_acal_get_fail()) {
149
fail();
150
return;
151
}
152
}
153
for(uint8_t i=0; i<_num_clients; i++) {
154
if(client_active(i)) {
155
_clients[i]->_acal_save_calibrations();
156
}
157
}
158
_saving = true;
159
}
160
return;
161
default:
162
case ACCEL_CAL_FAILED:
163
fail();
164
return;
165
}
166
} else if (_last_result != ACCEL_CAL_NOT_STARTED) {
167
// only continuously report if we have ever completed a calibration
168
uint32_t now = AP_HAL::millis();
169
if (now - _last_position_request_ms > AP_ACCELCAL_POSITION_REQUEST_INTERVAL_MS) {
170
_last_position_request_ms = now;
171
switch (_last_result) {
172
case ACCEL_CAL_SUCCESS:
173
_gcs->send_accelcal_vehicle_position(ACCELCAL_VEHICLE_POS_SUCCESS);
174
break;
175
case ACCEL_CAL_FAILED:
176
_gcs->send_accelcal_vehicle_position(ACCELCAL_VEHICLE_POS_FAILED);
177
break;
178
default:
179
// should never hit this state
180
break;
181
}
182
}
183
}
184
}
185
186
void AP_AccelCal::start(GCS_MAVLINK *gcs, uint8_t sysid, uint8_t compid)
187
{
188
if (gcs == nullptr || _started) {
189
return;
190
}
191
_start_collect_sample = false;
192
_num_active_calibrators = 0;
193
194
AccelCalibrator *cal;
195
for(uint8_t i=0; (cal = get_calibrator(i)); i++) {
196
cal->clear();
197
cal->start(ACCEL_CAL_AXIS_ALIGNED_ELLIPSOID, 6, 0.5f);
198
_num_active_calibrators++;
199
}
200
201
_started = true;
202
_saving = false;
203
_gcs = gcs;
204
_sysid = sysid;
205
_compid = compid;
206
_use_gcs_snoop = true;
207
_last_position_request_ms = 0;
208
_step = 0;
209
210
_last_result = ACCEL_CAL_NOT_STARTED;
211
212
update_status();
213
}
214
215
void AP_AccelCal::success()
216
{
217
_printf("Calibration successful");
218
219
for(uint8_t i=0 ; i < _num_clients ; i++) {
220
_clients[i]->_acal_event_success();
221
}
222
223
_last_result = ACCEL_CAL_SUCCESS;
224
225
clear();
226
}
227
228
void AP_AccelCal::cancel()
229
{
230
_printf("Calibration cancelled");
231
232
for(uint8_t i=0 ; i < _num_clients ; i++) {
233
_clients[i]->_acal_event_cancellation();
234
}
235
236
_last_result = ACCEL_CAL_NOT_STARTED;
237
238
clear();
239
}
240
241
void AP_AccelCal::fail()
242
{
243
_printf("Calibration FAILED");
244
245
for(uint8_t i=0 ; i < _num_clients ; i++) {
246
_clients[i]->_acal_event_failure();
247
}
248
249
_last_result = ACCEL_CAL_FAILED;
250
251
clear();
252
}
253
254
void AP_AccelCal::clear()
255
{
256
if (!_started) {
257
return;
258
}
259
260
AccelCalibrator *cal;
261
for(uint8_t i=0 ; (cal = get_calibrator(i)) ; i++) {
262
cal->clear();
263
}
264
265
_step = 0;
266
_started = false;
267
_saving = false;
268
269
update_status();
270
}
271
272
void AP_AccelCal::collect_sample()
273
{
274
if (_status != ACCEL_CAL_WAITING_FOR_ORIENTATION) {
275
return;
276
}
277
278
for(uint8_t i=0; i<_num_clients; i++) {
279
if (client_active(i) && !_clients[i]->_acal_get_ready_to_sample()) {
280
_printf("Not ready to sample");
281
return;
282
}
283
}
284
285
AccelCalibrator *cal;
286
for(uint8_t i=0 ; (cal = get_calibrator(i)) ; i++) {
287
cal->collect_sample();
288
}
289
_start_collect_sample = false;
290
update_status();
291
}
292
293
void AP_AccelCal::register_client(AP_AccelCal_Client* client) {
294
if (client == nullptr || _num_clients >= AP_ACCELCAL_MAX_NUM_CLIENTS) {
295
return;
296
}
297
298
299
for(uint8_t i=0; i<_num_clients; i++) {
300
if(_clients[i] == client) {
301
return;
302
}
303
}
304
_clients[_num_clients] = client;
305
_num_clients++;
306
}
307
308
AccelCalibrator* AP_AccelCal::get_calibrator(uint8_t index) {
309
AccelCalibrator* ret;
310
for(uint8_t i=0; i<_num_clients; i++) {
311
for(uint8_t j=0 ; (ret = _clients[i]->_acal_get_calibrator(j)) ; j++) {
312
if (index == 0) {
313
return ret;
314
}
315
index--;
316
}
317
}
318
return nullptr;
319
}
320
321
void AP_AccelCal::update_status() {
322
AccelCalibrator *cal;
323
324
if (!get_calibrator(0)) {
325
// no calibrators
326
_status = ACCEL_CAL_NOT_STARTED;
327
return;
328
}
329
330
for(uint8_t i=0 ; (cal = get_calibrator(i)) ; i++) {
331
if (cal->get_status() == ACCEL_CAL_FAILED) {
332
_status = ACCEL_CAL_FAILED; //fail if even one of the calibration has
333
return;
334
}
335
}
336
337
for(uint8_t i=0 ; (cal = get_calibrator(i)) ; i++) {
338
if (cal->get_status() == ACCEL_CAL_COLLECTING_SAMPLE) {
339
_status = ACCEL_CAL_COLLECTING_SAMPLE; // move to Collecting sample state if all the callibrators have
340
return;
341
}
342
}
343
344
for(uint8_t i=0 ; (cal = get_calibrator(i)) ; i++) {
345
if (cal->get_status() == ACCEL_CAL_WAITING_FOR_ORIENTATION) {
346
_status = ACCEL_CAL_WAITING_FOR_ORIENTATION; // move to waiting for user ack for orientation confirmation
347
return;
348
}
349
}
350
351
for(uint8_t i=0 ; (cal = get_calibrator(i)) ; i++) {
352
if (cal->get_status() == ACCEL_CAL_NOT_STARTED) {
353
_status = ACCEL_CAL_NOT_STARTED; // we haven't started if all the calibrators haven't
354
return;
355
}
356
}
357
358
_status = ACCEL_CAL_SUCCESS; // we have succeeded calibration if all the calibrators have
359
}
360
361
bool AP_AccelCal::client_active(uint8_t client_num)
362
{
363
return (bool)_clients[client_num]->_acal_get_calibrator(0);
364
}
365
366
#if HAL_GCS_ENABLED
367
void AP_AccelCal::handle_command_ack(const mavlink_command_ack_t &packet, uint8_t src_sysid, uint8_t src_compid)
368
{
369
if(_sysid != src_sysid || _compid != src_compid) {
370
return;
371
}
372
373
if (!_waiting_for_mavlink_ack) {
374
return;
375
}
376
// this is support for the old, non-accelcal-specific calibration.
377
// The GCS is expected to send back a COMMAND_ACK when the vehicle
378
// is posed, but we placed no constraints on the result code or
379
// the command field in the ack packet. That meant that any ACK
380
// would move the cal process forward - and since we don't even
381
// check the source system/component here the process could easily
382
// fail due to other ACKs floating around the mavlink network.
383
// GCSs should be moved to using the non-gcs-snoop method. As a
384
// round-up:
385
// MAVProxy: command=1-6 depending on pose, result=1
386
// QGC: command=0, result=1
387
// MissionPlanner: uses new ACCELCAL_VEHICLE_POS
388
if (packet.command > 6) {
389
// not an acknowledgement for a vehicle position
390
return;
391
}
392
if (packet.result != MAV_RESULT_TEMPORARILY_REJECTED) {
393
// not an acknowledgement for a vehicle position
394
return;
395
}
396
_waiting_for_mavlink_ack = false;
397
_start_collect_sample = true;
398
}
399
400
bool AP_AccelCal::gcs_vehicle_position(float position)
401
{
402
_use_gcs_snoop = false;
403
404
if (_status == ACCEL_CAL_WAITING_FOR_ORIENTATION && is_equal((float) _step, position)) {
405
_start_collect_sample = true;
406
return true;
407
}
408
409
return false;
410
}
411
#endif
412
413
// true if we are in a calibration process
414
bool AP_AccelCal::running(void) const
415
{
416
return _status == ACCEL_CAL_WAITING_FOR_ORIENTATION || _status == ACCEL_CAL_COLLECTING_SAMPLE;
417
}
418
#endif //HAL_INS_ACCELCAL_ENABLED
419
420