【Follow me第二季第4期】任务二:学习IMU基础知识,调试IMU传感器,通过串口打印...
[复制链接]
本帖最后由 mingzhe123 于 2024-12-1 12:16 编辑
IMU原理及选型介绍
所谓IMU(Inertial Measurement Unit)是指惯性测量单元,利用惯性的变化测量物体加速运动和旋转。其中目前主流的惯性测量器件是MEMS(微机电)传感器,它将用于机械结构缩小至纳米尺度,可测量机器人加速度与旋转角速度。后面讨论的内容应该可以归为捷联式惯导系统(Strapdown Inertial Navigation System),即测量单元直接安装于被测物体上,与平台式惯导系统区别开来。
需要注意的是,IMU或者说六轴(6Dof)传感器讨论的是三轴加速度计和三轴角速度计(俗称陀螺仪),与AHRS(航姿参考系)或者说9轴(9Dof)传感器是有区别的,后者增加了磁力计,以地磁场作为参考。但是在地面机器人运动并且夹杂大量电机磁干扰的情况下,引入微弱的地磁作为绝对航向并不是一个好主意。
原理
当物体运动的惯性改变时,根据牛顿第二定律实际上物体内部会有相对的作用力,我们可以想象一个加速度计的结构如上图,在整个系统加速或减速时弹簧会有不同程度的伸长或压缩,通过测量弹簧的所连接的物体的位移可以测量出实际加速度的大小,当然这其中也包含重力加速度。在目前MEMS工艺制造的传感器中这些结构都被缩得非常小,以至于测量的位置可以视为被缩在同一点上。
而角速度计的原理类似,但是测量原理是令质量块不断在轴的径向上产生移动,探测其切向偏移测量科氏力大小,从而推出角速度的大小。
性能
通常情况下,这两种传感器都有一般传感器所拥有的性能指标,例如不论是机械制造的误差还是A/D采样带来的偏置,满刻度误差,以及采样数据的噪声还有抗震性能,当然这些也会受到温度和输入电压的影响。有些误差的校正会在后续的校准中提到。
IMU参数
作为一种电子元器件,基本的参数都可以在其数据手册(datasheet)中查到,这些参数说明了IMU的性能范围,可以为选型提供参考,这里列举一些常用的参数
- 量程和灵敏度(sensitivity):代表最大角速度或加速度范围和取样精度,通常有数字接口的IMU都会有这项
- 敏感度温漂(sensitivity change over temperature/ sensitivity temperature drift):敏感度随温度的改变,通常用百分比或者FS/K(满刻度每度)
- 零偏(zero-rate offset/level):IMU中尤其针对角速度计的一个重要参数,它代表了角速度计/加速计在静止时仍然输出的大小,是后续IMU校准中重点讨论的参数。我们希望它越小越好,以避免积分时带来的累计误差。通常目前IMU给出的典型值都是在1deg/s以上。
- 温度漂移(zero-rate offset change over temperature):零偏随温度改变的大小,同样是角速度计的重要参数,越小代表IMU的零偏随温度影响越小。
- 噪声系数(noise):代表输出噪声与带宽的关系,单位一般为(ug/√Hz或者dps/√Hz),直观的说就是影响对真实信号的辨识程度。
- 交叉轴敏感度(cross-axis sensitivity):通常情况下三个轴的输出应该都是独立的,但实际上可能出现跨轴影响输出的现象,这个参数就是用于衡量各轴之间的影响程度
- 加速度敏感度(g-sensitivity):角速度计特有的参数,用于衡量加速度对角速度计输出的影响大小,通常单位为dps/g
- 零偏不稳定性:主要针对角速度计的参数,代表零偏在较长时间下的随机游走
IMU选型
目前可以找到的IMU厂商有以下几个:
- TDK InvenSense:MEMS加速度计,角速度计,磁力计,六轴/九轴厂商,有广泛使用的IMU MPU6050,价位中等(也有更贵的高性能的ICM系列)
- ST(意法半导体):MEMS加速度计,角速度计以及六轴厂商,主要为智能手机市场提供IMU,例如有LSM6DS3TR,价位偏低
- Bosch(博世):MEMS加速度计,角速度计,磁力计,六轴/九轴厂商,主要为机器人和无人机市场提供IMU,六轴传感器的有BMI系列,价位偏高
- ADI:老牌半导体公司,有加速度计和六轴,主要用于汽车和军事领域,性能最高但价位也是极高。当然也有低端的加速计ADXL35x系列
- QST(上海矽睿):磁力计,加速度计以及六轴的国产厂商,有QMI8658/QMI8610等六轴传感器
选型过程自然要比较性能,当然主要看零偏,温漂以及噪声。考虑到价格因素,ADI的六轴这个选项基本可以舍弃。高性能的一档有ICM4x6xx,BMI0xx,QMI86xx和LSM6Dx的性能略逊一筹。
截止目前,已知官方有库存的只有ST和QST。在疫情的背景下,选用国产的IMU或许更能确保稳定供应。
Arduino Nano RP2040 Connect -- LSM6DSOX
开发板使用的是LSM6DSOXTR是一款高性能的6轴惯性测量单元(IMU),由STMicroelectronics(意法半导体)制造。集成了3轴数字加速度计和3轴数字陀螺仪,采用系统级封装技术,具有出色的性能和低功耗特性。提供SPI、I²C和MIPI I3C串行接口,支持主处理器数据同步。是一款功能强大、性能卓越的6轴IMU。其手册特征如下图所示。意法半导体为其封装了传感器专用库- 库,使用Arduino编程只需调用其库即可。
IMU编程与调试
Arduino® Nano RP2040 Connect代码:
/* WIRING
In order to use the Adafruit lsm6dsox sensor with a ST nucleo board,
plug Nucleo "+3.3V" to AdafruitLSM6DOX "VIN",
plug Nucleo "GND" to AdafruitLSM6DOX "GND",
plug Nucleo "SCL"(D15) to AdafruitLSM6DOX "SCL",
plug Nucleo "SDA"(D14) to AdafruitLSM6DOX "SDA".*/
#include "LSM6DSOXSensor.h"
#include <Arduino.h>
// Declare LSM6DSOX sensor. Sensor address can have 2 values LSM6DSOX_I2C_ADD_L (corresponds to 0x6A I2C address) or LSM6DSOX_I2C_ADD_H (corresponds to 0x6B I2C address)
// On Adafruit lsm6dsox sensor, LSM6DSOX_I2C_ADD_L is the default address
LSM6DSOXSensor lsm6dsoxSensor = LSM6DSOXSensor(&Wire, LSM6DSOX_I2C_ADD_L);
int flag;
long last_update_time;
// Timer variables
unsigned long lastTime = 0;
unsigned long timerDelay = 1000;
void setup() {
Serial.begin(115200);
Serial1.begin(115200); // opensserial port, sets data rate to 115200 bps
while(Serial1.read()>= 0){}//clear serialbuffer
pinMode(LED_BUILTIN,OUTPUT);
digitalWrite(LED_BUILTIN,LOW);
Wire.begin();
// Default clock is 100kHz. LSM6DSOX also supports 400kHz, let's use it
Wire.setClock(400000);
// Init the sensor
lsm6dsoxSensor.begin();
// Enable accelerometer and gyroscope, and check success
if (lsm6dsoxSensor.Enable_X() == LSM6DSOX_OK && lsm6dsoxSensor.Enable_G() == LSM6DSOX_OK) {
Serial.println("Success enabling accelero and gyro");
} else {
Serial.println("Error enabling accelero and gyro");
}
// Read ID of device and check that it is correct
uint8_t id;
lsm6dsoxSensor.ReadID(&id);
if (id != LSM6DSOX_ID) {
Serial.println("Wrong ID for LSM6DSOX sensor. Check that device is plugged");
} else {
Serial.println("Receviced correct ID for LSM6DSOX sensor");
}
// Set accelerometer scale at +- 2G. Available values are +- 2, 4, 8, 16 G
lsm6dsoxSensor.Set_X_FS(2);
// Set gyroscope scale at +- 125 degres per second. Available values are +- 125, 250, 500, 1000, 2000 dps
lsm6dsoxSensor.Set_G_FS(125);
// Set Accelerometer sample rate to 208 Hz. Available values are +- 12.0, 26.0, 52.0, 104.0, 208.0, 416.0, 833.0, 1667.0, 3333.0, 6667.0 Hz
lsm6dsoxSensor.Set_X_ODR(208.0f);
// Set Gyroscope sample rate to 208 Hz. Available values are +- 12.0, 26.0, 52.0, 104.0, 208.0, 416.0, 833.0, 1667.0, 3333.0, 6667.0 Hz
lsm6dsoxSensor.Set_G_ODR(208.0f);
}
void IMU_update(int interval)
{
// Read accelerometer
uint8_t acceleroStatus;
lsm6dsoxSensor.Get_X_DRDY_Status(&acceleroStatus);
if (acceleroStatus == 1) { // Status == 1 means a new data is available
int32_t acceleration[3];
lsm6dsoxSensor.Get_X_Axes(acceleration);
// Plot data for each axis in mg
Serial.print("AccelerationX=");
Serial.print(acceleration[0]);
Serial.print("mg, AccelerationY=");
Serial.print(acceleration[1]);
Serial.print("mg, AccelerationZ=");
Serial.print(acceleration[2]);
Serial.println("mg");
if (millis() - last_update_time > interval)
{
if (acceleration[1] > 1000)
{
//encoder_diff--;
flag = 1;
Serial.print("flag=1");
Serial1.write(49);
digitalWrite(LED_BUILTIN,LOW);
}
else if (acceleration[1] < -1000)
{
// encoder_diff++;
flag = 2;
Serial.print("flag=2");
Serial1.write(50);
digitalWrite(LED_BUILTIN,HIGH);
}
else
{
flag = 0;
}
last_update_time = millis();
}
}
// Read gyroscope
uint8_t gyroStatus;
lsm6dsoxSensor.Get_G_DRDY_Status(&gyroStatus);
if (gyroStatus == 1) { // Status == 1 means a new data is available
int32_t rotation[3];
lsm6dsoxSensor.Get_G_Axes(rotation);
// Plot data for each axis in milli degrees per second
Serial.print("RotationX=");
Serial.print(rotation[0]);
Serial.print("mdps, RotationY=");
Serial.print(rotation[1]);
Serial.print("mdps, RotationZ=");
Serial.print(rotation[2]);
Serial.println("mdps");
}
}
void loop() {
IMU_update(200);
}
PICO2端代码:
int val;
void setup() {
Serial1.setRX(17);
Serial1.setTX(16);
Serial1.begin(115200); // opensserial port, sets data rate to 9600 bps
while(Serial.read()>= 0){}//clear serialbuffer
pinMode(25,OUTPUT);
}
void loop() {
if (Serial1.available() > 0) {
delay(100); // 等待数据传完
int numdata = Serial1.available();
val=Serial1.read();
Serial1.println(val);
if(val==49)
{
Serial.println("Test OK");
Serial.println(val);
// delay(2000);
digitalWrite(25, LOW);
}
if(val==50)
{
Serial.println("Test OK!!!");
Serial.println(val);
digitalWrite(25, HIGH);
}
while(Serial1.read()>=0){} //清空串口缓存
}
}
上位机打印的IMU数据:
总结:
实现IMU的控制与数据读取还是十分简单的,只需调用官方Arduino库即可,这大大降低了开发者的开发难度。
演示视频:
3c7aacbe69bae7d77d331d5a7bb42dd4
|