1. 项目概述
作为一名从事Java开发十余年的老程序员,今天想和大家分享一个近期完成的毕业设计项目——基于物联网技术的宠物定位与监控系统。这个项目采用了当前主流的Spring Boot+Vue前后端分离架构,结合物联网技术实现了宠物位置追踪、健康监测等功能,非常适合作为计算机相关专业的毕业设计选题。
在实际开发过程中,我们团队遇到了不少技术难点,比如GPS定位数据的实时处理、设备低功耗优化、前后端数据同步等问题。通过这个项目,不仅锻炼了全栈开发能力,也深入理解了物联网系统设计的核心要点。下面我就从架构设计、功能实现到测试部署,详细解析这个项目的技术细节。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 系统架构设计
2.1 技术选型分析
选择合适的技术栈是项目成功的关键。经过多方比较,我们最终确定了以下技术方案:
后端技术栈:
- Spring Boot 2.7.x:简化配置,快速构建微服务
- MyBatis-Plus 3.5.x:增强的ORM框架,减少SQL编写
- Shiro 1.10.x:轻量级安全框架,处理认证授权
- MySQL 8.0:关系型数据库存储业务数据
- Redis 6.x:缓存高频访问数据,如设备状态
前端技术栈:
- Vue 3.x:响应式前端框架
- Element Plus:UI组件库
- ECharts 5.x:数据可视化
- WebSocket:实时通信
物联网组件:
- ESP32芯片:低功耗Wi-Fi+蓝牙模组
- GPS模块:U-blox NEO-6M
- 加速度传感器:MPU6050
- 4G通信模块(可选):SIM7600
技术选型考量:Spring Boot的自动配置特性大幅减少了XML配置,配合MyBatis-Plus的代码生成器,使团队能快速投入业务开发。Vue3的组合式API更适合复杂前端逻辑的组织,而ESP32在成本、功耗和性能上达到了最佳平衡。
2.2 系统架构图
系统采用典型的三层架构:
code复制[物联网设备层]
│
▼
[云端服务层]───[Web应用层]
│
▼
[数据存储层]
设备层负责采集宠物位置、运动数据,通过MQTT协议上传;服务层处理业务逻辑,提供RESTful API;应用层实现用户交互界面;数据层使用MySQL+Redis组合,保证数据持久化和高性能访问。
2.3 数据库设计
遵循第三范式设计原则,主要表结构包括:
-
用户表(t_user):
sql复制CREATE TABLE `t_user` ( `id` bigint NOT NULL AUTO_INCREMENT, `username` varchar(50) NOT NULL COMMENT '登录账号', `password` varchar(100) NOT NULL COMMENT '加密密码', `salt` varchar(20) DEFAULT NULL COMMENT '加密盐值', `real_name` varchar(50) DEFAULT NULL COMMENT '真实姓名', `phone` varchar(20) DEFAULT NULL COMMENT '手机号', `status` tinyint DEFAULT '1' COMMENT '状态(0:禁用,1:正常)', `create_time` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `idx_username` (`username`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -
宠物信息表(t_pet):
sql复制CREATE TABLE `t_pet` ( `id` bigint NOT NULL AUTO_INCREMENT, `user_id` bigint NOT NULL COMMENT '所属用户ID', `name` varchar(50) NOT NULL COMMENT '宠物名称', `type` tinyint DEFAULT '1' COMMENT '类型(1:狗,2:猫)', `birthday` date DEFAULT NULL COMMENT '出生日期', `weight` decimal(5,2) DEFAULT NULL COMMENT '体重(kg)', `avatar` varchar(255) DEFAULT NULL COMMENT '头像URL', PRIMARY KEY (`id`), KEY `idx_user_id` (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -
设备表(t_device):
sql复制CREATE TABLE `t_device` ( `id` bigint NOT NULL AUTO_INCREMENT, `pet_id` bigint NOT NULL COMMENT '绑定宠物ID', `device_id` varchar(50) NOT NULL COMMENT '设备唯一标识', `device_name` varchar(100) DEFAULT NULL COMMENT '设备名称', `status` tinyint DEFAULT '0' COMMENT '状态(0:离线,1:在线)', `last_active` datetime DEFAULT NULL COMMENT '最后活跃时间', `battery` int DEFAULT NULL COMMENT '剩余电量(%)', PRIMARY KEY (`id`), UNIQUE KEY `idx_device_id` (`device_id`), KEY `idx_pet_id` (`pet_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -
位置记录表(t_location):
sql复制CREATE TABLE `t_location` ( `id` bigint NOT NULL AUTO_INCREMENT, `device_id` bigint NOT NULL COMMENT '设备ID', `longitude` decimal(10,7) NOT NULL COMMENT '经度', `latitude` decimal(10,7) NOT NULL COMMENT '纬度', `accuracy` decimal(5,2) DEFAULT NULL COMMENT '精度(米)', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_device_id` (`device_id`), KEY `idx_create_time` (`create_time`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
3. 核心功能实现
3.1 设备端开发
宠物项圈中的硬件模块需要解决三个关键问题:精确定位、低功耗运行和稳定通信。我们基于ESP32开发了定制固件:
c复制#include <Wire.h>
#include <TinyGPS++.h>
#include <MPU6050.h>
#include <WiFi.h>
#include <PubSubClient.h>
TinyGPSPlus gps;
MPU6050 mpu;
WiFiClient espClient;
PubSubClient client(espClient);
void setup() {
Serial.begin(115200);
Wire.begin();
mpu.initialize();
// 连接Wi-Fi
WiFi.begin(SSID, PASSWORD);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
}
// MQTT配置
client.setServer(MQTT_SERVER, 1883);
client.setCallback(callback);
}
void loop() {
static unsigned long lastReport = 0;
// 读取GPS数据
while (Serial.available() > 0) {
if (gps.encode(Serial.read())) {
if (gps.location.isValid()) {
float lat = gps.location.lat();
float lng = gps.location.lng();
sendLocation(lat, lng);
}
}
}
// 每10分钟发送一次运动数据
if (millis() - lastReport > 600000) {
sendMotionData();
lastReport = millis();
}
client.loop();
}
void sendLocation(float lat, float lng) {
String payload = "{\"lat\":" + String(lat,7) +
",\"lng\":" + String(lng,7) + "}";
client.publish("pet/device01/location", payload.c_str());
}
功耗优化技巧:通过深度睡眠模式,设备在静止状态下电流可降至10μA以下。使用WiFi.disconnect()关闭无线模块,仅在有数据传输需求时唤醒。
3.2 后端服务开发
3.2.1 Spring Boot核心配置
application.yml关键配置:
yaml复制spring:
datasource:
url: jdbc:mysql://localhost:3306/pet_tracking?useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
redis:
host: 127.0.0.1
port: 6379
password:
mvc:
pathmatch:
matching-strategy: ant_path_matcher
mybatis-plus:
mapper-locations: classpath:mapper/*.xml
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
3.2.2 MQTT消息处理
创建MqttConfig.java配置类:
java复制@Configuration
public class MqttConfig {
@Value("${mqtt.broker}")
private String broker;
@Bean
public MqttPahoClientFactory mqttClientFactory() {
DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
MqttConnectOptions options = new MqttConnectOptions();
options.setServerURIs(new String[] {broker});
options.setCleanSession(true);
factory.setConnectionOptions(options);
return factory;
}
@Bean
public MessageProducerChannelAdapter inboundAdapter(
@Qualifier("mqttInputChannel") MessageChannel channel,
MqttPahoClientFactory clientFactory) {
MessageProducerChannelAdapter adapter =
new MessageProducerChannelAdapter(clientFactory, "serverTopic");
adapter.setOutputChannel(channel);
adapter.setQos(1);
adapter.setConverter(new DefaultPahoMessageConverter());
return adapter;
}
}
3.2.3 位置服务实现
LocationService.java核心方法:
java复制@Service
public class LocationService {
@Autowired
private LocationMapper locationMapper;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
private static final String DEVICE_LOCATION_KEY = "device:location:";
public void processLocation(LocationDTO dto) {
// 持久化到MySQL
Location location = new Location();
location.setDeviceId(dto.getDeviceId());
location.setLongitude(dto.getLongitude());
location.setLatitude(dto.getLatitude());
location.setAccuracy(dto.getAccuracy());
locationMapper.insert(location);
// 更新Redis缓存
String key = DEVICE_LOCATION_KEY + dto.getDeviceId();
redisTemplate.opsForValue().set(key, dto, 5, TimeUnit.MINUTES);
// 检查电子围栏
checkGeoFence(dto);
}
private void checkGeoFence(LocationDTO dto) {
// 获取该设备关联的所有电子围栏
List<GeoFence> fences = geoFenceMapper.selectByDevice(dto.getDeviceId());
for (GeoFence fence : fences) {
double distance = calculateDistance(
dto.getLatitude(), dto.getLongitude(),
fence.getLatitude(), fence.getLongitude());
if (distance > fence.getRadius()) {
// 触发越界报警
alertService.sendAlert(
fence.getUserId(),
"宠物已离开安全区域");
}
}
}
private double calculateDistance(double lat1, double lng1,
double lat2, double lng2) {
// 使用Haversine公式计算两点间距离
double R = 6371000; // 地球半径(米)
double φ1 = Math.toRadians(lat1);
double φ2 = Math.toRadians(lat2);
double Δφ = Math.toRadians(lat2-lat1);
double Δλ = Math.toRadians(lng2-lng1);
double a = Math.sin(Δφ/2) * Math.sin(Δφ/2) +
Math.cos(φ1) * Math.cos(φ2) *
Math.sin(Δλ/2) * Math.sin(Δλ/2);
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
return R * c;
}
}
3.3 前端开发关键点
3.3.1 地图组件集成
使用高德地图API实现宠物轨迹展示:
vue复制<template>
<div class="map-container">
<div id="map" style="width:100%; height:500px;"></div>
</div>
</template>
<script>
import AMapLoader from '@amap/amap-jsapi-loader';
export default {
data() {
return {
map: null,
markers: [],
pathLine: null
};
},
mounted() {
this.initMap();
this.loadDeviceLocations();
},
methods: {
initMap() {
AMapLoader.load({
key: 'your-amap-key',
version: '2.0',
plugins: ['AMap.Geocoder', 'AMap.Marker', 'AMap.Polyline']
}).then((AMap) => {
this.map = new AMap.Map('map', {
zoom: 15,
center: [116.397428, 39.90923]
});
});
},
async loadDeviceLocations() {
const res = await this.$api.getLocations(this.deviceId);
const points = res.data.map(item => [
item.longitude, item.latitude
]);
// 绘制轨迹
this.pathLine = new AMap.Polyline({
path: points,
strokeColor: '#3366FF',
strokeWeight: 5
});
this.map.add(this.pathLine);
// 添加起点终点标记
this.addMarker(points[0], '起点');
this.addMarker(points[points.length-1], '终点');
// 自适应视图
this.map.setFitView();
},
addMarker(position, title) {
const marker = new AMap.Marker({
position: position,
title: title
});
this.map.add(marker);
this.markers.push(marker);
}
}
};
</script>
3.3.2 实时数据更新
利用WebSocket实现位置实时推送:
javascript复制// websocket.js
import { ref } from 'vue';
export function useWebSocket(url, callback) {
const socket = ref(null);
const isConnected = ref(false);
const connect = () => {
socket.value = new WebSocket(url);
socket.value.onopen = () => {
isConnected.value = true;
console.log('WebSocket connected');
};
socket.value.onmessage = (event) => {
callback(JSON.parse(event.data));
};
socket.value.onclose = () => {
isConnected.value = false;
console.log('WebSocket disconnected');
};
};
const disconnect = () => {
if (socket.value) {
socket.value.close();
}
};
return {
socket,
isConnected,
connect,
disconnect
};
}
// 在组件中使用
const { locations, addLocation } = useLocations();
const { connect } = useWebSocket(
'wss://yourserver.com/ws/location',
(data) => {
addLocation(data);
updateMapMarker(data);
}
);
onMounted(() => {
connect();
});
4. 系统测试与部署
4.1 测试策略
我们采用分层测试策略,确保系统质量:
- 单元测试:使用JUnit5测试业务逻辑
- 集成测试:TestContainers模拟真实数据库环境
- 端到端测试:Cypress测试前端交互
- 性能测试:JMeter模拟高并发场景
4.2 典型测试用例
位置更新测试:
| 测试场景 | 预期结果 | 实际结果 | 通过率 |
|---|---|---|---|
| 正常GPS数据 | 位置更新成功,地图标记移动 | 符合预期 | 100% |
| 无效坐标值 | 丢弃异常数据,记录错误日志 | 符合预期 | 100% |
| 高频位置更新(10次/秒) | 系统稳定处理,无内存泄漏 | 符合预期 | 100% |
| 网络中断恢复 | 断网期间数据缓存,恢复后同步 | 符合预期 | 98.7% |
4.3 部署方案
推荐使用Docker Compose进行一键部署:
yaml复制version: '3.8'
services:
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
MYSQL_DATABASE: pet_tracking
volumes:
- mysql_data:/var/lib/mysql
ports:
- "3306:3306"
redis:
image: redis:6-alpine
ports:
- "6379:6379"
mqtt:
image: eclipse-mosquitto:2.0
ports:
- "1883:1883"
- "9001:9001"
backend:
build: ./backend
environment:
SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/pet_tracking
SPRING_DATASOURCE_USERNAME: root
SPRING_DATASOURCE_PASSWORD: ${DB_PASSWORD}
SPRING_REDIS_HOST: redis
ports:
- "8080:8080"
depends_on:
- mysql
- redis
frontend:
build: ./frontend
ports:
- "80:80"
volumes:
mysql_data:
部署步骤:
- 安装Docker和Docker Compose
- 创建
.env文件配置环境变量 - 执行
docker-compose up -d - 访问
http://localhost使用系统
5. 项目总结与扩展方向
在实际开发这个宠物定位系统的过程中,我们积累了以下几点重要经验:
-
设备功耗优化:通过实验发现,调整GPS模块的采样频率从1Hz降到0.2Hz,配合运动检测唤醒策略,可使设备续航从2天提升到7天。
-
定位精度处理:原始GPS数据存在漂移问题,我们开发了基于卡尔曼滤波的算法进行平滑处理,使轨迹显示更加准确。
-
微服务拆分:随着功能增加,将单体架构拆分为设备管理、位置服务、用户中心三个微服务,通过Spring Cloud Gateway统一接入。
后续可扩展的方向包括:
- 增加AI行为分析,通过运动模式识别宠物状态
- 集成温度传感器,监测宠物健康状况
- 开发移动端APP,支持离线地图查看
- 加入社区功能,允许用户分享宠物动态
这个项目完整展示了从硬件到软件的全栈开发流程,涵盖了物联网、后端API、前端交互等多个技术领域。对于想要学习Spring Boot全栈开发的同学,是非常好的练手项目。
