Изучая спецификацию (datasheet) на МЕМС-датчик (акселерометр, гироскоп и проч.) мы сталкиваемся с такой процедурой, как самопроверка (self-test) или самодиагностика. Обычно в спецификациях есть описание, как это делать. Кому интересно: что это и как это правильно делать? — добро пожаловать под кат.

Самопроверка позволяет определить, что данные с датчика показывают то, что должны показывать. Датчики не всегда имели режим самодиагностики. Чтобы определить соответствие измеряемых данных тому, что указано в спецификации, разработчик должен был проверять это специальными экспериментами. В случае акселерометра — совершать наклоны на определённые углы и снимать изменение показаний. Анализируя результаты, можно было оценить погрешность, выдаваемую датчиком. Если показатели не соответствовали спецификации, датчик признавался бракованным.
Это усложняло массовое производство и во все датчики стали включать процедуру самодиагностики. Замысел процедуры достаточно прост — создать условия, при которых мы будем знать, что должен показывать датчик, и сравнить с тем, что он показывает. Отклонения выше заданных забраковывают датчик. Для компасов делают элемент, создающий магнитное поле (прямо в корпусе датчика). Включение режима самодиагностики включает этот элемент — эталонный источник. Для акселерометра и гироскопа элемент генерирует электростатическое поле, которое отклоняет грузик (см. фото ниже) и имитирует физическое движение или вращение. Величина смещения известна, соответственно известна величина, которую должен показать датчик. Если погрешность снятых показаний в заявленных пределах — значит с датчиком всё в порядке и он работает в соответствии со спецификацией.

(статья из интернет-журнала, откуда я позаимствовал картинку содержит ещё много красивых увеличенных фоток, ссылки см. внизу)
Рассмотрим акселерометр. Как указано ранее, в режиме самодиагностики на микромеханический грузик воздействует электростатическая сила, отклоняющая его на определённое расстояние, и имитирующая тем самым ускорение. Однако ожидаемую величину на выходе датчика мы не увидим. Дело в том, что в спокойном состоянии на грузик действуют другие силы (напр. сила тяжести). Они дополнительно отклоняют грузик.
Для того, чтобы учесть внешние силы, в процедуре самопроверки снимаются показания датчика в обычном режиме. Затем включается источник отклонения (переход в режим «самопроверка») и снова снимаются показания датчика.
aнорм = aвнешние
aST = aвнешние + aвнутр. ист.
Разница показаний этих двух режимов должна показывать величину с эталонного источника. Если величина находится в допустимом спецификацией диапазоне, то это значит, что датчик работает должным образом.
aвнутр. ист. = aST — aнорм
MIN < aвнутр. ист. < MAX
Отсюда важный вывод — внешние силы в обоих режимах должны быть равны. Т. е. датчик должен быть неподвижен. Когда устройство в руках — оно подвижно. Когда вы что-то нажимаете (или заканчиваете нажимать) на устройстве — оно тоже подвижно. Если на устройство действует вибрация — оно подвижно. И так далее.
При проведении самопроверки датчика также необходимо учитывать:
Величины отклонения грузика эталонным источником по осям:
Величина отклонения обычно указывается в условных единицах датчика — LSB. Это то, что в выходном регистре лежит. Рассмотрим значения, которые даёт внутренний источник датчика LIS302DL (датчик 8-мибитный):

Он интересен тем, что как раз содержит два режима работы внутреннего источника. Глядя в таблицу можно подумать, что значения приведены для обоих направлений внутреннего источника. Однако на самом деле это расширенный диапазон для одного направления — «Self test P». Обычно направление одно и значения указывают так:

Либо могут указать в измеряемых единицах:

Чтобы из значений датчика получить измеряемую величину, нужно воспользоваться формулой:
Величина_в_единицах_измерения = Значение_в_LSB * Чувствительность.
Чтобы перевести измеряемую величину обратно в единицы датчика, нужно воспользоваться формулой:
Значение_в_LSB = Величина_в_единицах_измерения / Чувствительность.
Чувствительность в спецификациях пишут так:

или так

Можно чувствительность посчитать самостоятельно:
Чувствительность = Измеряемый_диапазон / разрешение
Пример: Величина_LSB = Величина_измеряемая * Разрешение / Измеряемый_диапазон = 8 * 0,001 * g * 65536 / 4 * g = 131,072, т. е. ±131.
Допустимый диапазон может быть указан несколькими способами:
Уровень шума обычно указывают через среднеквадратичное отклонение (RMS). Диапазон погрешности будет тогда равен удвоенному среднеквадратичному отклонению (±). Примеры:


В первом примере указан корень из спектральной плотности мощности шумов. Чтобы получить СКО шума, следует значение умножить на корень из полосы фильтра, который у вас будет стоять по выходу датчика. Например, при полосе 25 Гц получите 750 ug в качестве СКО (спасибо Korogodin за подсказку). Если хотим получить значение без учёта фильтрации, берём частоту замеров. В данном случае при 100Гц получается 1,5mg. Во втором и третьем примере указан максимальный уровень шума. Например 8 * 0,001 * g = 0,0784 м/с2. Значит возможный разбег величины самопроверки = ±0,0784 м/с2. Здесь тоже нужно быть внимательным, т. к. иногда уровень шума указывают с включенным фильтром. Это значит, что замеры нужно делать с теми же настройками фильтра.
Примеры самодиагностики:

Введение
Самопроверка позволяет определить, что данные с датчика показывают то, что должны показывать. Датчики не всегда имели режим самодиагностики. Чтобы определить соответствие измеряемых данных тому, что указано в спецификации, разработчик должен был проверять это специальными экспериментами. В случае акселерометра — совершать наклоны на определённые углы и снимать изменение показаний. Анализируя результаты, можно было оценить погрешность, выдаваемую датчиком. Если показатели не соответствовали спецификации, датчик признавался бракованным.
Это усложняло массовое производство и во все датчики стали включать процедуру самодиагностики. Замысел процедуры достаточно прост — создать условия, при которых мы будем знать, что должен показывать датчик, и сравнить с тем, что он показывает. Отклонения выше заданных забраковывают датчик. Для компасов делают элемент, создающий магнитное поле (прямо в корпусе датчика). Включение режима самодиагностики включает этот элемент — эталонный источник. Для акселерометра и гироскопа элемент генерирует электростатическое поле, которое отклоняет грузик (см. фото ниже) и имитирует физическое движение или вращение. Величина смещения известна, соответственно известна величина, которую должен показать датчик. Если погрешность снятых показаний в заявленных пределах — значит с датчиком всё в порядке и он работает в соответствии со спецификацией.

(статья из интернет-журнала, откуда я позаимствовал картинку содержит ещё много красивых увеличенных фоток, ссылки см. внизу)
1. Влияние на самопроверку внешних сил
Рассмотрим акселерометр. Как указано ранее, в режиме самодиагностики на микромеханический грузик воздействует электростатическая сила, отклоняющая его на определённое расстояние, и имитирующая тем самым ускорение. Однако ожидаемую величину на выходе датчика мы не увидим. Дело в том, что в спокойном состоянии на грузик действуют другие силы (напр. сила тяжести). Они дополнительно отклоняют грузик.
Для того, чтобы учесть внешние силы, в процедуре самопроверки снимаются показания датчика в обычном режиме. Затем включается источник отклонения (переход в режим «самопроверка») и снова снимаются показания датчика.
aнорм = aвнешние
aST = aвнешние + aвнутр. ист.
Разница показаний этих двух режимов должна показывать величину с эталонного источника. Если величина находится в допустимом спецификацией диапазоне, то это значит, что датчик работает должным образом.
aвнутр. ист. = aST — aнорм
MIN < aвнутр. ист. < MAX
Отсюда важный вывод — внешние силы в обоих режимах должны быть равны. Т. е. датчик должен быть неподвижен. Когда устройство в руках — оно подвижно. Когда вы что-то нажимаете (или заканчиваете нажимать) на устройстве — оно тоже подвижно. Если на устройство действует вибрация — оно подвижно. И так далее.
2. Что ещё нужно учитывать при проведении самопроверки?
При проведении самопроверки датчика также необходимо учитывать:
- При переключении датчика в режим измерений, может потребоваться некоторое количество времени для того, чтобы акселерометр стал готов для сбора данных. Время уточнять в спецификации.
- Проведение самодиагностики желательно проводить в различных измеряемых диапазонах датчика (2G, 4G, и т. п.).
- Иногда возможно инверсионное включение эталонного источника (может тянуть в одну или в другую сторону). В спецификации тогда будет указано два режима самопроверки (например «Self-test P(ositive)» и «Self-test N(egative)»).
3. Как найти величину отклонения грузика эталонным источником?
Величины отклонения грузика эталонным источником по осям:
- Могут быть указаны явно в спецификации.
- Может быть указан широкий диапазон возможных значений. Принцип тот же — если полученная разница aвнутр. ист. попадает в этот диапазон, то датчик в порядке. Нужно только понимать, что такой подход практикуется не для самых точных датчиков.
- Значения могут храниться в специальных регистрах (что-нибудь вроде «SELF_TEST_X_ACCEL»). Это значит производитель уточнил величины отклонения для каждого конкретного датчика и положил в его память. Производитель может сэкономить память и записать не значение, а коэффициент формулы, с помощью которой можно рассчитать значение. Сам метод расчёта тогда будет в спецификации или в одном из её приложений.
Величина отклонения обычно указывается в условных единицах датчика — LSB. Это то, что в выходном регистре лежит. Рассмотрим значения, которые даёт внутренний источник датчика LIS302DL (датчик 8-мибитный):

Он интересен тем, что как раз содержит два режима работы внутреннего источника. Глядя в таблицу можно подумать, что значения приведены для обоих направлений внутреннего источника. Однако на самом деле это расширенный диапазон для одного направления — «Self test P». Обычно направление одно и значения указывают так:

Либо могут указать в измеряемых единицах:

Чтобы из значений датчика получить измеряемую величину, нужно воспользоваться формулой:
Величина_в_единицах_измерения = Значение_в_LSB * Чувствительность.
Чтобы перевести измеряемую величину обратно в единицы датчика, нужно воспользоваться формулой:
Значение_в_LSB = Величина_в_единицах_измерения / Чувствительность.
Чувствительность в спецификациях пишут так:

или так

Можно чувствительность посчитать самостоятельно:
Чувствительность = Измеряемый_диапазон / разрешение
Пример: Величина_LSB = Величина_измеряемая * Разрешение / Измеряемый_диапазон = 8 * 0,001 * g * 65536 / 4 * g = 131,072, т. е. ±131.
4. Как понять, что величина находится в допустимом диапазоне?
Допустимый диапазон может быть указан несколькими способами:
- Может быть явно указан минимум и максимум.
- Может быть указан в виде допустимого процента отклонения (например где-нибудь в сноске).
- По идее должен быть не больше уровня шума.
Уровень шума обычно указывают через среднеквадратичное отклонение (RMS). Диапазон погрешности будет тогда равен удвоенному среднеквадратичному отклонению (±). Примеры:


В первом примере указан корень из спектральной плотности мощности шумов. Чтобы получить СКО шума, следует значение умножить на корень из полосы фильтра, который у вас будет стоять по выходу датчика. Например, при полосе 25 Гц получите 750 ug в качестве СКО (спасибо Korogodin за подсказку). Если хотим получить значение без учёта фильтрации, берём частоту замеров. В данном случае при 100Гц получается 1,5mg. Во втором и третьем примере указан максимальный уровень шума. Например 8 * 0,001 * g = 0,0784 м/с2. Значит возможный разбег величины самопроверки = ±0,0784 м/с2. Здесь тоже нужно быть внимательным, т. к. иногда уровень шума указывают с включенным фильтром. Это значит, что замеры нужно делать с теми же настройками фильтра.
Примеры самодиагностики:
Пример кода самопроверки LSM303D
В одном положении получились такие результаты:

Затем плату с датчиком положил на бок и получил такие результаты:

Разбег значений получился 11, 2 и 5 g * 10-3. Если провести ещё несколько испытаний, то можно уточнить диапазон для конкретно этого датчика:
380 < X < 410
370 < Y < 390
680 < Z < 700
uint8_t sign = lsm303d_read(LSM303D_ADDR_WHO_AM_I); if (sign != LSM303D_WHO_I_AM) { //on_fatal_error("Нет сигнала с LSM303D"); } lsm303d_write(LSM303D_ADDR_CTRL_REG1, LSM303D_REG1_RATE_100HZ_A | LSM303D_REG1_X_ENABLE_A | LSM303D_REG1_Y_ENABLE_A | LSM303D_REG1_Z_ENABLE_A ); delay_ms(100); uint8_t i; int32_t avg_x__norm = 0, avg_y__norm = 0, avg_z__norm = 0; vector v; // делаем 100 замеров в обычном режиме for (i = 0; i < 100; i++) { lsm303d_get_acc(&v); avg_x__norm += v.x; avg_y__norm += v.y; avg_z__norm += v.z; } avg_x__norm /= 100; avg_y__norm /= 100; avg_z__norm /= 100; // Включаем внутренний источник (режим "self-test") lsm303d_write(LSM303D_ADDR_CTRL_REG2, LSM303D_REG2_FULL_SCALE_2G_A | 2 ); delay_ms(100); int32_t avg_x__st = 0, avg_y__st = 0, avg_z__st = 0; // делаем ещё 100 замеров for (i = 0; i < 100; i++) { lsm303d_get_acc(&v); avg_x__st += v.x; avg_y__st += v.y; avg_z__st += v.z; } avg_x__st /= 100; avg_y__st /= 100; avg_z__st /= 100; // Выключаем внутренний источник lsm303d_write(LSM303D_ADDR_CTRL_REG2, 0); delay_ms(100); float internal_source_x, internal_source_y, internal_source_z; // mg internal_source_x = (avg_x__st - avg_x__norm)*0.061; internal_source_y = (avg_y__st - avg_y__norm)*0.061; internal_source_z = (avg_z__st - avg_z__norm)*0.061; if ((internal_source_x < 70.0) || (internal_source_x >1700.0) || (internal_source_y < 70.0) || (internal_source_y >1700.0) || (internal_source_z < 70.0) || (internal_source_z >1700.0)) { // detect problem draw_EraseScreen(); draw_TextOut(0,0,"problem.."); draw_Show(); } else { draw_EraseScreen(); draw_TextOut(0,0,"ok"); draw_Show(); } while(1);
В одном положении получились такие результаты:

Затем плату с датчиком положил на бок и получил такие результаты:

Разбег значений получился 11, 2 и 5 g * 10-3. Если провести ещё несколько испытаний, то можно уточнить диапазон для конкретно этого датчика:
380 < X < 410
370 < Y < 390
680 < Z < 700
Пример кода самопроверки LIS302DL
В одном положении получились такие результат��:

В другом положении такие результаты:

Разбег значений получился в районе 1 единицы. А это второй режим работы внутреннего источника, когда он тянет в другую сторону (Self-test M):

Здесь мы видим показания другого датчика («Self-test P»):

И второй режим с поворотом платы («Self-test M»):

Согласно спецификации, он не проходит самопроверку — значения по оси Z выступают за заявленный предел: максимум 32, а мы имеем 35.
uint8_t dev_id = lis302dl_read(0x0F); if (dev_id != 0x3b) { while(1); // ошибка } lis302dl_write(0x20, 7 | // включить все оси // 100 Гц и диапазон 2G (1 << 6) // включить ); delay_ms(100); uint8_t i; int32_t avg_x__norm = 0, avg_y__norm = 0, avg_z__norm = 0; // делаем 100 замеров в обычном режиме for (i = 0; i < 100; i++) { lis302dl_get_acc(&v); avg_x__norm += v.x; avg_y__norm += v.y; avg_z__norm += v.z; } avg_x__norm /= 100; avg_y__norm /= 100; avg_z__norm /= 100; // Включаем внутренний источник (режим "self-test P") lis302dl_write(0x20, 7 | // включить все оси (1 << 4) | // self-test P // 100 Гц и диапазон 2G (1 << 6) // включить ); delay_ms(100); int32_t avg_x__st = 0, avg_y__st = 0, avg_z__st = 0; // делаем ещё 100 замеров for (i = 0; i < 100; i++) { lis302dl_get_acc(&v); avg_x__st += v.x; avg_y__st += v.y; avg_z__st += v.z; } avg_x__st /= 100; avg_y__st /= 100; avg_z__st /= 100; // Выключаем внутренний источник lis302dl_write(0x20, 7 | // включить все оси // 100 Гц и диапазон 2G (1 << 6) // включить ); delay_ms(100); int32_t internal_source_x, internal_source_y, internal_source_z; // LSB internal_source_x = avg_x__st - avg_x__norm; internal_source_y = avg_y__st - avg_y__norm; internal_source_z = avg_z__st - avg_z__norm; if ((internal_source_x < -32) || (internal_source_x > -3) || (internal_source_y < 3) || (internal_source_y > 32) || (internal_source_z < 3) || (internal_source_z > 32)) { // detect problem draw_EraseScreen(); draw_TextOut(0,0,"problem.."); draw_Show(); } else { draw_EraseScreen(); draw_TextOut(0,0,"ok"); draw_Show(); } while(1);
В одном положении получились такие результат��:

В другом положении такие результаты:

Разбег значений получился в районе 1 единицы. А это второй режим работы внутреннего источника, когда он тянет в другую сторону (Self-test M):

Здесь мы видим показания другого датчика («Self-test P»):

И второй режим с поворотом платы («Self-test M»):

Согласно спецификации, он не проходит самопроверку — значения по оси Z выступают за заявленный предел: максимум 32, а мы имеем 35.
Пример кода самопроверки MPU9250
В одном положении получились такие результаты:

В другом положении:

В этом датчике успешность прохождения самопроверки определяется допустимым отклонением от эталонного значения. Допуск отклонения равен ±3% (стр. 9 первой части спецификации). Т. е. этот датчик в порядке.
delay_ms(100); uint8_t i; int32_t avg_x__norm = 0, avg_y__norm = 0, avg_z__norm = 0; // делаем 100 замеров в обычном режиме for (i = 0; i < 100; i++) { mpu9250_get_acc(&v); avg_x__norm += v.x; avg_y__norm += v.y; avg_z__norm += v.z; } avg_x__norm /= 100; avg_y__norm /= 100; avg_z__norm /= 100; // Включаем внутренний источник (режим "self-test") mpu9250_selftest(1); delay_ms(100); int32_t avg_x__st = 0, avg_y__st = 0, avg_z__st = 0; // делаем ещё 100 замеров for (i = 0; i < 100; i++) { mpu9250_get_acc(&v); avg_x__st += v.x; avg_y__st += v.y; avg_z__st += v.z; } avg_x__st /= 100; avg_y__st /= 100; avg_z__st /= 100; mpu9250_selftest(0); // отключаем внутренний источник смещения delay_ms(100); // считываю коэффициенты, по которым определим значения смещения, определённые заводом uint8_t factory_acc_kx = spi_read_byte(SELF_TEST_X_ACCEL) - 1; uint8_t factory_acc_ky = spi_read_byte(SELF_TEST_Y_ACCEL) - 1; uint8_t factory_acc_kz = spi_read_byte(SELF_TEST_Z_ACCEL) - 1; // считаю значения смещения, которые получились сейчас int32_t now_acc_dx = avg_x__st - avg_x__norm; // internal_source int32_t now_acc_dy = avg_y__st - avg_y__norm; int32_t now_acc_dz = avg_z__st - avg_z__norm; // определяем заводские значения смещения int32_t full_scale_acc_k = 0; // 2G int32_t factory_acc_dx = (int32_t)((2620.0 / (1 << full_scale_acc_k)) * pow(1.01, factory_acc_kx)); int32_t factory_acc_dy = (int32_t)((2620.0 / (1 << full_scale_acc_k)) * pow(1.01, factory_acc_ky)); int32_t factory_acc_dz = (int32_t)((2620.0 / (1 << full_scale_acc_k)) * pow(1.01, factory_acc_kz)); // определяю процент разницы между заводскими значениями и полученными сейчас double acc_err_x = abs_double(100.0 - (100.0 * now_acc_dx) / factory_acc_dx); double acc_err_y = abs_double(100.0 - (100.0 * now_acc_dy) / factory_acc_dy); double acc_err_z = abs_double(100.0 - (100.0 * now_acc_dz) / factory_acc_dz); // спецификация допускает 3% отклонение if ((acc_err_x > 3.0) || (acc_err_y > 3.0) || (acc_err_z > 3.0)) { // detect problem draw_EraseScreen(); draw_TextOut(0,0,"problem.."); draw_Show(); } else { draw_EraseScreen(); draw_TextOut(0,0,"ok"); draw_Show(); } while(1);
В одном положении получились такие результаты:

В другом положении:

В этом датчике успешность прохождения самопроверки определяется допустимым отклонением от эталонного значения. Допуск отклонения равен ±3% (стр. 9 первой части спецификации). Т. е. этот датчик в порядке.
Ссылки
- Интернет-журнал «EDN Network». Статья «The embedded self-test feature in MEMS inertial sensors. Jay Esfandyari, Gang Xu, Marco Capovilla, Paolo Bendiscioli, Marco Bianco -July 22, 2012». Ссылка.
- Интернет-журнал «3D News». Статья «MEMS: микроэлектромеханические системы, часть 1». Ссылка.
- Спецификация (datasheet) на датчик LIS3DH. Ссылка.
- Спецификация (datasheet) на датчик LIS302DL. Ссылка.
- Спецификация (datasheet) на датчик LSM303D. Ссылка.
- Спецификация (datasheet) на датчик MPU9250.
