Часть моего будущего проекта квадрокоптера — инерциальный датчик с 9 степенями свободы. Над данными с сенсоров будут делаться всякие жуткие вычисления, и отлаживать их удобнее на компьютере. Поэтому я сделал небольшую тестовую штуку, которая будет собирать данные с MEMS-сенсоров и посылать их в компьютер по USB.

Используем плату STM32F3Discovery — на ней есть всё что нужно, и нам остаётся лишь написать код. Это довольно ленивый подход, но зачем делать свою плату, если уже есть готовая? К тому же она дешёвая, и точно ещё пригодится в других проектах. Короче, не знаю чего я тут перед вами оправдываюсь, в бой!

MEMS

По традиции, для начала немножко теории. Совсем чуть-чуть, чтобы вам не было скучно :)

Суть всех этих сенсоров (акселерометр и гироскоп) — они меряют некие ускорения, линейные или угловые. А как мы сами их чувствуем? У нас в ухе лежат специальные камни (отолиты), они закреплены подвижно и при движениях смещаются пропорционально проекциям ускорения на их оси. Природа умная и знает, что у нашего пространства три измерения — поэтому запихала нам в уши именно три отолита для разных осей (два комплекта, по одному в каждом ухе — для точности и для определения угловых ускорений).

Инженеры не стали возражать против мудрости природы и скопировали это в технологию MEMS. В кристалле кремния вытравливаются каналы и полости — чтобы сформировать грузик, висящий на гибких пружинах. Это всё вырезано из монолитного куска кремния, очуметь! Если нужно мерять ускорение на трёх осях — делаем три грузика по осям.

Теперь надо измерить отклонение. На грузик подают высокое напряжение, а рядом с ним вешают большую плоскость. Вместе они образуют конденсатор, ёмкость которого зависит от расстояния между обкладками — ну а поскольку смещение грузика пропорционально ускорению, то измеряя ёмкость можно определить ускорение. Ну и дальше понятно — усиливаем сигнал, меряем ёмкость, проводим первичную обработку (вроде масштабирования или смещения), и складываем в выходной буфер.

Гироскоп устроен точно так же, только там грузик более хитрый — он сидит на оси, и имеет радиальные выступы, которые точно так же приближаются или отдаляются от измерительных обкладок. Там всё продумано на случай предельных ускорений (грузики упираются в стенку), но понятно что лучше не кидать на пол девайсы с MEMS.

Магнитометр гораздо проще — это лишь три полупроводниковых магниторезистора в трёх перпендикулярных плоскостях, только очень маленькие.

Интерфейсы

Окей, с датчиками понятно — теперь про передачу данных.

Два интерфейса, по которым подключаются MEMS — это I2C и SPI. Логично, правда? Больше и нет никаких подходящих интерфейсов — не подключать же их по 1-Wire.

Выходные данные лежат в соответствующих регистрах, которые могут иметь или не иметь плюшки типа очереди, усреднения и подобных массовых штук. Также есть регистры управления датчиком — настройки предела измерений (выше предел — хуже точность), темпа измерений (можно и до 1кГц довести), выключения/выключения осей, и иногда даже фильтрации.

Часто выходной регистр устроен очень удобно: можно просто читать девайс, и вам будут последовательно отданы все три (или шесть) регистров. Если же вы недочитали какие-то из регистров, следующее измерение выполнено не будет.

В этом примере будем использовать SPI для гироскопа и I2C для акселерометра с магнитометром потому что так распаяно на плате так интереснее, и меня давно просили сделать пример с I2C.

Использование

Workflow работы с MEMS таков:

  1. Инициализируем интерфейс
  2. Инициализируем датчик — настраиваем нужные конфигурационные регистры
  3. Если в корпусе два датчика (как в LSM303DLHC) — настраиваем оба
  4. И дальше в цикле читаем данные:
    1. Проверяем флаг занятости девайса — пока занят, не идём дальше
    2. Как только освободился — читаем данные и начинаем сначала.

Окей, выглядит нестрашно. Для простоты пока вернёмся в 2005, возьмём микросхему FT232 и по ком-порту передим эти данные. Сейчас уже 2014, но всё-таки изнасилуем труп ещё раз. К настоящему USB вернёмся немного позже, цель сейчас не в этом.

Обработка в компьютере

Вот, теперь в компьютер приходят данные с сенсоров. Проверить это можно любой терминалкой — Putty, Terminal by Bray и прочие. Но это текст, а нам надо отобразить их в красивом и удобном виде. Для математических и статистических вычислений очень хорошо подходит язык R — к тому же, он имеет возможность читать данные из COM-порта. Я советую применять этот язык, потому что он имеет встроенные методы работы с массивами, матрицами и подобными чисто математическими абстракциями — и в нём есть куча статистических функций.

R-скрипт для приёма данных

Сделаем простую штуку: пускай скрипт проведёт тысячу экспериментов по вычислению ускорения свободного падения с помощью нашего акселерометра. Полученные данные статистически обработаем и представим в удобном для просмотра виде.

# ------ ПРИЁМ ДАННЫХ ------
# принимаем данные от платы: читаем файл COM4, принимаем 1000 порций
# данных по 9 штук (гироскоп, магнитометр, акселерометр по 3 оси),
# конвертируем их в double и складываем в удобный массив
len=1000
# это довольно долгий процесс, настройтесь на ожидание - минута или несколько минут
data=array(as.double(scan(file = "COM4", n = 9*len, quiet = "true", skip = 1)), c(3, 3, len));
# транспонируем каждую матрицу - не очень нужно, но так удобнее
for(i in 1:len) data[,,i]=t(data[,,i])
# вытаскиваем данные - проекция вектора g на ось z
g=data[3,3,]

# ------ ОЧИСТКА ДАННЫХ ОТ ВЫБРОСОВ ------
# сохраняем исходный массив
full_g=g
# вычисляем среднее отклонение
sd=sd(g)
# чистим данные от выбросов по критерию <3*sigma
surge=abs(g-mean(g))>3*sd(g)
j=0; for(i in 1:len) if(!surge[i]) { j=j+1; g[j]=full_g[i]; }
len=j; length(g)=len

# ------ СТАТИСТИКА ------
# вычисляем статистические метрики
mean=mean(g)
sd=sd(g)
# не забываем про коэффициент Стьюдента - у нас конечное число точек
d=sd*qt(c(0.95), df=len) # доверительный интервал 95%
e=d*100*sd/mean

# ------ ВЫВОД ТОЧЕК ------
# делим окно вывода графиков на две части по горизонтали
par(mfrow = c(1,2))
# отмечаем экспериментальные точки
plot(1:length(full_g), full_g, type="p", main="Экспериментальные точки: nизмерения величины g", xlab="Номер эксперимента", ylab="Измеренное значение")
# отмечаем границы 3*sigma
abline(h=mean-sd*3, col="red", lwd=2); abline(h=mean+sd*3, col="red", lwd=2)

# ------ ГИСТОГРАММА ------ 
# рисуем гистограмму распределения данных
h=hist(g, main="График распределения величины g", xlab="Значение g", ylab="Плотность распределения")
# добавляем на неё настоящий график идеального нормального распределения
xfit=min(g):max(g)
yfit=dnorm(xfit, mean=mean, sd=sd)*diff(h$mids[1:2])*len
# сместим график на пол-единицы влево, потому что столбики гистограммы
# рисуются вправо от значения и получается некрасиво
lines(xfit-0.5, yfit, col="blue", lwd=2);

# ------ ВЫВОД ------
cat("g = ", mean, 'ndelta_g = ', round(d), ' (epsilon_g = ', round(e), '%)n', sep="")

Я постарался прокомментировать каждое действие, но мне кажется что всё и так понятно.

В результате выполнения этого скрипта получится такая картинка (я использовал десять тысяч измерений, это заняло 4 минуты):

Синяя кривая — Гауссовская кривая, отражающая идеальное нормальное распределение; столбики — гистограмма распределения значений датчика.

Я не знаю, какими эпитетами описать идеальность нашего датчика :)Он просто великолепен!

**Статьи цикла:

** 1. Начинаем проект квадрокоптера

  1. Выбор деталей

  2. Заказ деталей на Hobbyking.com

  3. Управление положением квадрокоптера

  4. Получение данных с MEMS-акселерометра

  5. Калибровка и обработка сигнала MEMS-акселерометра