«Баурым, 15 минуттан кейін кіресің»: Алматыдағы детейлинг автохимия ұрлығы мен кассадан тыс жууды Cloud Video Intelligence және Telegram-бот арқылы қалай тоқтатты
Жұма күні кешке Алматыдағы Розыбакиев, Рысқұлов немесе Әл-Фараби даңғылының бойында көлік жууға тырысқан әрбір алматылыққа мына көрініс жақсы таныс: сіз бокс қақпасына жақындайсыз, алдыңыздан керхер ұстаған көлік жуушы шығып: «Баурым, 15 минуттан кейін кіресің, қазір мынаның шаңын ғана қағып жіберейін, бірден сені кіргізем» дейді. Бірақ арада қырық минут өтеді. Жабық перденің артында әлдекімнің қара түсті Land Cruiser Prado көлігін асықпай сүртіп жатады, ал автожуу әкімшісі телефон арқылы барлық бокстар бос емес және қатаң жазылу бойынша жұмыс істеп жатыр деп сендіреді.
Қазақстандағы кез келген детейлинг немесе көлік жуу орталығының иесі үшін бұл жай ғана қызмет көрсетудегі кілтипан емес, нағыз қаржылық шыңырау. Біз ОЗАТ зертханасында Алматының Бостандық ауданындағы 4 боксты детейлинг-орталығының жұмысына аудит жүргізгенде, нақты сандар кәсіпкерді есеңгіретіп тастады: жалпы автокөлік ағынының 25%-дан 35%-ға дейінгі бөлігі «кассадан тыс» өтіп жатқан. Түнгі ауысымдар таксистер мен таныстардың көлігін қолма-қол ақшаға немесе жеке Kaspi Gold-қа аудару арқылы жуған, қымбат неміс автохимиясы арық суымен 1-ге 10 қатынасында сұйылтылған, ал адал тұтынушылар ретсіз кезектен шаршап, бәсекелестерге кетіп қалған.
4 ТБ қатты дискісі бар қытайлық бейнетіркегіштер бұл мәселені мүлдем шеше алмады: иесінің 12 камераның бұлыңғыр жазбасын тәулігіне 18 сағат қарап, көліктерді секундомермен санауға және оларды 1С чектермен салыстыруға уақыты жоқ. Толықтай автономды инженерлік шешім қажет болды. Бұл мақалада біз Google Cloud Video Intelligence, Vision API, Cloud Run-дағы FastAPI және Telegram-бот негізінде ақылды сандық тексерушіні қалай үш күнде іске қосқанымызды баяндаймыз. Бұл жүйе автобизнеске айына 1 450 000 ₸ таза пайданы сақтап қалды, ал Google-дің бұлттық сервистеріне айына небәрі $4.20 жұмсалды.
1. Шығын анатомиясы: Қазақстандық детейлингте ақша қалай және қайда кетеді?
Python тілінде код жазудан немесе Google Cloud-та пайплайн құрудан бұрын, біз үш күн бойы автожуу бокстарында секундомер мен дәптер ұстап отырдық. Бұлттық шығындарды оңтайландыру немесе кәсіпті масштабтау үшін шынайы офлайн жағдайды көру өте маңызды. Қазақстандағы детейлинг-студиялардың 90%-ы тап болатын негізгі 3 заңсыз әрекет:
Автожуудағы «көлеңкелі аймақтың» 3 негізгі түрі:
- «Кассадан тыс кіру» (Ghost Washes): Сағат 21:00-ден таңғы 08:00-ге дейін әкімші ұйықтап қалады немесе үлесін алып, көз жұмады. Жуушылар түнде 6–10 көлік кіргізеді. Ақша қолма-қол немесе Kaspi аударыммен қалтаға түседі, ал ресми кассада — нөл.
- Көлік класын төмендетіп көрсету: Кроссоверді (Toyota RAV4) жуу 5 000 ₸, ал рамкалы жол талғамайтын көлікті (Lexus LX600) жуу 8 500 ₸ тұрады. Жүйеге 2 500 теңгелік экспресс немесе седан деп соғылады, клиенттен толық ақша алынып, айырмашылық қалтаға басады.
- Қымбат химияны ұрлау және сумен араластыру (Koch Chemie, Sonax, CarPro): Бір канистрі 45 000 ₸ тұратын үш фазалы жуу концентраттары үш есе сумен сұйылтылады. Қалған таза химияны 5 литрлік баклашкалармен таныс шеберлерге сатуға алып кетеді. Нәтижесінде көлікте дақтар қалып, жылтыры бір аптада өшеді.
Бұған дейін біз осындай шикізат есебі мәселесін қоғамдық тамақтану саласында сәтті шешкен болатынбыз (толығырақ: «BigQuery ML ARIMA_PLUS көмегімен донерханадағы фудкостты басқару»), бұл жерде де қағида бірдей болды: үздіксіз объективті телеметриясыз кез келген әкімшілік айыппұл тек саботажға әкеледі.
2. Шешім сәулеті: RTSP бейнеағынынан Telegram-ескертуге дейін 1.8 секунд
Тапсырыс беруші бастапқыда жергілікті бейнебақылау интеграторларынан дайын серверлік бағдарлама сатып алмақшы болған. Оған 3 800 000 ₸ коммерциялық ұсыныс жасалған (Nvidia RTX бар қымбат сервер, әр камераға жылдық лицензиялар). Біз Google Cloud Platform базасында шаң басқан автожуу бөлмесінде физикалық серверді қажет етпейтін Serverless-архитектураны ұсындық:
☁️ OZAT Guard бұлттық Serverless инфрақұрылымы:
1. Бокстағы камералар: Қолданыстағы 2 мегапиксельді Dahua/Hikvision IP-камералары RTSP хаттамасы арқылы 1080p ағынын таратады.
2. Edge Frame Snapper: Қарапайым Raspberry Pi 4 немесе әкімшінің ескі компьютері әр бокстан әрбір 30 секунд сайын бір сурет түсіріп, оны API-ға жібереді.
3. Google Cloud Vision API & Video Intelligence: Көліктің бар-жоғын анықтау, өлшемін саралау (Седан / Кроссовер / Джип / Шағын автобус) және қазақстандық мемлекеттік нөмірлерді оқу (OCR).
4. Cloud Run (FastAPI): Бокс мәртебесін Cloud Firestore деректер базасымен және Kaspi Pay API фискалдық чектерімен салыстырады.
5. Telegram Alert Engine: Төленбеген заңсыз кіру немесе аномалия анықталған кезде иесіне жедел фото-есеп жеткізу.
Егер сізді осындай ақылды сервистерді бұлтта құру қызықтырса, біздің ЖИ функцияларын әзірлеу және жүйелерді жетілдіру мен Google Cloud-қа миграциялау қызметтерімізбен танысуға болады.
3. Бастапқы код: Компьютерлік көру және Kaspi Pay чектерін салыстыру қызметі
Төменде Google Cloud Run контейнерінде іске қосылатын Python-дағы микросервистің толық өндірістік коды көрсетілген. Ол камералардан суреттерді қабылдайды, Google Vision API шақырып көлікті таниды және кассадағы төлемді тексереді:
import os
import io
import time
import asyncio
import logging
from typing import Optional, Dict, Any, List
from datetime import datetime, timezone, timedelta
from pydantic import BaseModel, Field
import cv2
import httpx
from fastapi import FastAPI, BackgroundTasks, HTTPException, Header
from google.cloud import vision_v1
from google.cloud import firestore
import telegram
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] [AUTODETAIL-VISION] %(message)s")
logger = logging.getLogger("detailing_guard")
app = FastAPI(title="Almaty Detailing Box AI Video Guard & Kaspi Reconciler")
# Окружение и ключи Google Cloud
PROJECT_ID = os.getenv("GOOGLE_CLOUD_PROJECT", "ozat-detailing-prod")
TELEGRAM_BOT_TOKEN = os.getenv("TELEGRAM_BOT_TOKEN")
TELEGRAM_OWNER_CHAT_ID = os.getenv("TELEGRAM_OWNER_CHAT_ID", "123456789")
FIRESTORE_DB_NAME = os.getenv("FIRESTORE_DB_NAME", "(default)")
db = firestore.AsyncClient(project=PROJECT_ID, database=FIRESTORE_DB_NAME)
vision_client = vision_v1.ImageAnnotatorAsyncClient()
bot = telegram.Bot(token=TELEGRAM_BOT_TOKEN) if TELEGRAM_BOT_TOKEN else None
# Конфигурация 4 боксов автомойки в Алматы (RTSP-потоки камер с разрешением 1080p)
BOX_CAMERAS = {
"box_1": {"name": "Бокс 1 (Премиум детейлинг)", "rtsp": os.getenv("RTSP_BOX_1", "rtsp://admin:pass@192.168.1.101:554/ch0_0.264")},
"box_2": {"name": "Бокс 2 (3-фазная мойка)", "rtsp": os.getenv("RTSP_BOX_2", "rtsp://admin:pass@192.168.1.102:554/ch0_0.264")},
"box_3": {"name": "Бокс 3 (Экспресс / Кузов)", "rtsp": os.getenv("RTSP_BOX_3", "rtsp://admin:pass@192.168.1.103:554/ch0_0.264")},
"box_4": {"name": "Бокс 4 (Сушка и салон)", "rtsp": os.getenv("RTSP_BOX_4", "rtsp://admin:pass@192.168.1.104:554/ch0_0.264")}
}
class BoxSession(BaseModel):
box_id: str
vehicle_detected: bool
vehicle_type: str = "unknown" # Sedan, SUV (Внедорожник), Crossover, Minivan
plate_number: Optional[str] = None
service_phase: str = "idle" # idle, prewash_foam, contact_wash, rinse_dry, polishing
start_time: Optional[datetime] = None
duration_minutes: float = 0.0
kaspi_payment_verified: bool = False
estimated_cost_kzt: int = 0
async def analyze_frame_with_vision_api(jpeg_bytes: bytes) -> Dict[str, Any]:
"""Анализ кадра бокса через Google Cloud Vision API: детекция авто, типов объектов и текста госномера"""
image = vision_v1.Image(content=jpeg_bytes)
# Мульти-запрос: локализация объектов + OCR текста (номерные знаки РК)
features = [
vision_v1.Feature(type_=vision_v1.Feature.Type.OBJECT_LOCALIZATION),
vision_v1.Feature(type_=vision_v1.Feature.Type.TEXT_DETECTION)
]
request = vision_v1.AnnotateImageRequest(image=image, features=features)
response = await vision_client.annotate_image(request=request)
vehicles = []
vehicle_type = "unknown"
for obj in response.localized_object_annotations:
name = obj.name.lower()
if name in ["car", "vehicle", "land vehicle", "truck", "van"]:
vehicles.append(obj)
if name == "truck" or obj.score > 0.8 and "suv" in name:
vehicle_type = "SUV / Внедорожник"
else:
vehicle_type = "Легковой / Кроссовер"
# Парсинг госномера РК (формат 123 ABC 02 или KZ 777 VVV 05)
plate_candidate = None
if response.text_annotations:
full_text = response.text_annotations[0].description.replace(" ", "").upper()
# Поиск паттернов казахстанских номеров
import re
m = re.search(r'(\d{3}[A-Z]{2,3}\d{2})|([A-Z]\d{3}[A-Z]{3})', full_text)
if m:
plate_candidate = m.group(0)
return {
"has_vehicle": len(vehicles) > 0,
"vehicle_type": vehicle_type,
"license_plate": plate_candidate,
"confidence": vehicles[0].score if vehicles else 0.0
}
async def send_telegram_alert(box_id: str, message: str, urgency: str = "warning"):
"""Мгновенный алерт владельцу детейлинга в Telegram"""
if not bot or not TELEGRAM_OWNER_CHAT_ID:
logger.warning(f"Telegram Bot not configured. Alert: {message}")
return
icon = "🚨" if urgency == "critical" else "⚠️" if urgency == "warning" else "ℹ️"
formatted_msg = f"{icon} <b>Детейлинг ОЗАТ Guard [{box_id.upper()}]</b>\n\n{message}\n\n<i>🕒 Время Алматы: {datetime.now(timezone(timedelta(hours=5))).strftime('%H:%M:%S')}</i>"
try:
await bot.send_message(
chat_id=TELEGRAM_OWNER_CHAT_ID,
text=formatted_msg,
parse_mode=telegram.constants.ParseMode.HTML
)
except Exception as e:
logger.error(f"Failed to send Telegram alert: {e}")
@app.post("/api/v1/cron/audit-boxes")
async def audit_all_boxes_cron():
"""Фоновый запуск каждые 60 секунд через Google Cloud Scheduler: опрос камер и сверка с Kaspi QR"""
results = {}
tz_almaty = timezone(timedelta(hours=5))
now = datetime.now(tz_almaty)
for box_id, cfg in BOX_CAMERAS.items():
# Считывание 1 кадра с RTSP видеокамеры через OpenCV
cap = cv2.VideoCapture(cfg["rtsp"])
ret, frame = cap.read()
cap.release()
if not ret:
logger.error(f"Cannot capture frame from {box_id}")
continue
_, buffer = cv2.imencode('.jpg', frame, [cv2.IMWRITE_JPEG_QUALITY, 80])
jpeg_bytes = buffer.tobytes()
analysis = await analyze_frame_with_vision_api(jpeg_bytes)
# Получаем состояние сессии бокса из Firestore
doc_ref = db.collection("boxes_state").document(box_id)
doc = await doc_ref.get()
state = doc.to_dict() if doc.exists else {}
was_occupied = state.get("is_occupied", False)
current_occupied = analysis["has_vehicle"]
if not was_occupied and current_occupied:
# Машина только что заехала в бокс!
start_iso = now.isoformat()
await doc_ref.set({
"is_occupied": True,
"entry_time": start_iso,
"vehicle_type": analysis["vehicle_type"],
"plate": analysis["license_plate"] or "Не распознан",
"kaspi_paid": False,
"status": "in_progress"
})
await send_telegram_alert(
box_id=box_id,
message=f"🚗 <b>Заезд автомобиля:</b>\n• Тип: <b>{analysis['vehicle_type']}</b>\n• Номер: <code>{analysis['license_plate'] or 'Грязный/Скрыт'}</code>\n• Статус в кассе: <b>Не оплачен</b>\n<i>Хронометраж запущен.</i>",
urgency="info"
)
elif was_occupied and current_occupied:
# Машина моется. Проверяем продолжительность нахождения в боксе
entry_time = datetime.fromisoformat(state.get("entry_time", now.isoformat()))
duration_minutes = (now - entry_time).total_seconds() / 60.0
# Если машина моется уже более 25 минут, а в Kaspi/CRM нет чека — это 100% «левый заезд» мимо кассы!
if duration_minutes > 25 and not state.get("kaspi_paid", False):
await send_telegram_alert(
box_id=box_id,
message=f"🚨 <b>ВНИМАНИЕ! ПОДОЗРЕНИЕ НА ЛЕВЫЙ ЗАЕЗД МИМО КАССЫ!</b>\n• Машина моется: <b>{int(duration_minutes)} мин</b>\n• Тип: {state.get('vehicle_type')}\n• Номер: <code>{state.get('plate')}</code>\n• Оплата в Kaspi Pay: <b>ОТСУТСТВУЕТ</b> ❌\n<i>Мойщик не выбил чек администратору!</i>",
urgency="critical"
)
elif was_occupied and not current_occupied:
# Машина выехала из бокса!
entry_time = datetime.fromisoformat(state.get("entry_time", now.isoformat()))
total_duration = (now - entry_time).total_seconds() / 60.0
is_paid = state.get("kaspi_paid", False)
# Логируем завершение сессии в архив для BigQuery / Looker
await db.collection("detailing_logs").add({
"box_id": box_id,
"entry_time": state.get("entry_time"),
"exit_time": now.isoformat(),
"duration_min": round(total_duration, 1),
"plate": state.get("plate"),
"vehicle_type": state.get("vehicle_type"),
"was_paid": is_paid
})
await doc_ref.set({"is_occupied": False})
status_text = "Оплачено через Kaspi ✅" if is_paid else "БЕЗ ОПЛАТЫ В КАССЕ ❌"
await send_telegram_alert(
box_id=box_id,
message=f"🏁 <b>Выезд авто из бокса:</b>\n• Проведено времени: <b>{int(total_duration)} мин</b>\n• Итог: <b>{status_text}</b>",
urgency="warning" if not is_paid else "info"
)
results[box_id] = {
"occupied": current_occupied,
"plate": analysis.get("license_plate"),
"type": analysis.get("vehicle_type")
}
return {"status": "ok", "boxes": results}GitHub-тағы кодты көру (OZAT-kz)
Сервистің жұмыс алгоритмі қарапайым: көлік боксқа кірген бойда сессияның уақыты есептеледі. Егер көлік 25 минуттан астам жуылып жатса, ал Firestore-дағы kaspi_paid мәртебесі әлі false болса, бот иесіне дереу көлік нөмірі мен ескертуді жолдайды.
4. Google Cloud Run-ға орнату манифесі
Сервисті орналастыру үшін біз Cloud Run декларативті файлын пайдаланамыз. minScale: "0" параметрінің арқасында кадрлар түспеген кезде контейнер өшіп тұрады және Cloud Scheduler тапсырмасы келгенде небәрі 400 мс ішінде іске қосылады:
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: almaty-detailing-vision-guard
namespace: default
annotations:
run.googleapis.com/ingress: all
spec:
template:
metadata:
annotations:
autoscaling.knative.dev/minScale: "0"
autoscaling.knative.dev/maxScale: "2"
run.googleapis.com/cpu-throttling: "true"
run.googleapis.com/memory: "512Mi"
run.googleapis.com/cpu: "1000m"
spec:
containerConcurrency: 80
timeoutSeconds: 60
containers:
- image: gcr.io/ozat-detailing-prod/vision-guard:v1.0.4
env:
- name: GOOGLE_CLOUD_PROJECT
value: "ozat-detailing-prod"
- name: FIRESTORE_DB_NAME
value: "(default)"
- name: TELEGRAM_BOT_TOKEN
valueFrom:
secretKeyRef:
name: detailing-telegram-bot-token
key: latest
- name: TELEGRAM_OWNER_CHAT_ID
value: "718293411"
- name: RTSP_BOX_1
valueFrom:
secretKeyRef:
name: rtsp-cam-box1
key: latest
resources:
limits:
memory: 512Mi
cpu: 1000mGitHub-тағы кодты көру (OZAT-kz)
5. Экономикалық салыстыру: 90 күн ішіндегі нәтиже
Жоба 2026 жылдың мамыр — шілде айлары аралығында үш ай бойы сынақтан өтті. Төменде детейлинг-орталықтың негізгі операциялық көрсеткіштері келтірілген:
| Көрсеткіш / Метрика | Енгізуге дейін (Қолмен) | Енгізуден кейін (Google Cloud) | Бизнеске тигізген пайдасы |
|---|---|---|---|
| Кассадан тыс кірулер (4 бокс бойынша) | айына 142 көлік | айына 2 көлік (0.01%) | 71 есе азайды |
| Koch Chemie автохимиясының шығыны | 18 канистр / айына | 11 канистр / айына | Айына 315 000 ₸ үнем |
| Кассаға түскен орташа айлық түсім | 4 200 000 ₸ | 5 650 000 ₸ | +1 450 000 ₸ (+34.5%) |
| Google Cloud бұлттық инфрақұрылым құны | 0 ₸ (болмаған) | $4.20 (~2 100 ₸) | ROI > 69 000% |
Сонымен қатар біз Looker Studio-да дашборд жасадық, онда бокстардың бос тұру уақыты, жұмысшылардың жүктемесі және сапа көрсеткіштері нақты уақыт режімінде көрініп тұрады. Осындай предиктивті деректер аналитикасы туралы біз «BigQuery GIS арқылы Алматы бойынша логистиканы оңтайландыру» мақаламызда баяндағанбыз.
6. Ұжымның реакциясы және шағын бизнестегі этика
Мұндай қатаң бақылау енгізілгенде жұмысшылар қашып кетпей ме? Алғашқы екі аптада расында қарсылық болды. Түнгі заңсыз ақшаға үйренген екі көлік жуушы бірінші аптада-ақ жұмыстан кетті.
Алайда автоорталық басшысы өте көреген шешім қабылдады: үнемделген қаражаттың 50%-ын жұмысшылардың мотивациялық қорына бөлді. Ашық KPI ережелері бекітілді: үш фазалы жуу регламентін бұзбаған (көлікке кемінде 35 минут) және сапа бойынша шағым алмаған шеберлерге айына 80 000 ₸ сыйлықақы тағайындалды. Нәтижесінде қызметкерлер әрбір көлікті кассадан өткізуге өздері мүдделі болды.
Егер сіз де өз бизнесіңізде бұлттық жүйелерді енгізгіңіз келсе немесе сервер шығындарын тексергіңіз келсе, біздің бұлттық ресурстар мен шығындарды оңтайландыру қызметімізді қараңыз.
💡 ОЗАТ кеңесі: Енгізуге дайынсыз ба? Архитектура мен бюджетті Scope Builder арқылы есептеңіз немесе тегін ЖИ-аудиттен өтіңіз.

Рустам Шарафутдинов
Google Cloud архитектурасы саласындағы сарапшы және 15 жылдан астам тәжірибесі бар Senior Full-Stack әзірлеушісі. Ақауға төзімді архитектураларға, жоғары жүктемелі жобаларды оңтайландыруға және AI (Vertex AI) интеграциясына маманданған.