有种痛苦叫你的邻居买了台大车

作者:







有种痛苦叫你的邻居买了台大车?用代码构建你的“车位守护者”


有种痛苦叫你的邻居买了台大车?用代码构建你的“车位守护者”

简介

最近,一个名为“有种痛苦叫你的邻居买了台大车”的话题引发了广泛共鸣。想象一下这个场景:你结束了一天疲惫的工作,回到小区,却发现自己的车位旁停着一辆庞然大物。它的车身霸气外露,却无情地将你的车位挤得满满当当,你不得不像练杂技一样从副驾驶艰难地爬进爬出,或者在狭窄的空间里进行无数次的“揉库”。这种痛苦,不仅是身体上的不便,更是一种每天都要面对的心理折磨。

汽车厂商热衷于推出尺寸更大、利润更高的SUV和MPV,这背后有复杂的原因,但留给普通车主的,往往是更局促的公共空间和更紧张的停车关系。与其每天与邻居“斗智斗勇”或生闷气,不如我们换个思路:运用技术,为自己打造一个智能的“车位守护者”系统。

在本教程中,我们将利用Python、计算机视觉和物联网的简单知识,创建一个能够自动监测你车位占用情况、并判断相邻车辆是否为“越界大车”的智能系统。当发现潜在问题时,它会通过微信等方式给你发送提醒。这不仅能让你提前掌握车位动态,更能作为与物业或邻居沟通时的客观证据。

前置准备

在开始之前,你需要准备以下硬件和软件:

硬件准备:
1. 一台可以运行的电脑(用于开发和测试)。
2. 一个网络摄像头:如果你的小区允许,可以安装在自家车位附近。如果不行,也可以从自家窗户对准车位进行监控。家用无线摄像头 或一个旧的 树莓派 搭配摄像头模块是不错的选择。
3. (可选) 一个小型服务器或常开的家用电脑:用于7×24小时运行监控程序。

软件与知识准备:
1. Python环境:安装Python 3.8及以上版本。
2. 基础Python知识:了解变量、函数、循环和库的基本使用。
3. 基础的命令行操作
4. 一个能收发消息的通道:我们将以微信(通过Server酱等平台)为例,你也可以改成短信或邮件。

核心思想:我们将使用一个预训练的AI模型(YOLOv5)来检测摄像头画面中的“车辆”,然后通过简单的逻辑计算判断车辆是否停在指定区域内(你的车位),以及它的尺寸是否“越界”。

第一步:搭建Python开发环境与安装依赖

首先,我们来准备我们的代码武器库。建议使用虚拟环境来管理项目依赖。

  1. 创建一个项目文件夹,并在其中创建虚拟环境:
    bash
    mkdir parking_guardian && cd parking_guardian
    python -m venv venv
    # 在Linux/macOS中激活
    source venv/bin/activate
    # 在Windows中激活
    venv\Scripts\activate

  2. 安装必要的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 的值,直到系统能稳定工作。

相关工具与好物推荐

要打造一个稳定的监控系统,合适的硬件能让事半功倍。以下是一些推荐:

  1. 高清广角摄像头:为了捕捉更全面的车位画面,一个画质清晰、视角广的摄像头至关重要。萤石C6CN家用摄像头 或 海康威视无线摄像头 都是可靠的选择。
  2. 微型计算主机:如果你不想让主力电脑24小时开机,一台低功耗的微型主机是完美选择。树莓派4B 或 英特尔NUC迷你主机 能够轻松运行我们的Python程序。
  3. 编程学习资源:如果你想深入学习本教程涉及的Python、OpenCV或AI知识,可以参考一些经典书籍。《Python编程:从入门到实践》 是极佳的入门读物,而 《深度学习入门:基于Python的理论与实现》 能帮你理解模型背后的原理。
  4. 备用电源方案:为了防止停电导致监控中断,一个 UPS不间断电源 能为你的监控设备提供宝贵的缓冲时间。

常见问题

Q1: AREA_THRESHOLD 这个阈值怎么设定?
A: 这是最需要耐心调试的一步。建议你先在程序中暂时去掉通知逻辑,只打印和显示车辆面积。让自己的车停在正常位置,观察它在画面中的面积值。让邻居的车(尤其是那些大