В этом уроке мы узнаем что такое MEMS, как работают акселерометр, гироскоп и магнитометр MEMS и как их использовать с платой Arduino.
Ранее на нашем сайте мы уже рассматривали подключение отдельно акселерометра, гироскопа и магнитометра к плате Arduino в следующих проектах:
- определение ориентации с помощью акселерометра ADXL345 и Arduino;
- подключение гироскопа MPU6050 к Arduino;
- самобалансирующийся робот на Arduino Uno;
- подключение датчика MPU9250 (акселерометр, гироскоп и магнитометр) к Arduino;
- цифровой компас на основе Arduino и магнитометра HMC5883L.
Наглядно представленный материал статьи вы можете посмотреть в следующем видео:
Что такое MEMS (МЭМС)?
MEMS (Micro-Electro-Mechanical-Systems) или на русском МЭМС — это очень маленькие системы или устройства, состоящие из микрокомпонентов размером от 0,001 мм до 0,1 мм. Эти компоненты изготовлены из кремния, полимеров, металлов и/или керамики и обычно объединяются с процессором (микроконтроллером) для завершения системы.
Теперь мы кратко объясним, как работает каждый из этих датчиков микроэлектромеханических систем (МЭМС).
MEMS акселерометр
Он измеряет ускорение путем измерения изменения емкости. Его микроструктура выглядит примерно так. Он имеет массу, прикрепленную к пружине, которая может двигаться в одном направлении, и фиксированные внешние пластины. Таким образом, когда будет приложено ускорение в определенном направлении, масса будет двигаться, и емкость между пластинами и массой изменится. Это изменение емкости будет измерено, обработано и будет соответствовать определенному значению ускорения.
MEMS гироскоп
Гироскоп измеряет угловую скорость, используя эффект Кориолиса. Когда масса движется в определенном направлении с определенной скоростью и когда применяется внешняя угловая скорость, как показано зеленой стрелкой, возникает сила, как показано сине-красной стрелкой, которая вызывает перпендикулярное смещение массы. Подобно акселерометру, это смещение вызовет изменение емкости, которая будет измерена, обработана и будет соответствовать определенной угловой скорости.
Микроструктура гироскопа выглядит примерно так, как показано на следующем рисунке. Масса, которая постоянно движется или колеблется, и при приложении внешней угловой скорости гибкая часть массы будет двигаться и совершать перпендикулярное перемещение.
MEMS магнитометр
Он измеряет магнитное поле Земли, используя эффект Холла или магниторезистивный эффект. На самом деле почти 90% датчиков на рынке используют эффект Холла, и вот как это работает.
Если у нас есть проводящая пластина, как показано на фотографии, и мы пропускаем через нее ток, электроны будут течь прямо от одной стороны пластины к другой. Теперь, если мы поднесем некоторое магнитное поле к пластине, мы нарушим прямой поток, и электроны отклонятся к одной стороне пластины, а положительные полюсы - к другой стороне пластины. Это означает, что если мы поместим метр между этими двумя сторонами, мы получим некоторое напряжение, которое зависит от силы магнитного поля и его направления.
Остальные 10% датчиков на рынке используют магниторезистивный эффект. В этих датчиках используются материалы, чувствительные к магнитному полю, обычно состоящие из железа (Fe) и никеля (Ne). Поэтому, когда эти материалы подвергаются воздействию магнитного поля, они меняют свое сопротивление.
Необходимые компоненты
Хорошо, теперь давайте подключим эти датчики к плате Arduino и воспользуемся ими. В качестве примера я буду использовать коммутационную плату GY-80, которая имеет следующие датчики: 3-осевой акселерометр ADXL345 , 3-осевой гироскоп L3G4200D, 3-осевой магнитометр MC5883L, а также барометр и термометр, которые мы не будем использовать в этом уроке.
Также можно приобрести эти компоненты по отдельности:
- Акселерометр ADXL345 (купить на AliExpress).
- Магнитометр (Magnetometer sensor) HMC5883L (купить на AliExpress).
- Модуль акселерометра и гироскопа MPU6050 (купить на AliExpress).
Реклама: ООО "АЛИБАБА.КОМ (РУ)" ИНН: 7703380158
Схема проекта
Схема подключения коммутационной платы GY-80 к плате Arduino показана на следующем рисунке.
Для подключения мы использовали протокол связи I2C, что означает, что мы можем применять все датчики используя всего два проводами. Таким образом, чтобы обеспечить связь между Arduino и датчиками, нам необходимо знать их уникальные адреса устройств и адреса их внутренних регистров для получения от них данных. Эти адреса можно найти в даташитах датчиков:
- Техническое описание акселерометра ADXL345.
- Техническое описание гироскопа L3G4200D.
- Техническое описание магнитометра MC5883L.
Для получения более подробной информации о том, как работает связь по протоколу I2C, вы можете посмотреть статью про использование интерфейса I2C в Arduino.
Теперь давайте посмотрим исходные коды программ получения данных с датчиков. Мы начнем с акселерометра, и перед каждым кодом будут некоторые пояснения, а также некоторые дополнительные описания в комментариях к коду.
Код акселерометра для Arduino
Сначала нам нужно подключить библиотеку Wire для Arduino и определить адреса регистров датчика. В функции setup() нам нужно инициализировать библиотеку Wire и последовательную связь, поскольку мы будем использовать последовательный монитор для отображения результатов. Также здесь нам нужно активировать датчик или разрешить измерение, отправив соответствующий байт в регистр Power_CTL, и вот как мы это делаем. Используя функцию Wire.beginTransmission(), мы выбираем, с каким датчиком мы будем общаться, в данном случае с 3-осевым акселерометром. Затем с помощью функции Wire.write() мы указываем, с каким внутренним регистром мы будем обращаться. После этого мы отправим соответствующий байт для включения измерения. Используя функцию Wire.endTransmission(), мы завершим передачу и передадим данные в регистры.
В функции loop() нам нужно прочитать данные для каждой оси. Начнем с оси X. Итак, сначала мы выберем, с какими регистрами мы будем работать, в данном случае с двумя внутренними регистрами оси X. Затем с помощью функции Wire.requestFrom() мы запросим переданные данные или два байта из двух регистров. Функция Wire.available() вернет количество байтов, доступных для извлечения, и если это число соответствует запрошенным байтам, в нашем случае 2 байтам, с помощью функции Wire.read() мы прочитаем байты из двух регистров ось Х.
Выходные данные регистров представляют собой дополнение до двух, где X0 является младшим байтом, а X1 — старшим байтом, поэтому нам необходимо преобразовать эти байты в значения с плавающей запятой от -1 до +1 в зависимости от направления относительной оси X к ускорению Земли или силе тяжести. Мы повторим эту процедуру для двух других осей и в конце распечатаем эти значения на последовательном мониторе.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
#include <Wire.h> //--- Accelerometer Register Addresses #define Power_Register 0x2D #define X_Axis_Register_DATAX0 0x32 // Hexadecima address for the DATAX0 internal register. #define X_Axis_Register_DATAX1 0x33 // Hexadecima address for the DATAX1 internal register. #define Y_Axis_Register_DATAY0 0x34 #define Y_Axis_Register_DATAY1 0x35 #define Z_Axis_Register_DATAZ0 0x36 #define Z_Axis_Register_DATAZ1 0x37 int ADXAddress = 0x53; //Device address in which is also included the 8th bit for selecting the mode, read in this case. int X0,X1,X_out; int Y0,Y1,Y_out; int Z1,Z0,Z_out; float Xa,Ya,Za; void setup() { Wire.begin(); // Initiate the Wire library Serial.begin(9600); delay(100); Wire.beginTransmission(ADXAddress); Wire.write(Power_Register); // Power_CTL Register // Enable measurement Wire.write(8); // Bit D3 High for measuring enable (0000 1000) Wire.endTransmission(); } void loop() { // X-axis Wire.beginTransmission(ADXAddress); // Begin transmission to the Sensor //Ask the particular registers for data Wire.write(X_Axis_Register_DATAX0); Wire.write(X_Axis_Register_DATAX1); Wire.endTransmission(); // Ends the transmission and transmits the data from the two registers Wire.requestFrom(ADXAddress,2); // Request the transmitted two bytes from the two registers if(Wire.available()<=2) { // X0 = Wire.read(); // Reads the data from the register X1 = Wire.read(); /* Converting the raw data of the X-Axis into X-Axis Acceleration - The output data is Two's complement - X0 as the least significant byte - X1 as the most significant byte */ X1=X1<<8; X_out =X0+X1; Xa=X_out/256.0; // Xa = output value from -1 to +1, Gravity acceleration acting on the X-Axis } // Y-Axis Wire.beginTransmission(ADXAddress); Wire.write(Y_Axis_Register_DATAY0); Wire.write(Y_Axis_Register_DATAY1); Wire.endTransmission(); Wire.requestFrom(ADXAddress,2); if(Wire.available()<=2) { Y0 = Wire.read(); Y1 = Wire.read(); Y1=Y1<<8; Y_out =Y0+Y1; Ya=Y_out/256.0; } // Z-Axis Wire.beginTransmission(ADXAddress); Wire.write(Z_Axis_Register_DATAZ0); Wire.write(Z_Axis_Register_DATAZ1); Wire.endTransmission(); Wire.requestFrom(ADXAddress,2); if(Wire.available()<=2) { Z0 = Wire.read(); Z1 = Wire.read(); Z1=Z1<<8; Z_out =Z0+Z1; Za=Z_out/256.0; } // Prints the data on the Serial Monitor Serial.print("Xa= "); Serial.print(Xa); Serial.print(" Ya= "); Serial.print(Ya); Serial.print(" Za= "); Serial.println(Za); } |
Код гироскопа для Arduino
Для получения данных с гироскопа у нас будет код, аналогичный предыдущему. Итак, сначала нам нужно определить адреса регистров и некоторые переменные для данных. В функции setup() нам нужно проснуться и перевести датчик в обычный режим с помощью CTRL_REG1, а также выбрать чувствительность датчика. Для этого примера я выберу режим чувствительности 2000dps.
В функции loop(), аналогичном акселерометру, мы будем считывать данные по осям X, Y и Z. Затем необработанные данные необходимо преобразовать в значения углов. Из таблицы данных датчика мы видим, что режиму чувствительности 2000 dps соответствует единица измерения 70 mdps/разряд. Это означает, что нам нужно умножить необработанные выходные данные на 0,07, чтобы получить угловую скорость в градусах в секунду. Затем, если умножить угловую скорость на время, мы получим значение угла. Поэтому нам нужно вычислить временной интервал каждой секции цикла, и мы можем сделать это, используя функцию millis() вверху и внизу секции цикла, и мы сохраним ее значение в переменной dt. Таким образом, для каждого выполненного цикла мы будем вычислять угол и прибавлять его к конечному значению угла. Мы сделаем то же самое для двух других осей и в конце распечатаем результаты на последовательном мониторе.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
#include <Wire.h> //--- Gyro Register Addresses #define Gyro_gX0 0x28 #define Gyro_gX1 0x29 #define Gyro_gY0 0x2A #define Gyro_gY1 0x2B #define Gyro_gZ0 0x2C #define Gyro_gZ1 0x2D int Gyro = 0x69; //Device address in which is also included the 8th bit for selecting the mode, read in this case. int gX0, gX1, gX_out; int gY0, gY1, gY_out; int gZ0, gZ1, gZ_out; float Xg,Yg,Zg; float angleX,angleY,angleZ,angleXc,angleYc,angleZc; unsigned long start, finished, elapsed; float dt=0.015; void setup() { Wire.begin(); Serial.begin(9600); delay(100); Wire.beginTransmission(Gyro); Wire.write(0x20); // CTRL_REG1 - Power Mode Wire.write(15); // Normal mode: 15d - 00001111b Wire.endTransmission(); Wire.beginTransmission(Gyro); Wire.write(0x23); // CTRL_REG4 - Sensitivity, Scale Selection Wire.write(48); // 2000dps: 48d - 00110000b Wire.endTransmission(); } void loop() { start=millis(); //---- X-Axis Wire.beginTransmission(Gyro); // transmit to device Wire.write(Gyro_gX0); Wire.endTransmission(); Wire.requestFrom(Gyro,1); if(Wire.available()<=1) { gX0 = Wire.read(); } Wire.beginTransmission(Gyro); // transmit to device Wire.write(Gyro_gX1); Wire.endTransmission(); Wire.requestFrom(Gyro,1); if(Wire.available()<=1) { gX1 = Wire.read(); } //---- Y-Axis Wire.beginTransmission(Gyro); // transmit to device Wire.write(Gyro_gY0); Wire.endTransmission(); Wire.requestFrom(Gyro,1); if(Wire.available()<=1) { gY0 = Wire.read(); } Wire.beginTransmission(Gyro); // transmit to device Wire.write(Gyro_gY1); Wire.endTransmission(); Wire.requestFrom(Gyro,1); if(Wire.available()<=1) { gY1 = Wire.read(); } //---- Z-Axis Wire.beginTransmission(Gyro); // transmit to device Wire.write(Gyro_gZ0); Wire.endTransmission(); Wire.requestFrom(Gyro,1); if(Wire.available()<=1) { gZ0 = Wire.read(); } Wire.beginTransmission(Gyro); // transmit to device Wire.write(Gyro_gZ1); Wire.endTransmission(); Wire.requestFrom(Gyro,1); if(Wire.available()<=1) { gZ1 = Wire.read(); } //---------- X - Axis // Raw Data gX1=gX1<<8; gX_out =gX0+gX1; // From the datasheet: 70 mdps/digit Xg=gX_out*0.07; // Angular rate // Angular_rate * dt = angle angleXc = Xg*dt; angleX = angleX + angleXc; //---------- Y - Axis gY1=gY1<<8; gY_out =gY0+gY1; Yg=gY_out*0.07; angleYc = Yg*dt; angleY = angleY + angleYc; //---------- Z - Axis gZ1=gZ1<<8; gZ_out =gZ0+gZ1; Zg=gZ_out*0.07; angleZc = Zg*dt; angleZ = angleZ + angleZc; // Prints the data on the Serial Monitor Serial.print("angleX= "); Serial.print(angleX); Serial.print(" angleY= "); Serial.print(angleY); Serial.print(" angleZ= "); Serial.println(angleZ); delay(10); // Calculating dt finished=millis(); elapsed=finished-start; dt=elapsed/1000.0; start = elapsed = 0; } |
Код магнитометра для Arduino
Мы снова будем использовать технику, аналогичную предыдущей. Сначала нам нужно определить адреса регистров, а затем в разделе настройки установить датчик в режим непрерывного измерения. В разделе цикла мы получим необработанные данные для каждой оси тем же методом, что и для предыдущих датчиков.
Затем нам нужно преобразовать необработанные данные в значение магнитного поля или единицы Гаусса. Из таблицы данных датчика видно, что режим чувствительности по умолчанию составляет 0,92 мг/цифру. Это означает, что нам нужно умножить необработанные данные на 0,00092, чтобы получить магнитное поле Земли в единицах Гаусса. В конце мы распечатаем значения на последовательном мониторе.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
#include <Wire.h> //I2C Arduino Library #define Magnetometer_mX0 0x03 #define Magnetometer_mX1 0x04 #define Magnetometer_mZ0 0x05 #define Magnetometer_mZ1 0x06 #define Magnetometer_mY0 0x07 #define Magnetometer_mY1 0x08 int mX0, mX1, mX_out; int mY0, mY1, mY_out; int mZ0, mZ1, mZ_out; float Xm,Ym,Zm; #define Magnetometer 0x1E //I2C 7bit address of HMC5883 void setup(){ //Initialize Serial and I2C communications Serial.begin(9600); Wire.begin(); delay(100); Wire.beginTransmission(Magnetometer); Wire.write(0x02); // Select mode register Wire.write(0x00); // Continuous measurement mode Wire.endTransmission(); } void loop(){ //---- X-Axis Wire.beginTransmission(Magnetometer); // transmit to device Wire.write(Magnetometer_mX1); Wire.endTransmission(); Wire.requestFrom(Magnetometer,1); if(Wire.available()<=1) { mX0 = Wire.read(); } Wire.beginTransmission(Magnetometer); // transmit to device Wire.write(Magnetometer_mX0); Wire.endTransmission(); Wire.requestFrom(Magnetometer,1); if(Wire.available()<=1) { mX1 = Wire.read(); } //---- Y-Axis Wire.beginTransmission(Magnetometer); // transmit to device Wire.write(Magnetometer_mY1); Wire.endTransmission(); Wire.requestFrom(Magnetometer,1); if(Wire.available()<=1) { mY0 = Wire.read(); } Wire.beginTransmission(Magnetometer); // transmit to device Wire.write(Magnetometer_mY0); Wire.endTransmission(); Wire.requestFrom(Magnetometer,1); if(Wire.available()<=1) { mY1 = Wire.read(); } //---- Z-Axis Wire.beginTransmission(Magnetometer); // transmit to device Wire.write(Magnetometer_mZ1); Wire.endTransmission(); Wire.requestFrom(Magnetometer,1); if(Wire.available()<=1) { mZ0 = Wire.read(); } Wire.beginTransmission(Magnetometer); // transmit to device Wire.write(Magnetometer_mZ0); Wire.endTransmission(); Wire.requestFrom(Magnetometer,1); if(Wire.available()<=1) { mZ1 = Wire.read(); } //---- X-Axis mX1=mX1<<8; mX_out =mX0+mX1; // Raw data // From the datasheet: 0.92 mG/digit Xm = mX_out*0.00092; // Gauss unit //* Earth magnetic field ranges from 0.25 to 0.65 Gauss, so these are the values that we need to get approximately. //---- Y-Axis mY1=mY1<<8; mY_out =mY0+mY1; Ym = mY_out*0.00092; //---- Z-Axis mZ1=mZ1<<8; mZ_out =mZ0+mZ1; Zm = mZ_out*0.00092; //Print out values of each axis Serial.print("x: "); Serial.print(Xm); Serial.print(" y: "); Serial.print(Ym); Serial.print(" z: "); Serial.println(Zm); delay(50); } |
Материал данного урока мы использовали в проекте цифрового компаса MEMS на Arduino, созданном с использованием Processing IDE.
446 просмотров