智慧农业传感器采集:温湿度、土壤EC、PH、光照、CO2数据解析

76 阅读8分钟

在这里插入图片描述

09-智慧农业传感器采集:温湿度、土壤EC、PH、光照、CO2数据解析

搞智慧农业的朋友应该都有同感:大棚里一排排传感器,天天吐数据,可你拿到的是一串十六进制——01 03 02 01 2C B9 B2,这玩意儿到底代表几度?几克?几 lux?

别急,这篇就把智慧农业最常见的传感器挨个盘一遍,从寄存器映射到数据解析,把"原始值 → 工程量"的换算公式和代码全给你,让你从"读报文"进化到"看得懂数据"。

一、先认识智慧农业的"感觉器官"

大棚环境监控,无外乎"空气、土壤、光照、气象"四大类:

传感器测量物理量典型量程常见单位通讯方式
空气温湿度温度/相对湿度-4080℃ / 0100%RH℃ / %RHRS485 Modbus
土壤温湿度土壤温度/体积含水率-4080℃ / 0100%℃ / %RS485 Modbus
土壤EC电导率(肥力)0~10000uS/cmRS485 Modbus
土壤PH酸碱度0~14pHRS485 Modbus
光照强度光合有效辐射0~200000LuxRS485 Modbus
CO2浓度二氧化碳0~5000ppmRS485 Modbus
风速风向风速/风向角060m/s / 0360°m/s / °RS485 Modbus
雨量降雨量0~9999mmRS485 Modbus

看出规律没?RS485 + Modbus 几乎是农业传感器的"普通话"。原因很简单:成本低、抗干扰、一根双绞线可以挂几十个设备,大棚布线友好。

二、寄存器映射:传感器怎么"开口说话"

每个传感器相当于一个小 PLC,内部有寄存器。Modbus 主站(通常是边缘网关或 PLC)通过 功能码03 读保持寄存器 或 04 读输入寄存器 去问它。

举个例子,某品牌"空气温湿度二合一"传感器的寄存器表:

寄存器地址(十进制)寄存器内容数据类型说明
0x0000空气温度int16有符号,缩放0.01,单位℃
0x0001空气湿度uint16无符号,缩放0.01,单位%RH
0x0002露点温度int16有符号,缩放0.01,单位℃
0x0003校验值uint16设备状态字

关键点:几乎所有农业传感器都是"整数存储 + 缩放系数"的模式。因为 Modbus 寄存器是 16 位整数,没法直接装小数,厂商就用"乘个系数"把小数变成整数存进去,你在解析时再除回来。

这就是新手最常踩的坑:寄存器读回来 0x012C,直接当成 300 用,结果大棚温度 300℃ 把植物都烤没了。实际上要除以 100,是 3.00℃。凡事先看文档里的缩放系数!

三、数据解析实战:从原始值到工程量

1. 温湿度(有符号16位,缩放0.01)

假设读温度寄存器返回 0xFF6C:

0xFF6C = 65452(无符号视角)
有符号解析:0xFF6C 首位为1,负数。
补码转真值:65452 - 65536 = -84
工程量 = -84 × 0.01 = -0.84℃

Python 实现:

def parse_int16(raw: int) -> int:
    """把寄存器原始值转成有符号16位整数"""
    if raw >= 0x8000:          # 最高位为1表示负数
        return raw - 0x10000
    return raw

temp_raw = 0xFF6C
temp_c = parse_int16(temp_raw) * 0.01
print(f"温度: {temp_c:.2f} ℃")   # 输出: -0.84 ℃

湿度一般是无符号,直接除系数:

hum_raw = 0x1F40          # 8000
hum_rh = hum_raw * 0.01   # 80.00 %RH

2. 土壤EC(uS/cm 换算)

EC(Electrical Conductivity)电导率,反映土壤里可溶性盐离子浓度,是判断"该不该施肥"的核心指标。常见寄存器存的就是 uS/cm 原始值,缩放系数 1(即无需缩放),有些老款传感器按 0.1 缩放。

换算公式(uS/cm → mS/cm,业界更爱用 mS/cm):

EC(mS/cm) = EC(uS/cm) / 1000
ec_raw = 0x08FC          # 2300
ec_us = ec_raw * 1.0     # 2300 uS/cm
ec_ms = ec_us / 1000.0   # 2.3 mS/cm
print(f"土壤电导率: {ec_ms:.2f} mS/cm")

农业经验值参考:蔬菜大田适宜 EC 大约 1.0~2.5 mS/cm,超过 3 说明盐分过高,要控制施肥了。

3. PH(0.01 精度)

PH 寄存器通常是 uint16,缩放 0.01。即 0x0341 = 833 → PH 8.33。

ph_raw = 0x0341           # 833
ph_val = ph_raw * 0.01    # 8.33

注意:有些廉价 PH 传感器用 0.1 缩放,0x0321=801 → 8.0。同型号看文档,不同厂商别硬套同一个系数。

4. 光照强度(Lux)

光照传感器量程差异巨大,低配 020000 Lux,高配 0200000 Lux。寄存器存 uint32 的也不少见(跨两个寄存器),读取时注意高位寄存器在前。

# 两个寄存器组成 uint32,高位在前
hi_raw = 0x0003    # 寄存器0
lo_raw = 0xD090    # 寄存器1
lux = (hi_raw << 16) | lo_raw   # 250000
print(f"光照强度: {lux} Lux")

光照单位换算别踩坑:Lux(照度)和 umol/(m²·s)(光合有效辐射 PAR)是两个东西,转换系数约 0.0185(依光源光谱而定),直接拿 Lux 当 PAR 算光补偿点会得出离谱结论。

5. CO2(ppm)

CO2 传感器一般 05000 ppm,uint16 直接存储,缩放 1。大棚 CO2 浓度 350800 ppm 属于正常范围,光合作用最适浓度 1000~1500 ppm。

co2_raw = 0x03E8   # 1000
co2_ppm = co2_raw  # 1000 ppm

四、通用解析代码模板

把上面逻辑收拢成一个通用函数,覆盖 "有符号/无符号 + 缩放系数" 两种最常见的组合:

def reg_to_eng(raw: int, scale: float = 1.0,
               signed: bool = False, signed_bits: int = 16) -> float:
    """
    寄存器原始值 -> 工程量
    raw: 寄存器读回的整数
    scale: 缩放系数,如 0.01
    signed: 是否有符号
    """
    val = raw
    if signed and raw >= (1 << (signed_bits - 1)):
        val = raw - (1 << signed_bits)
    return val * scale

# 例子
print(reg_to_eng(0xFF6C, 0.01, signed=True))   # -0.84
print(reg_to_eng(0x1F40, 0.01))                # 80.0

C 语言版本(嵌入式网关常用):

#include <stdint.h>
#include <stdio.h>

// 16位有符号
int16_t raw_int16(uint16_t raw) {
    return (int16_t)raw;   // C里强转即可完成补码解释
}

// 32位浮点:两个寄存器拼成IEEE754
float regs_to_float(uint16_t hi, uint16_t lo) {
    uint32_t bits = ((uint32_t)hi << 16) | lo;
    float f;
    memcpy(&f, &bits, sizeof(f));
    return f;
}

int main(void) {
    printf("%.2f\n", raw_int16(0xFF6C) * 0.01f);   // -0.84
    return 0;
}

五、异常值识别:别让"假数据"毁了自动控制

传感器解析出来是一回事,值可不可信是另一回事。农业生产系统里,一个假数据可能导致自动灌溉误动作——半夜给作物淹了水。几个必须做的检查:

1. 超量程检查

物理世界给传感器返回值设了边界,超了就基本可判定异常:

物理量合理范围可疑值
空气温度-40~80℃< -50 或 > 100
湿度0~100%RH> 100
PH0~14< 0 或 > 14
CO2350~5000 ppm< 300 或 > 10000
光照0~200000 Lux负数(不可能)

2. 特征值识别传感器故障

很多厂商在故障时返回特殊值:

  • 0x8000 / 0xFFFF:很多国产传感器读不到物理量时回 0xFFFF(-1),解析后是离谱负数,注意识别;
  • 数据长时间恒定不变:湿度传感器探头进水、结露后可能"死数据",连续 10 次轮询值完全相同且超出物理合理范围,标记为故障;
  • EC 突然跳 0 且持续:探头可能开路,或没插入土壤。

3. 变化速率判断(工程级兜底)

温度两分钟内从 20℃ 变到 60℃,这在温室里物理上不可能(除非着火)。加一阶差分限幅滤波:

PREV = None
MAX_DELTA = {"temp": 5.0, "hum": 15.0}   # 每轮询周期的最大合理变化量

def sanity_check(name: str, val: float):
    global PREV
    if PREV is None:
        PREV = val
        return True
    if abs(val - PREV) > MAX_DELTA[name]:
        print(f"[告警] {name} 变化过快: {PREV} -> {val}")
        return False
    PREV = val
    return True

六、小结

一句话总结解析套路:读懂寄存器表 → 注意缩放系数 → 注意有符号 → 补上合理性校验。温湿度、EC、PH、光照、CO2 这五类数据在智慧农业里占比 80% 以上,把这套公式记熟,再遇到陌生传感器也就是"查文档换系数"的事。数据能可靠上来了,下一步自然是让设备"动起来"——控制执行器,那是另一个方向的实战。