4. 传感器故障诊断:传感器自检、阈值校验、多传感器一致性校验、传感器冗余切换

传感器这东西,在温室里就是我们的「眼睛」和「耳朵」。眼睛花了,耳朵聋了,那整个控制系统就是在瞎指挥。我见过太多温室项目,最后出问题都出在传感器上——不是数据不准,而是压根没发现它已经坏了。

所以这一节,咱们重点聊聊怎么让传感器「自己报故障」。说白了,就是给它装一套自我体检系统。

4.1 传感器自检——上电先查体

我个人习惯,每次系统上电,第一件事不是干活,而是让所有传感器做一遍自检。就像你去医院体检,先量血压、测心率,对吧?

自检分两个层面:

  • 硬件自检:传感器内部有没有短路、断路?供电电压稳不稳?
  • 通信自检:数据线通不通?能不能正常响应主机的查询指令?

举个例子,我常用的温湿度传感器 SHT30,它内部有个状态寄存器。上电后我发一条指令读这个寄存器,如果返回的值不对,那基本可以判定传感器挂了。

// 伪代码:传感器自检流程
bool sensor_self_check(uint8_t sensor_id) {
    // 1. 发送自检指令
    send_command(sensor_id, CMD_READ_STATUS);
    
    // 2. 等待响应,超时500ms
    uint16_t status = wait_response(sensor_id, 500);
    
    // 3. 检查状态位
    if (status & STATUS_BIT_ALERT) {
        log_error("传感器 %d 报警", sensor_id);
        return false;
    }
    
    // 4. 读取一次数据,看是否在合理范围
    float temp = read_temperature(sensor_id);
    if (temp < -40.0f || temp > 85.0f) {
        log_error("传感器 %d 数据异常", sensor_id);
        return false;
    }
    
    return true;
}
我的小技巧:自检不要只做一次。我建议上电后连续自检3次,取多数一致的结果。有一次我在项目里只做一次自检,结果传感器偶尔上电瞬间电压不稳,自检失败,系统直接报错停机。后来改成三次自检,问题就解决了。

4.2 阈值校验——数据不能太离谱

阈值校验,说白了就是给数据画个「安全框」。温室里温度再高,也不可能到80度;湿度再低,也不会是0%。

我一般设两级阈值:

阈值类型 温度范围(℃) 湿度范围(%RH) CO2浓度(ppm)
合理范围 -10 ~ 50 10 ~ 100 300 ~ 2000
警告范围 0 ~ 45 20 ~ 95 400 ~ 1500

超出合理范围,直接判定传感器故障。超出警告范围,记录日志,但系统继续运行,只是提醒维护人员检查。

嗯,这里要注意:阈值不能设得太死。我记得有一次在北方冬季的温室项目,室外温度零下20度,传感器靠近门口安装,偶尔会测到零下15度。如果阈值设成零下10度,那传感器天天报故障。后来我把阈值放宽到零下20度,同时加了「持续超限」的判断——连续5次采样都超限,才判定故障。

// 阈值校验示例
typedef struct {
    float min_valid;
    float max_valid;
    float min_warn;
    float max_warn;
    uint8_t fault_count;
} ThresholdConfig;

bool threshold_check(float value, ThresholdConfig *cfg) {
    // 超出合理范围,直接故障
    if (value < cfg->min_valid || value > cfg->max_valid) {
        return false;
    }
    
    // 超出警告范围,计数
    if (value < cfg->min_warn || value > cfg->max_warn) {
        cfg->fault_count++;
        if (cfg->fault_count >= 5) {
            return false;  // 连续5次超限,判定故障
        }
    } else {
        cfg->fault_count = 0;  // 恢复正常,清零计数
    }
    
    return true;
}

4.3 多传感器一致性校验——三个和尚没水喝?

温室里同一个区域,我通常会装2~3个同类型传感器。为什么?因为单个传感器可能骗你,但多个传感器一起骗你的概率就小多了。

一致性校验的核心思想:如果两个传感器测同一个东西,结果差太多,那肯定有一个有问题。

我常用的方法是「多数投票法」:

  1. 取3个传感器的读数
  2. 两两比较差值
  3. 如果某两个差值小于阈值(比如温度差小于1℃),认为它们一致
  4. 取一致的那组数据的平均值作为最终值
  5. 如果三个数据两两都不一致,那... 全都有问题,触发报警
避坑指南:我曾经在番茄温室里遇到过一个怪事——三个温度传感器,两个显示28度,一个显示30度。按多数投票,应该取28度。但后来发现,那个显示30度的传感器才是对的!因为那两个28度的传感器安装位置靠近通风口,被冷风影响了。所以一致性校验不能只看数据,还要考虑传感器的安装位置、环境差异。

所以我现在做一致性校验,会加一个「位置权重」:

// 带位置权重的一致性校验
typedef struct {
    float value;
    float weight;  // 位置权重,中心区域权重高,边缘权重低
    uint8_t location_id;
} SensorData;

float consensus_check(SensorData sensors[], uint8_t count) {
    float weighted_sum = 0;
    float total_weight = 0;
    uint8_t valid_count = 0;
    
    for (int i = 0; i < count; i++) {
        for (int j = i + 1; j < count; j++) {
            float diff = fabs(sensors[i].value - sensors[j].value);
            if (diff < CONSENSUS_THRESHOLD) {
                // 这两个传感器一致,都标记为有效
                sensors[i].weight = MAX(sensors[i].weight, 0.5f);
                sensors[j].weight = MAX(sensors[j].weight, 0.5f);
            }
        }
    }
    
    // 计算加权平均值
    for (int i = 0; i < count; i++) {
        if (sensors[i].weight > 0) {
            weighted_sum += sensors[i].value * sensors[i].weight;
            total_weight += sensors[i].weight;
            valid_count++;
        }
    }
    
    if (valid_count < 2) {
        // 有效传感器不足2个,触发故障
        return NAN;
    }
    
    return weighted_sum / total_weight;
}

4.4 传感器冗余切换——主备无缝衔接

冗余切换,说白了就是「备胎转正」。主传感器坏了,备用的立刻顶上,系统不能停。

我常用的冗余架构有两种:

  • 冷备份:备用传感器平时不工作,主传感器故障时才启动。优点是省电,缺点是切换时有短暂的数据空白。
  • 热备份:备用传感器一直工作,数据同步更新。主传感器故障时,直接切换数据源,几乎无感。

温室控制我建议用热备份。为什么?因为温室的惯性大,几分钟的数据空白可能就导致温度失控了。

注意:热备份不是简单的「两个传感器同时工作」就完事了。你想想看,如果主传感器和备用传感器都接在同一个电源上,电源一坏,两个一起挂。所以冗余要彻底——独立供电、独立通信线路、甚至独立安装位置。

切换逻辑我一般这样设计:

// 冗余切换逻辑
typedef struct {
    uint8_t primary_id;
    uint8_t backup_id;
    bool primary_fault;
    bool backup_fault;
    float last_valid_value;
    uint32_t switch_time;
} RedundancyManager;

float get_redundant_value(RedundancyManager *mgr) {
    // 先读主传感器
    float primary_val = read_sensor(mgr->primary_id);
    bool primary_ok = threshold_check(primary_val, &threshold_cfg);
    
    if (primary_ok) {
        mgr->primary_fault = false;
        mgr->last_valid_value = primary_val;
        return primary_val;
    }
    
    // 主传感器故障,切到备用
    mgr->primary_fault = true;
    log_warning("主传感器 %d 故障,切换到备用传感器 %d", 
                mgr->primary_id, mgr->backup_id);
    
    float backup_val = read_sensor(mgr->backup_id);
    bool backup_ok = threshold_check(backup_val, &threshold_cfg);
    
    if (backup_ok) {
        mgr->backup_fault = false;
        mgr->last_valid_value = backup_val;
        mgr->switch_time = get_system_time();
        return backup_val;
    }
    
    // 两个都故障,返回最后一次有效值
    mgr->backup_fault = true;
    log_error("主备传感器均故障,使用最后有效值 %.2f", mgr->last_valid_value);
    return mgr->last_valid_value;
}

这里有个细节:切换后要记录切换时间。如果短时间内频繁切换(比如几分钟内切了十几次),说明两个传感器可能都有问题,或者环境有剧烈变化。这时候应该触发高级报警,通知维护人员介入。

好了,传感器故障诊断这块,核心就是这四个步骤:自检、阈值、一致性、冗余切换。每一步都不能省,每一步都有坑。你把这些做好了,温室控制系统至少能扛住90%的传感器故障场景。