STM32模拟IIC驱动MS5611气压传感器实现高精度高度测量

STM32模拟IIC驱动MS5611气压传感器实现高精度高度测量

1. 项目概述:从气压到高度,一个传感器的工程实践

最近在做一个需要精确测量海拔高度的项目,选来选去,最终敲定了MS5611这款气压传感器。这玩意儿在无人机、气象站、室内导航里用得挺多,核心就俩字:。它不仅能测气压,还能测温度,然后通过一套标准的公式,把气压值换算成相对高度。听起来简单,但真动起手来,从硬件连接到软件驱动,再到数据处理,每一步都有不少门道。

我这次用的主控是STM32F103C8T6,也就是大家常说的“蓝桥杯”或者“最小系统板”核心,资源够用,性价比高。通信方式上,我没有用芯片自带的硬件I2C,而是选择了模拟IIC。为啥?因为STM32F1系列的硬件I2C在复杂应用里偶尔会出些幺蛾子,比如锁死总线,调试起来头疼。模拟IIC虽然占用点CPU时间,但时序完全自己掌控,出了问题也好排查,对于MS5611这种对时序要求不算特别严苛的传感器来说,是个稳妥的选择。

整个项目的目标很明确:用STM32F103C8T6,通过模拟IIC驱动MS5611,稳定地读取气压和温度原始数据,然后利用传感器手册提供的算法,计算出当前的海拔高度。下面,我就把从硬件连接到软件实现,再到数据处理和调试的完整过程,以及踩过的坑和总结的经验,详细拆解一遍。

2. 核心思路与方案选型:为什么是MS5611和模拟IIC?

2.1 传感器选型:MS5611的优势与挑战

市面上气压传感器不少,像BMP280、BMP388、LPS22HB等等。我选MS5611,主要是看中它几个特点:

  1. 高分辨率与高精度:MS5611-01BA03的分辨率可以达到0.012毫巴(约0.1米高度变化),长期稳定性也好,这对于需要感知微小高度变化的场景(比如定高飞行、楼层识别)至关重要。
  2. 内置高精度温度传感器:高度解算需要温度数据对气压进行补偿,MS5611内部集成的温度传感器精度足够,避免了外接温度传感器带来的校准和同步问题。
  3. 接口简单,驱动成熟:标准的I2C或SPI接口,协议公开,网上参考代码多,降低了开发门槛。

当然,它也有“挑战”。它的输出是原始的数字压力值(D1)和温度值(D2),需要根据出厂校准参数(PROM)进行一系列复杂的计算,才能得到最终的气压和温度,进而算出高度。这个过程涉及大量定点或浮点运算,对MCU的算力有一定要求,好在STM32F103完全能胜任。

2.2 通信协议抉择:硬件I2C vs. 模拟IIC

这是一个经典的老问题了。STM32F103的硬件I2C外设(I2C1, I2C2)功能强大,支持DMA,不占用CPU。但在实际项目中,尤其是在中断嵌套复杂、电源波动或总线负载稍大的情况下,硬件I2C偶尔会出现总线忙(BUSY)标志无法清除、从机无应答(AF)等异常,导致通信卡死,需要复位才能恢复。

模拟IIC的优势就在于其极致的可控性:

  • 完全掌控时序:启动、停止、发送应答/非应答、读写每一位数据,都由你写的Delay_us()函数和GPIO操作决定。你可以根据MS5611数据手册的要求,精确调整SCL高电平和低电平的保持时间。
  • 极强的鲁棒性:即使某次通信被意外打断(比如高优先级中断),你也可以在代码里轻松加入超时判断和总线恢复机制(例如,连续发送9个时钟脉冲释放总线)。
  • 便于调试:你可以随时在任意两个步骤之间插入调试信息(点灯、串口打印),精准定位通信失败发生在“发送设备地址”后还是“读取数据字节”时。

对于MS5611,其最高时钟频率是400kHz(高速模式)。我们用模拟IIC实现时,通过精心调整延时,跑到200-300kHz是没问题的,完全满足其数据读取速率要求。因此,在稳定性和开发效率的权衡下,我选择了模拟IIC

2.3 整体软件架构设计

整个程序的运行逻辑是一个清晰的“初始化-采集-计算”循环:

  1. 初始化阶段:配置GPIO(模拟SCL和SDA线)、初始化延时函数、复位MS5611、读取其内部的6个工厂校准系数(PROM)并校验。
  2. 数据采集循环
    • 发送命令,启动一次气压(D1)的ADC转换。
    • 等待转换完成(根据精度选择延时,如OSR=4096时需约9ms)。
    • 发送命令,读取转换好的气压原始值D1。
    • 同理,启动并读取温度原始值D2。
    • 这个循环周期可以根据应用需求设定,比如每秒10次(100ms)。
  3. 数据处理阶段:利用读取到的D1、D2以及之前读出的6个校准系数,按照数据手册给出的公式,依次计算校准后的温度TEMP、补偿后的气压P,最后利用气压高度公式计算相对高度ALTITUDE
  4. 输出与应用:将计算得到的高度、温度、气压值通过串口发送到上位机显示,或者用于飞控、导航等核心算法。

3. 硬件连接与底层驱动实现

3.1 电路连接与引脚配置

MS5611模块(通常是一个带滤波电容的小板子)一般有5个引脚:VCC(3.3V)、GND、SCL、SDA、PS(接地选择I2C地址)。STM32F103C8T6的任意两个GPIO口都可以用来模拟I2C,我选择了PB6PB7,这样万一以后想切换到硬件I2C1也方便。

连接方式非常简单:

  • MS5611.VCC -> 3.3V
  • MS5611.GND -> GND
  • MS5611.SCL -> PB6
  • MS5611.SDA -> PB7
  • MS5611.PS -> GND (此时I2C地址为0xEC,写地址;0xED,读地址)

注意:一定要确保电源稳定。MS5611对电源噪声比较敏感,如果电源纹波大,会影响ADC转换精度。建议在模块的VCC和GND之间就近并联一个0.1uF和10uF的电容。

在STM32CubeMX里配置PB6和PB7为推挽输出模式(Output Push Pull),初始电平设为高。这里有个关键点:I2C总线是开漏输出,需要外接上拉电阻。但我们在软件模拟时,将GPIO设置为推挽输出高电平,可以模拟上拉的效果,驱动能力更强。在释放总线(即作为输入读取SDA时)前,我们需要将SDA线的模式临时切换为浮空输入(Input floating)或带上拉输入,以正确读取从机应答。

3.2 模拟IIC时序的精准实现

模拟IIC的核心就是四个基本时序:起始信号(START)、停止信号(STOP)、发送字节(WRITE BYTE)、读取字节(READ BYTE)。每一个都要严格按照MS5611数据手册的时序图来写。

// 宏定义引脚操作,提高代码可读性和执行效率 #define MS5611_SCL_HIGH() HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET) #define MS5611_SCL_LOW() HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_RESET) #define MS5611_SDA_HIGH() HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET) #define MS5611_SDA_LOW() HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET) #define MS5611_SDA_READ() HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7) // 微秒级延时函数,需要根据你的系统时钟精确调教 void Delay_us(uint32_t us) { uint32_t ticks = us * (SystemCoreClock / 1000000) / 5; // 粗略计算,需校准 while(ticks--); } // 产生IIC起始信号 void IIC_Start(void) { MS5611_SDA_HIGH(); MS5611_SCL_HIGH(); Delay_us(5); // 保持时间>4.7us MS5611_SDA_LOW(); Delay_us(5); MS5611_SCL_LOW(); // 钳住总线,准备发送数据 } // 产生IIC停止信号 void IIC_Stop(void) { MS5611_SDA_LOW(); MS5611_SCL_HIGH(); Delay_us(5); MS5611_SDA_HIGH(); Delay_us(5); }

发送一个字节的函数需要循环8次,将数据的最高位(MSB)先放到SDA线上,然后拉高SCL,保持一定时间后再拉低SCL,完成一位的发送。关键是要在SCL低电平期间变化SDA数据。

读取一个字节的函数则相反,先将SDA线设置为输入模式(在HAL库中,可以先SDA_HIGH(),然后快速将引脚模式切换为输入,或者直接使用读引脚函数,前提是硬件上已有上拉),在SCL高电平期间去读取SDA线的电平。

实操心得:延时函数的校准Delay_us的准确性直接决定了IIC通信的速率和稳定性。最土但最有效的方法是用逻辑分析仪或者示波器抓取波形。如果没有仪器,可以写一个测试程序,让一个GPIO口每隔10us翻转一次,然后用示波器测量实际周期,反过来调整Delay_us函数里的计算参数。我最终调出的一个大约1us的延时,在72MHz系统时钟下,其核心是一个__NOP()指令的空循环。

3.3 MS5611的初始化与PROM读取

MS5611上电后需要一个小延时(约20ms)等待稳定,然后发送一个复位命令0x1E。复位后,必须读取其PROM中的6个校准系数(C1-C6)。这些系数是每个传感器在出厂时单独校准的,用于后续的高精度计算。

// MS5611复位 void MS5611_Reset(void) { IIC_Start(); IIC_Send_Byte(0xEC); // 发送器件地址+写命令 IIC_Wait_Ack(); IIC_Send_Byte(0x1E); // 复位命令 IIC_Wait_Ack(); IIC_Stop(); HAL_Delay(20); // 等待复位完成 } // 读取PROM中的一个字(16位) uint16_t MS5611_Read_PROM(uint8_t addr) { uint16_t data = 0; uint8_t cmd = 0xA0 + (addr * 2); // PROM读取命令基址 IIC_Start(); IIC_Send_Byte(0xEC); IIC_Wait_Ack(); IIC_Send_Byte(cmd); IIC_Wait_Ack(); IIC_Stop(); // 重新启动,读取数据 IIC_Start(); IIC_Send_Byte(0xED); // 发送器件地址+读命令 IIC_Wait_Ack(); data = IIC_Read_Byte(1) << 8; // 读取高8位,并发送ACK data |= IIC_Read_Byte(0); // 读取低8位,发送NACK IIC_Stop(); return data; } // 初始化并读取所有PROM系数,并进行CRC校验 uint8_t MS5611_Init(void) { MS5611_Reset(); HAL_Delay(10); for(int i=0; i<6; i++) { prom[i] = MS5611_Read_PROM(i+1); // PROM地址从0xA2开始(C1) } // 此处应进行CRC校验,校验码在prom[6]的低4位 // uint8_t crc_read = prom[6] & 0x000F; // uint8_t crc_calc = MS5611_CRC4(prom); // 需实现CRC4校验函数 // if(crc_calc != crc_read) return 1; // 校验失败 return 0; // 初始化成功 }

注意事项:PROM的CRC校验数据手册提供了一个4位CRC校验算法,用于验证读取的PROM数据是否正确。在实际应用中,强烈建议实现并启用这个校验。我曾遇到过因为电源干扰,导致读出的C1系数错了一个位,最后计算出的高度飘了几百米。加上CRC校验后,一旦发现错误,可以重新读取或报错,极大地提高了系统的可靠性。

4. 数据采集、转换与核心算法实现

4.1 启动转换与读取ADC值

MS5611的ADC转换有多个精度选项,通过“过采样率(OSR)”设置。OSR越高,转换时间越长,分辨率也越高,噪声越低。常用的有256、512、1024、2048、4096。我选择了OSR=4096,以获得最高的分辨率。

// 启动D1(气压)或D2(温度)转换 void MS5611_StartConversion(uint8_t cmd) { // cmd: 0x40+OSR值 用于D1, 0x50+OSR值 用于D2 IIC_Start(); IIC_Send_Byte(0xEC); IIC_Wait_Ack(); IIC_Send_Byte(cmd); IIC_Wait_Ack(); IIC_Stop(); } // 读取ADC转换结果(24位) uint32_t MS5611_ReadADC(void) { uint32_t adc_value = 0; IIC_Start(); IIC_Send_Byte(0xEC); IIC_Wait_Ack(); IIC_Send_Byte(0x00); // 读ADC命令 IIC_Wait_Ack(); IIC_Stop(); IIC_Start(); IIC_Send_Byte(0xED); IIC_Wait_Ack(); adc_value = (uint32_t)IIC_Read_Byte(1) << 16; // 读第一个字节,发ACK adc_value |= (uint32_t)IIC_Read_Byte(1) << 8; // 读第二个字节,发ACK adc_value |= (uint32_t)IIC_Read_Byte(0); // 读第三个字节,发NACK IIC_Stop(); return adc_value; } // 一次完整的温度和气压数据获取流程 void MS5611_GetRawData(uint32_t *D1, uint32_t *D2) { MS5611_StartConversion(0x48); // 启动D1转换,OSR=4096 (0x40+8) HAL_Delay(10); // 等待转换完成,OSR=4096需约9ms,留有余量 *D1 = MS5611_ReadADC(); MS5611_StartConversion(0x58); // 启动D2转换,OSR=4096 (0x50+8) HAL_Delay(10); *D2 = MS5611_ReadADC(); }

关键点:转换等待时间启动转换后,必须等待足够的时间让ADC完成工作。HAL_Delay()是阻塞延时,在实时性要求高的系统(如飞控)中,更好的做法是记录转换开始的时间戳,然后在主循环中查询是否超时,避免长时间阻塞。MS5611数据手册里给出了不同OSR对应的最大转换时间,OSR=4096约为9ms,保险起见我延时了10ms。

4.2 从原始数据到物理量:核心解算公式

这是整个项目的数学核心。我们需要用读出的原始值D1(气压)、D2(温度)和PROM系数C1-C6,计算出真实温度dTTEMP、补偿温度OFFSENS,最终得到真实气压P。公式看起来复杂,但按步骤来就很清晰。

为了平衡精度和速度,我使用了int64_t(或long long)类型进行中间计算,避免溢出。

// 使用64位整数进行计算,防止溢出(假设C1-C6已从PROM读出并存入全局变量) void MS5611_Calculate(int32_t *temperature, int32_t *pressure) { uint32_t D1, D2; int64_t OFF, SENS, OFF2, SENS2, T2; int64_t dT, TEMP; int64_t P; MS5611_GetRawData(&D1, &D2); // 1. 计算dT和实际温度(单位:0.01摄氏度) dT = (int64_t)D2 - ((int64_t)prom[4] << 8); // dT = D2 - C5 * 2^8 TEMP = 2000 + ((dT * (int64_t)prom[5]) >> 23); // TEMP = 2000 + dT * C6 / 2^23 // 2. 计算第二温度补偿(当温度低于20°C时) OFF2 = 0; SENS2 = 0; T2 = 0; if(TEMP < 2000) { // 如果温度低于20°C T2 = (dT * dT) >> 31; OFF2 = 5 * ((TEMP - 2000) * (TEMP - 2000)) >> 1; SENS2 = 5 * ((TEMP - 2000) * (TEMP - 2000)) >> 2; if(TEMP < -1500) { // 如果温度低于-15°C OFF2 = OFF2 + 7 * (TEMP + 1500) * (TEMP + 1500); SENS2 = SENS2 + ((11 * (TEMP + 1500) * (TEMP + 1500)) >> 1); } } // 3. 计算补偿值OFF和SENS OFF = ((int64_t)prom[1] << 16) + (((int64_t)prom[3] * dT) >> 7); // OFF = C2 * 2^16 + (C4 * dT) / 2^7 SENS = ((int64_t)prom[0] << 15) + (((int64_t)prom[2] * dT) >> 8); // SENS = C1 * 2^15 + (C3 * dT) / 2^8 // 4. 应用第二温度补偿 TEMP = TEMP - T2; OFF = OFF - OFF2; SENS = SENS - SENS2; // 5. 计算最终气压(单位:帕斯卡 * 0.01,即0.01Pa) P = ((((int64_t)D1 * SENS) >> 21) - OFF) >> 15; // P = (D1 * SENS / 2^21 - OFF) / 2^15 *temperature = (int32_t)TEMP; // 单位:0.01 °C *pressure = (int32_t)P; // 单位:0.01 Pa }

公式解读与定点运算技巧: 这些公式直接来自数据手册,但手册中使用的是浮点数。在MCU上,浮点运算(尤其是double)速度较慢。因此,我全部采用了定点运算。注意观察公式中的>> 23>> 7等操作,这就是在模拟除以2的幂次方(即/ (2^23))。通过将系数预先左移(如C2 << 16),我们实际上是在进行整数运算的同时,保留了足够的小数精度。这是嵌入式编程中处理小数的常用技巧。

4.3 从气压到相对高度:国际标准大气模型

得到以0.01Pa为单位的气压值P后,我们就可以计算相对高度了。这里使用的是国际标准大气(ISA)模型下的气压-高度公式。这个公式假设海平面标准气压为101325Pa,温度递减率为每升高1000米下降6.5°C。

// 根据气压计算相对高度(单位:米) float MS5611_GetAltitude(int32_t pressure_pa, int32_t sea_level_pa) { // pressure_pa: 当前气压(单位:Pa) // sea_level_pa: 海平面(或参考点)气压(单位:Pa),通常用初始位置的气压值 float altitude; altitude = 44330.0f * (1.0f - powf((float)pressure_pa / (float)sea_level_pa, 0.190294957f)); return altitude; }

核心要点:参考气压的设定公式中的sea_level_pa是关键。它不一定是真实的海平面气压(那需要气象站数据),在大多数应用中,我们关心的是相对高度变化。因此,常见的做法是:

  1. 在设备启动后,连续读取多次气压值(比如1秒内读10次)并求平均,将这个平均值作为“零高度”的参考气压sea_level_pa
  2. 此后计算出的高度,就是相对于这个初始位置的高度变化。

如果你需要绝对海拔高度,那么就需要知道当前位置的真实海平面气压,这通常需要通过GPS、网络或其他气象服务来获取,然后用那个值作为sea_level_pa

5. 系统集成、滤波与性能优化

5.1 主程序逻辑与数据流

将上述所有模块组合起来,形成一个稳定的数据采集系统。我通常会在一个定时器中断(例如10ms一次)里设置标志位,在主循环中查询这个标志,以固定的频率(如100Hz)去执行数据采集和计算,确保数据更新的周期性。

// 全局变量 int32_t g_temperature = 0; // 单位:0.01°C int32_t g_pressure = 0; // 单位:0.01Pa float g_altitude = 0.0f; // 单位:米 int32_t g_sea_level_pressure = 10132500; // 初始海平面气压,单位0.01Pa (即101325.00 Pa) int main(void) { // HAL初始化、时钟配置等 // ... MS5611_Init(); // 初始化MS5611,读取PROM // 获取初始位置气压,作为相对高度零点 MS5611_Calculate(&g_temperature, &g_pressure); g_sea_level_pressure = g_pressure; // 将第一次测量的气压设为参考点 while (1) { if(data_update_flag) { // 由定时器置位,例如每秒10次 data_update_flag = 0; MS5611_Calculate(&g_temperature, &g_pressure); g_altitude = MS5611_GetAltitude(g_pressure/100, g_sea_level_pressure/100); // 转换为Pa单位 // 通过串口发送数据:温度(°C)、气压(Pa)、高度(m) printf("T:%.2f C, P:%.2f Pa, ALT:%.2f m\r\n", g_temperature/100.0f, g_pressure/100.0f, g_altitude); } // 其他任务... } }

5.2 数据滤波:让高度值更“安静”

原始的气压和高度数据会有噪声,直接使用会跳变得很厉害。必须加入滤波算法。对于高度数据,我推荐使用一阶互补滤波卡尔曼滤波。这里给一个简单有效的一阶低通滤波实现:

float alt_filtered = 0.0f; float filter_factor = 0.2f; // 滤波系数,越小越平滑,但延迟越大。0.1~0.3之间常用。 // 在主循环中,计算完g_altitude后 alt_filtered = alt_filtered * (1.0f - filter_factor) + g_altitude * filter_factor;

对于更复杂的动态场景(如无人机快速升降),需要使用自适应滤波或卡尔曼滤波,将加速度计的数据也融合进来,以区分真实运动和环境噪声。

5.3 精度与稳定性优化实践

  1. 电源去耦:重申一遍,在MS5611模块的VCC和GND引脚间并联一个10uF钽电容和一个0.1uF陶瓷电容,尽可能靠近传感器引脚,这是提升数据稳定性的成本最低、效果最显著的方法。
  2. 温度补偿的重要性:MS5611内部的计算公式已经包含了温度补偿。但要注意,传感器自身发热会影响测量。避免将MS5611放置在发热大的芯片(如LDO、主控)旁边。如果条件允许,可以让MCU间歇性工作,减少持续发热的影响。
  3. 软件异常处理
    • IIC通信超时:在IIC_Wait_Ack()等函数中加入超时机制,如果等待ACK超过一定时间(如500us),则判定为通信失败,执行总线恢复序列(连续发9个SCL时钟)。
    • 数据合理性判断:对计算出的温度、气压值进行范围判断。例如,温度是否在-40°C到85°C(芯片工作范围)内?气压是否在10hPa到1200hPa(传感器量程)内?如果超出,则丢弃该次数据,并尝试重新初始化传感器。
  4. 计算速度优化MS5611_Calculate函数中的64位乘除法比较耗时。如果计算频率很高(>100Hz),可以考虑将一些常数计算提前,或者使用编译器优化选项。对于固定OSR,dTOFFSENS的计算部分是固定的,可以尝试用查表法或简化公式进行近似,但这会牺牲一些精度。

6. 常见问题排查与调试技巧

调试嵌入式传感器,逻辑分析仪是神器。没有的话,串口打印调试信息是主要手段。

6.1 问题速查表

现象可能原因排查步骤
IIC通信完全无应答1. 硬件连接错误(线接反、虚焊)
2. 电源问题(电压不对、电流不足)
3. 上拉电阻未接或阻值过大(>10K)
4. 器件地址错误
1. 用万用表检查VCC、GND、SCL、SDA电压。
2. 检查PS引脚电平,确认地址(0xEE/0xEC)。
3. 用模拟IIC读一个已知器件(如EEPROM)测试时序。
能读到数据,但全是0xFF或0x001. 通信时序不对(速度过快/过慢)
2. 启动转换后未等待足够时间就读取
3. 读取ADC值的命令或流程错误
1. 用逻辑分析仪抓取IIC波形,对照手册看时序。
2. 确保HAL_Delay或等待函数准确。
3. 检查MS5611_ReadADC函数,确认是发0x00命令读3个字节。
读出的PROM数据CRC校验失败1. 电源噪声导致数据传输出错
2. IIC时序在临界状态
3. 传感器本身损坏(少见)
1. 加强电源滤波。
2. 适当增加IIC时钟的延时(降低速率)。
3. 多次读取PROM,看数据是否稳定。
计算出的高度值跳动很大(>1米)1. 未进行软件滤波
2. 电源噪声大
3. 传感器暴露在气流中(风扇、风吹)
4. 参考气压(海平面气压)设置不当
1. 加入低通滤波。
2. 检查电源,添加滤波电容。
3. 给传感器加上机械保护(海绵、静压腔)。
4. 确认sea_level_pa是稳定后的平均值。
高度值有固定的漂移(随时间缓慢变化)1. 传感器温度漂移(自身发热或环境温变)
2. 实际环境气压在变化(天气变化)
1. 让系统预热几分钟后再取参考气压。
2. 对于需要绝对高度的应用,必须引入外部参考(如GPS海拔)。
温度值明显不准1. 自热效应:MCU和传感器持续工作发热。
2. 公式计算错误,或PROM系数读取错误。
1. 尝试间歇性测量(如每秒测一次),减少发热。
2. 打印出原始的D2和所有prom[]值,与手册示例核对。

6.2 调试心得与技巧

  • 分步验证:不要一下子写完全部代码。先写一个最简单的IIC字节读写函数,去读一个已知地址的器件(比如24C02 EEPROM),确保底层时序是对的。然后再移植到MS5611上。
  • 打印原始数据:在调试初期,一定要把每一步的原始数据都打印出来:D1D2prom[0]~prom[6]。和MS5611数据手册第9页的“Example of conversion”例子进行对比,如果中间某一步的数据对不上,问题就定位在那里。
  • 利用逻辑分析仪:这是最强大的工具。连接SCL、SDA和一根GPIO(用于在代码里打时间戳)。你可以清晰地看到起始信号、地址、应答、数据、停止信号是否完全符合规范。可以精确测量SCL高低电平时间,调整Delay_us
  • 注意数据类型和溢出:在MS5611_Calculate函数中,D1D2是24位数据,prom系数是16位,它们相乘后很容易超过32位整型的范围。这就是为什么我全程使用int64_t。如果使用32位int,在计算OFFSENS时肯定会溢出,得到错误结果。
  • 高度跳变的应对:即使滤波后,在绝对静止时,高度可能仍有几厘米到十几厘米的无规律跳动。这是传感器本身的分辨率和环境微气流造成的物理极限。对于需要厘米级精度的应用(如室内定位),单靠气压计很难实现,需要融合IMU(惯性测量单元)数据进行更高级的传感器融合。