DeCrop系统的整体架构设计
作物健康监测的核心矛盾在于:作物状态是连续变化的,但人工巡田是离散且低效的。DeCrop的设计目标就是把这种人工判断转化为自动化流程,让摄像头和传感器代替人眼持续观察农田。整个系统采用分层架构,最底层是数据采集层,包括田间的摄像头、温湿度传感器和土壤水分传感器;中间是数据处理层,由Node.js服务承担,负责接收图像流、调用图像分析算法、存储时序数据;最上层是展示与预警层,提供Web面板和异常推送。
选择Node.js作为核心有几个实际考虑。首先,田间设备大多通过MQTT或HTTP上传数据,Node.js的事件驱动模型天然适合处理大量并发连接,一台普通服务器轻松承载几百个设备的上报。其次,Node.js生态中有sharp这样的高性能图像处理库,可以在服务端完成图像裁剪、缩放和色彩空间转换,再交给识别服务处理。最后,前后端统一使用JavaScript,对于小型农业创业团队来说维护成本更低。
系统数据流设计如下:设备端定时采集数据,通过MQTT发布到消息主题,Node.js服务订阅这些主题后,将结构化数据写入时序数据库,图像则经过预处理后送入识别模块,识别结果与健康评分一起落库。下面是MQTT接入的基础代码:
const mqtt = require('mqtt');
const client = mqtt.connect('mqtt://192.168.0.1:1883');
client.on('connect', () => {
// 订阅田间设备数据主题
client.subscribe('field/+/sensors', (err) => {
if (err) console.error('订阅失败:', err);
});
client.subscribe('field/+/image');
});
client.on('message', (topic, payload) => {
const fieldId = topic.split('/')[1];
if (topic.endsWith('sensors')) {
const data = JSON.parse(payload.toString());
// 保存温湿度、土壤水分等时序数据
saveSensorData(fieldId, data);
} else if (topic.endsWith('image')) {
// 图像走二进制处理流程
processCropImage(fieldId, payload);
}
});图像识别模块:从叶片照片判断健康状况
作物健康问题的早期信号大多体现在叶片上,比如缺氮时叶片发黄、染上霜霉病会出现斑块、虫害留下啃食痕迹。DeCrop的图像分析采用两步走策略:先用传统图像处理快速筛查,再做精细识别。快速筛查环节利用sharp库提取图像的平均色相和HSV分布,健康的作物叶片在正常光照下色相集中在绿色区间,如果大片区域偏黄或偏褐,就标记为可疑样本。
这里有一个容易踩的坑:不能直接拿RGB值判断颜色,因为田间光照随时间和天气剧烈变化,同样是健康的叶片,正午和傍晚拍出来色值差异很大。正确做法是转换到HSV色彩空间,色相通道对光照变化不敏感,饱和度和明度则可以辅助判断是否过曝。以下是预处理与色彩分析的关键代码:
const sharp = require('sharp');
async function processCropImage(fieldId, imageBuffer) {
// 统一缩放并转换为RGB原始像素数据
const { data, info } = await sharp(imageBuffer)
.resize(640, 480)
.removeAlpha()
.raw()
.toBuffer({ resolveWithObject: true });
let greenPixels = 0, yellowPixels = 0, total = info.width * info.height;
for (let i = 0; i < data.length; i += 3) {
const r = data[i], g = data[i + 1], b = data[i + 2];
const max = Math.max(r, g, b), min = Math.min(r, g, b);
if (max === min) continue;
// 简化的色相计算
let hue;
if (max === g) hue = 60 * (2 + (b - r) / (max - min));
else if (max === r) hue = 60 * ((g - b) / (max - min));
else hue = 60 * (4 + (r - g) / (max - min));
if (hue < 0) hue += 360;
if (hue >= 70 && hue <= 160) greenPixels++;
else if (hue > 40 && hue < 70) yellowPixels++;
}
const healthScore = greenPixels / total;
const yellowRatio = yellowPixels / total;
// 黄化比例超过15%触发复查
if (yellowRatio > 0.15) {
markForReview(fieldId, imageBuffer, healthScore);
}
saveImageAnalysis(fieldId, healthScore, yellowRatio);
}快速筛查只能发现明显异常,准确识别病害种类还是需要深度学习模型。实际项目中可以把标记为可疑的图像转发给TensorFlow.js加载的分类模型,或者调用云端识别接口。Node.js在这里扮演调度者角色,控制调用频率避免流量超限,同时把识别结果与历史数据对比,只有连续多张图像都指向同一类问题才发出告警,避免单张照片误判造成频繁骚扰。
传感器数据与预警逻辑的实现
图像能反映地上部分的状态,但很多问题的根源在土壤。高温高湿环境容易滋生真菌病害,土壤水分过低会引发萎蔫,这些环境条件可以作为预警的先行指标。比如连续三天相对湿度高于85%且夜间温度在18到25度之间,霜霉病爆发的概率就会显著上升,此时即使图像还没有发现异常,也应该提前通知农户预防性喷药。
数据存储推荐使用InfluxDB这类时序数据库,查询某块田最近7天的温度曲线非常高效。Node.js端封装一个健康评估函数,把环境指标和图像评分加权合并成综合健康分,逻辑大致如下:
async function evaluateFieldHealth(fieldId) {
const env = await queryRecentEnv(fieldId, 7); // 近7天环境数据
const img = await queryLatestImageScore(fieldId);
// 环境风险:持续高湿是病害高发信号
const humidDays = env.filter(d => d.humidity > 85).length;
let envRisk = 0;
if (humidDays >= 3) envRisk = 30;
else if (humidDays >= 2) envRisk = 15;
// 土壤干旱风险
const dryRisk = env[0].soilMoisture < 25 ? 25 : 0;
// 图像健康分占比50%
const totalScore = 100 - envRisk - dryRisk
- (1 - img.healthScore) * 50;
if (totalScore < 60) {
sendAlert(fieldId, {
level: 'high',
message: '作物健康分偏低,建议实地查看',
score: totalScore.toFixed(1)
});
}
return totalScore;
}预警触达方式建议多样化。短信适合紧急情况,微信或App推送适合日常提醒,面板上则保留完整的历史趋势图供回溯分析。值得一提的是告警去重机制:同一块田的同一种异常,24小时内不应重复推送,否则用户会关掉通知,真正的紧急情况反而被忽略。可以在Redis中记录告警键值,发送前先检查是否已推送过。
部署方面,DeCrop服务端建议用PM2守护进程并配置集群模式,数据库与Node.js服务分机部署。田间设备如果网络不稳定,务必在设备端实现本地缓存重传,Node.js接口层也要做好幂等处理,防止设备重传导致同一时刻的数据被重复记录。上线前用一段时间的真实农田数据回测阈值参数,不同作物、不同地区的合理阈值差异很大,直接套用别人的参数往往效果不佳。