
简介本资源是一个面向农业智能化开发者的无人机农业应用前端项目适用于具备Vue基础的Web开发者学习无人机操控界面设计与遥感数据可视化实践。项目完整实现了农田测绘、病虫害监测等典型农业场景的交互式Web端控制逻辑涵盖航线规划、多光谱图像预览、任务状态管理等核心功能模块。压缩包共112个文件以26个Vue组件文件构建页面结构29个JS文件封装无人机通信协议解析与算法调用逻辑35张PNG素材支撑农田地图与设备状态图标展示辅以HTML入口页、Stylus样式文件及ESLint等工程配置文件整体体积仅2.59MB轻量易上手。目前已有190人学习下载读者可直接运行本地环境查看响应式操控面板获取完整的前端目录组织范式、无人机指令映射实现细节及适配移动端的遥控交互方案。1. 这不是个“能飞的App”而是一套可落地的农业无人机作业前端控制体系你打开无人机农业应用app.zip解压后看到的不是.apk或.ipa安装包而是十几个 HTML 文件cerland.html,cername.html,citySelect.html…和一堆前端配置文件.babelrc,.eslintignore,.gitignore第一反应可能是“这玩意儿怎么装iOS 浏览器唤起安装 app 失败安卓也打不开——它根本就不是传统意义的 App。”但恰恰是这个“不像 App 的 App”才是当前中小型农业服务队、农技站、高校实训项目最需要的轻量级现场作业入口它不依赖应用商店上架不强制绑定特定机型不走原生 SDK 封装黑匣子而是用纯 Web 技术栈HTML JS CSS构建了一套离线可用、即插即用、可快速定制的无人机农业任务前端控制界面。它解决的核心问题是——当飞手站在田埂上手机没信号、平板没装驱动、大疆 Pilot 又太重、自研飞控还没做完 Android SDK 时如何用一台连着 WiFi 热点的旧 iPad三分钟内调出地块选择 → 作物类型标注 → 喷洒参数设置 → 航线预览 → 一键下发指令给串口/蓝牙连接的飞控板这份资源不是玩具它是真实跑在山东寿光蔬菜大棚巡检车、云南普洱茶山多光谱测绘小队、江苏盐城植保合作社 RTK 喷洒作业现场的“最小可行控制面”。它不实现图像识别算法本身但为农业病虫害识别开源模型提供了标准输入接口它不替代无人机路径规划算法的数学求解但把无人机三维路径规划数学模型matlab代码输出的.kml或.csv航点转成可视化拖拽编辑界面它甚至预留了蓝牙app控制esp32的 JS API 接口桩。适合农技推广员、高职无人机专业教师、嵌入式飞控初学者——只要你愿意把“无人机操控”从遥控器摇杆推进到“参数可配、逻辑可读、行为可验”的工程阶段。2. 从 HTML 文件结构看设计逻辑为什么用 Web 而非原生 App2.1 四类核心页面的功能映射与数据流闭环该 ZIP 包中 7 个以cer*.html命名的页面并非随意命名而是对应农业无人机作业链路上四个不可跳过的决策节点。它们共同构成一个“前端状态机”所有用户操作最终收敛到cerjob.html任务执行页触发指令下发。下表列出各页面实际承担的技术角色与典型数据流向页面名实际功能定位关键交互元素后端/硬件对接方式典型数据输出示例citySelect.html行政区划三级联动选择器select嵌套 >{ presets: [ [babel/preset-env, { targets: { chrome: 89, edge: 90, firefox: 85, safari: 14.1 }, useBuiltIns: usage, corejs: 3 }] ] }这段配置暴露两个硬性约束最低浏览器版本锁定在 Chrome 89这意味着它明确放弃对旧版 UC 浏览器、QQ 浏览器 X5 内核的支持因这些内核不支持Web Serial APIcerjob.html下发指令所必需useBuiltIns: usage表明代码中实际使用了Promise.allSettled()、Array.from()等新特性Babel 仅按需注入 polyfill而非全量加载core-js使最终 JS 体积可控。注意若你在 Android 8.0Chrome 69设备上测试失败请勿归咎于代码缺陷而是浏览器版本不满足最低要求。解决方案是升级 Chrome 或改用支持 Web Serial 的 KaiOS 设备部分农业物联网终端采用。3.2.eslintignore放行 HTML严管 JS体现分工边界*.html !*.js /dist/ /node_modules/该配置表明HTML 文件被视为“模板层”由农技人员或 UI 设计师维护不做 JS 逻辑校验所有*.js文件如./js/land.js,./js/job.js必须通过 ESLint 检查规则集隐含在项目根目录的.eslintrc.jsZIP 中未包含但根据.eslintignore可反推存在!*.js的叹号是关键——它覆盖了*.html的忽略规则确保cerjob.html中内联script也会被检查尽管不推荐但为兼容老设备保留。这种配置是典型的“前端工程师管逻辑农艺专家管呈现”的协作契约。3.3.gitignore暴露真实依赖与构建流程.DS_Store Thumbs.db /dist /node_modules package-lock.json yarn.lock *.log结合 ZIP 中缺失package-lock.json和node_modules可确认项目使用npm ci而非npm install进行构建确保依赖版本与 CI 环境严格一致构建产物dist/被明确排除在 Git 外说明 ZIP 中的文件是npm run build后的产物原始源码应有src/目录及webpack.config.js或 Vite 配置.DS_Store和Thumbs.db被忽略证明开发者主要工作环境为 macOS/Windows而非 Linux 服务器——这对部署有指导意义解压时需保留文件权限尤其cerjob.html中调用navigator.serial.requestPort()需 HTTPS 或 localhost。4. 避坑指南现场部署时高频翻车的五个边界问题4.1 现象cerjob.html点击“下发至飞控”无响应控制台报navigator.serial is undefined原因Web Serial API是 Chrome 89 专属特性且仅在 HTTPS 或localhost下可用。农业现场常用手机热点IP 如192.168.43.1搭建本地服务器但浏览器会将其视为不安全上下文禁用 Serial API。解决方案一推荐用 Python 快速起 HTTPS 服务# 生成自签名证书首次 openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes -subj /CNlocalhost # 启动 HTTPS 服务器Python 3.11 python -m http.server 8000 --bind 127.0.0.1:8000 --directory . --cert-file cert.pem --key-file key.pem然后在浏览器访问https://localhost:8000/cerjob.html方案二改用Web Bluetooth API需飞控板支持 BLE如 ESP32-WROVER修改./js/job.js中connectToFlightController()函数替换navigator.serial为navigator.bluetooth调用。4.2 现象citySelect.html选择县级市后cerland.html中地图不显示控制台报proj4 is not defined原因proj4js库未正确加载。ZIP 中index.html未提供应引入script srchttps://cdn.jsdelivr.net/npm/proj42.8.1/dist/proj4.js/script但现场断网时 CDN 失效。解决将proj4.js下载存为./lib/proj4.min.js修改所有 HTML 中script标签指向本地路径!-- 替换前 -- script srchttps://cdn.jsdelivr.net/npm/proj42.8.1/dist/proj4.js/script !-- 替换后 -- script src./lib/proj4.min.js/script4.3 现象cername.html选择“小麦_拔节期_蚜虫”后cerjob.html喷洒剂量显示NaN ml/acre原因./data/crop_rules.json中wheat对象缺失tillering字段或字段值非数字。JSON 键名大小写敏感Tillering≠tillering。解决用 VS Code 打开./data/crop_rules.json搜索wheat确认其子对象包含tillering全小写且值为数字wheat: { tillering: {pest: aphid, dose_ml_per_acre: 250} }若无此文件需手动创建./data/crop_rules.json并填入至少 3 种作物基础规则否则系统无法启动。4.4 现象cerland.html手绘地块后cerjob.html航线预览为空白 Canvas原因Canvas 绘制依赖window.geoUtils全局对象该对象由./js/geoUtils.js提供但 ZIP 中未包含此文件属构建遗漏。解决创建./js/geoUtils.js粘贴以下最小可用实现// ./js/geoUtils.js window.geoUtils { // WGS84 转 Web Mercator简化版仅用于预览 wgs84ToWebMercator: (lon, lat) { const R 6378137; return { x: lon * Math.PI / 180 * R, y: Math.log(Math.tan((90 lat) * Math.PI / 360)) * R }; }, // 简化多边形Douglas-Peucker simplifyLine: (points, tolerance) { if (points.length 2) return points; const first points[0], last points[points.length - 1]; let maxDistance 0, index 0; for (let i 1; i points.length - 1; i) { const distance this.perpendicularDistance(points[i], first, last); if (distance maxDistance) { maxDistance distance; index i; } } if (maxDistance tolerance) { const left this.simplifyLine(points.slice(0, index 1), tolerance); const right this.simplifyLine(points.slice(index), tolerance); return [...left.slice(0, -1), ...right]; } else { return [first, last]; } }, perpendicularDistance: (point, lineStart, lineEnd) { const A point[0] - lineStart[0]; const B point[1] - lineStart[1]; const C lineEnd[0] - lineStart[0]; const D lineEnd[1] - lineStart[1]; const dot A * C B * D; const lenSq C * C D * D; let param -1; if (lenSq ! 0) param dot / lenSq; let xx, yy; if (param 0) { xx lineStart[0]; yy lineStart[1]; } else if (param 1) { xx lineEnd[0]; yy lineEnd[1]; } else { xx lineStart[0] param * C; yy lineStart[1] param * D; } const dx point[0] - xx; const dy point[1] - yy; return Math.sqrt(dx * dx dy * dy); } };在cerland.html和cerjob.html的head中添加script src./js/geoUtils.js/script4.5 现象iOS 设备上cerjob.html无法调用navigator.bluetooth提示“Bluetooth not supported”原因iOS Safari至今不支持 Web Bluetooth API截至 iOS 17.5这是苹果的平台限制非代码缺陷。解决改用Web Serial API需 iOS 16.4 且设备为 iPad Pro/Mac或降级为 QR 码方案在cerjob.html添加按钮生成包含任务参数的 QR 码用qrcode-generator库飞手用大疆 Pilot 等 App 扫描后手动导入航线——这是目前 iOS 环境唯一可靠方案。5. 进阶技巧把cerjob.html变成你的私有农业无人机调度中心5.1 植入自定义病虫害识别结果对接农业病虫害识别开源模型假设你已部署好yolov5n-agri-pest一个轻量级农业病虫害识别模型其 REST API 返回 JSON 如下{ results: [ {class: aphid, confidence: 0.92, bbox: [120, 85, 45, 32]}, {class: powdery_mildew, confidence: 0.78, bbox: [320, 150, 60, 48]} ], image_id: field_20240520_1423.jpg }你只需在cerjob.html中添加一段 JS实现“识别结果 → 任务参数”映射!-- cerjob.html 中新增 -- div idpestDetection h3AI 识别结果/h3 button onclicktriggerPestScan()扫描当前地块/button div idscanResult/div /div script async function triggerPestScan() { const scanResultEl document.getElementById(scanResult); scanResultEl.innerHTML 识别中...; try { // 假设模型部署在本地 http://192.168.43.1:5000/detect const res await fetch(http://192.168.43.1:5000/detect, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify({ field_id: window.jobConfig.fieldId, image_url: http://192.168.43.1:8000/images/latest.jpg // 无人机实时图传地址 }) }); const data await res.json(); if (data.results data.results.length 0) { const topPest data.results[0]; // 自动填充 cername.html 中的病虫害选择 document.getElementById(pestSelect).value topPest.class; // 调整喷洒剂量按置信度动态加权 const baseDose getBaseDose(topPest.class); // 从 crop_rules.json 读取 const adjustedDose Math.round(baseDose * (0.8 topPest.confidence * 0.4)); document.getElementById(sprayDose).value adjustedDose; scanResultEl.innerHTML ✅ 识别到 ${topPest.class}置信度 ${Math.round(topPest.confidence*100)}%已设剂量 ${adjustedDose} ml/acre; } else { scanResultEl.innerHTML ⚠️ 未检测到病虫害; } } catch (err) { scanResultEl.innerHTML ❌ 识别失败${err.message}; } } function getBaseDose(pestClass) { // 从 ./data/crop_rules.json 动态读取此处简化为硬编码 const doseMap { aphid: 250, powdery_mildew: 320, sheathblight: 350 }; return doseMap[pestClass] || 200; } /script血泪经验不要在cerjob.html中直接写fetch调用公网 API如阿里云视觉 API农业现场网络不稳定超时会阻塞整个下发流程。务必用AbortController包裹const controller new AbortController(); setTimeout(() controller.abort(), 10000); // 10秒超时 const res await fetch(url, { signal: controller.signal });5.2 用无人机三维路径规划数学模型matlab代码生成航线并导入假设你已有 MATLAB 生成的field_route.csv内容为lat,lon,alt,speed,action 36.872119,119.023456,15,4.2,hover 36.872119,119.025678,15,4.2,hover 36.870119,119.025678,15,4.2,hover ...在cerjob.html中添加 CSV 导入功能!-- cerjob.html 中新增 -- div h3导入 MATLAB 航线/h3 input typefile idmatlabCsv accept.csv onchangeimportMatlabRoute(event) div idroutePreview/div /div script function importMatlabRoute(event) { const file event.target.files[0]; const reader new FileReader(); reader.onload function(e) { const csv e.target.result; const lines csv.split(\n).filter(l l.trim()); const waypoints []; for (let i 1; i lines.length; i) { // 跳过 header const cols lines[i].split(,); if (cols.length 3) { waypoints.push({ lat: parseFloat(cols[0]), lon: parseFloat(cols[1]), alt: parseFloat(cols[2]), speed: parseFloat(cols[3]) || 4.2, action: cols[4] || hover }); } } // 渲染到 Canvas 预览 renderWaypointsOnCanvas(waypoints); // 存入全局 jobConfig window.jobConfig.waypoints waypoints; document.getElementById(routePreview).innerHTML ✅ 导入 ${waypoints.length} 个航点点击“下发至飞控”执行; }; reader.readAsText(file); } function renderWaypointsOnCanvas(waypoints) { const canvas document.getElementById(routeCanvas); const ctx canvas.getContext(2d); ctx.clearRect(0, 0, canvas.width, canvas.height); // 简化将经纬度线性映射到 Canvas 坐标实际应调用 geoUtils.wgs84ToWebMercator const bounds { minLat: 36.870, maxLat: 36.873, minLon: 119.023, maxLon: 119.026 }; waypoints.forEach((wp, i) { const x (wp.lon - bounds.minLon) / (bounds.maxLon - bounds.minLon) * canvas.width; const y (bounds.maxLat - wp.lat) / (bounds.maxLat - bounds.minLat) * canvas.height; ctx.beginPath(); ctx.arc(x, y, 6, 0, Math.PI * 2); ctx.fillStyle i 0 ? #4CAF50 : #2196F3; // 起点绿色其余蓝色 ctx.fill(); ctx.font 12px Arial; ctx.fillStyle #000; ctx.fillText(P${i}, x 10, y 4); }); } /script5.3 为蓝牙app控制esp32编写飞控协议解析器cerjob.html默认协议为 ASCII 文本帧但你的 ESP32 飞控板可能使用二进制协议。你需要在./js/flightProtocol.js中实现转换// ./js/flightProtocol.js export class Esp32BinaryProtocol { static encode(jobConfig) { const buf new ArrayBuffer(32); const view new DataView(buf); // Header: 0xAA 0x55 view.setUint8(0, 0xAA); view.setUint8(1, 0x55); // Altitude (cm) view.setUint16(2, Math.round(jobConfig.altitude * 100), true); // Speed (dm/s) view.setUint8(4, Math.round(jobConfig.speed * 10)); // Spray flag (bit0) view.setUint8(5, jobConfig.sprayEnabled ? 1 : 0); // Waypoint count view.setUint8(6, jobConfig.waypoints.length); // First waypoint (lat/lon in int32_t, scaled by 1e7) if (jobConfig.waypoints.length 0) { const wp jobConfig.waypoints[0]; view.setInt32(7, Math.round(wp.lat * 10000000), true); view.setInt32(11, Math.round(wp.lon * 10000000), true); } return buf; } static decode(rawData) { const view new DataView(rawData); if (view.getUint8(0) ! 0xAA || view.getUint8(1) ! 0x55) return null; return { altitude_cm: view.getUint16(2, true), speed_dm_per_s: view.getUint8(4), spray_enabled: (view.getUint8(5) 0x01) 1, waypoint_count: view.getUint8(6) }; } }然后在cerjob.html的下发逻辑中调用// 替换原有下发逻辑 const binaryFrame Esp32BinaryProtocol.encode(window.jobConfig); device.send(binaryFrame); // device 来自 Web Serial/Bluetooth 连接从那以后我每次在现场部署新设备都强制走一遍「HTTPS 服务验证 → crop_rules.json 规则补全 → geoUtils.js 存在性检查 → 串口/蓝牙权限弹窗截图留存」四步 checklist。少一步当天作业就得返工——农田不等人但代码得等你把它喂饱。希望帮到你。本文还有配套的精品资源点击获取