กราฟน้ำมัน — คู่มือการทำงานและโค้ด

แปลงข้อมูลเซนเซอร์แรงดัน → เส้น "ปริมาณน้ำมัน" ที่การเติมเป็นขั้นตั้งฉาก การใช้เป็นเส้นลาดเรียบ พร้อมตรวจจับการเติม/น้ำมันหายผิดปกติ · อธิบายทุกขั้นตอน ที่มาพารามิเตอร์ และโค้ด 3 แพลตฟอร์มไว้ในที่เดียว

log server VID 109338 · 615 records
โจทย์: เซนเซอร์ BD ให้ค่า แรงดันไฟ (voltage) ไม่ใช่ลิตร และมี 2 ปัญหา

1. กลับหัว

แรงดันต่ำ = น้ำมันเยอะ (ผกผันกับปริมาณจริง)

2. noise เยอะ

มี spike กระตุก + ช่วงเซนเซอร์หลุดปนมา

เครื่องมือ & ดาวน์โหลด

📊
กราฟ Interactive
ปรับ invert/สมูท/เกณฑ์ได้สด · มีมาร์กเติม▲/หาย▼
📋
ตารางข้อมูล
615 records ค้นหา/กรอง/เรียงได้
Code.gs
Google Apps Script (Google Sheets)
🗄️
fuel_graph.sql
T-SQL (SQL Server) มีคำอธิบายพารามิเตอร์
📥
ข้อมูลดิบ (Excel)
ต้นฉบับทั้งหมด

ภาพรวม — Pipeline 8 ขั้น

ข้อมูลดิบ 1 กรองติดเครื่อง 2 กลับหัว 3 ตัดอุปกรณ์ปิด 4 ตัด spike 5 สมูท 6-7 จุดตั้งฉาก 8 เติม/หาย เส้นน้ำมัน
#หลักการทำไมต้องทำ
1กรองเฉพาะติดเครื่อง — IO_STATUS หลักแรก = 1ตอนดับเครื่องเซนเซอร์ค่าเพี้ยน อย่าเอามาคิด
2กลับหัวfuel = BPIV − v, BPIV = min+maxให้ "เส้นสูง = น้ำมันเยอะ" · คิดจากข้อมูล → ทุกสเกล
3ตัดค่าอุปกรณ์ปิด — ต่ำกว่า LOWค่าตกวูบใกล้ 0 = เซนเซอร์หลุด ไม่ใช่น้ำมันจริง
4ตัด spike — Hampel (median + MAD)ใช้ robust stats spike จึงไม่ทำลายเกณฑ์ตัวเอง
5สมูทแบบแบ่งช่วง — ไม่เฉลี่ยข้ามจุดเปลี่ยนรักษาขอบขั้นให้คม ไม่กลายเป็นเนินลาด
6-7วาดขั้นตั้งฉาก — 2 จุดที่เวลาเดียวกันต้องใช้แกนเวลาแบบตัวเลข ไม่งั้นเส้นเฉียง
8แยกเหตุการณ์ตามทิศทาง — เติม(+)/หาย(−)สวนทางกัน ใช้เกณฑ์เดียว การใช้ปกติไม่ถูกนับ

รายละเอียดการทำงานแต่ละขั้น

0อ่านข้อมูล

ใช้ 4 คอลัมน์: VLCTL_GPS_TIME (เวลา HHMMSS), VLCTL_IO_STATUS (สถานะ), VLCTL_FUEL (AK), VLCTL_ANALOGUE_PORT1 (BD แรงดัน) · AK ค่า 999 = เสีย ตัดทิ้ง

1กรองเฉพาะติดเครื่อง

IO_STATUS เขียนเป็นเลขฐานสอง 16 หลัก ถ้าหลักแรก = 1 คือเครื่องติด ตอนดับเครื่องไม่คิดค่าน้ำมัน

// "1000000000000000" → หลักแรก = 1 = ติดเครื่อง
eng = padStart(status, 16, "0")[0] === "1"

2กลับหัว (invert)

เซนเซอร์ให้ แรงดันสูง = น้ำมันน้อย เราพลิกด้วยจุดพลิก BPIV ที่คิดจากข้อมูลเอง จึงไม่ผูกสเกล

BPIV = min(ค่าใช้งานได้) + max(ค่าใช้งานได้)
fuel = BPIV − voltage   // 1.64→ต่ำ(น้ำมันน้อย), 0.90→สูง(น้ำมันเยอะ)

3ตัดค่าอุปกรณ์ปิด

ช่วงเซนเซอร์หลุด แรงดันตกเหลือ ~0.06–0.09 (น้ำมันยังอยู่) ตัดค่าที่ต่ำกว่า LOW ทิ้ง

4ตัด spike — Hampel filter

ดูเพื่อนบ้าน 7 จุด หา median ถ้าจุดไหนห่างจาก median เกิน Sig × (1.4826 × MAD) = spike ตัดทิ้ง

ทำไม median/MAD ไม่ใช่ mean/std: median ไม่โดน spike ลากไปด้วย เกณฑ์จึงไม่เพี้ยนเพราะ spike เอง

5สมูทแบบแบ่งช่วง (segmented)

เฉลี่ยเคลื่อนที่ 15 จุด แต่ไม่ข้ามจุดเปลี่ยน — แบ่ง segment ตรงจุดที่ค่าเปลี่ยนเกิน EDGE ก่อน แล้วเฉลี่ยเฉพาะในช่วงเดียวกัน

ทำไม: ถ้าเฉลี่ยข้ามจุดเติม ขอบขั้นจะถูกลบให้มน กลายเป็นเนินลาดแทนที่จะเป็นขั้นชัด

6·7ทำให้จุดเปลี่ยนตั้งฉาก 90°

ค้างค่าเดิมไว้จนถึงเวลาที่เปลี่ยน แล้วแทรก "มุมหัก" 2 จุดที่ เวลาเดียวกัน (ค่าเก่า→ค่าใหม่) เกิดเส้นตั้งฉาก

เคล็ดลับ: ต้องใช้แกนเวลาแบบ ตัวเลข (linear) ไม่ใช่แบบช่อง (category) จุด 2 จุดถึงวางที่เวลาเดียวกันได้

ขั้น 8 — จับ "เติมน้ำมัน" & "น้ำมันหายผิดปกติ"

🔄 สองเหตุการณ์เป็น "กระจกเงา" กัน — เกณฑ์เดียว ต่างแค่ทิศทาง

// คิดเป็นปริมาณน้ำมัน · Δ = ค่าใหม่ − ค่าเก่า ที่จุดเปลี่ยน
Δ > +EDGE   →  ⛽ เติมน้ำมัน        ขึ้นฮวบ
Δ < −EDGE   →  ⚠️ น้ำมันหายผิดปกติ  ลงฮวบ · รั่ว/ถูกดูด

คั่นจากการใช้ปกติอย่างไร: การใช้น้ำมันปกติลดช้าๆ (~14% ของช่วงต่อจุด, ต่ำกว่า EDGE) จึงไม่ถูกนับ · เฉพาะ ลงเร็วผิดธรรมชาติ เท่านั้นที่เป็น "น้ำมันหาย" · บนกราฟปัก ▲ เขียว = เติม / ▼ แดง = หาย

ที่มาของค่าพารามิเตอร์ทุกตัว

ค่าที่ hardcode ไม่ได้สุ่มมา — แบ่งเป็น 3 ประเภท: คงที่ทางสถิติ, จากโครงสร้างข้อมูล, และ derive จากช่วงข้อมูลจริง

ค่าประเภทที่มา / เหตุผล (อิงข้อมูลจริง)
1.4826คงที่สถิติแปลง MAD → σ สำหรับข้อมูลปกติ (=1/Φ⁻¹(0.75)) ทำให้เกณฑ์เทียบเป็น "กี่ σ" — ห้ามเปลี่ยน
Sig = 3คงที่สถิติกฎ 3 ซิกมา: ห่าง median เกิน 3σ = outlier (~99.7%) · มาตรฐาน Hampel
HampelWin = 7อิงอัตรา log±3 ≈ 3–7 นาที (log ~1จุด/นาที) สั้นพอที่ระดับน้ำมันคงที่ → ค่าที่เบี่ยง = spike
SmoothWin = 15อิงอัตรา log±7 ≈ 15 นาที กลบ jitter รายนาที (~±0.05V) แต่คงเทรนด์การใช้ทั้งวัน
LOW = 0.4×medianderive จากข้อมูลอุปกรณ์ปิดกองใกล้ 0 (0.06–0.09) · ใช้งานจริงกอง ~1.0–1.5 · 40% ตกกลาง gap → LOW=0.596 แยกสะอาด
EDGE = 0.2×spanderive จากข้อมูล ⭐ใช้ปกติเปลี่ยน ≤14% ของ span · เติมจริง 28–86% · ตั้ง 20% = กึ่งกลาง gap → คั่นได้มีระยะเผื่อ
floor = 0.02×spanderive จากข้อมูลกัน Hampel ตัดแรงตอนข้อมูลนิ่ง (MAD≈0 → เกณฑ์≈0)
BPIV = min+maxderive จากข้อมูลจุดพลิกกลับหัว คิดจากข้อมูลเอง → รองรับทุกสเกล
999 / bit-16โครงสร้างข้อมูล999 = sentinel AK เสีย · IO_STATUS เป็น bit-field 16 บิต บิตแรก = สตาร์ท/ดับ

รายละเอียดเต็มพร้อมแท็ก [P1]..[P14] กำกับในโค้ด อยู่ในไฟล์ fuel_graph.sql

รองรับเซนเซอร์ทุกสเกล 🎯

เพราะเกณฑ์ทั้งหมดคำนวณจากช่วงข้อมูลจริง ทดสอบกับข้อมูลจริงคูณสเกลต่างๆ ได้ผลเหมือนกันเป๊ะทุกสเกล:

สเกลช่วงค่า BDLOWEDGEตัดปิดspikeจับจุดเติม
×1 (เดิม)0.83–1.690.600.1769342 ✓
×6 (0–10)4.98–10.143.581.0369342 ✓
×0.50.41–0.840.300.0969342 ✓

ทำไว้ 3 แพลตฟอร์ม (ตรรกะเดียวกัน)

แพลตฟอร์มไฟล์จุดเด่น / วิธีใช้
HTML + Chart.jsfuel_graph.htmlเปิดในเบราว์เซอร์ ปรับเกณฑ์ได้สด เห็นผลทันที
Google SheetsCode.gsวางใน Apps Script → เมนู ⛽ กราฟน้ำมัน → สร้างชีต + กราฟ + ชีต FuelEvents
SQL Serverfuel_graph.sqlstored procedure usp_ProcessFuelGraph · set-based · คืนเส้นกราฟ + เหตุการณ์

การแปลง JS → SQL (set-based)

JSเทคนิค T-SQL
loop + indexROW_NUMBER()
median()PERCENTILE_CONT(0.5) OVER (PARTITION BY..)
window ±kself-join Seq BETWEEN t.Seq−k AND t.Seq+k
arr[i]−arr[i−1]LAG()
running counterSUM(..) OVER (ORDER BY.. ROWS UNBOUNDED PRECEDING)

3 กับดักที่ dev ต้องรู้

ไฟล์ทั้งหมด: fuel_graph.html (กราฟ) · data.html (ตาราง) · Code.gs (Google Sheets) · fuel_graph.sql (SQL Server) · Excel ต้นฉบับ
log server VID 109338 · ประมวลผล 615 records