板卡介绍
本次活动所使用的板卡为STMicroelectronics推出的X-NUCLEO-IKS4A1,这是一款专为STM32 Nucleo系列开发板设计的运动MEMS和环境传感器扩展板。X-NUCLEO-IKS4A1由主板IQS4A1和可拆卸的Qvar触摸/滑动电极附加板MKE001A组成,为动作检测、环境监测等IoT领域提供了多种传感器参考解决方案。
该板卡集成了多种传感器,包括LSM6DSO16IS(MEMS 3D加速度计 + 3D陀螺仪)、LIS2MDL(MEMS 3D磁力计)、LIS2DUXS12(超低功耗MEMS 3轴加速度计)、低功率和高精度MEMS绝对数字输出气压计、SHT40AD1B(高精度超低功耗的温湿度传感器)以及STTS22H(低电压,超低功耗,0.5°C精度的温度传感器)。本实验主要使用IMU(包括加速度计和陀螺仪)、磁力计以及气压传感器来实现VR体感追踪器的功能。
完成任务
本次活动的任务是利用X-NUCLEO-IKS4A1板卡上的IMU、磁场和气压传感器,制作成VR体感追踪器。通过算法计算板卡的空间姿态以及相对海拔高度变化,并将数据传输到电脑上。
加速度计和陀螺仪介绍
加速度计是一种测量物体加速度的传感器,其实现原理基于牛顿第二定律(F=ma),其中F代表力,m代表质量,a代表加速度。在加速度计中,一个敏感质量(通常是微小的质量块)被悬挂在弹簧或弹性体上。当物体加速时,这个敏感质量会受到惯性力的作用而相对于外壳产生位移。通过测量这个位移量,可以推算出物体在特定方向上的加速度。
现代的加速度计通常采用微机电系统(MEMS)技术制造,其中包含一个或多个微小的电容或压阻式传感器来检测敏感质量的位移。这些传感器将位移转换为电信号输出,经过放大和滤波后,可以提供给处理器进行进一步处理。
陀螺仪是一种用于测量或维持旋转的装置,其实现原理基于角动量守恒定律。在陀螺仪中,一个高速旋转的转子(陀螺)被安装在支架上,当转子受到外力作用时,它会试图保持其旋转轴的方向不变。
现代的陀螺仪通常也采用MEMS技术制造,它们包含一个微小的转子和一个或多个检测转子偏转的传感器。当物体发生旋转时,转子会受到科里奥利力的作用而产生微小的偏转。这个偏转量被传感器检测并转换为电信号输出,经过放大和滤波后,可以提供给处理器进行进一步处理。通过测量转子的偏转量,可以推算出物体在特定方向上的角速度。
磁力计介绍
在霍尔效应中,当电流通过一个导体(霍尔元件)并在垂直于电流方向施加一个磁场时,会在导体的两侧产生电势差(霍尔电压)。这个电势差与磁场强度和电流大小成正比。通过测量霍尔电压,可以推算出磁场的大小和方向。
磁阻效应是指某些材料的电阻值随磁场变化而变化的现象。在磁阻传感器中,当磁场变化时,材料的电阻值也会发生变化。通过测量电阻值的变化,可以推算出磁场的大小和方向。
现代的磁力计通常采用这些原理之一或它们的组合来测量磁场。它们通常包含多个敏感元件(如霍尔元件或磁阻元件),以便在三个方向上测量磁场的变化。通过解算这些方向上的磁场数据,可以确定物体的方向或姿态。
绝对数字输出气压计介绍
绝对数字输出气压计是一种用于测量大气压力的传感器,其实现原理基于电容变化。在气压计中,两个金属电极被密封在一个薄膜之间,形成一个电容器。当大气压力变化时,薄膜会发生形变,导致两个电极之间的距离或重叠面积发生变化,进而改变电容器的电容值。通过测量这个电容值的变化,也可以推算出大气压力的大小。
绝对数字输出气压计通常将测量到的压力值转换为数字信号输出,以便与微处理器或其他数字系统接口。它们具有高精度、低噪声和低功耗的特点,广泛应用于气象监测、高度测量、无人机导航等领域。
实现思路
- 数据采集:通过STM32 Nucleo开发板与X-NUCLEO-IKS4A1板卡进行通信,实时采集加速度计、陀螺仪、磁力计和气压计的数据。
- 数据处理:利用传感器融合算法(如卡尔曼滤波或互补滤波)对采集到的数据进行处理,以计算物体的空间姿态(包括俯仰角、偏航角和翻滚角)和相对海拔高度变化。
- 数据传输:将处理后的数据通过串口或其他通信方式传输到电脑上进行显示和分析。
实现方法
- 初始化 LPS22DF 气压计。
可以看到,默认情况下,WHO_AM_I 寄存器的值为 0xB4。
找到气压计后,设置采样频率为 200Hz。
bool LPS22DF_Init(I2C_HandleTypeDef hi2c1)
{
uint8_t id;
HAL_I2C_Mem_Read(&hi2c1, 0xBA, 0x0F, I2C_MEMADD_SIZE_8BIT, &id, 1, -1);
if (id != 0xB4)
{
printf("LPS22DF ID is not correct: 0x%x\r\n", id);
return false;
}
uint8_t d = 0x08;
HAL_I2C_Mem_Write(&hi2c1, 0xBA, 0x10, I2C_MEMADD_SIZE_8BIT, &d, 1, -1); // CTRL_REG1
return true;
}
- 初始化 LIS2MDL 磁力计。
可以看到,默认情况下,WHO_AM_I 寄存器的值为 0x40。
然后设置采样率为 10Hz。
bool LIS2MDL_Init(I2C_HandleTypeDef hi2c1)
{
uint8_t id;
HAL_I2C_Mem_Read(&hi2c1, 0x3C, 0x4F, I2C_MEMADD_SIZE_8BIT, &id, 1, -1);
if (id != 0x40)
{
printf("LIS2MDL ID is not correct: 0x%x\r\n", id);
return false;
}
uint8_t d = 0x00;
HAL_I2C_Mem_Write(&hi2c1, 0x3C, 0x60, I2C_MEMADD_SIZE_8BIT, &d, 1, -1); // CFG_REG_A
return true;
}
- 初始化 LSM6DSV16X 加速度计和陀螺仪
可以看到,默认情况下,WHO_AM_I 寄存器的值为 0x70。
使能传感器,并设置采样频率为 7.68 kHz。
bool LSM6DSV16X_Init(I2C_HandleTypeDef hi2c1)
{
uint8_t id;
HAL_I2C_Mem_Read(&hi2c1, 0xD6, 0x0F, I2C_MEMADD_SIZE_8BIT, &id, 1, -1);
if (id != 0x70)
{
printf("LSM6DSV16X ID is not correct: 0x%x\r\n", id);
return false;
}
uint8_t d = 0x0A;
HAL_I2C_Mem_Write(&hi2c1, 0xD6, 0x10, I2C_MEMADD_SIZE_8BIT, &d, 1, -1); // CTRL1_A
d = 0x0A;
HAL_I2C_Mem_Write(&hi2c1, 0xD6, 0x11, I2C_MEMADD_SIZE_8BIT, &d, 1, -1); // CTRL2_G
d = 0x04;
HAL_I2C_Mem_Write(&hi2c1, 0xD6, 0x15, I2C_MEMADD_SIZE_8BIT, &d, 1, -1); // CTRL2_G
return true;
}
- 初始化 Fusion 库。
在现代导航和定位系统中,结合加速度计、陀螺仪和磁力计的数据,通过Mahony融合算法,可以准确获取物体的欧拉角(翻滚角、俯仰角和偏航角),从而实现对物体姿态的精确描述。
首先,加速度计用于测量物体在三维空间中的加速度,包括重力加速度。当物体静止时,加速度计主要测量的是重力加速度,这可以用来确定物体的倾斜角度(翻滚角和俯仰角)。然而,加速度计对于快速旋转的响应不够灵敏,因此需要结合陀螺仪的数据。
陀螺仪通过测量角速度来确定物体的旋转方向和速度。陀螺仪对角速度的测量非常敏感,可以实时跟踪物体的旋转情况。但是,陀螺仪在长时间运行时会因为积分误差而导致姿态漂移,这时就需要利用加速度计的数据进行校正。
磁力计则用于测量地球磁场的方向,从而确定物体的偏航角。磁力计可以提供一个稳定的参考方向,帮助校正陀螺仪的误差。
Mahony融合算法通过结合这三种传感器的数据,实现姿态的准确估计。该算法首先根据加速度计和磁力计的数据计算出物体的重力向量和磁场向量在机体坐标系下的表示,然后利用陀螺仪提供的角速度数据进行姿态更新。在更新过程中,通过计算重力向量和磁场向量在机体坐标系下的估计值与测量值之间的误差,对陀螺仪数据进行校正,以减少姿态漂移。
具体来说,Mahony算法将加速度计和磁力计的数据融合为一个参考向量,并与陀螺仪数据一起输入到滤波器中。滤波器通过计算参考向量与当前姿态估计值之间的误差,并结合陀螺仪数据,更新姿态估计值。这个过程不断迭代,以实现对物体姿态的实时、准确跟踪。
xioTechnologies/Fusion (github.com) 提供了简易的调用方法,可以简单地融合不同的传感器数据。
// Define calibration (replace with actual calibration data if available)
const FusionMatrix gyroscopeMisalignment = {
.array = {
{1.0f, 0.0f, 0.0f},
{0.0f, 1.0f, 0.0f},
{0.0f, 0.0f, 1.0f}}};
const FusionVector gyroscopeSensitivity = {{1.0f, 1.0f, 1.0f}};
const FusionVector gyroscopeOffset = {{0.0f, 0.0f, 0.0f}};
const FusionMatrix accelerometerMisalignment = {.array = {{1.0f, 0.0f, 0.0f},
{0.0f, 1.0f, 0.0f},
{0.0f, 0.0f, 1.0f}}};
const FusionVector accelerometerSensitivity = {{1.0f, 1.0f, 1.0f}};
const FusionVector accelerometerOffset = {{0.0f, 0.0f, 0.0f}};
const FusionMatrix softIronMatrix = {.array = {{1.0f, 0.0f, 0.0f},
{0.0f, 1.0f, 0.0f},
{0.0f, 0.0f, 1.0f}}};
const FusionVector hardIronOffset = {{0.0f, 0.0f, 0.0f}};
// Initialise algorithms
FusionOffset offset;
FusionAhrs ahrs;
FusionOffsetInitialise(&offset, SAMPLE_RATE);
FusionAhrsInitialise(&ahrs);
// Set AHRS algorithm settings
const FusionAhrsSettings settings = {
.convention = FusionConventionNwu,
.gain = 0.5f,
.gyroscopeRange = 2000.0f, /* replace this with actual gyroscope range in degrees/s */
.accelerationRejection = 10.0f,
.magneticRejection = 10.0f,
.recoveryTriggerPeriod = 5 * SAMPLE_RATE, /* 5 seconds */
};
FusionAhrsSetSettings(&ahrs, &settings);
- 循环读取传感器值并进行数据融合
float mx, my, mz;
float gx, gy, gz, ax, ay, az;
LIS2MDL_ReadData(hi2c1, &mx, &my, &mz);
int altitude = LPS22DF_ReadData(hi2c1) * 100;
LSM6DSV16X_ReadData(hi2c1, &gx, &gy, &gz, &ax, &ay, &az);
FusionVector gyroscope = {.array = {gx, -gy, -gz}};
FusionVector accelerometer = {.array = {-ax, ay, az}};
FusionVector magnetometer = {.array = {mx, -my, mz}};
gyroscope = FusionCalibrationInertial(gyroscope, gyroscopeMisalignment, gyroscopeSensitivity, gyroscopeOffset);
accelerometer = FusionCalibrationInertial(accelerometer, accelerometerMisalignment, accelerometerSensitivity, accelerometerOffset);
magnetometer = FusionCalibrationMagnetic(magnetometer, softIronMatrix, hardIronOffset);
gyroscope = FusionOffsetUpdate(&offset, gyroscope);
FusionAhrsUpdate(&ahrs, gyroscope, accelerometer, magnetometer, 1.0f / SAMPLE_RATE);
const FusionEuler euler = FusionQuaternionToEuler(FusionAhrsGetQuaternion(&ahrs));
- 传输数据到匿名助手
匿名助手的传输数据协议如表所示,只要按照规范填写即可。
const int16_t roll = euler.angle.roll * 100;
const int16_t pitch = euler.angle.pitch * 100;
const int16_t yaw = euler.angle.yaw * 100;
uint8_t buffer[21] = {0};
buffer[0] = 0xAB;
buffer[1] = 0xDF;
buffer[2] = 0xFE;
buffer[3] = 0x03;
buffer[4] = 0x07;
buffer[5] = 0x00;
buffer[6] = (roll & 0xFF);
buffer[7] = ((roll >> 8) & 0xFF);
buffer[8] = (pitch & 0xFF);
buffer[9] = ((pitch >> 8) & 0xFF);
buffer[10] = (yaw & 0xFF);
buffer[11] = ((yaw >> 8) & 0xFF);
buffer[12] = 0x01;
uint8_t sum = 0, add_sum = 0;
for (int i = 0; i < 13; i++)
{
sum += buffer[i];
add_sum += sum;
}
buffer[13] = sum;
buffer[14] = add_sum;
HAL_UART_Transmit(&huart2, (uint8_t *)buffer, 16, 1000);
buffer[0] = 0xAB;
buffer[1] = 0xDF;
buffer[2] = 0xFE;
buffer[3] = 0x05;
buffer[4] = 13;
buffer[5] = 0x00;
buffer[6] = altitude & 0xFF;
buffer[7] = altitude >> 8 & 0xFF;
buffer[8] = altitude >> 16 & 0xFF;
buffer[9] = altitude >> 24 & 0xFF;
buffer[10] = 0x00;
buffer[11] = 0x00;
buffer[12] = 0x00;
buffer[13] = 0x00;
buffer[14] = 0x00;
buffer[15] = 0x00;
buffer[16] = 0x00;
buffer[17] = 0x00;
buffer[18] = 0x00;
sum = 0, add_sum = 0;
for (int i = 0; i < 19; i++)
{
sum += buffer[i];
add_sum += sum;
}
buffer[19] = sum;
buffer[20] = add_sum;
HAL_UART_Transmit(&huart2, (uint8_t *)buffer, 21, 1000);
实物展示
遇到的问题及解决方案
- 数据噪声大:在数据采集过程中发现数据噪声较大,影响姿态计算的准确性。解决方案:采用低通滤波器对数据进行滤波处理,减少噪声干扰。
- 算法优化:在初始的算法实现中发现姿态计算存在一定的延迟和抖动。解决方案:优化传感器融合算法,采用更高效的滤波方法和参数调整,提高姿态计算的稳定性和准确性。
- 通信问题:在数据传输过程中偶尔出现数据丢失或乱码现象。解决方案:检查通信线路和接口是否稳定可靠,调整串口通信的波特率和数据格式等参数,确保数据能够正确传输。
总结感想
首先,我要衷心感谢硬禾学堂举办的这次活动,为我提供了一个宝贵的机会,让我能够深入探索和实践MEMS传感器的应用。这次活动不仅让我收获了丰富的知识和经验,还激发了我对物联网和嵌入式系统开发的浓厚兴趣。
通过本次项目实践,我成功利用X-NUCLEO-IKS4A1板卡上的IMU、磁场和气压传感器制作了VR体感追踪器,实现了对物体空间姿态和相对海拔高度变化的测量。在实验过程中,我不仅学习了传感器的工作原理和数据处理方法,还提高了对嵌入式系统开发的实践能力。同时,我也遇到了一些问题并成功解决了它们,这让我更加深入地理解了嵌入式系统开发。