Skip to content

Commit 62fa34d

Browse files
authored
Add files via upload
1 parent fef3180 commit 62fa34d

File tree

4 files changed

+273
-0
lines changed

4 files changed

+273
-0
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"data": "10-09-2025",
3+
"hora": "22:06:53",
4+
"carros": 5,
5+
"motos": 2,
6+
"total_veiculos": 7
7+
}
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
# pc_detector_sender.py
2+
import time
3+
import threading
4+
import requests
5+
import cv2
6+
import numpy as np
7+
from collections import deque
8+
from ultralytics import YOLO
9+
from datetime import datetime
10+
11+
# ---------- CONFIGURAÇÃO ----------
12+
VIDEO_SOURCE = 0 # 0 = webcam. Pode ser "carros.mp4" ou "rtsp://..."
13+
YAOLO_MODEL = "yolo11n.pt" # caminho para o modelo (já baixado)
14+
LIMIT_CONFIANCA = 0.45
15+
VEICULOS_CLASSES = {2:"Carro", 3:"Moto", 5:"Onibus", 7:"Caminhao"} # COCO ids relevantes
16+
17+
PI_URL = "http://10.186.150.9:5000/update" # <-- Troque pelo IP do seu Raspberry Pi
18+
SEND_INTERVAL = 1.0 # segundos entre POSTs ao Pi
19+
20+
# Parâmetros de movimento / estacionamento
21+
MOVE_TOLERANCE = 15 # pixels — movimento mínimo para considerar "em movimento"
22+
PARK_SECONDS = 10.0 # se sem movimento por X secs, considera estacionado
23+
24+
# ---------- RASTREADOR SIMPLES ----------
25+
class SimpleTracker:
26+
def __init__(self, max_disappeared=50):
27+
self.next_id = 0
28+
self.objects = {} # id -> centroid (x,y)
29+
self.disappeared = {} # id -> frames desaparecido
30+
# para lógica de estacionamento:
31+
self.last_moved_time = {} # id -> timestamp of last movement
32+
self.last_position = {} # id -> last centroid
33+
self.max_disappeared = max_disappeared
34+
35+
def register(self, centroid):
36+
i = self.next_id
37+
self.objects[i] = centroid
38+
self.disappeared[i] = 0
39+
self.last_position[i] = centroid
40+
self.last_moved_time[i] = time.time()
41+
self.next_id += 1
42+
43+
def deregister(self, oid):
44+
for d in (self.objects, self.disappeared, self.last_moved_time, self.last_position):
45+
if oid in d:
46+
del d[oid]
47+
48+
def update(self, input_centroids):
49+
# input_centroids: list of (x,y)
50+
if len(input_centroids) == 0:
51+
# increment disappeared count
52+
for oid in list(self.disappeared.keys()):
53+
self.disappeared[oid] += 1
54+
if self.disappeared[oid] > self.max_disappeared:
55+
self.deregister(oid)
56+
return self.objects
57+
58+
if len(self.objects) == 0:
59+
for c in input_centroids:
60+
self.register(c)
61+
return self.objects
62+
63+
# build arrays
64+
object_ids = list(self.objects.keys())
65+
object_centroids = list(self.objects.values())
66+
67+
D = np.linalg.norm(np.array(object_centroids)[:, np.newaxis] - np.array(input_centroids), axis=2)
68+
rows = D.min(axis=1).argsort()
69+
cols = D.argmin(axis=1)[rows]
70+
71+
used_rows, used_cols = set(), set()
72+
for row, col in zip(rows, cols):
73+
if row in used_rows or col in used_cols:
74+
continue
75+
oid = object_ids[row]
76+
new_centroid = input_centroids[col]
77+
# update disappeared
78+
self.disappeared[oid] = 0
79+
# check movement distance
80+
old = self.last_position.get(oid, new_centroid)
81+
dist = np.linalg.norm(np.array(old) - np.array(new_centroid))
82+
if dist >= MOVE_TOLERANCE:
83+
self.last_moved_time[oid] = time.time()
84+
self.last_position[oid] = new_centroid
85+
self.objects[oid] = new_centroid
86+
used_rows.add(row)
87+
used_cols.add(col)
88+
89+
# rows not matched -> disappeared
90+
unused_rows = set(range(0, D.shape[0])) - used_rows
91+
for row in unused_rows:
92+
oid = object_ids[row]
93+
self.disappeared[oid] += 1
94+
if self.disappeared[oid] > self.max_disappeared:
95+
self.deregister(oid)
96+
97+
# cols not matched -> new objects
98+
unused_cols = set(range(0, D.shape[1])) - used_cols
99+
for col in unused_cols:
100+
self.register(input_centroids[col])
101+
102+
return self.objects
103+
104+
def get_active_ids(self):
105+
"""Retorna IDs que NÃO estão estacionados (moved within PARK_SECONDS)"""
106+
now = time.time()
107+
active = []
108+
for oid, centroid in self.objects.items():
109+
last_moved = self.last_moved_time.get(oid, 0)
110+
if (now - last_moved) <= PARK_SECONDS:
111+
active.append(oid)
112+
return active
113+
114+
# ---------- ENVIADOR ASSÍNCRONO ----------
115+
class SenderThread(threading.Thread):
116+
def __init__(self, url, interval=1.0):
117+
super().__init__(daemon=True)
118+
self.url = url
119+
self.interval = interval
120+
self.payload = {"vehicle_present": False, "moving_count": 0}
121+
self._stop = threading.Event()
122+
123+
def run(self):
124+
while not self._stop.is_set():
125+
try:
126+
requests.post(self.url, json=self.payload, timeout=1.5)
127+
except Exception as e:
128+
# opcional: print("Erro ao enviar para Pi:", e)
129+
pass
130+
time.sleep(self.interval)
131+
132+
def update_payload(self, present, moving_count):
133+
self.payload = {"vehicle_present": bool(present), "moving_count": int(moving_count)}
134+
135+
def stop(self):
136+
self._stop.set()
137+
138+
# ---------- LÓGICA PRINCIPAL ----------
139+
def main():
140+
model = YOLO(YAOLO_MODEL)
141+
cap = cv2.VideoCapture(VIDEO_SOURCE)
142+
if not cap.isOpened():
143+
print("Erro ao abrir fonte:", VIDEO_SOURCE)
144+
return
145+
146+
tracker = SimpleTracker()
147+
sender = SenderThread(PI_URL, interval=SEND_INTERVAL)
148+
sender.start()
149+
150+
try:
151+
while True:
152+
ret, frame = cap.read()
153+
if not ret:
154+
break
155+
156+
# run detection (note: may be heavy; adjust skip frames if needed)
157+
results = model(frame)
158+
159+
# collect centroids for vehicle classes
160+
centroids = []
161+
for r in results:
162+
for box in r.boxes:
163+
cls_id = int(box.cls[0])
164+
conf = float(box.conf[0])
165+
if conf < LIMIT_CONFIANCA:
166+
continue
167+
if cls_id not in VEICULOS_CLASSES:
168+
continue
169+
x1, y1, x2, y2 = map(int, box.xyxy[0])
170+
# optional: skip tiny boxes
171+
area = (x2 - x1) * (y2 - y1)
172+
if area < 500: # ajuste conforme vídeo
173+
continue
174+
cx, cy = (x1 + x2) // 2, (y1 + y2) // 2
175+
centroids.append((cx, cy))
176+
# draw box
177+
name = VEICULOS_CLASSES[cls_id]
178+
cv2.rectangle(frame, (x1, y1), (x2, y2), (0,255,0), 2)
179+
cv2.putText(frame, f"{name} {conf:.2f}", (x1, y1-8), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,255,0), 1)
180+
181+
# update tracker
182+
objs = tracker.update(centroids)
183+
active_ids = tracker.get_active_ids() # ids considered "moving" (not parked)
184+
185+
# show tracker ids on frame
186+
for oid, centroid in objs.items():
187+
cx, cy = map(int, centroid)
188+
text = f"ID{oid}"
189+
if oid in active_ids:
190+
color = (0,255,0)
191+
else:
192+
color = (0,0,255) # parked = red id
193+
cv2.putText(frame, text, (cx-10, cy-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 1)
194+
cv2.circle(frame, (cx,cy), 4, color, -1)
195+
196+
# update sender payload
197+
sender.update_payload(present=len(active_ids)>0, moving_count=len(active_ids))
198+
199+
cv2.imshow("PC - Detector", frame)
200+
if cv2.waitKey(1) & 0xFF == ord('q'):
201+
break
202+
finally:
203+
sender.stop()
204+
sender.join(timeout=2.0)
205+
cap.release()
206+
cv2.destroyAllWindows()
207+
208+
if __name__ == "__main__":
209+
main()
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# pi_receiver_led.py
2+
from flask import Flask, request, jsonify
3+
import RPi.GPIO as GPIO
4+
import threading
5+
import time
6+
7+
# ---------- CONFIG ----------
8+
LED_PIN = 18 # GPIO BCM pin onde o LED está conectado
9+
HOST = "0.0.0.0"
10+
PORT = 5000
11+
12+
# ---------- SETUP GPIO ----------
13+
GPIO.setmode(GPIO.BCM)
14+
GPIO.setup(LED_PIN, GPIO.OUT)
15+
GPIO.output(LED_PIN, GPIO.LOW)
16+
17+
app = Flask(__name__)
18+
19+
# Estado atual (thread-safe)
20+
state = {"vehicle_present": False, "moving_count": 0}
21+
lock = threading.Lock()
22+
23+
@app.route("/update", methods=["POST"])
24+
def update():
25+
data = request.get_json(silent=True)
26+
if not data:
27+
return jsonify({"error": "invalid json"}), 400
28+
29+
vehicle_present = bool(data.get("vehicle_present", False))
30+
moving_count = int(data.get("moving_count", 0))
31+
32+
with lock:
33+
state["vehicle_present"] = vehicle_present
34+
state["moving_count"] = moving_count
35+
# controla o LED imediatamente
36+
GPIO.output(LED_PIN, GPIO.HIGH if vehicle_present else GPIO.LOW)
37+
38+
return jsonify({"status":"ok"}), 200
39+
40+
@app.route("/status", methods=["GET"])
41+
def status():
42+
with lock:
43+
return jsonify(state)
44+
45+
def run_flask():
46+
# flask built-in server (suficiente para LAN + baixo throughput)
47+
app.run(host=HOST, port=PORT, threaded=True)
48+
49+
if __name__ == "__main__":
50+
try:
51+
print("Iniciando receptor no Raspberry Pi...")
52+
run_flask()
53+
except KeyboardInterrupt:
54+
pass
55+
finally:
56+
GPIO.output(LED_PIN, GPIO.LOW)
57+
GPIO.cleanup()
5.35 MB
Binary file not shown.

0 commit comments

Comments
 (0)