Изучая спецификацию (datasheet) на МЕМС-датчик (акселерометр, гироскоп и проч.) мы сталкиваемся с такой процедурой, как самопроверка (self-test) или самодиагностика. Обычно в спецификациях есть описание, как это делать. Кому интересно: что это и как это правильно делать? — добро пожаловать под кат.
Введение
Самопроверка позволяет определить, что данные с датчика показывают то, что должны показывать. Датчики не всегда имели режим самодиагностики. Чтобы определить соответствие измеряемых данных тому, что указано в спецификации, разработчик должен был проверять это специальными экспериментами. В случае акселерометра — совершать наклоны на определённые углы и снимать изменение показаний. Анализируя результаты, можно было оценить погрешность, выдаваемую датчиком. Если показатели не соответствовали спецификации, датчик признавался бракованным.
Это усложняло массовое производство и во все датчики стали включать процедуру самодиагностики. Замысел процедуры достаточно прост — создать условия, при которых мы будем знать, что должен показывать датчик, и сравнить с тем, что он показывает. Отклонения выше заданных забраковывают датчик. Для компасов делают элемент, создающий магнитное поле (прямо в корпусе датчика). Включение режима самодиагностики включает этот элемент — эталонный источник. Для акселерометра и гироскопа элемент генерирует электростатическое поле, которое отклоняет грузик (см. фото ниже) и имитирует физическое движение или вращение. Величина смещения известна, соответственно известна величина, которую должен показать датчик. Если погрешность снятых показаний в заявленных пределах — значит с датчиком всё в порядке и он работает в соответствии со спецификацией.
(статья из интернет-журнала, откуда я позаимствовал картинку содержит ещё много красивых увеличенных фоток, ссылки см. внизу)
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). Диапазон погрешности будет тогда равен удвоенному среднеквадратичному отклонению (±). Примеры:
В первом примере применена неизвестная мне единица измерения, кто знает как это перевести в mg, пишите в комментарии (то что в числителе микро-ж, это понятно, а что в знаменателе?). В последнем примере максимальный уровень шума = 8 * 0,001 * g = 0,0784 м/с2. Значит возможный разбег величины самопроверки = ±0,0784 м/с2. Здесь тоже нужно быть внимательным, т. к. иногда уровень шума указывают с включенным фильтром. Это значит, что замеры нужно делать с теми же настройками фильтра.
Примеры самодиагностики:
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
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.
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.
Автор: 1div0