1. 项目概述
作为一名从事Java开发十余年的老程序员,今天想和大家分享一个非常适合作为毕业设计的项目——基于物联网技术的智能宠物追踪监控系统微信小程序。这个项目结合了当下热门的物联网技术和小程序开发,技术栈涵盖了Spring Boot、Vue.js和MySQL,既体现了技术的前沿性,又保证了实现的可行性。
在实际开发中,我发现这个项目特别适合计算机相关专业的同学作为毕业设计选题。它不仅包含了完整的前后端开发流程,还涉及到了物联网设备接入、实时定位、数据可视化等实用功能,能够很好地展示学生的综合技术能力。我自己在指导学生的过程中,发现这个项目的难度适中,既不会过于简单显得没有技术含量,也不会过于复杂导致难以完成。
2. 系统架构设计
2.1 技术选型解析
在技术选型上,我们采用了当前主流的开发框架组合:
后端技术栈:
- Spring Boot 2.7.x:简化了Spring应用的初始搭建和开发过程
- MyBatis-Plus 3.5.x:强大的ORM框架,简化数据库操作
- Shiro 1.10.x:负责系统的认证和授权
- WebSocket:实现实时通信功能
前端技术栈:
- 微信小程序原生框架
- Vue.js 2.x:用于管理后台开发
- ECharts:用于数据可视化展示
数据库:
- MySQL 8.0:关系型数据库
- Redis:缓存和会话管理
物联网设备:
- ESP32开发板:低成本Wi-Fi+蓝牙双模芯片
- GPS模块:用于宠物定位
- 加速度传感器:监测宠物活动状态
提示:在实际开发中,建议使用开发板模拟物联网设备,既节省成本又方便调试。
2.2 系统架构设计
系统采用标准的MVC架构,分为表现层、业务逻辑层和数据访问层:
-
表现层:
- 微信小程序端:面向宠物主人
- 管理后台:面向系统管理员
- 设备通信接口:处理物联网设备数据
-
业务逻辑层:
- 用户服务:处理用户注册、登录、权限管理
- 设备服务:管理物联网设备绑定、状态监控
- 定位服务:处理位置数据,计算运动轨迹
- 告警服务:监控异常情况并触发通知
-
数据访问层:
- MySQL:存储结构化数据
- Redis:缓存热点数据,提高系统响应速度
- 文件存储:保存设备上传的图片等多媒体数据
3. 核心功能实现
3.1 宠物定位功能实现
定位功能是整个系统的核心,我们通过以下步骤实现:
- 设备端实现:
java复制// ESP32代码示例 - 获取GPS数据
#include <TinyGPS++.h>
#include <HardwareSerial.h>
TinyGPSPlus gps;
HardwareSerial SerialGPS(1);
void setup() {
Serial.begin(115200);
SerialGPS.begin(9600, SERIAL_8N1, 16, 17);
}
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();
// 将位置数据发送到服务器
sendToServer(latitude, longitude);
}
}
}
}
- 服务端接口设计:
java复制@RestController
@RequestMapping("/api/position")
public class PositionController {
@Autowired
private PositionService positionService;
@PostMapping("/upload")
public Result uploadPosition(@RequestBody PositionDTO positionDTO) {
return positionService.processPosition(positionDTO);
}
@GetMapping("/history/{petId}")
public Result getPositionHistory(@PathVariable Long petId,
@RequestParam String startTime,
@RequestParam String endTime) {
return positionService.getPositionHistory(petId, startTime, endTime);
}
}
- 数据库设计:
sql复制CREATE TABLE `pet_position` (
`id` bigint NOT NULL AUTO_INCREMENT,
`pet_id` bigint NOT NULL COMMENT '宠物ID',
`latitude` decimal(10,7) NOT NULL COMMENT '纬度',
`longitude` decimal(10,7) NOT NULL COMMENT '经度',
`accuracy` float DEFAULT NULL COMMENT '定位精度',
`speed` float DEFAULT NULL COMMENT '移动速度',
`direction` float DEFAULT NULL COMMENT '移动方向',
`timestamp` datetime NOT NULL COMMENT '定位时间',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_pet_time` (`pet_id`,`timestamp`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
3.2 实时监控功能实现
实时监控功能主要依赖WebSocket技术实现:
- WebSocket配置:
java复制@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws")
.setAllowedOrigins("*")
.withSockJS();
}
}
- 位置推送服务:
java复制@Service
public class PositionPushService {
@Autowired
private SimpMessagingTemplate messagingTemplate;
public void pushPosition(Long userId, PositionVO position) {
messagingTemplate.convertAndSendToUser(
userId.toString(),
"/topic/position",
position
);
}
}
- 前端订阅代码:
javascript复制// 小程序端WebSocket连接
const connectWebSocket = () => {
wx.connectSocket({
url: 'wss://yourdomain.com/ws',
success: () => {
console.log('WebSocket连接成功')
}
})
wx.onSocketMessage((res) => {
const data = JSON.parse(res.data)
if (data.topic === 'position') {
updatePetPosition(data.payload)
}
})
}
4. 系统特色功能
4.1 电子围栏告警
电子围栏是宠物安全的重要保障,实现逻辑如下:
- 围栏数据模型:
java复制public class GeoFence {
private Long id;
private Long petId;
private String fenceName;
private Integer fenceType; // 1-圆形 2-多边形
private String centerPoint; // 圆形中心点
private Double radius; // 圆形半径
private String polygonPoints; // 多边形顶点
private Integer alertType; // 1-进入告警 2-离开告警
private Boolean isActive;
}
- 围栏检测算法:
java复制public class GeoFenceChecker {
// 检查点是否在圆形围栏内
public static boolean isInCircleFence(double lat1, double lng1,
double lat2, double lng2, double radius) {
double distance = calculateDistance(lat1, lng1, lat2, lng2);
return distance <= radius;
}
// 计算两点间距离(米)
private static double calculateDistance(double lat1, double lng1,
double lat2, double lng2) {
double radLat1 = Math.toRadians(lat1);
double radLat2 = Math.toRadians(lat2);
double a = radLat1 - radLat2;
double b = Math.toRadians(lng1) - Math.toRadians(lng2);
double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a/2),2) +
Math.cos(radLat1)*Math.cos(radLat2)*Math.pow(Math.sin(b/2),2)));
s = s * 6378137.0; // 地球半径
return Math.abs(s);
}
}
- 告警触发逻辑:
java复制@Service
public class AlertService {
@Autowired
private GeoFenceMapper geoFenceMapper;
@Autowired
private MessageService messageService;
public void checkPositionAlert(Position position) {
List<GeoFence> fences = geoFenceMapper.selectByPetId(position.getPetId());
for (GeoFence fence : fences) {
if (!fence.getIsActive()) continue;
boolean isInside = checkPositionInFence(position, fence);
boolean shouldAlert = false;
if (fence.getFenceType() == 1) { // 圆形围栏
if (fence.getAlertType() == 1 && !isInside) {
shouldAlert = true; // 离开围栏告警
} else if (fence.getAlertType() == 2 && isInside) {
shouldAlert = true; // 进入围栏告警
}
}
if (shouldAlert) {
sendAlert(fence, position);
}
}
}
private void sendAlert(GeoFence fence, Position position) {
String content = String.format("宠物%s触发围栏%s告警,当前位置:%f,%f",
fence.getPetId(), fence.getFenceName(),
position.getLatitude(), position.getLongitude());
messageService.pushAlert(fence.getPetId(), content);
}
}
4.2 健康监测功能
通过加速度传感器收集宠物活动数据,分析健康状态:
- 活动数据收集:
java复制public class ActivityData {
private Long id;
private Long petId;
private Date recordTime;
private Integer steps;
private Double distance;
private Integer activeTime; // 活跃时间(秒)
private Integer restTime; // 休息时间(秒)
private Double calories;
}
- 健康分析算法:
java复制@Service
public class HealthAnalyzer {
private static final double NORMAL_STEPS = 5000; // 每日正常步数
private static final double NORMAL_DISTANCE = 3.0; // 每日正常距离(km)
public HealthReport generateReport(List<ActivityData> dataList) {
HealthReport report = new HealthReport();
int totalSteps = dataList.stream().mapToInt(ActivityData::getSteps).sum();
double totalDistance = dataList.stream().mapToDouble(ActivityData::getDistance).sum();
int totalActiveTime = dataList.stream().mapToInt(ActivityData::getActiveTime).sum();
report.setTotalSteps(totalSteps);
report.setTotalDistance(totalDistance);
report.setTotalActiveTime(totalActiveTime);
// 计算健康指数 (0-100)
double activityScore = Math.min(100,
(totalSteps/NORMAL_STEPS + totalDistance/NORMAL_DISTANCE) * 50);
report.setHealthScore((int) activityScore);
// 分析活动模式
analyzeActivityPattern(dataList, report);
return report;
}
private void analyzeActivityPattern(List<ActivityData> dataList, HealthReport report) {
// 实现活动模式分析逻辑
// ...
}
}
5. 开发经验与避坑指南
5.1 物联网设备连接稳定性
在实际开发中,物联网设备的连接稳定性是一个常见问题。以下是几个提高稳定性的技巧:
- 心跳机制:设备应定期发送心跳包,服务端检测超时后重连
java复制// 设备端心跳实现
void sendHeartbeat() {
unsigned long currentMillis = millis();
if (currentMillis - previousHeartbeat >= heartbeatInterval) {
previousHeartbeat = currentMillis;
String heartbeatMsg = "HEARTBEAT|" + deviceId;
client.println(heartbeatMsg);
}
}
- 消息重试机制:重要消息需要确认机制,未收到确认时重发
java复制// 服务端消息处理
public void handleDeviceMessage(DeviceMessage message) {
try {
processMessage(message);
sendAck(message.getMessageId());
} catch (Exception e) {
log.error("处理设备消息失败", e);
// 加入重试队列
retryQueue.add(message);
}
}
- 离线缓存:设备断网时缓存数据,网络恢复后上传
cpp复制// ESP32数据缓存实现
void cacheData(String data) {
if (!isNetworkConnected()) {
// 将数据写入Flash
writeToFlash(data);
} else {
sendToServer(data);
}
}
void onNetworkConnected() {
// 网络恢复时上传缓存数据
std::vector<String> cachedData = readFromFlash();
for (String data : cachedData) {
sendToServer(data);
}
clearFlashCache();
}
5.2 定位数据优化处理
原始GPS数据可能存在漂移问题,需要进行优化处理:
- 数据滤波算法:
java复制public class PositionFilter {
// 卡尔曼滤波实现
public Position kalmanFilter(Position newPos, Position lastPos) {
// 实现省略...
return filteredPos;
}
// 移动平均滤波
public Position movingAverage(List<Position> positions) {
double sumLat = 0, sumLng = 0;
for (Position pos : positions) {
sumLat += pos.getLatitude();
sumLng += pos.getLongitude();
}
return new Position(
sumLat / positions.size(),
sumLng / positions.size()
);
}
}
- 轨迹优化算法:
java复制public class PathOptimizer {
// Douglas-Peucker轨迹压缩算法
public static List<Position> simplifyPath(List<Position> path, double epsilon) {
if (path.size() < 3) return path;
// 找到离首尾连线最远的点
double dmax = 0;
int index = 0;
Position first = path.get(0);
Position last = path.get(path.size()-1);
for (int i = 1; i < path.size()-1; i++) {
double d = perpendicularDistance(path.get(i), first, last);
if (d > dmax) {
index = i;
dmax = d;
}
}
// 如果最大距离大于阈值,递归处理
if (dmax > epsilon) {
List<Position> result1 = simplifyPath(path.subList(0, index+1), epsilon);
List<Position> result2 = simplifyPath(path.subList(index, path.size()), epsilon);
List<Position> result = new ArrayList<>(result1);
result.addAll(result2.subList(1, result2.size()));
return result;
} else {
return Arrays.asList(first, last);
}
}
private static double perpendicularDistance(Position point, Position lineStart, Position lineEnd) {
// 计算点到线段的垂直距离
// 实现省略...
}
}
6. 系统部署方案
6.1 服务器环境配置
推荐以下服务器配置作为生产环境:
-
基础配置:
- CPU:4核以上
- 内存:8GB以上
- 存储:100GB SSD
- 带宽:5Mbps以上
-
软件环境:
- JDK 11+
- MySQL 8.0+
- Redis 6.0+
- Nginx 1.18+
-
部署脚本示例:
bash复制#!/bin/bash
# 安装基础依赖
apt-get update
apt-get install -y openjdk-11-jdk mysql-server redis-server nginx
# 配置MySQL
mysql_secure_installation
mysql -uroot -p -e "CREATE DATABASE pet_tracker CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
mysql -uroot -p -e "CREATE USER 'petuser'@'localhost' IDENTIFIED BY 'StrongPassword123';"
mysql -uroot -p -e "GRANT ALL PRIVILEGES ON pet_tracker.* TO 'petuser'@'localhost';"
# 部署Spring Boot应用
mkdir -p /opt/pet-tracker
cp pet-tracker.jar /opt/pet-tracker/
cat > /etc/systemd/system/pet-tracker.service <<EOF
[Unit]
Description=Pet Tracker Backend Service
After=syslog.target
[Service]
User=petuser
ExecStart=/usr/bin/java -jar /opt/pet-tracker/pet-tracker.jar
SuccessExitStatus=143
Restart=always
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable pet-tracker
systemctl start pet-tracker
# 配置Nginx反向代理
cat > /etc/nginx/conf.d/pet-tracker.conf <<EOF
server {
listen 80;
server_name yourdomain.com;
location / {
proxy_pass http://localhost:8080;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
}
location /ws/ {
proxy_pass http://localhost:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection "upgrade";
}
}
EOF
nginx -t
systemctl reload nginx
6.2 小程序发布流程
微信小程序发布需要注意以下关键点:
-
准备工作:
- 注册微信小程序账号(企业或个体户类型)
- 完成开发者资质认证
- 配置合法域名(API服务器地址)
-
发布步骤:
- 开发版本测试
- 提交审核(准备完整的功能说明文档)
- 审核通过后发布
-
注意事项:
- 确保小程序不收集不必要的用户隐私数据
- 定位功能需要在小程序配置中声明
- 地图组件需要使用腾讯地图服务
7. 项目扩展方向
这个基础项目还有多个可以扩展的方向,适合想要深入研究的同学:
-
AI行为分析:
- 使用TensorFlow Lite在设备端实现宠物行为识别
- 分析宠物日常活动模式,检测异常行为
-
多宠物管理:
- 支持一个账号管理多只宠物
- 宠物社交网络功能
-
智能硬件扩展:
- 增加环境传感器(温湿度、空气质量)
- 添加摄像头模块实现远程监控
- 设计更小巧的宠物可穿戴设备
-
数据分析平台:
- 使用Flink实现实时数据分析
- 构建宠物健康大数据模型
在实际开发过程中,我建议同学们可以先完成基础功能,再根据个人兴趣和能力选择1-2个扩展方向深入研究。这样既能保证项目完整性,又能体现个人技术特色。