有种痛苦叫你的邻居买了台大车?用代码构建你的“车位守护者”
简介
最近,一个名为“有种痛苦叫你的邻居买了台大车”的话题引发了广泛共鸣。想象一下这个场景:你结束了一天疲惫的工作,回到小区,却发现自己的车位旁停着一辆庞然大物。它的车身霸气外露,却无情地将你的车位挤得满满当当,你不得不像练杂技一样从副驾驶艰难地爬进爬出,或者在狭窄的空间里进行无数次的“揉库”。这种痛苦,不仅是身体上的不便,更是一种每天都要面对的心理折磨。
汽车厂商热衷于推出尺寸更大、利润更高的SUV和MPV,这背后有复杂的原因,但留给普通车主的,往往是更局促的公共空间和更紧张的停车关系。与其每天与邻居“斗智斗勇”或生闷气,不如我们换个思路:运用技术,为自己打造一个智能的“车位守护者”系统。
在本教程中,我们将利用Python、计算机视觉和物联网的简单知识,创建一个能够自动监测你车位占用情况、并判断相邻车辆是否为“越界大车”的智能系统。当发现潜在问题时,它会通过微信等方式给你发送提醒。这不仅能让你提前掌握车位动态,更能作为与物业或邻居沟通时的客观证据。
前置准备
在开始之前,你需要准备以下硬件和软件:
硬件准备:
1. 一台可以运行的电脑(用于开发和测试)。
2. 一个网络摄像头:如果你的小区允许,可以安装在自家车位附近。如果不行,也可以从自家窗户对准车位进行监控。家用无线摄像头 或一个旧的 树莓派 搭配摄像头模块是不错的选择。
3. (可选) 一个小型服务器或常开的家用电脑:用于7×24小时运行监控程序。
软件与知识准备:
1. Python环境:安装Python 3.8及以上版本。
2. 基础Python知识:了解变量、函数、循环和库的基本使用。
3. 基础的命令行操作。
4. 一个能收发消息的通道:我们将以微信(通过Server酱等平台)为例,你也可以改成短信或邮件。
核心思想:我们将使用一个预训练的AI模型(YOLOv5)来检测摄像头画面中的“车辆”,然后通过简单的逻辑计算判断车辆是否停在指定区域内(你的车位),以及它的尺寸是否“越界”。
第一步:搭建Python开发环境与安装依赖
首先,我们来准备我们的代码武器库。建议使用虚拟环境来管理项目依赖。
-
创建一个项目文件夹,并在其中创建虚拟环境:
bash
mkdir parking_guardian && cd parking_guardian
python -m venv venv
# 在Linux/macOS中激活
source venv/bin/activate
# 在Windows中激活
venv\Scripts\activate -
安装必要的Python库:
bash
pip install opencv-python numpy torch torchvision ultralytics requests Pillow
这些库的功能分别是:opencv-python: 处理摄像头视频流。torch,torchvision,ultralytics: 用于运行YOLOv5物体检测模型。requests: 用于发送网络请求(发送通知)。Pillow: 图像处理辅助。
第二步:加载YOLOv5预训练模型与视频流
YOLOv5是一个非常强大且易用的物体检测模型,它能够识别包括汽车在内的80种常见物体。我们将直接调用Ultralytics提供的官方接口。
创建一个新的Python文件 monitor.py,并写入以下代码:
import cv2
import torch
import numpy as np
import requests
import time
from datetime import datetime
# 1. 加载预训练的YOLOv5模型 (首次运行会自动下载模型)
print("正在加载AI模型...")
model = torch.hub.load('ultralytics/yolov5', 'yolov5s', pretrained=True)
# 我们只关心‘car’, ‘truck’, ‘bus’这几类,它们的类别ID在COCO数据集中是2,5,7
# 也可以设置置信度阈值,过滤掉不确定的检测结果
model.classes = [2, 5, 7] # car, truck, bus
model.conf = 0.5 # 置信度阈值
print("模型加载完成!")
# 2. 连接摄像头(替换为你的真实地址,0是本地摄像头)
# 可以是RTSP流,也可以是视频文件路径
camera_url = '0' # 暂时用本地摄像头测试
cap = cv2.VideoCapture(camera_url)
if not cap.isOpened():
print("无法打开摄像头!请检查路径或设备。")
exit()
# 3. 定义你的车位区域(一个矩形)
# 你需要根据你的摄像头画面,通过尝试来确定这四个点的坐标 (x1, y1), (x2, y2)
# 这里是一个示例坐标,你需要自行修改!
MY_PARKING_SPOT = (100, 200, 300, 400) # (左上角x, 左上角y, 右下角x, 右下角y)
print("开始监控... 按'q'键退出。")
while True:
ret, frame = cap.read()
if not ret:
print("无法读取视频帧,尝试重连...")
cap.release()
time.sleep(5)
cap = cv2.VideoCapture(camera_url)
continue
# 4. 使用YOLOv5进行目标检测
results = model(frame)
detections = results.xyxy[0].cpu().numpy() # 获取检测结果
# 在画面上绘制你的车位区域(红色矩形)
x1, y1, x2, y2 = MY_PARKING_SPOT
cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 0, 255), 2)
# ... 下一步我们将在这里处理检测结果并判断是否越界
# 显示画面(调试用,部署时可以注释掉)
cv2.imshow('Parking Guardian', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# 释放资源
cap.release()
cv2.destroyAllWindows()
运行前请注意:
– 你需要修改 MY_PARKING_SPOT 的坐标。可以通过在代码中临时添加 print 语句或使用画图工具测量截图来获得准确坐标。
– camera_url 需要替换为你真实的摄像头地址。对于IP摄像头,通常是类似 rtsp://user:password@ip:port/stream 的地址。
第三步:编写核心逻辑——判断车位占用与车辆越界
现在,让我们在循环中添加智能判断逻辑。我们将在 monitor.py 的循环中,# ... 下一步我们将在这里处理检测结果并判断是否越界 的注释下方添加代码。
# 初始化状态变量
is_my_spot_occupied = False
is_neighbor_car_oversized = False
detected_cars = []
for det in detections:
# det 的格式是 [x1, y1, x2, y2, confidence, class_id]
obj_x1, obj_y1, obj_x2, obj_y2, conf, cls_id = det[:6]
cls_id = int(cls_id)
# 我们只关心车辆类别 (2,5,7)
if cls_id in [2, 5, 7]:
# 计算车辆的中心点
car_center_x = (obj_x1 + obj_x2) / 2
car_center_y = (obj_y1 + obj_y2) / 2
car_width = obj_x2 - obj_x1
car_height = obj_y2 - obj_y1
car_area = car_width * car_height
# 判断:车辆中心点是否在“我的车位”区域内
if (MY_PARKING_SPOT[0] <= car_center_x <= MY_PARKING_SPOT[2] and
MY_PARKING_SPOT[1] <= car_center_y <= MY_PARKING_SPOT[3]):
is_my_spot_occupied = True
my_car_area = car_area
# 在画面上给我的车画个绿色框
cv2.rectangle(frame, (int(obj_x1), int(obj_y1)), (int(obj_x2), int(obj_y2)), (0, 255, 0), 2)
cv2.putText(frame, 'MY CAR', (int(obj_x1), int(obj_y1)-10), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)
else:
# 这是邻居的车,记录下来
detected_cars.append({
'box': (obj_x1, obj_y1, obj_x2, obj_y2),
'area': car_area,
'cls_id': cls_id
})
# 在画面上给邻居的车画个黄色框
cv2.rectangle(frame, (int(obj_x1), int(obj_y1)), (int(obj_x2), int(obj_y2)), (0, 255, 255), 2)
# 简单的越界判断逻辑:如果我的车被占用,并且附近有邻居的车,
# 而且邻居的车的面积比我的车(如果已停)的平均面积要大很多,就可能越界。
# 这里需要一个更科学的基准,例如通过一段时间学习我的车的平均面积。
# 为简化,我们假设:如果邻居车面积 > 设定阈值,则认为可能有问题。
AREA_THRESHOLD = 80000 # 这个阈值需要根据你摄像头分辨率和距离实测调整!
for car in detected_cars:
if car['area'] > AREA_THRESHOLD:
is_neighbor_car_oversized = True
# 在画面上标记“可疑大车”
x1, y1, x2, y2 = car['box']
cv2.putText(frame, 'OVERSIZED?', (int(x1), int(y1)-10), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
# 将状态显示在画面上
status_text = f"Spot Occupied: {is_my_spot_occupied} | Neighbor Oversized: {is_neighbor_car_oversized}"
cv2.putText(frame, status_text, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)
逻辑说明:
1. 我们遍历所有检测到的车辆。
2. 通过计算车辆边界框的中心点,判断它是否位于我们定义的“MY_PARKING_SPOT”区域内,从而判断车位是否被占用(无论是自己还是别人)。
3. 对于区域外的车辆(邻居的车),我们记录其面积。AREA_THRESHOLD 是一个需要你根据实际画面校准的关键参数。你需要先观察自己正常停车时车辆在画面中的面积,然后设定一个合理的上限。
4. 这个逻辑是启发式的,但足以捕捉到那些明显过大的车辆。更精确的方法可能需要学习和建模停车位的空间布局。
第四步:集成通知功能——发现问题即时提醒
光在画面上显示是不够的,我们需要一个自动通知机制。我们使用免费的 Server酱(https://sct.ftqq.com/)来实现微信通知。你需要去该网站注册并获取你的 SCKEY。
在 monitor.py 文件顶部添加:
SCKEY = '你的Server酱SCKEY'
NOTIFY_URL = f'https://sctapi.ftqq.com/{SCKEY}.send'
然后,在上一步的状态判断之后,添加通知逻辑。为了避免频繁发送消息,我们添加一个冷却时间(例如,每5分钟通知一次)。
# 在循环开始前初始化
last_notify_time = 0
NOTIFY_COOLDOWN = 300 # 5分钟
# ... 在上一步的状态判断代码之后 ...
current_time = time.time()
# 如果检测到问题,且距离上次通知已超过冷却时间
if (is_my_spot_occupied or is_neighbor_car_oversized) and (current_time - last_notify_time > NOTIFY_COOLDOWN):
title = "车位守护者警报"
msg = ""
if is_my_spot_occupied and is_neighbor_car_oversized:
msg = f"⚠️ 发现问题:你的车位可能被一辆疑似越界的大车影响。时间:{datetime.now().strftime('%H:%M')}"
elif is_my_spot_occupied:
msg = f"ℹ️ 提醒:你的车位当前有车辆占用。时间:{datetime.now().strftime('%H:%M')}"
elif is_neighbor_car_oversized:
msg = f"⚠️ 发现可疑大车停在你的车位附近,可能影响上下车。时间:{datetime.now().strftime('%H:%M')}"
if msg:
# 发送通知
try:
data = {'title': title, 'desp': msg}
response = requests.post(NOTIFY_URL, data=data)
if response.status_code == 200:
print(f"通知已发送:{msg}")
last_notify_time = current_time
else:
print(f"通知发送失败,状态码:{response.status_code}")
except Exception as e:
print(f"发送通知时出错:{e}")
# 将通知状态也显示在画面上
notify_status = f"Last Notify: {time.strftime('%H:%M:%S', time.localtime(last_notify_time))}"
cv2.putText(frame, notify_status, (10, 60), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 0), 2)
至此,一个基础的“车位守护者”系统核心已经完成!你可以运行 python monitor.py 进行测试。你需要不断调整 MY_PARKING_SPOT 的坐标和 AREA_THRESHOLD 的值,直到系统能稳定工作。
相关工具与好物推荐
要打造一个稳定的监控系统,合适的硬件能让事半功倍。以下是一些推荐:
- 高清广角摄像头:为了捕捉更全面的车位画面,一个画质清晰、视角广的摄像头至关重要。萤石C6CN家用摄像头 或 海康威视无线摄像头 都是可靠的选择。
- 微型计算主机:如果你不想让主力电脑24小时开机,一台低功耗的微型主机是完美选择。树莓派4B 或 英特尔NUC迷你主机 能够轻松运行我们的Python程序。
- 编程学习资源:如果你想深入学习本教程涉及的Python、OpenCV或AI知识,可以参考一些经典书籍。《Python编程:从入门到实践》 是极佳的入门读物,而 《深度学习入门:基于Python的理论与实现》 能帮你理解模型背后的原理。
- 备用电源方案:为了防止停电导致监控中断,一个 UPS不间断电源 能为你的监控设备提供宝贵的缓冲时间。
常见问题
Q1: AREA_THRESHOLD 这个阈值怎么设定?
A: 这是最需要耐心调试的一步。建议你先在程序中暂时去掉通知逻辑,只打印和显示车辆面积。让自己的车停在正常位置,观察它在画面中的面积值。让邻居的车(尤其是那些大