ข้ามไปที่เนื้อหา

AI Node (Edge AI Nodes)

ใช้ได้เฉพาะ EPG-002S, EPG-004S และ EPG-AI เท่านั้น

หน้านี้ใช้ได้เฉพาะเกตเวย์รุ่นที่มี NPU ในตัว (Rockchip RK3566/RK3588): EPG-002S, EPG-004S และ EPG-AI เท่านั้น เกตเวย์รุ่นอื่นไม่มี ฮาร์ดแวร์รองรับสิ่งนี้เลย — โหนด npu-* จะยังปรากฏใน palette แต่ทุก คำสั่งที่เรียกไปยัง engine จะล้มเหลวทันที เพราะไม่มี daemon npu-engined และไม่มี NPU ให้ประมวลผล

Edge AI Nodes นำการประมวลผลคอมพิวเตอร์วิทัศน์ (computer vision) แบบ เร่งความเร็วด้วย NPU (ตรวจจับวัตถุ) เข้ามาอยู่ในโฟลว์ Node-RED — "มีคนอยู่ ในเฟรมนี้ไหม" "หมวกนิรภัยหายไปกี่ใบ" "นับจำนวนรถบรรทุก" — โดยที่ Node-RED ไม่ต้องแตะพิกเซลหรือ NPU โดยตรงเลย ระบบนี้ประกอบด้วย 3 ส่วนที่ทำงานร่วมกัน:

ส่วนประกอบ หน้าที่
npu-engined daemon เบื้องหลัง (ทำงานอยู่แล้วบนเกตเวย์ที่รองรับ AI) ที่ดูแล NPU แหล่งภาพ/วิดีโอ และตัวเฟรมเองในหน่วยความจำที่ใช้ร่วมกัน
@epithos/node-red-contrib-npu-ai แพ็กเกจโหนด Node-RED — โหนด npu-* ทั้ง 11 ตัว นี่คือส่วนที่คุณใช้สร้างโฟลว์จริง และเป็นเนื้อหาหลักของหน้านี้
Model Forge (npu-forge) เครื่องมือออฟไลน์บนเครื่อง x86+GPU ที่แปลงโมเดลที่เทรนแล้ว (ONNX) ให้เป็นบันเดิล .npumodel สำหรับฮาร์ดแวร์นี้ — ไม่ใช่สิ่งที่รันบนตัวเกตเวย์เอง

ข้อความของ Node-RED ไม่เคยพกพิกเซลของภาพไปด้วยโดยตรง (ยกเว้น 2 กรณี เท่านั้น ดูหัวข้อ "การนำพิกเซลเข้า-ออก" ด้านล่าง) — แต่จะพก frame handle ชิ้นเล็ก ๆ ที่ชี้ไปยังพิกเซลที่อยู่ในหน่วยความจำร่วมของ engine แทน วิธีนี้ทำให้ภาพขนาดใหญ่ไม่ไปกองอยู่ใน event loop ของ Node.js และไม่ถูก เก็บไว้ในประวัติข้อความของโฟลว์

รายชื่อโหนด

กรอง palette ด้วยคำว่า "npu" จะเจอโหนดเหล่านี้ภายใต้หมวด NPU (บวกกับ config node npu-engine ซึ่งอยู่ในหมวด config):

Node-RED palette กรองด้วยคำว่า "npu"

โหนด หน้าที่
npu-engine (config) การเชื่อมต่อไปยัง daemon npu-engined หนึ่งตัว โหนดอื่นทุกตัวด้านล่างอ้างอิงถึงตัวนี้ และยังเป็นที่ตั้งค่า Labels ที่ใช้ร่วมกันด้วย
npu-model (config) ระบุว่าโหนด npu-detect ควรใช้บันเดิล .npumodel ตัวไหนที่ติดตั้งไว้
npu-camera เปิดแหล่งภาพ (โฟลเดอร์ภาพ, ไฟล์วิดีโอ หรือสตรีมที่ป้อนด้วย push) และส่ง frame handle ออกมาหนึ่งอันต่อเฟรมที่ได้รับ
npu-buffer-to-frame ลงทะเบียน Buffer ของไบต์ภาพ JPEG/PNG/raw ให้เป็น frame handle — พิกเซลที่เข้าสู่ engine จาก Node-RED
npu-detect รันการตรวจจับวัตถุบนเฟรมเทียบกับโมเดล → รายการผลตรวจจับ
npu-watch อยู่ถัดจาก npu-detect — เฝ้าดู label เดียว รายงานว่ามี/นับจำนวน/สแนปช็อต ไม่ได้เรียกโมเดลเอง
npu-draw วาดกรอบ/label ของผลตรวจจับลงบนเฟรม คืนค่าเป็น frame handle ใหม่
npu-roi ครอปพื้นที่สี่เหลี่ยมหนึ่งจุดขึ้นไปออกจากเฟรม เป็น frame handle ใหม่
npu-filter กรองรายการผลตรวจจับด้วย JS ล้วน ๆ ตาม label/score/ขนาด/อัตราส่วนภาพ — ไม่เรียก engine เลย ใช้งานได้แม้ engine จะออฟไลน์
npu-frame-to-jpeg แปลง frame handle ให้เป็นไบต์ JPEG/PNG ใน msg.payload — พิกเซลที่ออกจาก engine
npu-engine-status ตรวจสอบสถานะ/ความสามารถของ engine แล้วส่งออกเป็น msg.payload สำหรับแดชบอร์ดหรือหัวข้อ MQTT health-check

โหนดฟังก์ชันทุกตัวมี 2 เอาต์พุต — (1) ผลลัพธ์ (2) error — และแสดง สถานะการเชื่อมต่อ/สุขภาพ (fps, การดรอปเฟรม, offline/degraded) บนตัวโหนด เอง

การเชื่อมต่อกับ engine

ดับเบิลคลิกโหนด npu-* ตัวใดก็ได้ แล้วคลิกไอคอนดินสอข้าง Engine เพื่อ เปิด config node npu-engine ที่ใช้ร่วมกัน:

config node npu-engine — ส่วน Connection และ Labels

ฟิลด์ หมายเหตุ
Runtime dir ต้องตรงกับ runtime directory ของโปรเซส npu-engined ที่กำลังรันอยู่ — /run/npu-engine ตามการตั้งค่า systemd จริงที่แสดงด้านบน เว้น Control/Events socket ว่างไว้เพื่อใช้ผัง path เริ่มต้นของ engine เองใต้ไดเรกทอรีนั้น
Request timeout (ms) ระยะเวลารอผลตอบกลับของคำสั่งหนึ่งครั้งก่อนถือว่าล้มเหลว
Health timeout (ms) engine จะส่ง health ping มาทุกราว 5 วินาที หากไม่มีสัญญาณเข้ามานานเกินนี้ (ค่าเริ่มต้น 15 วินาที — พลาด 3 ครั้งติด) โหนดทุกตัวบนการเชื่อมต่อนี้จะขึ้นสถานะแดงเป็น offline แม้ socket จะยังดูเหมือนเชื่อมต่ออยู่
Test connection การตรวจสอบแบบอ่านอย่างเดียว — รายงาน backend, SoC, สุขภาพของระบบ และจำนวนโมเดลที่ลงทะเบียนอยู่ในขณะนี้

โหนดทุกตัวที่ใช้ config node npu-engine ตัวเดียวกันจะใช้คู่ socket เดียวกันซ้ำ และเชื่อมต่อใหม่อัตโนมัติ (พร้อม backoff) หาก daemon รีสตาร์ท

Labels

อยู่บน config node npu-engine เช่นกัน เนื่องจากในทางปฏิบัติมีเพียงโมเดล เดียวที่รันอยู่ในแต่ละช่วงเวลาบนบอร์ดที่มี NPU core เดียว (RK3566 — ดู หัวข้อ "มีโมเดลเดียวที่รันได้ในแต่ละช่วงเวลาบน RK3566" ด้านล่าง):

  • Use the model's built-in labels.txt (ค่าเริ่มต้น) — ชื่อ class มา จาก labels.txt ของบันเดิล .npumodel ที่โหลดอยู่โดยตรง
  • Preset list — รายการสำเร็จรูป (ปัจจุบันมีแค่ coco80 ลำดับ class มาตรฐาน 80 ชนิดของ COCO ที่ทั้งโมเดลเริ่มต้นและ YOLOv8/v11 ที่เทรนด้วย COCO ใช้ร่วมกัน)
  • Custom list — วางชื่อของคุณเอง หนึ่งชื่อต่อบรรทัดหรือคั่นด้วย comma ตามลำดับ classId

การตั้งค่านี้จะเปลี่ยน label ของทุกผลตรวจจับจากทุกโหนด npu-detect/ npu-watch บน engine นั้นตาม classId ที่เป็นตัวเลข — เป็นเพียงการ override ชั้นการแสดงผลเท่านั้น ไม่ได้เปลี่ยนสิ่งที่โมเดลตรวจจับได้จริง

การเลือกโมเดล

npu-detect ต้องรู้ว่าจะรันโมเดลไหน คุณเลือกได้สองทาง: ชี้ไปที่ config node Model (สร้างจาก config node npu-model — เลือกบันเดิลที่ติดตั้ง ไว้แล้วจาก dropdown ซึ่งมีเครื่องหมาย [loaded] กำกับตัวที่โหลดอยู่ตอนนี้ พร้อมปุ่ม Load now สำหรับ warm up โมเดลล่วงหน้าแทนที่จะรอให้คำขอ ตรวจจับครั้งแรกเป็นตัวโหลดเอง) หรือข้าม config node แล้วพิมพ์ชื่อ/เวอร์ชัน โมเดลลงในฟิลด์ or model name ตรง ๆ — ซึ่งเป็นวิธีที่ตัวอย่างโฟลว์ทั้ง สองของเกตเวย์ใช้จริง เช่น hardhat-detect หรือ yolox-s-coco

การติดตั้งบันเดิลโมเดลใหม่ก็ทำผ่านจุดเดียวกัน จากส่วน Upload new ของ config node npu-model: เลือกไฟล์ zip บันเดิล .npumodel (ผลลัพธ์จาก npu-forge convert ที่รันแยกต่างหากบนเครื่อง x86+GPU — ตัวเอดิเตอร์ ไม่ รับไฟล์ .onnx/.rknn ดิบ) แล้วคลิก Upload & install engine จะตรวจสอบความถูกต้องของบันเดิล (schema ของ manifest, checksum ของแต่ละ variant) ก่อนยอมรับ

มีโมเดลเดียวที่รันได้ในแต่ละช่วงเวลาบน RK3566

RK3566 (EPG-002S) มี NPU เพียงคอร์เดียว การโหลดโมเดลอื่นจึงจะดันโมเดลที่ โหลดอยู่ก่อนหน้าออกไปโดยอัตโนมัติเสมอ — ไม่มีทางเลี่ยงบนฮาร์ดแวร์นี้ ส่วน เกตเวย์ที่ใช้ RK3588 (EPG-004S/EPG-AI) มี NPU หลายคอร์ จึงเก็บโมเดลไว้ใน หน่วยความจำพร้อมกันได้มากกว่าหนึ่งตัว ควรวางแผนโฟลว์ให้สอดคล้องกับข้อ จำกัดนี้: หากมีโฟลว์สาธิตสองชุดบนเกตเวย์ RK3566 ตัวเดียวกัน แต่ละชุด ต้องการโมเดลคนละตัว โมเดลทั้งสองจะแย่งกันเป็นตัวที่โหลดอยู่จริง — ทุกครั้ง ที่เรียกตรวจจับจะดันโมเดลอีกตัวออกไปโดยปริยาย

npu-detect: การรันการตรวจจับ

ป้อน frame handle และ (ถ้าต้องการ) การอ้างอิงโมเดลเข้าไป:

โหนด npu-detect ตั้งค่าด้วย gateway engine และชื่อโมเดลแบบพิมพ์เอง

ฟิลด์ หมายเหตุ
Model การอ้างอิง config node npu-model ถ้าคุณตั้งค่าไว้
or model name ใช้เฉพาะเมื่อไม่ได้ตั้ง config node Model ไว้ — ชื่อเปล่า (yolox-nano-coco) หรือ name@version นี่คือวิธีที่ตัวอย่างโฟลว์ทั้งสองที่มาพร้อมเกตเวย์ใช้จริง
Threshold ค่าความมั่นใจขั้นต่ำที่จะเก็บไว้ (ใช้ค่าเริ่มต้นของโมเดลถ้าเว้นว่าง)
IoU เกณฑ์ IoU สำหรับ non-max-suppression (ใช้ค่าเริ่มต้นของโมเดลถ้าเว้นว่าง)
Classes class ID คั่นด้วย comma ที่จะจำกัดผลลัพธ์ไว้เฉพาะ; เว้นว่าง = ทั้งหมด

msg.payload จะได้ผลลัพธ์กลับมาแบบนี้:

{
  model: "[email protected]",
  inferMs: 191.9, preMs: 3.1, postMs: 1.8,
  detections: [
    { label: "Hardhat", classId: 0, score: 0.91,
      box: { x: 412, y: 130, w: 220, h: 480 },
      boxNorm: { x: 0.2146, y: 0.1204, w: 0.1146, h: 0.4444 } }
  ]
}

สามารถ override ต่อข้อความได้ผ่าน msg.npu.model/threshold/iou/ classes/roi เอาต์พุตที่ 2 จะทำงานเมื่อไม่ได้ตั้งค่าโมเดล, frame handle หมดอายุ, หรือโมเดลที่ระบุไม่ได้ติดตั้งไว้ — พร้อมสถานะเฉพาะเจาะจง ("model not found", "frame expired") แทนที่จะเป็น error ทั่วไป

npu-watch: "มี X อยู่ไหม มีกี่ตัว ขอดูภาพหน่อย"

อยู่ ถัดจาก npu-detect — ไม่ได้เรียกโมเดลเอง แค่กรองรายการผลตรวจจับ ที่มีอยู่แล้วหา label เดียว:

โหนด npu-watch ตั้งค่า Watch for label เป็น "Hardhat"

ฟิลด์ หมายเหตุ
Watch for label ข้อความ label ที่ต้องตรงเป๊ะ (ตรงกับค่าที่ตั้งค่า Labels บน engine แปลงออกมา)
Min score ความมั่นใจขั้นต่ำที่จะนับว่า "มีอยู่"
Snapshot JPEG quality คุณภาพของภาพสแนปช็อตที่สร้างขึ้น
Always snapshot สร้างสแนปช็อตทุกครั้ง แม้ label นั้นจะไม่มีอยู่ เทียบกับสร้างเฉพาะตอนที่เจอ

เอาต์พุต: { label, present, count, detections: [...เฉพาะที่ตรงกัน], snapshot: "data:image/jpeg;base64,..." }snapshot ใส่ลงใน src ของ image widget บนแดชบอร์ดได้ตรง ๆ และ msg.frame จะพก handle ใหม่ที่วาดเฉพาะกรอบที่ตรงกัน (ธรรมเนียมเดียวกับ npu-draw)

npu-detect หนึ่งตัวป้อนให้โหนด npu-watch หลายตัวพร้อมกันได้ แต่ละตัว เฝ้าดูคนละ label — ดูหัวข้อ "ตัวอย่าง: เฝ้าดูหลาย class พร้อมกัน" ด้านล่าง

การนำพิกเซลเข้า-ออก

มีเพียงสองโหนดเท่านั้นที่เป็นจุดที่ไบต์พิกเซลข้ามพรมแดนระหว่าง Node-RED กับ engine จริง ๆ:

  • npu-buffer-to-frame — ขาเข้า: รับ Buffer จาก msg.payload (JPEG/PNG ตรวจจับอัตโนมัติจาก magic bytes หรือ raw rgb24 ที่ตั้งค่า width/height ไว้) แล้วลงทะเบียนกับ engine ส่งออกเป็น frame handle
  • npu-frame-to-jpeg — ขาออก: รับ frame handle แล้วแปลงเป็นไบต์ JPEG/PNG ใน msg.payload เป็น Buffer ดิบ หรือ (ถ้าติ๊ก output a data: URL string) เป็นสตริง data:image/jpeg;base64,... พร้อมใช้กับ image widget บนแดชบอร์ด

โหนด npu-frame-to-jpeg เอาต์พุต Data URL ไม่ได้ติ๊กไว้

พรีวิวภาพในเอดิเตอร์โดยไม่ต้องมีแดชบอร์ด

เว้น output a data: URL string ไม่ติ๊กไว้ (โหนดจะส่ง Buffer ดิบออกมา) แล้วป้อนเข้าโหนด debug ธรรมดาตรง ๆ debug sidebar ของ Node-RED เองจะเรนเดอร์ Buffer ที่หน้าตาเหมือนภาพให้เป็นภาพย่อที่คลิกได้ — ไม่ต้องใช้แพ็กเกจแดชบอร์ด ไม่ต้องเพิ่มโหนดใด ๆ:

ภาพสแนปช็อตผลตรวจจับจริงที่แสดงอยู่ใน debug sidebar ของ Node-RED

การครอปและการวาด

  • npu-roi ครอปเฟรมได้สองแบบ: ครอปตามผลตรวจจับแต่ละรายการ (msg.payload.detections[].box — หนึ่งครอปต่อหนึ่งกรอบ) หรือครอปตาม สี่เหลี่ยมตำแหน่งคงที่ที่คุณตั้งค่าไว้ คืนค่าเป็น frame handle ใหม่หนึ่ง หรือหลายอัน ส่งออกเป็นข้อความเดียวที่มี array ของ handle หรือหนึ่ง ข้อความต่อหนึ่งครอปก็ได้ มีตัวเลือก padding และครอปเป็นสี่เหลี่ยมจัตุรัส ด้วย ไม่รองรับ ROI แบบหลายเหลี่ยม (polygon) — รับเฉพาะสี่เหลี่ยมผืนผ้า
  • npu-draw รับเฟรมกับรายการผลตรวจจับ แล้ววาดกรอบ/label/score ลงบน frame handle ใหม่ — ตัวต้นฉบับไม่ถูกแตะต้อง ส่งผลลัพธ์ต่อไปยัง npu-frame-to-jpeg เพื่อดูภาพจริง
  • npu-filter คือ "if แบบไม่ต้องเขียนโค้ด" สำหรับรายการผลตรวจจับ: allow-list ของ label, score ต่ำสุด/สูงสุด, ความกว้าง/สูงขั้นต่ำของกรอบ, อัตราส่วนภาพต่ำสุด/สูงสุด เป็น JavaScript ล้วน ๆ ไม่เรียก engine — ยัง ทำงานได้แม้ engine เองจะออฟไลน์

npu-engine-status

ทุกครั้งที่มีข้อความเข้า (หรือตามรอบเวลาที่ตั้งไว้) จะตรวจสอบสุขภาพ/ ความสามารถของ engine แล้วส่งออกเป็น msg.payload: { soc, npuCores, driver, runtime, apiVersion, backend, uptime, models, sources, health, error } ป้อนจากโหนด inject เพื่อใช้กับ tile บนแดชบอร์ดหรือหัวข้อ MQTT health-check

ตัวอย่าง: เฝ้าระวังความปลอดภัยด้วยการตรวจหมวกนิรภัย

ตัวอย่างโฟลว์ตรวจสอบการสวมอุปกรณ์ป้องกัน (PPE) ของเกตเวย์เอง — npu-cameranpu-detect (โมเดลตรวจหมวกนิรภัย/ไม่สวมหมวกนิรภัย) → npu-watch (เฝ้าดู "Hardhat") → พรีวิวสแนปช็อตใน debug sidebar:

ตัวอย่างโฟลว์เฝ้าระวังความปลอดภัยด้วยการตรวจหมวกนิรภัย สถานะแบบเรียลไทม์แสดงผลตรวจจับจริง

โหนด npu-camera ในที่นี้เป็นแหล่งภาพแบบ folder (โฟลเดอร์ภาพ/เฟรม วิดีโอเดินตรวจที่ engine วนอ่านไปเรื่อย ๆ) — ดูหัวข้อ "รายชื่อโหนด" ด้านบนสำหรับแหล่งภาพชนิดอื่น สถานะที่แสดงบนโหนดเป็นตัวเลขจริงแบบเรียล ไทม์จากเกตเวย์ที่กำลังทำงานอยู่: เฟรมที่สตรีมเข้ามา ผลตรวจจับต่อรอบ เวลา ที่ใช้ในการตรวจจับ และจำนวนครั้งที่เจอ label ที่เฝ้าดู

ตัวอย่าง: เฝ้าดูหลาย class พร้อมกัน

npu-detect หนึ่งตัวสามารถกระจายผลไปยังโหนด npu-watch หลายตัว แต่ละตัว เฝ้าดูคนละ label ได้ โดยไม่ต้องรันโมเดลมากกว่าหนึ่งครั้งต่อเฟรม — ตัวอย่างโฟลว์ที่สองของเกตเวย์นำโมเดลตรวจจับ COCO ทั่วไปกลับมาใช้ซ้ำ (yolox-s-coco — ไม่ต้องใช้โมเดลใหม่ เพราะ 80 class ของ COCO มีชนิดยาน พาหนะอยู่แล้ว) แล้วกระจายไปยังการเฝ้าดู 4 ทางพร้อมกัน:

npu-detect หนึ่งตัวป้อนให้ npu-draw บวกกับ npu-watch สี่ตัวพร้อมกัน (car/truck/bus/motorcycle)

npu-draw (วาดกรอบยานพาหนะทุกคันเพื่อทำสแนปช็อตภาพรวม) และโหนด npu-watch แต่ละตัวต่างอ่านรายการผลตรวจจับชุดเดียวกันจากต้นทาง — โมเดล รันเพียงครั้งเดียวต่อเฟรม ไม่ว่าจะเฝ้าดูกี่ label ก็ตาม

ข้อจำกัดที่ทราบอยู่แล้ว

สืบทอดมาจากบันทึกทางวิศวกรรมของโปรเจกต์เองอย่างตรงไปตรงมา — อย่าสมมติว่า สิ่งต่อไปนี้ใช้งานได้เพียงเพราะมีฟิลด์ให้กรอก:

  • ไม่รองรับกล้อง RTSP และ USB/V4L2 แบบสด Kind ของ npu-camera จำกัดอยู่แค่ folder (ลำดับภาพ), file (ไฟล์วิดีโอ ถอดรหัสด้วย ซอฟต์แวร์) และ push (ป้อนโดย npu-buffer-to-frame หรือผู้เรียกจาก ภายนอก) กล้องเครือข่าย/USB แบบสดเป็นแผนงานในอนาคต ยังใช้ไม่ได้ในตอนนี้
  • ตัวเลขการดรอปเฟรมของ npu-camera เป็นค่าประมาณ อนุมานจากช่องว่าง ของหมายเลขลำดับเฟรมฝั่ง client — engine ยังไม่มีตัวนับการดรอปที่แม่นยำ ต่อแหล่งภาพให้ใช้
  • frame handle จะหมดอายุ 5 วินาทีหลังถูกส่งออกมา ถ้าไม่มีอะไรมาใช้ งานมัน โฟลว์ที่ทำงานช้าเกินไปเทียบกับอัตราเฟรมของกล้องจะเจอ error E_FRAME_EXPIRED ที่เอาต์พุตที่ 2 ของโหนดที่พยายามใช้ handle ที่หมด อายุแล้ว — นี่คือพฤติกรรม backpressure ที่ตั้งใจให้เป็นแบบนี้ ไม่ใช่บั๊ก ที่ต้องหาทางเลี่ยง
  • มีโมเดลเดียวที่รันได้ในแต่ละช่วงเวลาบน RK3566 (EPG-002S) — ดูหัวข้อ "มีโมเดลเดียวที่รันได้ในแต่ละช่วงเวลาบน RK3566" ด้านบน
  • บันเดิลโมเดลเริ่มต้นที่มีมาให้ใช้ตระกูลที่มีสัญญาอนุญาตแบบเปิดกว้าง (YOLOX, PP-YOLOE, MobileNetV3, PP-OCRv4) สถาปัตยกรรม YOLOv8/v11 ก็ รองรับโดย engine หากคุณนำ weight ของตัวเองมาใช้ แต่ต้นทางมีสัญญาอนุญาต แบบ AGPL-3.0 จึงไม่ใช่ตัวเลือกเริ่มต้นที่ระบบเสนอให้ — เป็นทางเลือกด้าน สัญญาอนุญาต ไม่ใช่ข้อจำกัดทางเทคนิค

ต่อไป: MQTT broker (Aedes) หรือย้อนกลับไปที่ การเชื่อมต่อ Modbus / PLC สำหรับการเชื่อมต่อ PLC แทน คอมพิวเตอร์วิทัศน์