api_version: cmvr.edge.ai/v1 runtime: # PyAV plus the PPE and phone-use YOLO branches share this bounded pool. thread_workers: 4 shutdown_timeout_s: 8 endpoints: cmvr_es: transport: grpc # cmvr-es gRPC address. Change this value for each deployed robot. target: 192.168.0.119:50052 tls: false timeout_s: 5 options: max_receive_mb: 32 ppe_alert_platform: transport: http # Detection-alert platform HTTP base URL. The alert payload model_id and # rule_id distinguish PPE violations from phone-use violations. base_url: http://192.168.0.222:13080 timeout_s: 3 pipelines: detection: enabled: true nodes: camera: uses: cmvr.grpc.camera_rgb_stream@1 with: endpoint: cmvr_es device_id: wrist_cam pixel_format: BGR8 reconnect: true reconnect_initial_s: 0.5 reconnect_max_s: 10 # Log stream state immediately on first frame and emit a periodic # progress/stall heartbeat without printing every encoded frame. stream_log_interval_s: 5 decoder: uses: media.video_decoder.pyav@1 detector: uses: detection.model@1 with: model: construction-ppe-yolov8@2 # Omitting detect_labels means all registered labels. This example # asks the backend to return only PPE violations used by the rules. detect_labels: - No-Boots - No-Ear-Protection - No-Glass - No-Glove - No-Helmet - No-Mask - No-Vest confidence: 0.50 max_fps: 10 # Log the first completed inference immediately, then aggregate one # heartbeat every 5 seconds so model activity is visible without # printing every frame. Set to 1 for one-second debugging, or omit to disable. inference_log_interval_s: 5 # Keep the decoded threshold frame available to repeat_gate so an # annotated alert image can be rendered only when a rule triggers. attach_frame: true model_options: # Model artifact and inference provider are deployment configuration, # not process environment requirements. This repository-relative # path requires launching cmvr-edge-ai from the repository root. weights: models/detection/construction-ppe-yolov8/v2/model.onnx providers: [CPUExecutionProvider] intra_op_threads: 1 inter_op_threads: 1 imgsz: 640 iou: 0.70 max_det: 100 repeat_gate: uses: detection.repeat_gate@1 with: # cmvr-es currently omits capture timestamps on successful stream # frames, so received time is the deterministic deployment default. time_source: received alert_image: enabled: true jpeg_quality: 85 rules: - id: no-boots labels: [No-Boots] min_hits: 3 window_ms: 2000 cooldown_ms: 30000 min_confidence: 0.50 scope: source - id: no-ear-protection labels: [No-Ear-Protection] min_hits: 3 window_ms: 2000 cooldown_ms: 30000 min_confidence: 0.50 scope: source - id: no-glass labels: [No-Glass] min_hits: 3 window_ms: 2000 cooldown_ms: 30000 min_confidence: 0.50 scope: source - id: no-glove labels: [No-Glove] min_hits: 3 window_ms: 2000 cooldown_ms: 30000 min_confidence: 0.50 scope: source - id: no-helmet labels: [No-Helmet] min_hits: 3 window_ms: 2000 cooldown_ms: 30000 min_confidence: 0.50 scope: source - id: no-mask labels: [No-Mask] min_hits: 3 window_ms: 2000 cooldown_ms: 30000 min_confidence: 0.50 scope: source - id: no-vest labels: [No-Vest] min_hits: 3 window_ms: 2000 cooldown_ms: 30000 min_confidence: 0.50 scope: source phone_detector: uses: detection.model@1 with: model: people-talking-yolov8x@2 # The source model also contains a generic class named "label". It is # retained in model registration for class-ID safety but is not an # actionable phone-use event, so this branch selects only class 1. detect_labels: - talking on phone confidence: 0.50 # YOLOv8x is substantially heavier than the PPE model. Start with a # conservative CPU rate and tune only after measuring target hardware. max_fps: 5 inference_log_interval_s: 5 attach_frame: true model_options: weights: models/detection/people-talking-yolov8x/v2/model.onnx providers: [CPUExecutionProvider] intra_op_threads: 1 inter_op_threads: 1 imgsz: 640 iou: 0.70 max_det: 100 phone_repeat_gate: uses: detection.repeat_gate@1 with: time_source: received alert_image: enabled: true jpeg_quality: 85 rules: - id: talking-on-phone labels: [talking on phone] min_hits: 3 window_ms: 2000 cooldown_ms: 30000 min_confidence: 0.50 scope: source alert_platform: uses: platform.http_json_sink@1 with: endpoint: ppe_alert_platform path: /v1/detection-alerts # Include the IP from endpoints.cmvr_es.target in every platform # report so the platform can identify the originating edge device. grpc_endpoint: cmvr_es # Platform outages must not stop camera capture or inference. After # bounded retries, log a WARNING and drop only this report. failure_mode: log_and_drop max_attempts: 3 retry_initial_s: 0.25 retry_max_s: 2 edges: # Encoded H264/H265 packets must remain contiguous before decode. - from: camera.frames to: decoder.frames qos: profile: video_contiguous capacity: 8 overflow: block # Once frames are decoded, keeping only the newest frame bounds latency. - from: decoder.frames to: detector.frames qos: profile: realtime_latest capacity: 1 overflow: drop_oldest # Fan out the already decoded image; do not open a second camera stream # or decode the same H264/H265 packet twice. - from: decoder.frames to: phone_detector.frames qos: profile: realtime_latest capacity: 1 overflow: drop_oldest - from: detector.detections to: repeat_gate.detections qos: profile: telemetry # DetectionResult carries a decoded frame when attach_frame is enabled; # keep this queue short so raw image buffers cannot accumulate. Under # sustained overload drop_oldest also means dropped results do not count. capacity: 2 overflow: drop_oldest - from: phone_detector.detections to: phone_repeat_gate.detections qos: profile: telemetry # attach_frame carries the decoded image until the rule is evaluated. capacity: 2 overflow: drop_oldest - from: repeat_gate.alerts to: alert_platform.input qos: profile: telemetry capacity: 64 overflow: block - from: phone_repeat_gate.alerts to: alert_platform.input qos: profile: telemetry capacity: 64 overflow: block