尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
智慧农业集成管理系统:多协议设备统一接入与规则引擎实战
简介本资源为一套完整的智慧农业集成管理系统源码工程面向高校计算机/农林信息化专业学生、Java后端开发者及农业数字化项目实践者解决农业场景中环境监测、智能决策与设备联动等核心开发需求。压缩包共224个文件含142个Java业务逻辑与控制器类、30个HTML前端页面如sensorFile.html、device.html、field.html等、25个XML配置与MyBatis映射文件、20个JS交互脚本及CSS样式文件辅以SQL建表语句和基础图标资源整体仅476KB结构紧凑、模块清晰便于快速理解系统分层架构与IoT数据流转逻辑。目前已有174人学习下载可直接导入IDE运行完整覆盖用户认证、设备管理、农田监控、灌溉计划、GIS地块展示等十大功能模块并内置农业知识库接口与数据安全基础实现是学习Spring BootWeb物联网集成开发的典型轻量级实战案例。1. 智慧农业集成管理系统不是“大屏炫技”而是让大棚温控、虫情识别、灌溉策略全在同一个后台里闭环跑通去年帮某高校实验室部署一套农业物联网系统现场看到三台电脑并排开着一台刷着传感器数据曲线一台卡在虫情图像识别界面反复报错第三台连着PLC控制器却手动调参数——三个模块互不认账日志格式五花八门运维同学靠Excel手工对齐设备状态。直到我拉出这个「智慧农业集成管理系统」源码包用它自带的设备接入网关把温湿度探头、高清虫情相机、电磁阀控制器全注册进一个统一设备池再拖拽配置一个“高温虫口密度50/㎡→自动开启雾化喷淋”的规则链整个流程从数据采集、AI识别到执行反馈15分钟内走通闭环。这不是PPT里的“智慧农业”而是一个能真正在田间地头扛住7×24小时运行、支持Modbus/RTU、MQTT、HTTP多协议混接、带离线缓存和断网续传能力的生产级集成底座。适合农业物联网方案商快速封装硬件、农技站技术人员自主配置策略、高校课题组做算法验证——它不替代你的摄像头或土壤传感器但让你所有硬件不再各自为战。2. 架构拆解后端服务分层设计与核心通信网关实现这个系统不是单体巨石而是按职责切得非常清晰的三层结构最底层是设备接入网关device-gateway负责和真实硬件打交道中间是业务中台core-service管设备管理、规则引擎、告警中心这些通用能力最上层是Web管理后台web-admin提供可视化配置界面。三者通过本地Redis做轻量消息总线避免引入Kafka这类重依赖降低部署门槛——这点对县乡级农技站特别友好他们往往只有一台4核8G的旧服务器。2.1 设备接入网关如何同时吃下Modbus RTU、MQTT和HTTP上报网关模块的核心是device-gateway/src/main/java/com/agri/gateway/handler/下的三类处理器ModbusHandler、MqttHandler、HttpHandler。它们共用一个抽象基类BaseDeviceHandler强制实现parseRawData()和buildCommand()两个方法确保无论什么协议进来最终都转成统一的DeviceMessage对象// device-gateway/src/main/java/com/agri/gateway/handler/ModbusHandler.java public class ModbusHandler extends BaseDeviceHandler { Override protected DeviceMessage parseRawData(byte[] raw) { // 解析Modbus RTU帧跳过地址码、功能码取寄存器数据区 // 假设寄存器0x0001存温度2字节0x0002存湿度2字节 int temp (raw[3] 0xFF) 8 | (raw[4] 0xFF); // 高位在前 int humi (raw[5] 0xFF) 8 | (raw[6] 0xFF); return DeviceMessage.builder() .deviceId(modbus-001) // 设备唯一ID从配置文件读取 .timestamp(System.currentTimeMillis()) .data(Map.of(temperature, temp / 10.0, humidity, humi / 10.0)) // 单位还原 .build(); } Override protected byte[] buildCommand(DeviceCommand command) { // 构造写单个寄存器指令0x0001地址写入值command.getValue() * 10恢复整数精度 int value (int) Math.round(command.getValue() * 10); return new byte[]{0x01, 0x06, 0x00, 0x01, (byte)(value 8), (byte)(value 0xFF)}; } }提示parseRawData()返回的DeviceMessage.data字段必须是MapString, Objectkey名要和后续规则引擎里定义的字段名严格一致比如规则里写temperature 35这里key就必须是temperature。这是跨协议统一的关键契约改错一个字母就导致规则不触发。2.2 规则引擎用Groovy脚本实现“高温虫多→喷淋”这种业务逻辑规则不是硬编码在Java里而是存在MySQL的rule_definition表中字段包括rule_id、rule_name、script_content、trigger_condition。系统启动时会把所有启用的规则加载进内存并用GroovyShell动态编译执行。看一个典型虫情联动规则// rule_definition.script_content 字段内容 // 触发条件过去5分钟内同一设备虫口密度平均值 50 def avgInsectCount context.getAvgValue(insect_count, 5 * 60 * 1000) if (avgInsectCount 50 context.getCurrentValue(temperature) 35) { // 执行动作向设备发送开启喷淋指令 context.sendCommand(valve-001, open, 300) // 参数300表示持续300秒 // 同时记录告警日志 context.logAlert(HIGH_INSECT_AND_TEMP, 虫口密度${avgInsectCount} 50且温度${context.getCurrentValue(temperature)} 35已启动喷淋) }context对象是引擎注入的上下文封装了getAvgValue()、getCurrentValue()、sendCommand()等方法。关键点在于所有数据查询都走Redis缓存不是实时查数据库。网关每收到一条设备数据会同时写入Redis的device:latest:{deviceId}最新值和device:history:{deviceId}时间序列用Sorted Set存score为毫秒时间戳。这样getAvgValue()只需ZREVRANGEBYSCORE取最近N条再计算平均值毫秒级响应。2.3 Web管理后台Vue3 Pinia实现设备树与规则拖拽配置前端用Vue3 Composition API Pinia状态管理核心交互是设备树左侧和规则画布右侧的联动。设备树数据来自/api/device/tree接口返回嵌套JSON{ id: root, name: 全部设备, children: [ { id: greenhouse-01, name: 1号温室, type: group, children: [ {id: sensor-001, name: 温湿度传感器, type: device, protocol: modbus}, {id: camera-001, name: 虫情监测相机, type: device, protocol: http} ] } ] }规则画布用vue-draggable-next实现节点拖拽每个节点对应一个RuleNode组件其props包含nodeType如condition、action和config具体参数。当用户拖拽一个“温度大于35”条件节点到画布组件会渲染一个带输入框的表单!-- components/RuleNodeCondition.vue -- template div classnode condition h3温度阈值/h3 div classform-group label设备/label select v-modelconfig.deviceId option v-ford in deviceList :keyd.id :valued.id{{ d.name }}/option /select /div div classform-group label比较方式/label select v-modelconfig.operator option valuegt大于/option option valuelt小于/option /select input typenumber v-model.numberconfig.value placeholder35 / /div /div /template script setup import { defineProps, defineEmits } from vue const props defineProps([config, deviceList]) const emit defineEmits([update:config]) /script保存规则时前端把画布上所有节点序列化成JSONPOST到/api/rule/save后端校验语法后存入数据库。这种设计让非程序员的农技员也能通过图形化界面配置复杂联动而不是改代码。3. 运行部署从零开始在CentOS 7上跑通全流程这套系统对环境要求不高但有几个关键依赖必须手动确认。我一般会在新服务器上按这个顺序操作避免后面排查半天发现是基础环境问题。3.1 环境准备JDK 11、MySQL 5.7、Redis 6.2、Nginx 1.18先确认JDK版本必须11因为网关模块用了var关键字# 查看当前JDK java -version # 如果是OpenJDK 8卸载并安装11 sudo yum remove java-1.8.0-openjdk* sudo yum install java-11-openjdk-devel # 设置JAVA_HOME写入/etc/profile echo export JAVA_HOME/usr/lib/jvm/java-11-openjdk | sudo tee -a /etc/profile echo export PATH$JAVA_HOME/bin:$PATH | sudo tee -a /etc/profile source /etc/profileMySQL建库并初始化表结构SQL文件在db/init.sql-- 创建数据库注意字符集必须utf8mb4 CREATE DATABASE agri_system CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- 导入初始表含device、rule_definition、user等12张表 mysql -u root -p agri_system /path/to/your/zip/db/init.sqlRedis配置需开启AOF持久化防止断电丢数据# 编辑redis.conf sudo vim /etc/redis.conf # 修改以下两行 appendonly yes appendfilename appendonly.aof # 重启Redis sudo systemctl restart redis3.2 后端服务启动按依赖顺序启动三个Spring Boot模块三个模块都是标准Spring Boot Jar包启动顺序不能乱必须先启网关再启中台最后启Web后台。因为中台启动时会向网关注册心跳Web后台启动时会从中台拉取设备列表。# 1. 启动设备接入网关监听8081端口 nohup java -jar device-gateway-1.0.0.jar \ --spring.profiles.activeprod \ --server.port8081 \ --gateway.redis.host127.0.0.1 \ --gateway.mysql.urljdbc:mysql://127.0.0.1:3306/agri_system?useSSLfalse \ /var/log/agri/gateway.log 21 # 2. 启动业务中台监听8082端口 nohup java -jar core-service-1.0.0.jar \ --spring.profiles.activeprod \ --server.port8082 \ --core.redis.host127.0.0.1 \ --core.mysql.urljdbc:mysql://127.0.0.1:3306/agri_system?useSSLfalse \ /var/log/agri/core.log 21 # 3. 启动Web后台监听8083端口但实际通过Nginx反代 nohup java -jar web-admin-1.0.0.jar \ --spring.profiles.activeprod \ --server.port8083 \ --admin.core.urlhttp://127.0.0.1:8082 \ /var/log/agri/admin.log 21 注意--spring.profiles.activeprod会加载application-prod.yml里面配置了真实的数据库密码、Redis密码等。千万别用dev配置跑生产环境密码是明文写在application-dev.yml里的3.3 Nginx反向代理与静态资源部署Web后台的Vue打包产物放在web-admin/dist目录下需要复制到Nginx的html目录并配置反代API请求# /etc/nginx/conf.d/agri.conf server { listen 80; server_name agri.local; # 静态资源Vue打包后的index.html和js/css location / { root /usr/share/nginx/html/agri; try_files $uri $uri/ /index.html; } # API请求反向代理到后端中台 location /api/ { proxy_pass http://127.0.0.1:8082/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } # WebSocket用于设备实时状态推送网关暴露8081/ws location /ws/ { proxy_pass http://127.0.0.1:8081/ws/; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection upgrade; } }重启Nginx后访问http://your-server-ip就能看到登录页。默认账号密码是admin/123456首次登录后强制修改。4. 常见问题排查那些让我重启三次才定位到的玄学故障部署不是一帆风顺的尤其在农业现场常遇到老旧服务器、网络不稳定、传感器协议私有化等问题。以下是我在5个不同项目中踩过的坑按出现频率排序每条都附带真实日志线索和解决路径。4.1 现象设备在线状态一直显示“离线”但网关日志里明明有数据上报原因网关模块默认每30秒向中台发送一次心跳但中台服务启动后未正确监听网关注册端点或防火墙拦截了8081→8082的HTTP请求。排查查网关日志grep register to core /var/log/agri/gateway.log如果没输出说明注册失败查中台日志grep gateway register /var/log/agri/core.log如果没输出说明中台没暴露注册接口在服务器上手动curl测试curl -X POST http://127.0.0.1:8082/api/gateway/register -d {id:gw-001,ip:127.0.0.1,port:8081}返回200才算通。解决检查中台application-prod.yml中core.gateway.register-url是否配置为http://127.0.0.1:8082/api/gateway/register并确认CoreServiceApplication类上是否有EnableScheduling注解心跳任务需要。4.2 现象虫情相机HTTP上报图片但规则引擎里insect_count字段始终为0原因相机上报的是原始JPEG图片二进制流而网关的HttpHandler默认只解析JSON格式的文本数据图片被直接丢弃。排查查网关日志grep HttpHandler parse error /var/log/agri/gateway.log会看到Content-Type: image/jpeg not supported抓包确认相机发的是POST /api/upload带Content-Type: image/jpeg。解决在HttpHandler.parseRawData()里增加图片处理分支if (image/jpeg.equals(request.getContentType())) { BufferedImage img ImageIO.read(request.getInputStream()); // 调用本地Python脚本做YOLOv5虫体识别需提前部署好 Process proc Runtime.getRuntime().exec(python3 /opt/agri/ai/insect_detect.py tempImagePath); // 解析Python脚本输出的JSON{count: 127, confidence: 0.92} return DeviceMessage.builder() .deviceId(camera-001) .data(Map.of(insect_count, 127)) .build(); }4.3 现象规则保存后不生效日志里反复打印GroovyScriptEngine: compile failed原因Groovy脚本里用了Java 11不支持的语法比如List.of()Java 9才有或脚本中引用了未导入的类如import java.time.LocalDateTime但没加。排查查中台日志grep compile failed /var/log/agri/core.log后面会跟具体错误行号登录MySQL查rule_definition表看script_content字段里第3行是不是写了List.of(1,2,3)。解决把List.of()换成Arrays.asList()所有日期操作用new Date()代替LocalDateTime.now()。Groovy引擎默认用JDK 11的ScriptEngineManager不支持新语法。4.4 现象Nginx访问页面空白F12看Network全是404但/usr/share/nginx/html/agri目录下文件齐全原因Nginx配置里root路径写错了比如写成/usr/share/nginx/html/agri/末尾多斜杠导致Nginx实际查找路径变成/usr/share/nginx/html/agri//index.html。排查ls -l /usr/share/nginx/html/agri/确认文件存在nginx -t检查配置语法tail -f /var/log/nginx/error.log会看到open() /usr/share/nginx/html/agri//index.html failed (2: No such file or directory)。解决删掉root配置末尾的斜杠改为root /usr/share/nginx/html/agri;。4.5 现象Modbus设备数据能读但写指令如开阀无响应串口调试工具测设备本身正常原因网关的Modbus RTU写指令默认用ASCII模式但设备实际需要RTU模式CRC校验或波特率/停止位配置不匹配。排查查网关日志grep Modbus write /var/log/agri/gateway.log看发出的字节是否符合设备手册要求用modbus-cli工具直连设备测试modbus write -m rtu -p /dev/ttyUSB0 -b 9600 -d 8 -s 1 1 1。解决修改device-gateway/src/main/resources/application-prod.ymlgateway: modbus: mode: rtu # 改为rtu不是ascii baud-rate: 9600 ># app.py from flask import Flask, request, jsonify import joblib import numpy as np from datetime import datetime, timedelta app Flask(__name__) # 加载训练好的LSTM模型scikit-learn风格封装 model joblib.load(/opt/agri/ml/soil-predict/lstm_model.pkl) app.route(/predict, methods[POST]) def predict_soil_moisture(): # 请求体示例{device_id: sensor-001, history_24h: [32.1,31.9,...], weather_forecast: [25,26,24]} data request.get_json() device_id data[device_id] history np.array(data[history_24h]).reshape(1, 24, 1) # LSTM输入形状 weather np.array(data[weather_forecast]).reshape(1, 3, 1) # 模型预测未来3小时墒情单位% pred model.predict([history, weather]) moisture_3h float(pred[0][0]) # 计算建议灌溉时长墒情40%时每低1%加30秒 if moisture_3h 40: duration int((40 - moisture_3h) * 30) duration min(duration, 1800) # 最长30分钟 else: duration 0 return jsonify({ device_id: device_id, predicted_moisture: round(moisture_3h, 2), recommended_duration: duration, timestamp: datetime.now().isoformat() }) if __name__ __main__: app.run(host0.0.0.0, port5000, debugFalse)启动服务cd /opt/agri/ml/soil-predict pip3 install flask scikit-learn numpy nohup python3 app.py /var/log/agri/ml-predict.log 21 5.2 在Groovy规则中调用预测服务修改原有“高温虫多→喷淋”规则在判断前先调用预测接口// 新规则预测墒情不足 → 提前灌溉 def deviceId sensor-001 def history24h context.getHistoryValues(soil_moisture, 24 * 60 * 60 * 1000) // 获取24小时历史值 def weatherForecast [25, 26, 24] // 实际应从气象API获取此处简化 // 调用Python预测服务 def url new URL(http://127.0.0.1:5000/predict) def conn url.openConnection() conn.setRequestMethod(POST) conn.setDoOutput(true) conn.setRequestProperty(Content-Type, application/json) conn.getOutputStream().write( {\device_id\:\${deviceId}\,\history_24h\:${history24h},\weather_forecast\:${weatherForecast}}.getBytes(UTF-8) ) def response conn.getInputStream().getText(UTF-8) def result new groovy.json.JsonSlurper().parseText(response) if (result.recommended_duration 0) { context.sendCommand(valve-001, open, result.recommended_duration) context.logAlert(SOIL_PREDICT_IRRIGATION, 预测3小时后墒情${result.predicted_moisture}% 40%建议灌溉${result.recommended_duration}秒) }关键细节Groovy里URL.openConnection()默认超时很长必须加超时控制否则规则卡死conn.setConnectTimeout(5000) // 连接超时5秒 conn.setReadTimeout(10000) // 读取超时10秒5.3 模型训练与部署流水线给算法同学的交付物我们不把模型训练塞进Java工程而是给算法同学一份标准化交付清单文件名用途格式要求lstm_model.pkl训练好的LSTM模型joblib.dump()生成输入shape(1,24,1)和(1,3,1)preprocess.py数据预处理脚本必须含def preprocess(history_24h, weather_forecast):函数requirements.txtPython依赖只允许flask2.2.5,scikit-learn1.2.2,numpy1.23.5算法同学每次更新模型只需替换lstm_model.pkl重启Python服务即可Java侧规则脚本完全不用动。这种分工让农业项目里“懂作物的农技员”、“懂硬件的工程师”、“懂算法的数据科学家”能真正并行工作。从那以后我每次接到农业类需求第一件事就是检查客户有没有现成的土壤传感器数据——如果有就直接搭这个预测模块如果没有就先用规则引擎做基础联动等数据积累够了再无缝升级。这套系统真正的价值不是它现在能做什么而是它留出了足够干净的扩展缝让技术能跟着农田的实际需求一起生长。希望帮到你。本文还有配套的精品资源点击获取
RELATED

相关推荐

营销技能拆解:从热词感知到数据复盘的实战方法论

营销技能拆解:从热词感知到数据复盘的实战方法论

做营销这行,很多人问我到底什么算“营销技能过硬”。我的回答一直是:能不能把一个新出现的热词,快速翻译成自己的选题、文案和转化动作,并且每做完一轮都知道下一轮怎么改。所谓 marketingskills,拆开看就是这三件事—…

📅 2026/10/27 5:21:33
TSL1401线性CCD循迹原理与实战调试指南

TSL1401线性CCD循迹原理与实战调试指南

简介:本资源是一份面向智能车竞赛光电组参赛者及嵌入式图像采集初学者的TSL1401线性CCD快速入门指南,聚焦解决线阵CCD原理理解难、英文文档实操性弱、曝光时序编程模糊等核心痛点。文档系统梳理了TSL1401与面阵CCD的本质区别(如仅128像素单行…

📅 2026/10/27 5:21:33
Android 2.2开发教学实验:JDK6+Eclipse+ADT全链路避坑指南

Android 2.2开发教学实验:JDK6+Eclipse+ADT全链路避坑指南

简介:本资源是一份面向软件学院本科生的《移动应用软件开发技术》课程实验报告,聚焦Android平台基础开发能力培养,适用于移动应用开发入门学习与课程实践复盘。报告系统覆盖Android开发环境搭建(JDK、Eclipse、SDK及ADT插件的安装…

📅 2026/10/27 5:21:33
MORE NEWS

更多资讯

📰

Docker容器GPU加速实战:CUDA驱动与框架版本对齐指南

1. 项目概述:Docker容器里跑GPU模型,不是“配个参数”就完事的你是不是也遇到过这种情况:本地训练一个PyTorch模型,单卡RTX 4090跑得飞快,但一打包进Docker镜像,nvidia-smi能看见显卡,torch.cud…

📰

10个提升Mac Dock效率的实用技巧

1. Mac程序坞的隐藏价值与常见误区作为一名使用Mac超过8年的资深用户,我见过太多朋友把Dock(程序坞)仅仅当作一个简单的应用启动栏。实际上,这个看似简单的界面元素蕴含着苹果工程师精心设计的诸多交互逻辑。很多人不知道的是&…

📰

Git删除指定commit的完整操作指南

1. Git分支中删除指定commit的完整指南作为版本控制系统的核心工具,Git在日常开发中扮演着重要角色。最近在团队协作时遇到一个典型场景:某次错误提交的commit已经推送到远程分支,需要在不影响其他提交的情况下将其从分支历史中彻底移除。经过…

📰

AI Agent开发中Skills的演进与实战应用

1. Skills的演进:从编程工具配角到Agent研发核心十年前我刚入行时,Skills还只是IDE里一个不起眼的插件功能。当时在Eclipse里安装Code Recommenders插件,它会根据上下文提示可能的API调用——这种初级代码补全就是Skills的雏形。如今在AI Age…

📰

Git删除特定commit的实用指南与最佳实践

1. 为什么需要删除分支中的特定commit?在团队协作开发中,Git分支管理是日常工作的核心部分。我遇到过无数次这样的情况:某个功能分支中混入了不该提交的敏感信息,或者不小心提交了错误的代码修改,又或者需要清理历史记…

📰

Agent-Reach:面向AI Agent的轻量级跨平台API通信中枢

1. 项目概述:Agent-Reach 是什么,它解决的是哪类真实问题? Agent-Reach 不是一个泛泛而谈的“智能体概念”,而是面向开发者、自动化工程师和内容运营者的一套 可落地、可嵌入、可调度的轻量级代理通信中枢 。它本质上是一套标准…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬