2、原始数据采集实战:使用Python读取BMP280原始气压值、温度补偿计算、高度转换公式推导、数据可视化

好,咱们正式开始动手了。

上一章聊了气压测高的基本原理,你可能觉得「嗯,懂了」。但懂原理和能跑出数据,中间还隔着一条河。我当年第一次调BMP280的时候,就卡在「读出来的数怎么跟气压对不上」这个问题上,折腾了一下午。后来才发现,是忘了做温度补偿。

这一章,咱们就把这条河趟过去。我会带着你,从I2C读取原始寄存器值开始,一步步算出温度、气压,最后转成海拔高度,再画成图看看效果。

2.1 硬件连接与I2C地址确认

先把BMP280连到你的开发板或树莓派上。接线很简单:

  • VCC → 3.3V(别接5V,会烧)
  • GND → GND
  • SCL → 主控的SCL引脚
  • SDA → 主控的SDA引脚

BMP280的I2C地址有两种可能:0x760x77。具体看你的模块上SDO引脚的电平。接地就是0x76,接VDDIO就是0x77。我习惯先扫描一下总线确认:

import smbus2
bus = smbus2.SMBus(1)
for addr in [0x76, 0x77]:
    try:
        bus.read_byte(addr)
        print(f"找到设备: 0x{addr:02X}")
    except:
        pass
小技巧: 如果你用的是树莓派,记得先开启I2C接口(sudo raspi-config)。我第一次用的时候忘了开,还以为是硬件坏了。

2.2 读取校准参数

BMP280出厂时,每个芯片都内置了一套校准系数。这些系数存储在寄存器 0x880xA1 之间,一共24个字节。说白了,就是芯片的「个性数据」——没有它们,你算出来的气压就是错的。

读取方法很简单:

def read_calibration(bus, addr=0x76):
    calib = bus.read_i2c_block_data(addr, 0x88, 24)
    # 解析为有符号/无符号16位整数
    dig_T1 = calib[0] | (calib[1] << 8)          # 无符号
    dig_T2 = calib[2] | (calib[3] << 8)          # 有符号
    if dig_T2 > 32767: dig_T2 -= 65536
    dig_T3 = calib[4] | (calib[5] << 8)
    if dig_T3 > 32767: dig_T3 -= 65536
    # ... 气压校准系数类似
    return dig_T1, dig_T2, dig_T3, ...

嗯,这里要注意:温度校准系数是3个(T1无符号,T2/T3有符号),气压校准系数是9个(P1无符号,P2~P9有符号)。别搞混了,我一开始就栽在这个坑里。

2.3 读取原始温度与气压值

校准参数拿到手,接下来读原始数据。温度寄存器在 0xFA(MSB)、0xFB(LSB)、0xFC(XLSB),一共20位。气压寄存器在 0xF70xF80xF9,也是20位。

def read_raw_temp(bus, addr=0x76):
    data = bus.read_i2c_block_data(addr, 0xFA, 3)
    raw = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4)
    return raw

def read_raw_pressure(bus, addr=0x76):
    data = bus.read_i2c_block_data(addr, 0xF7, 3)
    raw = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4)
    return raw
注意: 原始值不是直接的气压值!它是ADC的原始读数,范围0~1048575。必须经过补偿计算才能得到真实气压。我曾经见过有人直接拿这个数当气压用,结果差了30多百帕。

2.4 温度补偿计算

温度补偿的公式,BMP280数据手册里写得挺清楚。我直接给你代码,咱们一步步来:

def compensate_temperature(raw_temp, dig_T1, dig_T2, dig_T3):
    var1 = (raw_temp / 16384.0 - dig_T1 / 1024.0) * dig_T2
    var2 = ((raw_temp / 131072.0 - dig_T1 / 8192.0) ** 2) * dig_T3
    t_fine = var1 + var2
    temp = t_fine / 5120.0
    return temp, t_fine

这里有个关键变量 t_fine,它是个中间结果。气压补偿也要用到它。所以我把温度补偿和气压补偿分开写,但共享 t_fine

2.5 气压补偿计算

有了 t_fine,气压补偿就顺理成章了:

def compensate_pressure(raw_pressure, dig_P1, dig_P2, ..., t_fine):
    var1 = (t_fine / 2.0) - 64000.0
    var2 = var1 * var1 * dig_P6 / 32768.0
    var2 = var2 + var1 * dig_P5 * 2.0
    var2 = (var2 / 4.0) + (dig_P4 * 65536.0)
    var1 = (dig_P3 * var1 * var1 / 524288.0 + dig_P2 * var1) / 524288.0
    var1 = (1.0 + var1 / 32768.0) * dig_P1
    if var1 == 0:
        return 0
    p = 1048576.0 - raw_pressure
    p = (p - var2 / 4096.0) * 6250.0 / var1
    var1 = dig_P9 * p * p / 2147483648.0
    var2 = p * dig_P8 / 32768.0
    p = p + (var1 + var2 + dig_P7) / 16.0
    return p / 100.0  # 单位:hPa

公式看着长,其实逻辑很清晰。说白了就是先用温度修正气压的零点,再用非线性项修正曲线。我建议你把这个函数封装好,以后每次调用就行。

2.6 高度转换公式推导

气压转高度,用的是国际气压公式。咱们用简化版就够了:

标准气压公式:

h = 44330 * (1 - (P / P0) ** (1/5.255))

其中 P0 是海平面标准气压(1013.25 hPa),P 是当前实测气压。这个公式假设大气是理想气体,温度递减率固定。实际中会有误差,但做一般的高度测量够用了。

我习惯再加一个修正:如果知道当地当天的海平面气压(比如从气象局查),把 P0 替换掉,精度能提升不少。

def pressure_to_altitude(pressure, sea_level_pressure=1013.25):
    return 44330 * (1 - (pressure / sea_level_pressure) ** (1/5.255))

2.7 数据可视化

数据算出来了,光看数字没意思。咱们画个图,看看气压和高度随时间的变化。我用 matplotlibdatetime 来做:

import matplotlib.pyplot as plt
import datetime

times = []
pressures = []
altitudes = []

# 假设你已经采集了100个数据点
for i in range(100):
    raw_t = read_raw_temp(bus)
    raw_p = read_raw_pressure(bus)
    temp, t_fine = compensate_temperature(raw_t, dig_T1, dig_T2, dig_T3)
    press = compensate_pressure(raw_p, dig_P1, dig_P2, ..., t_fine)
    alt = pressure_to_altitude(press)
    
    times.append(datetime.datetime.now())
    pressures.append(press)
    altitudes.append(alt)
    time.sleep(1)  # 每秒采一次

# 画图
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 6))
ax1.plot(times, pressures, 'b-')
ax1.set_ylabel('气压 (hPa)')
ax2.plot(times, altitudes, 'r-')
ax2.set_ylabel('高度 (m)')
plt.show()
核心要点: 气压数据天然带有噪声(风、温度波动、传感器自身噪声),所以你会看到曲线有抖动。这很正常,下一章咱们就讲怎么把这些抖动滤掉。

2.8 本章知识体系

我把这一章的核心逻辑画成了流程图,方便你对照理解:

硬件连接 & I2C扫描 读取校准参数 (0x88~0xA1) 读取原始温度/气压 (20位) 温度补偿 → t_fine 气压补偿 → 真实气压 (hPa) 高度转换 → 可视化

你看,整个流程就是一条直线:从硬件到原始数据,再到补偿计算,最后出结果。每一步都有坑,但咱们都填上了。

2.9 避坑指南

  • 我曾经 忘了设置BMP280的工作模式。默认是休眠模式,读出来的数据全是0。记得写寄存器 0xF4 设置 0x2F(正常模式,过采样16倍)。
  • 我曾经 在气压补偿时用了错误的符号位处理。有符号数转Python整数时,一定要手动处理补码。不然算出来的气压忽高忽低。
  • 数据抖动 是正常的。别慌,下一章咱们就讲滤波。

好了,这一章的内容就到这儿。代码都给你了,建议你亲手跑一遍。看着气压曲线在屏幕上跳动,那种感觉,比光看公式爽多了。


公众号:蓝海资料掘金营,微信deep3321