1. 项目概述
作为一名从事物联网系统开发多年的工程师,我最近完成了一个基于Spring Boot和Vue.js的宠物定位与监控系统。这个系统专为宠物主人设计,能够实时追踪宠物位置、监控宠物活动状态,并在异常情况下及时发出警报。整套系统采用前后端分离架构,前端使用Vue.js构建响应式界面,后端采用Spring Boot框架提供RESTful API服务。
在实际开发过程中,我发现很多学生在做类似毕业设计时常常会遇到架构设计不合理、定位精度不足、实时性差等问题。本文将详细分享这个项目的完整实现过程,包括技术选型考量、核心功能实现细节以及我在开发过程中积累的实战经验。
2. 系统架构设计
2.1 技术栈选型
选择合适的技术栈是项目成功的关键。经过多方比较,我最终确定了以下技术组合:
前端技术栈:
- Vue.js 3.x:轻量级、响应式的渐进式框架
- Element Plus:提供丰富的UI组件
- ECharts:用于数据可视化展示
- WebSocket:实现实时数据传输
- 高德地图API:用于位置展示和轨迹绘制
后端技术栈:
- Spring Boot 2.7:简化Spring应用初始搭建和开发
- Spring Security:处理认证和授权
- MyBatis-Plus:简化数据库操作
- Redis:缓存热点数据,提高系统响应速度
- MySQL 8.0:关系型数据库存储业务数据
- MQTT协议:用于物联网设备通信
硬件部分:
- ESP32开发板:低成本Wi-Fi+蓝牙双模芯片
- GPS模块:UBLOX NEO-6M,定位精度2.5米
- 加速度传感器:MPU6050,用于活动监测
- 微型摄像头:OV2640,200万像素
提示:选择ESP32是因为它兼具性价比和性能,内置Wi-Fi和蓝牙模块可以大大简化硬件设计。GPS模块建议选择UBLOX系列,其冷启动时间短,定位精度高。
2.2 系统架构图
整个系统采用经典的三层架构设计:
code复制┌───────────────────────────────────────────────────┐
│ 客户端层 │
│ ┌───────────┐ ┌───────────┐ ┌──────────────┐ │
│ │ Web端 │ │ 移动APP端 │ │ 硬件设备端 │ │
│ └───────────┘ └───────────┘ └──────────────┘ │
└───────────────────────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────┐
│ 服务层 │
│ ┌───────────┐ ┌───────────┐ ┌──────────────┐ │
│ │ API网关 │ │ 业务逻辑 │ │ 消息队列 │ │
│ │ (Spring │ │ 服务 │ │ (RabbitMQ) │ │
│ │ Cloud │ │ │ │ │ │
│ │ Gateway) │ │ │ │ │ │
│ └───────────┘ └───────────┘ └──────────────┘ │
└───────────────────────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────┐
│ 数据层 │
│ ┌───────────┐ ┌───────────┐ ┌──────────────┐ │
│ │ MySQL │ │ Redis │ │ 文件存储 │ │
│ │ │ │ │ │ (MinIO) │ │
│ └───────────┘ └───────────┘ └──────────────┘ │
└───────────────────────────────────────────────────┘
这种分层架构的优势在于:
- 各层职责明确,便于维护和扩展
- 前后端完全分离,可以独立开发和部署
- 通过API网关统一管理接口,提高安全性
- 引入消息队列解耦系统组件,提高可靠性
3. 核心功能实现
3.1 实时定位功能
定位功能是系统的核心,实现过程分为硬件端数据采集和服务端数据处理两部分。
硬件端实现:
cpp复制// ESP32 Arduino代码片段
#include <TinyGPS++.h>
#include <HardwareSerial.h>
TinyGPSPlus gps;
HardwareSerial SerialGPS(1);
void setup() {
Serial.begin(115200);
SerialGPS.begin(9600, SERIAL_8N1, 16, 17); // GPS模块连接RX16,TX17
// 连接WiFi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
}
}
void loop() {
while (SerialGPS.available() > 0) {
if (gps.encode(SerialGPS.read())) {
if (gps.location.isValid()) {
float latitude = gps.location.lat();
float longitude = gps.location.lng();
// 通过MQTT发送位置数据
String payload = "{"lat":" + String(latitude,6) +
","lng":" + String(longitude,6) + "}";
mqttClient.publish("pet/tracker/position", payload.c_str());
}
}
}
delay(1000); // 每秒发送一次位置数据
}
服务端实现(Spring Boot):
java复制@RestController
@RequestMapping("/api/position")
public class PositionController {
@Autowired
private SimpMessagingTemplate messagingTemplate;
@MessageMapping("/tracker/{petId}")
@SendTo("/topic/position/{petId}")
public Position updatePosition(@DestinationVariable String petId,
Position position) {
// 保存到数据库
position.setPetId(petId);
position.setTimestamp(System.currentTimeMillis());
positionService.save(position);
// 推送给前端
messagingTemplate.convertAndSend("/topic/position/" + petId, position);
return position;
}
@GetMapping("/history/{petId}")
public List<Position> getPositionHistory(
@PathVariable String petId,
@RequestParam(required = false) Long startTime,
@RequestParam(required = false) Long endTime) {
return positionService.getPositionHistory(petId, startTime, endTime);
}
}
前端实现(Vue.js):
javascript复制// 在Vue组件中订阅位置更新
export default {
data() {
return {
petId: '123',
map: null,
marker: null,
polyline: null
}
},
mounted() {
this.initMap();
this.subscribePosition();
},
methods: {
initMap() {
this.map = new AMap.Map('map-container', {
zoom: 15,
center: [116.397428, 39.90923]
});
this.marker = new AMap.Marker({
map: this.map
});
this.polyline = new AMap.Polyline({
map: this.map,
strokeColor: "#3366FF",
strokeWeight: 5
});
},
subscribePosition() {
const stompClient = Stomp.over(new SockJS('/ws'));
stompClient.connect({}, () => {
stompClient.subscribe(`/t
解锁全文
加入我们的会员,获取最新、最热、最精彩的开发者技术内容