尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

无人机飞行管控平台源码解析:MySQL/Redis/MongoDB三库协同设计

无人机飞行管控平台源码解析:MySQL/Redis/MongoDB三库协同设计 简介一套基于Java与Redis、MySQL、MongoDB的无人机飞行管控平台完整源码及项目说明适合有Java基础的开发者或无人机系统学习者用于快速搭建管控平台。资源从前端、后端、无人机客户端三大模块组织整合关系型与非关系型数据库修改配置中的数据库地址和密码即可运行。压缩包共701个文件约8.35MB其中299个Java文件承载后端业务与客户端逻辑109个Vue文件实现前端界面另含SVG、JS、XML、SQL等资源与配置脚本覆盖页面元素、前端交互、环境部署和数据库初始化。另附项目说明与若依环境使用手册便于本地环境搭建。目前已吸引90人学习可借此掌握多数据库协同开发、前后端分离实战及无人机业务建模并为二次开发提供清晰骨架。1. 无人机飞行管控平台源码一套能直接跑通全流程的后端骨架做无人机相关系统的朋友应该都有这个体感网上能找到的源码要么是只画了页面的半成品要么是单机版玩具。真正把 MySQL 的订单事务、Redis 的实时位置缓存、MongoDB 的轨迹点存储串在一起的完整后端项目少之又少。这套基于 Java Redis MySQL MongoDB 的无人机飞行管控平台源码我拆完之后第一印象是它不追求花哨但把管控平台该有的骨架都搭齐了——航班申请、飞行计划审批、禁飞区校验、实时位置上报、历史轨迹回放这些核心链路都有可跑的代码。适合谁两类人。一类是课程设计或毕业设计需要「能演示、能答辩、能解释清楚」的完整项目另一类是工作里突然接到无人机管控、低空经济相关需求的 Java 工程师用这套源码做底子改业务比自己从零搭省太多时间。项目说明文档写得比较全数据库初始化脚本也是现成的后面我会把启动步骤和几个最容易翻车的点都过一遍。2. 技术选型拆解为什么是 MySQL Redis MongoDB 三库并存2.1 三库各管一摊事的划分逻辑很多第一次看到这个技术栈组合的人会问一个项目为什么要用三个数据库是不是炫技拆完代码以后我的结论是这个选型基本符合无人机管控场景的真实需求不是硬凑。先看 MySQL。它管的是「不能丢、要强一致」的数据用户表、角色权限表、航班申请表、飞行计划审批记录、设备注册信息。这些数据的特点是单日增量不大、条数到百万级已经很多但每条都牵扯事务和修改历史用 MySQL 完全够而且运维成本最低。再看 Redis。它负责两件事飞行器的实时位置缓存和分布式锁。无人机的位置上报频率高每秒可能几十上百条但地面站看大屏只需要「最近一次的位置」这个用 Redis 的 String 结构存 JSON 就行读写都是微秒级。分布式锁用在「一个飞行计划只能被一个审批员处理」这种并发场景防止两个人同时审批同一条计划。最后是 MongoDB。它管飞行轨迹点。一架无人机飞行一小时每秒上报一个点就是 3600 条轨迹记录而这些记录几乎没有修改需求只有追加和查询而且超过一定时间比如 90 天就可以清理。MongoDB 的文档模型天然适合这种稀疏、嵌套的数据结构TTL 索引又能自动过期清理比 MySQL 定期删表舒服太多。2.2 数据流时序一次飞行任务怎么在三库之间流转把三库串起来看一次完整飞行任务的流转就能理解整个平台的架构了。用户提交航班申请 - MySQL: 插入航班申请表 - 系统校验禁飞区 - Redis: 读取缓存中的禁飞区数据 - 审批员审批 - MySQL: 更新审批状态 Redis: 获取分布式锁 - 飞行器起飞 - MongoDB: 开始写入轨迹点 - 飞行中 - Redis: 实时更新最新位置 - 飞行结束 - MongoDB: 关闭轨迹记录 MySQL: 更新飞行任务状态这里有个关键设计值得注意禁飞区数据不是每次都查 MySQL而是启动时加载到 Redis 里。我拆代码时看到项目里有一个NoFlyZoneCacheLoader实现了 Spring 的ApplicationRunner接口应用启动后自动把 MySQL 的禁飞区表全量加载到 Redis之后校验直接查询 Redis。这种设计在禁飞区数据量不大几千条以内时非常实用响应速度快实现也简单。2.3 一个容易被忽略的选型细节轨迹点为什么不用 Redis 持久化还有一个细节值得展开。实时位置和轨迹点是两个不同的概念这个平台把它们分开存储了Redis 里只存「最新一个位置点」MongoDB 里存「全部历史轨迹点」。看着是小事但如果设计反了会出大问题——把所有轨迹点都塞 Redis内存会成倍增长而且冷数据长期占用内存非常浪费反过来如果实时位置也写 MongoDB大屏刷新的延迟会明显升高。对应到代码里项目对 Redis 的使用限定了非常明确的数据类型位置用 String锁用 SETNX飞行计划缓存用 Hash。没有滥用 Redis 做复杂聚合查询这是加分项。3. 核心业务模块实战航班管理、禁飞区校验与轨迹回放3.1 航班申请与审批链路的关键实现这个模块算是整个管控平台的中枢代码里对应的 Controller、Service、Mapper 三层结构很清晰适合当模板看。核心逻辑在FlightPlanService里申请和审批走的是同一个事务边界。Service public class FlightPlanService { Autowired private FlightPlanMapper planMapper; Autowired private NoFlyZoneService noFlyZoneService; Transactional(rollbackFor Exception.class) public FlightPlanResult createFlightPlan(FlightPlanRequest request) { // 1. 校验时间窗口申请的起止时间不能非法防止脏数据入库 if (request.getStartTime().isAfter(request.getEndTime())) { return FlightPlanResult.failed(起飞时间不能晚于降落时间); } // 2. 校验禁飞区碰撞坐标点是否在禁飞区内 NoFlyZoneCheckResult zoneResult noFlyZoneService.checkCoordinates( request.getTakeoffPoint(), request.getRoutePoints()); if (zoneResult.isForbidden()) { return FlightPlanResult.failed(飞行路径经过禁飞区: zoneResult.getZoneName()); } // 3. 落库状态默认为 PENDING等待审批员审核 FlightPlan plan new FlightPlan(); BeanUtils.copyProperties(request, plan); plan.setStatus(PENDING); planMapper.insert(plan); return FlightPlanResult.success(plan.getId()); } }这个方法的三个步骤是有先后逻辑的先做参数自校验再做业务规则校验最后落库。用Transactional保证如果中间抛异常已经插入的航班记录能回滚。我在实际项目中踩过类似的坑——最开始没加rollbackFor Exception.classSpring 默认只在 RuntimeException 时回滚如果你在业务里 catch 了异常自己包装成 checked exception 抛出事务就会失效。参数说明FlightPlanRequest里的routePoints是一个ListCoordinate代表航线途经点禁飞区校验就是拿这些点逐一遍历判定的。实际用的是射线法判断点是否在多边形禁飞区内逻辑在NoFlyZoneServiceImpl里复杂度是 O(n)禁飞区边缘点数不多性能没问题。3.2 禁飞区校验与 Redis 缓存加载禁飞区的存储和校验是管控平台的核心安全能力。项目里把禁飞区设计成了一个多边形集合每个多边形由若干经纬度坐标点构成存在 MySQL 的no_fly_zone表。启动时通过NoFlyZoneCacheLoader加载到 RedisComponent public class NoFlyZoneCacheLoader implements ApplicationRunner { Autowired private StringRedisTemplate redisTemplate; Autowired private NoFlyZoneMapper zoneMapper; private static final String ZONE_KEY_PREFIX nofly:zone:; Override public void run(ApplicationArguments args) { ListNoFlyZone zones zoneMapper.selectAll(); // 先清理旧缓存防止更新后的禁飞区数据叠加上旧数据 redisTemplate.delete(redisTemplate.keys(ZONE_KEY_PREFIX *)); for (NoFlyZone zone : zones) { String key ZONE_KEY_PREFIX zone.getId(); // 直接序列化成 JSON 字符串查询时反序列化 redisTemplate.opsForValue().set(key, JSON.toJSONString(zone)); } log.info(禁飞区数据加载完成共 {} 条, zones.size()); } public NoFlyZone getZoneById(Long zoneId) { String json redisTemplate.opsForValue().get(ZONE_KEY_PREFIX zoneId); if (StringUtils.hasText(json)) { return JSON.parseObject(json, NoFlyZone.class); } return null; } }这里的核心设计点有两个。第一是用了ApplicationRunner保证应用完全启动前禁飞区数据就已经在 Redis 里了不会出现服务刚起来时查不到禁飞区导致误放行。第二是缓存 JSON 字符串而不是 Java 对象序列化这样 Redis 里的数据可以跨语言读取排查问题的时候用redis-cli get直接看到明文坐标不用反序列化工具。需要注意这里用的是StringRedisTemplate序列化方式是 String。如果你用默认的RedisTemplate而没有指定序列化器存进去的 key 会带\xac\xed\x00\x05t...这样的乱码前缀会导致keys匹配失效、跨系统查不到数据。这属于 Redis 使用里最常见的翻车点之一后面避坑章节我会再展开。3.3 轨迹存储与回放的前后端协作设计轨迹记录是 MongoDB 的主场。每次飞行任务开始后端会创建一个以flightId命名的轨迹文档集合飞行过程中无人机上报的位置点逐个插入飞行结束后前端选择某个历史航班后端按时间范围分页读取轨迹点返回给前端画线。Document(collection flight_track) public class FlightTrack { Id private String id; // 关联 MySQL 里的航班计划 ID private Long flightPlanId; // 经纬度坐标点列表嵌套结构 private ListTrackPoint points; // 轨迹开始与结束时间用于条件查询 private Date startTime; private Date endTime; Data public static class TrackPoint { private Double longitude; private Double latitude; private Double altitude; private Long timestamp; } }这个文档结构的设计值得说一下。轨迹点不是一条一条单独插入的而是先创建一条包含flightPlanId的文档然后通过$push把位置点追加到points数组里。这样查询时一次就能取出整条轨迹不用跨文档聚合。这样做的好处是读性能高但上限也要心里有数当单文档的points数组超过 16MBMongoDB 单文档大小上限时需要手动拆分段。实测下来一个点大约 80 字节16MB 大概能存 20 万个点按每秒上报一个计算能撑 55 小时连续飞行。对绝大多数无人机场景来说完全够用但如果你做的是长航时固定翼就得考虑分桶存储的策略了。另外一个细节timestamp字段是 Long 类型存毫秒值而不是 Date 类型。这样做的好处是前端拿到后可以直接new Date(timestamp)显示不需要做时区转换也避免了 MongoDB 和 Java 之间日期序列化的时区偏移问题。4. 本地环境搭建与启动全流程从空环境到跑通接口4.1 初始化 MySQL 数据库与账号配置拿到源码第一步不是急着启动而是先把数据库初始化好。项目里有一个sql目录里面是建库建表和初始化数据脚本。建议用 Navicat 或命令行逐条执行不要用导入 SQL 文件的方式——初始化数据里如果有中文编码问题导入失败不容易排查。# 登录 MySQL密码为你的本地 root 密码 mysql -u root -p # 创建数据库字符集必须用 utf8mb4否则禁飞区名称可能有中文乱码 CREATE DATABASE IF NOT EXISTS drone_control DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; exit; # 切换到项目 sql 目录下执行脚本 mysql -u root -p drone_control sql/init_table.sql mysql -u root -p drone_control sql/init_data.sql执行完可以验证一下数据是否正常查一下no_fly_zone表里有没有禁飞区记录再查flight_plan表里有没有测试航班数据。如果没有测试数据说明脚本执行不完整后面启动后页面是空的不好验证效果。参数说明init_table.sql里包含所有建表语句我数了一下总共 12 张表涵盖用户、角色、设备、航班、计划、审批记录、禁飞区等核心实体。init_data.sql里包含一个管理员账号和几条初始化禁飞区数据管理员的用户名密码在文档里都有写默认密码是加密过的不用管直接用文档给的即可。4.2 配置 Redis 与 MongoDB 连接参数启动前要检查三个配置文件application.yml、application-dev.yml或application-prod.yml。根据你的环境在启动参数里指定激活哪个 profile。项目里默认是 dev 环境。spring: redis: host: 127.0.0.1 port: 6379 password: 你的redis密码 database: 0 timeout: 3000ms lettuce: pool: max-active: 8 max-idle: 8 min-idle: 0 data: mongodb: uri: mongodb://127.0.0.1:27017/drone_control # 如果 MongoDB 启用了认证改成如下格式 # uri: mongodb://username:password127.0.0.1:27017/drone_control?authSourceadmin datasource: url: jdbc:mysql://127.0.0.1:3306/drone_control?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: root password: 你的mysql密码 driver-class-name: com.mysql.cj.jdbc.Driver这里有一个常见的坑spring.data.mongodb.uri如果 MongoDB 没开认证但你配了用户名密码启动时会报认证失败反过来如果开了认证但没配启动会卡在连接超时。建议先确认本地 MongoDB 到底开没开--auth再决定 URI 怎么写。参数说明Redis 的database: 0表示使用第 0 个库如果你本地 Redis 里以前存过其他项目的缓存建议改成database: 1避免 key 冲突。连接池max-active: 8对这个项目的并发量来说足够不用调大。4.3 启动 Spring Boot 应用与验证接口配置完成后直接用 IDE 启动主类或者用 Maven 命令行打包启动。# 进入项目根目录先执行打包 mvn clean package -DskipTests # 启动后端服务指定使用 dev 配置 java -jar target/drone-control.jar --spring.profiles.activedev看到控制台输出Started DroneControlApplication in xx.xx seconds且没有报错就说明启动成功了。然后验证几个核心接口# 登录获取 token替换成你自己的管理员账号密码 curl -X POST http://localhost:8080/api/auth/login \ -H Content-Type: application/json \ -d {username:admin,password:123456} # 用返回的 token 查询航班列表 curl -X GET http://localhost:8080/api/flightPlan/list \ -H Authorization: Bearer 你的token如果登录接口返回了 token 且查询接口返回了航班数据说明 MySQL、Redis、MongoDB 三库的连接和基础逻辑都通了。接下来可以试着提交一个航班申请然后去 MongoDB 里查看是否生成了对应的轨迹文档验证整条链路。4.4 前端项目启动与联调参数校正这套源码还带一个前端管理页面技术栈用的 Vue。如果你要完整演示前端也得跑起来。# 进入前端目录 cd frontend # 安装依赖如果网络慢可以用国内镜像 npm install --registryhttps://registry.npmmirror.com # 启动开发服务默认端口 8081 npm run dev前端启动后访问http://localhost:8081用管理员账号登录应该能看到仪表盘、航班管理、禁飞区管理、轨迹回放等菜单。前端调用后端的接口地址一般配置在vue.config.js的proxy里如果后端改了端口这里要同步改不然会出现前端页面 404 或接口连接失败的诡异问题。5. 避坑与排查这套源码最容易翻车的五个地方5.1 Redis key 乱码导致缓存查询失效现象启动不报错但禁飞区校验永远通过Redis 里手动也查不到对应的 key。原因用默认RedisTemplate而没有指定StringRedisSerializer导致 key 被 JDK 序列化器处理redis-cli 里看到的是\xac\xed\x00\x05t...这样的乱码代码里用nofly:zone:1去查它是永远查不到的。解决把禁飞区缓存相关操作全部换成StringRedisTemplate这是最省事的方式。如果必须用RedisTemplate就单独为 key 和 value 指定序列化器。5.2 MongoDB 轨迹点插入失败但是日志不报错现象飞行任务跑完了前端轨迹回放却是空白后端日志没有异常。原因FlightTrack文档的points数组在插入时没有做$push的批量更新而是每次通过save整体覆盖。如果两个位置点同一毫秒到达后一条覆盖前一条数据看起来像丢了。解决改成Update$push的方式追加轨迹点。$push天然是原子性的同一毫秒多个点也不会互相覆盖。5.3 MySQL 时间字段类型导致日期显示偏移现象前端显示的航班计划起飞时间比数据库里存的时间早或晚 8 个小时。原因JDBC URL 里没有配置serverTimezone或者配置成了 UTC。MySQL 驱动默认读取的是 JVM 时区如果应用服务器和数据库服务器时区不一致就会出现时间偏移。解决JDBC 连接串里加上serverTimezoneAsia/Shanghai同时检查application.yml里是否设置了spring.jackson.time-zone: GMT8。这两个位置保持一致时间基本不会再错。5.4 Redis 分布式锁误删他人锁现象审批操作偶尔会报「当前申请已被其他审批员处理」但实际只有一个人操作。原因锁的 value 没有设置唯一标识。线程 A 获取锁后业务处理时间较长锁自动过期线程 B 获取锁A 处理完释放锁时把自己的 value 删了导致 B 的锁提前被释放。解决释放锁之前判断当前锁的 value 是否是自己线程持有用 Lua 脚本保证判断和删除是原子性的。5.5 前端接口跨域导致登录失败现象前端页面打不开或者登录按钮点了没反应浏览器控制台报 CORS 错误。原因前端和后端没有配置跨域。前端跑在 8081后端跑在 8080浏览器的同源策略会拦下请求。解决在 Spring Boot 里配置跨域过滤器允许本地开发地址的请求。注意要写在WebMvcConfigurer的实现里而不是在 Controller 上单独加注解后者只对单个接口生效。6. 进阶玩法把这套源码变成生产级管控平台的三个关键改造先说说改造前的前置认知这套源码的定位是「骨架」它的稳定性在中小规模应用里没有问题但要支撑真实商用还有三件事值得优先做。第一件事是禁飞区校验算法的升级。源码里用的是射线法判断坐标点是否在多边形内部这个算法适合禁飞区形状不太复杂的情况。但如果你们承接的是政府监管项目禁飞区可能是带弧线的电子围栏或者是多个多边形叠加的联合区域建议把算法升级成 GEOS 或 JTS 库的几何运算它们内置了contains、intersects等标准 OGC 函数稳定性和精度都远高于手写射线法。第二件事是 Redis 的高可用改造。本地单机 Redis 跑起来没问题但生产环境必须做成哨兵集群或 Cluster 模式。改造时最核心的工作是调整 Redis 连接工厂的配置从单机连接改成哨兵连接同时把database: 0去掉——Redis Cluster 模式不支持多个 database用了就报错。改完以后故障转移是自动的不用半夜爬起来手动切主从。第三件事是轨迹数据的分级存储。前面说了单文档 16MB 上限对普通无人机够用但如果你管的是每天都有几十架次起降的规模化运营场景建议把 MongoDB 里的轨迹按天分集合存储同时利用 TTL 索引自动清理 90 天前的数据。这个改造基本不涉及业务代码改动只需要在创建集合时指定expireAfterSeconds参数。验证这三个改造是否成功我的习惯是模拟故障而不是看日志。比如做 Redis 主从切换测试时直接kill主节点进程然后观察应用能不能在 15 秒内继续正常写入数据做轨迹存储改造时手动插入一条超过 16MB 的文档确认它会正常报错而不是静默失败。这些都是我踩过坑换来的习惯——从那以后每次改完生产环境我都会强制走一遍模拟故障排查流程确认系统在异常情况下不是靠运气在跑。如果你正需要一个能完整演示无人机管控核心流程的源码做底子这套项目值得你下载后自己跑一遍。把技术栈选型逻辑、业务模块实现、环境搭建流程和上面的踩坑记录对照着看会比单独看文档理解深得多。希望帮到你。本文还有配套的精品资源点击获取
返回列表