【Follow me第二季第4期】任务二:学习IMU基础知识,调试IMU传感器,通过串口打印...
本帖最后由 mingzhe123 于 2024-12-1 12:16 编辑<p><span style="font-size:18px;"><strong>IMU原理及选型介绍</strong></span></p>
<p>所谓IMU(Inertial Measurement Unit)是指惯性测量单元,利用惯性的变化测量物体加速运动和旋转。其中目前主流的惯性测量器件是MEMS(微机电)传感器,它将用于机械结构缩小至纳米尺度,可测量机器人加速度与旋转角速度。后面讨论的内容应该可以归为捷联式惯导系统(Strapdown Inertial Navigation System),即测量单元直接安装于被测物体上,与平台式惯导系统区别开来。</p>
<p>需要注意的是,IMU或者说六轴(6Dof)传感器讨论的是三轴加速度计和三轴角速度计(俗称陀螺仪),与AHRS(航姿参考系)或者说9轴(9Dof)传感器是有区别的,后者增加了磁力计,以地磁场作为参考。但是在地面机器人运动并且夹杂大量电机磁干扰的情况下,引入微弱的地磁作为绝对航向并不是一个好主意。</p>
<h3 id="原理">原理</h3>
<p> </p>
<div style="text-align: center;"></div>
<p> </p>
<p>当物体运动的惯性改变时,根据牛顿第二定律实际上物体内部会有相对的作用力,我们可以想象一个加速度计的结构如上图,在整个系统加速或减速时弹簧会有不同程度的伸长或压缩,通过测量弹簧的所连接的物体的位移可以测量出实际加速度的大小,当然这其中也包含重力加速度。在目前MEMS工艺制造的传感器中这些结构都被缩得非常小,以至于测量的位置可以视为被缩在同一点上。</p>
<p>而角速度计的原理类似,但是测量原理是令质量块不断在轴的径向上产生移动,探测其切向偏移测量科氏力大小,从而推出角速度的大小。</p>
<h3 id="性能">性能</h3>
<p>通常情况下,这两种传感器都有一般传感器所拥有的性能指标,例如不论是机械制造的误差还是A/D采样带来的偏置,满刻度误差,以及采样数据的噪声还有抗震性能,当然这些也会受到温度和输入电压的影响。有些误差的校正会在后续的校准中提到。</p>
<h2 id="IMU参数">IMU参数</h2>
<p>作为一种电子元器件,基本的参数都可以在其数据手册(datasheet)中查到,这些参数说明了IMU的性能范围,可以为选型提供参考,这里列举一些常用的参数</p>
<ul>
<li><strong>量程和灵敏度</strong>(sensitivity):代表最大角速度或加速度范围和取样精度,通常有数字接口的IMU都会有这项</li>
<li><strong>敏感度温漂</strong>(sensitivity change over temperature/ sensitivity temperature drift):敏感度随温度的改变,通常用百分比或者FS/K(满刻度每度)</li>
<li><strong>零偏</strong>(zero-rate offset/level):IMU中尤其针对角速度计的一个重要参数,它代表了角速度计/加速计在静止时仍然输出的大小,是后续IMU校准中重点讨论的参数。我们希望它越小越好,以避免积分时带来的累计误差。通常目前IMU给出的典型值都是在1deg/s以上。</li>
<li><strong>温度漂移</strong>(zero-rate offset change over temperature):零偏随温度改变的大小,同样是角速度计的重要参数,越小代表IMU的零偏随温度影响越小。</li>
<li><strong>噪声系数</strong>(noise):代表输出噪声与带宽的关系,单位一般为(ug/√Hz或者dps/√Hz),直观的说就是影响对真实信号的辨识程度。</li>
<li><strong>交叉轴敏感度</strong>(cross-axis sensitivity):通常情况下三个轴的输出应该都是独立的,但实际上可能出现跨轴影响输出的现象,这个参数就是用于衡量各轴之间的影响程度</li>
<li><strong>加速度敏感度</strong>(g-sensitivity):角速度计特有的参数,用于衡量加速度对角速度计输出的影响大小,通常单位为dps/g</li>
<li><strong>零偏不稳定性</strong>:主要针对角速度计的参数,代表零偏在较长时间下的随机游走</li>
</ul>
<h2 id="IMU选型">IMU选型</h2>
<p>目前可以找到的IMU厂商有以下几个:</p>
<ul>
<li><a href="https://invensense.tdk.com/" rel="noopener" target="_blank">TDK InvenSense</a>:MEMS加速度计,角速度计,磁力计,六轴/九轴厂商,有广泛使用的IMU MPU6050,价位中等(也有更贵的高性能的ICM系列)</li>
<li><a href="https://www.st.com/en/mems-and-sensors/inemo-inertial-modules.html" rel="noopener" target="_blank">ST(意法半导体)</a>:MEMS加速度计,角速度计以及六轴厂商,主要为智能手机市场提供IMU,例如有LSM6DS3TR,价位偏低</li>
<li><a href="https://www.bosch-sensortec.com/products/motion-sensors/" rel="noopener" target="_blank">Bosch(博世)</a>:MEMS加速度计,角速度计,磁力计,六轴/九轴厂商,主要为机器人和无人机市场提供IMU,六轴传感器的有BMI系列,价位偏高</li>
<li><a href="https://www.analog.com/cn/parametricsearch/11172#/" rel="noopener" target="_blank">ADI</a>:老牌半导体公司,有加速度计和六轴,主要用于汽车和军事领域,性能最高但价位也是极高。当然也有低端的加速计ADXL35x系列</li>
<li><a href="http://www.qstcorp.com/101/" rel="noopener" target="_blank">QST(上海矽睿)</a>:磁力计,加速度计以及六轴的国产厂商,有QMI8658/QMI8610等六轴传感器</li>
</ul>
<p>选型过程自然要比较性能,当然主要看零偏,温漂以及噪声。考虑到价格因素,ADI的六轴这个选项基本可以舍弃。高性能的一档有ICM4x6xx,BMI0xx,QMI86xx和LSM6Dx的性能略逊一筹。</p>
<p>截止目前,已知官方有库存的只有ST和QST。在疫情的背景下,选用国产的IMU或许更能确保稳定供应。</p>
<p><span style="font-size:18px;"><strong><u>Arduino</u> Nano RP2040 Connect -- LSM6DSOX</strong></span></p>
<p>开发板使用的是LSM6DSOXTR是一款高性能的6轴惯性测量单元(IMU),由STMicroelectronics(意法半导体)制造。集成了3轴数字加速度计和3轴数字陀螺仪,采用系统级封装技术,具有出色的性能和低功耗特性。提供SPI、I²C和MIPI I3C串行接口,支持主处理器数据同步。是一款功能强大、性能卓越的6轴IMU。其手册特征如下图所示。意法半导体为其封装了传感器专用库-<a href="https://github.com/stm32duino/X-NUCLEO-IKS01A3" style="font-family:"Open Sans", "Lucida Grande", lucida, verdana, sans-serif; box-sizing:inherit; scrollbar-color:var(--color-scrollbar-body) transparent; scrollbar-width:thin; font-size:1rem; font-weight:600 !important; letter-spacing:0.01em; color:var(--color-accent-4); cursor:pointer; font-style:normal; text-decoration:none; transition:color 0.2s ease-out; 27.2px">STM32duino X-NUCLEO-IKS01A3</a>库,使用Arduino编程只需调用其库即可。</p>
<p> </p>
<p><strong><span style="font-size:18px;">IMU编程与调试</span></strong></p>
<p><b><a href="https://www.digikey.cn/zh/products/detail/arduino/ABX00052/14123941" target="_blank">Arduino® Nano RP2040 Connect</a>代码:</b></p>
<pre>
<code class="language-cpp">
/* 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;
longlast_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;
lsm6dsoxSensor.Get_X_Axes(acceleration);
// Plot data for each axis in mg
Serial.print("AccelerationX=");
Serial.print(acceleration);
Serial.print("mg, AccelerationY=");
Serial.print(acceleration);
Serial.print("mg, AccelerationZ=");
Serial.print(acceleration);
Serial.println("mg");
if (millis() - last_update_time > interval)
{
if (acceleration > 1000)
{
//encoder_diff--;
flag = 1;
Serial.print("flag=1");
Serial1.write(49);
digitalWrite(LED_BUILTIN,LOW);
}
else if (acceleration < -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;
lsm6dsoxSensor.Get_G_Axes(rotation);
// Plot data for each axis in milli degrees per second
Serial.print("RotationX=");
Serial.print(rotation);
Serial.print("mdps, RotationY=");
Serial.print(rotation);
Serial.print("mdps, RotationZ=");
Serial.print(rotation);
Serial.println("mdps");
}
}
void loop() {
IMU_update(200);
}</code></pre>
<p><strong>PICO2端代码:</strong></p>
<pre>
<code class="language-cpp">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){} //清空串口缓存
}
}</code></pre>
<p>上位机打印的IMU数据:</p>
<p><strong><span style="font-size:18px;"> 总结:</span></strong></p>
<p><span style="font-size: 18px;"><b> </b></span>实现IMU的控制与数据读取还是十分简单的,只需调用官方Arduino库即可,这大大降低了开发者的开发难度。</p>
<p><span style="font-size: 18px;"><b> 演示视频:</b></span></p>
<p> 32888557c56d34de4aa82d0d0364dcf8<br />
</p>
视频中应该是调用意法半导体官方的arduino库https://github.com/stm32duino <p>IMU厂商,你这都是大厂啊 </p>
页:
[1]