【免费】基于YOLO26的车牌识别+智慧停车场系统(深度学习+目标检测+微信小程序+LPRNet+CCPD++FastAPI+Vue3) 人工智能 机器视觉 计算机视觉 锋哥原创出品,必属精品
·
大家好,我是Java1234_小锋老师,分享一套锋哥原创的基于YOLO26的车牌识别+智慧停车场系统(深度学习+目标检测+微信小程序+LPRNet+CCPD++FastAPI+Vue3) 。

项目介绍
针对传统停车场依赖人工发卡、车牌识别精度受光照和拍摄角度影响较大、车主缴费流程繁琐等问题,本文设计并实现了一套基于 YOLO26+LPRNet 深度学习的智慧停车场管理系统。系统采用检测与识别解耦的两阶段方案:首先使用在中国城市停车车牌数据集 CCPD 上训练得到的 YOLO26-Pose 模型定位车牌并回归四个角点,再对车牌区域进行透视矫正与双层牌拆分,最后由轻量级 LPRNet 结合 CTC 贪心解码输出车牌号码。业务系统采用 FastAPI 构建 REST 接口,Vue3 与 Element Plus 实现管理员后台,微信小程序实现车主端。管理员可完成入场识别、出场识别、视频抽帧择优、车主车辆审核、区域车位维护、收费规则配置和数据看板统计;车主可通过用户名密码注册登录、绑定车牌、查询余位与停车记录,并在出口扫描缴费二维码完成模拟支付。数据库采用 MySQL 8,库名为 db_smart_parking,核心表均以 t_ 开头。测试表明,系统能够完成从车牌识别、车位分配、计费到扫码缴费的闭环业务,图片识别耗时约数百毫秒,满足本科毕业设计对完整性、可用性和工程规范性的要求。
源码下载
链接: https://pan.baidu.com/s/1S5tB_tcPvn6Mh6krgNm4Ew?pwd=1234
提取码: 1234
系统展示








核心代码
# -*- coding: utf-8 -*-
"""
停车业务服务。
封装入场登记、出场结算、车位分配与缴费订单生成。
"""
from datetime import datetime
from config.settings import Settings
from db.dao import (
AreaDao,
FeeRuleDao,
ParkingRecordDao,
PayOrderDao,
SpaceDao,
VehicleDao,
)
from utils.common import calc_fee, gen_no, now_str
from utils.qrcode_util import make_pay_qrcode
class ParkingService:
"""停车出入场业务服务类。"""
@staticmethod
def enter(plate_no: str, image_path: str = "", plate_record_id: int = 0) -> dict:
"""
车辆入场:校验未在场、分配空闲车位、写入停车记录。
:param plate_no: 车牌号码
:param image_path: 入场抓拍图路径
:param plate_record_id: 关联识别记录
:return: 业务结果字典
"""
plate_no = (plate_no or "").strip().upper()
if len(plate_no) < 6:
return {"ok": False, "msg": "车牌号码无效"}
exists = ParkingRecordDao.find_in_lot(plate_no)
if exists:
return {"ok": False, "msg": f"车牌 {plate_no} 已在场,请勿重复入场", "record": exists}
vehicle = VehicleDao.find_by_plate(plate_no)
owner_id = int(vehicle.get("owner_id") or 0) if vehicle else 0
prefer = "新能源" if (vehicle and "新能源" in (vehicle.get("plate_type") or "")) else ""
space = SpaceDao.find_free(prefer)
if not space:
return {"ok": False, "msg": "暂无空闲车位"}
now = now_str()
record_no = gen_no("PK")
rid = ParkingRecordDao.insert({
"record_no": record_no,
"plate_no": plate_no,
"owner_id": owner_id,
"area_id": space.get("area_id") or 0,
"space_id": space.get("id") or 0,
"in_time": now,
"out_time": None,
"in_image": image_path or "",
"out_image": "",
"duration_min": 0,
"fee_amount": 0,
"pay_status": "未支付",
"status": "在场",
"plate_record_id": plate_record_id,
"remark": "",
"create_time": now,
})
SpaceDao.set_status(space["id"], "占用")
AreaDao.inc_used(space["area_id"], 1)
record = ParkingRecordDao.find_by_id(rid)
return {"ok": True, "msg": "入场成功", "record": record, "space": space}
@staticmethod
def exit_lot(plate_no: str, image_path: str = "") -> dict:
"""
车辆出场:计算时长与费用,空闲车位,生成待支付订单。
:param plate_no: 车牌号码
:param image_path: 出场抓拍图路径
:return: 业务结果字典
"""
plate_no = (plate_no or "").strip().upper()
record = ParkingRecordDao.find_in_lot(plate_no)
if not record:
return {"ok": False, "msg": f"未找到车牌 {plate_no} 的在场记录"}
in_time = datetime.strptime(record["in_time"], Settings.DATETIME_FMT)
out_time = datetime.now()
duration_min = max(0, int((out_time - in_time).total_seconds() // 60))
rule = FeeRuleDao.get_default() or {}
fee = calc_fee(duration_min, rule)
pay_status = "免费" if fee <= 0 else "未支付"
status = "已离场"
out_str = out_time.strftime(Settings.DATETIME_FMT)
ParkingRecordDao.update_exit(record["id"], {
"out_time": out_str,
"out_image": image_path or "",
"duration_min": duration_min,
"fee_amount": fee,
"pay_status": pay_status,
"status": status,
})
if record.get("space_id"):
SpaceDao.set_status(record["space_id"], "空闲")
if record.get("area_id"):
AreaDao.inc_used(record["area_id"], -1)
order = None
if fee > 0:
order_no = gen_no("PO")
qr_path = make_pay_qrcode(order_no)
oid = PayOrderDao.insert({
"order_no": order_no,
"parking_record_id": record["id"],
"owner_id": record.get("owner_id") or 0,
"plate_no": plate_no,
"amount": fee,
"pay_type": "微信模拟",
"pay_status": "待支付",
"pay_time": None,
"trade_no": "",
"qrcode_path": qr_path,
"create_time": now_str(),
})
order = PayOrderDao.find_by_id(oid)
updated = ParkingRecordDao.find_by_id(record["id"])
return {
"ok": True,
"msg": "出场结算完成" if fee > 0 else "免费离场",
"record": updated,
"fee": fee,
"durationMin": duration_min,
"order": order,
}
<template>
<div class="profile">
<div class="page-card left">
<h3 class="page-title">个人资料</h3>
<div class="avatar-box">
<el-avatar :size="96" :src="form.avatar">{{ form.real_name?.[0] || '管' }}</el-avatar>
<el-upload :show-file-list="false" :http-request="uploadAvatar">
<el-button type="primary" style="margin-top:12px">上传头像</el-button>
</el-upload>
</div>
<el-form :model="form" label-width="80px">
<el-form-item label="账号"><el-input :model-value="form.username" disabled /></el-form-item>
<el-form-item label="姓名"><el-input v-model="form.real_name" /></el-form-item>
<el-form-item label="性别">
<el-radio-group v-model="form.gender">
<el-radio value="男">男</el-radio>
<el-radio value="女">女</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="手机"><el-input v-model="form.phone" /></el-form-item>
<el-form-item label="邮箱"><el-input v-model="form.email" /></el-form-item>
<el-form-item>
<el-button type="primary" @click="saveProfile">保存资料</el-button>
</el-form-item>
</el-form>
</div>
<div class="page-card right">
<h3 class="page-title">修改密码</h3>
<el-form :model="pwd" label-width="100px">
<el-form-item label="原密码"><el-input v-model="pwd.oldPassword" type="password" show-password /></el-form-item>
<el-form-item label="新密码"><el-input v-model="pwd.newPassword" type="password" show-password /></el-form-item>
<el-form-item label="确认新密码"><el-input v-model="pwd.confirmPassword" type="password" show-password /></el-form-item>
<el-form-item>
<el-button type="warning" @click="savePwd">确认修改</el-button>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script setup>
import { onMounted, reactive } from 'vue'
import { ElMessage } from 'element-plus'
import http from '../../utils/request'
import { useUserStore } from '../../store/user'
const user = useUserStore()
const form = reactive({ username: '', real_name: '', gender: '男', phone: '', email: '', avatar: '' })
const pwd = reactive({ oldPassword: '', newPassword: '', confirmPassword: '' })
async function load() {
const info = await user.fetchInfo()
Object.assign(form, {
username: info.username,
real_name: info.realName,
gender: info.gender || '男',
phone: info.phone,
email: info.email,
avatar: info.avatar
})
}
async function saveProfile() {
await http.put('/api/profile/update', {
realName: form.real_name,
gender: form.gender || '男',
phone: form.phone,
email: form.email
})
ElMessage.success('资料已更新')
load()
}
/** 提交修改密码,需两次新密码一致 */
async function savePwd() {
if (!pwd.newPassword) {
ElMessage.warning('新密码不能为空')
return
}
if (pwd.newPassword !== pwd.confirmPassword) {
ElMessage.warning('两次输入的新密码不一致')
return
}
await http.put('/api/profile/password', pwd)
ElMessage.success('密码修改成功')
pwd.oldPassword = ''
pwd.newPassword = ''
pwd.confirmPassword = ''
}
async function uploadAvatar({ file }) {
const data = new FormData()
data.append('file', file)
await http.post('/api/profile/avatar', data)
ElMessage.success('头像已更新')
load()
}
onMounted(load)
</script>
<style scoped>
.profile {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
}
.avatar-box { text-align: center; margin-bottom: 16px; }
@media (max-width: 900px) {
.profile { grid-template-columns: 1fr; }
}
</style>
更多推荐


所有评论(0)