บทเรียน 2.3 — เสียงและหลายเซนเซอร์บนเส้นเวลาเดียว: PDM 16 kHz ประทับเวลา และ jitter

มัดสองสัญญาณให้เป็น dataset เดียว

โมดูล 2 — เก็บข้อมูลจากเซนเซอร์ (DAQ)

โมดูล 2 · Pillar 1 — DAQ (ต่อจากบทเรียน 2.1–2.2)

คาถาประจำบทเรียน: "ก่อนจะฝึกโมเดลหลายเซนเซอร์ได้ ต้องเก็บสัญญาณทุกตัวให้อยู่บนเวลาเดียวกันก่อน"

MicroPython บนบอร์ด BENTO (PSoC Edge · ไมโครโฟน PDM + IMU BMI270)

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก Edge AI Developer (รศ.วิรุฬห์ ศรีบริรักษ์, BUU) · CC BY-NC 4.0

เปิดบทเรียนด้วยของจริงก่อน

เหมือนทุกบทเรียน เราเริ่มแบบ กลับด้าน — รันของที่ทำงานได้ก่อน แล้วค่อยแกะ วันนี้ของจริงคือ "โต๊ะเก็บ dataset" ที่อัดเสียงและการเคลื่อนไหวพร้อมกัน

รันตัวเก็บก่อน VU + อัด WAV แกะดูข้างใน PDM · เส้นเวลา เติม/แก้เอง 4 ก้าวในลูป ได้ dataset ฐานของ Training

เราจะรัน VU meter กับตัวอัด WAV ที่มีให้แล้ว (ตัวอย่าง 06/07) เพื่อ "เห็นเสียงกลายเป็นตัวเลข" ก่อน แล้วค่อยเอาสองความสามารถนี้ — อ่านเสียง + อ่าน IMU — มามัดรวมบนเส้นเวลาเดียว

ชุดบทเรียนนี้ยังไม่ต้อง train อะไร ขอแค่ได้ไฟล์ dataset ที่ทุกแถวมี "ทั้งเสียงและการเคลื่อนไหวของช่วงเวลาเดียวกัน" ก็ถือว่าถึงเป้าแล้ว

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก Edge AI Developer (รศ.วิรุฬห์ ศรีบริรักษ์, BUU) · CC BY-NC 4.0

เป้าหมายของชุดบทเรียนนี้

จบชุดบทเรียนนี้เราจะเดินครบ 4 เรื่อง แล้วปิดท้ายด้วยการเก็บ dataset จริง:

  1. ไมโครโฟน PDM อ่านเสียงยังไง — จาก bitstream เป็น PCM เป็นระดับ dBFS
  2. เส้นเวลาร่วม (shared timeline) — ทำไมสองเซนเซอร์ต้องแชร์ t_ms เดียวกัน
  3. การอ่านหลายเซนเซอร์ในลูปเดียว — IMU + MIC ต่อแถว บนอัตราที่คุมได้
  4. รูปแบบ dataset ที่เอาไป train ต่อได้ (CSV ต่อแถว + เสียงดิบเป็น WAV)
  5. ลงมือ: เติม s05_multicapture.py ให้เก็บ ≥2 เซนเซอร์บนเวลาเดียว ลงไฟล์

ปลายทางของวันนี้: กดปุ่ม label ทำท่า + ส่งเสียงพร้อมกัน แล้วได้ไฟล์ /multicapture.csv ที่ทุกแถวมี IMU และระดับเสียงบน t_ms เดียวกัน

วันนี้เราเน้น "เก็บให้ตรงเวลา" ส่วนการแปลงสัญญาณเป็น feature (FFT / spectrogram) เก็บไว้เป็นเป้าหมายของ โมดูล 4 (Analysis)

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก Edge AI Developer (รศ.วิรุฬห์ ศรีบริรักษ์, BUU) · CC BY-NC 4.0

ย้อนชุดบทเรียนก่อนหน้า — DAQ I เราเก็บ IMU ได้แล้ว

บทเรียน 2.1–2.2 เราสร้าง s04_daq_logger.py เก็บ IMU 6 แกนลง CSV ทีละ label ได้แล้ว นั่นคือ เซนเซอร์เดียว บนเส้นเวลาเดียว — วันนี้เราต่อยอดเป็น สองเซนเซอร์

บทเรียน 2.1–2.2 — DAQ I IMU -> CSV (label,ax..gz) เซนเซอร์เดียว บทเรียน 2.3–2.4 — DAQ II (วันนี้) IMU + MIC -> CSV (+t_ms,db) สองเซนเซอร์ เวลาเดียว เพิ่มเสียง + เส้นเวลา
  • โครงเดิมยังอยู่: ปุ่ม label · ฟังก์ชัน record() · เขียน CSV ทีละแถว · finally เก็บกวาด
  • ของใหม่ที่เพิ่มเข้ามามีแค่สาม: เปิดไมโครโฟน, อ่านเสียงหนึ่งเฟรมต่อแถว, ประทับ t_ms ให้ทุกแถว

ถ้าบทเรียน 2.1–2.2 เข้าหัวแล้ว ชุดบทเรียนนี้คือการ "บวกอีกหนึ่งเซนเซอร์" — เห็นชัดว่า pattern การเก็บข้อมูลขยายได้เรื่อยๆ ไม่ใช่เขียนใหม่ทั้งหมด

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก Edge AI Developer (รศ.วิรุฬห์ ศรีบริรักษ์, BUU) · CC BY-NC 4.0

ทำไมต้องเก็บมากกว่าหนึ่งสัญญาณ

โมเดล Edge AI ที่ทรงพลังหลายตัวไม่ได้ดูสัญญาณเดียว มันดู หลายสัญญาณพร้อมกัน แล้วตัดสินใจจากภาพรวม — แต่จะทำแบบนั้นได้ dataset ต้องเก็บมาให้ตรงเวลากันตั้งแต่แรก

  • เสียง + การเคลื่อนไหว — เช่น แยก "ไอจริง" จาก "เคาะโต๊ะ" ต้องดูทั้งเสียงและว่าตัวคนขยับไหม
  • หลายเซนเซอร์ยืนยันกัน — เรดาร์เห็นคนเข้ามา + เสียงเปลี่ยน = มั่นใจกว่าใช้ตัวเดียว
  • บริบท — ระดับเสียงพื้นหลังช่วยให้โมเดลรู้ว่าตอนนี้เงียบหรือมีเสียงรบกวน

หัวใจของชุดบทเรียน: ถ้าเก็บเสียงกับ IMU มา "คนละไฟล์ คนละเวลา" เราจะจับคู่มันทีหลังไม่ได้เลย — ต้องมัดด้วยเส้นเวลาเดียวตั้งแต่ตอนเก็บ

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก Edge AI Developer (รศ.วิรุฬห์ ศรีบริรักษ์, BUU) · CC BY-NC 4.0

เสียงคือสนามใหญ่ของ Edge AI

จำเมนู 6 โมเดลชุดบทเรียนแรกได้ไหม — 4 ใน 6 ใช้ไมโครโฟน (Baby Cry / Cough / Alarm / Siren) งานเสียงจึงเป็นสนามใหญ่ และการเก็บเสียงเป็นทักษะ DAQ ที่ขาดไม่ได้

เสียง (MIC) Baby Cry · Cough Alarm · Siren การเคลื่อนไหว (IMU) Motion: idle/circle/shaking เรดาร์ (RADAR) Push Detection มี-ไม่มีคน
  • เก็บเสียงดิบได้ = เปิดประตูสู่ dataset ของทุกโมเดลเสียง (โมดูล 5 (Training) เราจะฝึกเอง)
  • ชุดบทเรียนนี้เราจับคู่เสียงกับ IMU เพื่อฝึกทักษะ "sync หลายเซนเซอร์" ซึ่งใช้ได้กับทุกคู่เซนเซอร์

เก็บเสียงบนอุปกรณ์แล้ววิเคราะห์ในเครื่อง = ความเป็นส่วนตัว เสียงไม่ต้องออกไปคลาวด์ — นี่คือจุดขายของ Edge AI ที่เราเห็นตั้งแต่บทเรียน 1.1–1.3

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก Edge AI Developer (รศ.วิรุฬห์ ศรีบริรักษ์, BUU) · CC BY-NC 4.0

ไมโครโฟน PDM ทำงานยังไง

ไมโครโฟนบนบอร์ดเป็นแบบ PDM (Pulse-Density Modulation) มันส่งบิต 0/1 ความเร็วสูงมา ฮาร์ดแวร์แปลงเป็นตัวอย่างเสียง PCM (ตัวเลข 16-bit) ให้เราอ่านเป็น array ได้

ไมค์ PDM บิต 0/1 เร็วมาก ฮาร์ดแวร์ PDM_PCM แปลงเป็น PCM 16-bit buf (array 'h') ตัวอย่างเสียง 16000/s readinto()
from machine import PDM_PCM
pdm = PDM_PCM(0, sck="P8_5", data="P8_6", sample_rate=16000)
buf = array.array("h", (0 for _ in range(512)))   # 'h' = 16-bit signed
pdm.readinto(buf)                                  # เติม buf ด้วยตัวอย่างเสียงชุดใหม่

sample_rate=16000 คือ 16000 ตัวอย่างต่อวินาที ซึ่งเป็นอัตรามาตรฐานของงานเสียงพูด/เสียงสิ่งแวดล้อม — เพียงพอสำหรับโมเดลเสียงส่วนใหญ่ในคอร์ส

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก Edge AI Developer (รศ.วิรุฬห์ ศรีบริรักษ์, BUU) · CC BY-NC 4.0

อ่านเสียงหนึ่งเฟรม — จากคลื่นเป็นตัวเลขเดียว

buf เต็มไปด้วยตัวเลขหลายร้อยตัว เราย่อมันเป็น ค่าเดียวต่อเฟรม ที่บอก "ตอนนี้ดังแค่ไหน" — วิธีมาตรฐานคือหา RMS แล้วแปลงเป็น dBFS

def dbfs(chunk):
    acc = 0
    for s in chunk:
        acc += s * s               # กำลังสองของแต่ละตัวอย่าง
    rms = math.sqrt(acc / len(chunk))
    return 20 * math.log10(rms / 32768) if rms > 0 else -96.0
  • RMS (root-mean-square) = ขนาดเฉลี่ยของคลื่นในเฟรมนั้น สะท้อน "พลังงานเสียง"
  • dBFS = เทียบ RMS กับค่าสูงสุดที่ 16-bit ทำได้ (32768) แล้วเป็นเดซิเบล 0 = ดังสุด, ยิ่งลบยิ่งเบา
  • ค่าเดียวนี้แหละที่เราจะเก็บลง CSV เป็นคอลัมน์ db ต่อแถว

ทำไมไม่เก็บทุกตัวอย่างลง CSV? เพราะ 16000 ค่า/วินาที จะใหญ่มหาศาล — ในชุดบทเรียนนี้เราเก็บ สรุประดับเสียง ต่อแถวไว้ใน CSV ส่วนเสียงดิบเก็บแยกเป็น WAV (ในฉบับเต็ม)

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก Edge AI Developer (รศ.วิรุฬห์ ศรีบริรักษ์, BUU) · CC BY-NC 4.0

คณิตของการสุ่มเสียง — 16 kHz หมายความว่าอะไร

ไมค์ PDM ถูกแปลงเป็น PCM ที่ อัตราสุ่ม (sample rate) — sample_rate=16000 แปลว่าเก็บ 16,000 ตัวอย่างต่อวินาที ระยะห่างระหว่างสองตัวอย่างจึงคงที่:

Ts=1fs=116,000 Hz=62.5 μsT_s = \frac{1}{f_s} = \frac{1}{16{,}000\ \text{Hz}} = 62.5\ \mu s

  • fsf_s = อัตราสุ่ม (samples per second) — ที่นี่คือ 16,000
  • TsT_s = ระยะเวลาระหว่างสองตัวอย่างที่ติดกัน ยิ่ง fsf_s สูง ตัวอย่างยิ่งถี่ เสียงยิ่งละเอียด
  • mono = ช่องสัญญาณเดียว หนึ่งตัวอย่าง = หนึ่งตัวเลข (ไม่ใช่คู่ซ้าย/ขวาแบบสเตอริโอ) จึงตรงกับ array.array("h", ...) ที่เก็บเลข 16-bit เรียงกันตรงๆ

ทำไมเลือก 16 kHz? เสียงพูดและเสียงสิ่งแวดล้อมส่วนใหญ่มีพลังงานต่ำกว่า 8 kHz ตามกฎ Nyquist อัตราสุ่มต้อง ≥\geq สองเท่าของความถี่สูงสุดที่อยากเก็บ 16 kHz จึงพอดีกับงานเสียงในคอร์ส และประหยัดหน่วยความจำกว่า 44.1 kHz ของเพลง

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก Edge AI Developer (รศ.วิรุฬห์ ศรีบริรักษ์, BUU) · CC BY-NC 4.0

หนึ่งเฟรมเสียงกินเวลาเท่าไร — N ตัวอย่าง

เราไม่ได้อ่านทีละตัวอย่าง แต่อ่านทีละก้อน buf ขนาด N=N = CHUNK ตัวอย่าง ก้อนหนึ่งจึงครอบคลุมช่วงเวลา:

twin=Nfst_{win} = \frac{N}{f_s}

ลองแทนค่า CHUNK = 512 (จากโค้ด range(512)):

twin=51216,000=0.032 s=32 mst_{win} = \frac{512}{16{,}000} = 0.032\ \text{s} = 32\ \text{ms}

  • NN = จำนวนตัวอย่างในหนึ่งเฟรม (ขนาด buffer) — คือ CHUNK ที่ส่งเข้า array.array
  • twint_{win} = "หน้าต่างเวลา" ที่ค่า db หนึ่งค่าเป็นตัวแทน — db แถวนี้คือพลังงานเสียงเฉลี่ยของ 32 ms ช่วงนี้
  • ยิ่ง CHUNK ใหญ่ เฟรมยิ่งยาว readinto() ยิ่งรอนานขึ้น จึงเพิ่ม jitter ในลูป ~20 ms ของเรา

นี่คือเหตุผลเบื้องหลังคำใบ้ "ลด CHUNK เพื่อลด jitter": ถ้า twint_{win} ยาวใกล้หรือเกินจังหวะแถว (RATE_MS = 20) เฟรมเสียงจะกลืนเวลาจนแถวห่างเกินเป้า — และเพราะเราเก็บ t_ms จริงไว้ทุกแถว เราจึง "เห็น" อาการนี้ย้อนหลังได้

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก Edge AI Developer (รศ.วิรุฬห์ ศรีบริรักษ์, BUU) · CC BY-NC 4.0

ของเจ๋งก่อน (1) — VU meter ที่มีให้แล้ว

ก่อนเขียนเอง รันตัวอย่าง 06_mic_level_meter.py ให้เห็นเสียงกลายเป็นแถบระดับสดๆ — นี่คือครึ่งแรกของสิ่งที่เราจะเอาไปมัดรวม

pdm.readinto(buf)
rms = math.sqrt(sum(s*s for s in buf) / len(buf))
db = 20 * math.log10(rms / 32768) if rms > 0 else -96.0
bar.value(max(0, int(db + 60)))         # แกน 0..60 = -60..0 dBFS
seg.text("%d" % int(db))
  • ลองพูด / ปรบมือ ใส่ไมค์ แล้วดูแถบขยับ + ตัวเลข dBFS วิ่ง — เสียงเป็นตัวเลขจริงๆ แล้ว
  • โครงเดียวกับที่เราจะใช้: readinto -> dbfs -> เอาค่าไปใช้ต่อ

"รันของที่ทำงานได้ก่อน" — พอเห็นแถบ VU ขยับตามเสียงตัวเอง คุณจะเข้าใจ dbfs() โดยไม่ต้องท่องสูตร แล้วค่อยเอาไปประกอบเป็นตัวเก็บ dataset

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก Edge AI Developer (รศ.วิรุฬห์ ศรีบริรักษ์, BUU) · CC BY-NC 4.0

ของเจ๋งก่อน (2) — อัดเสียงเป็น WAV

ตัวอย่าง 07_mic_record_wav.py เก็บ เสียงดิบทั้งก้อน ลงไฟล์ .wav — นี่คืออีกครึ่งที่ฉบับเต็มของเราจะใช้เก็บคลื่นเสียงคู่กับ CSV

with open("/rec.wav", "wb") as f:
    f.write(wav_header(total, RATE))     # หัวไฟล์ WAV
    while written < total:
        pdm.readinto(buf)
        f.write(buf)                     # ต่อท้ายเสียงดิบทีละก้อน
        written += CHUNK
  • WAV = หัวไฟล์สั้นๆ บอกอัตรา/ช่องสัญญาณ แล้วตามด้วยตัวอย่างเสียงดิบตรงๆ
  • ดึงไฟล์ออกจากบอร์ดด้วย BENTO IDE (file transfer) หรือ mpremote เอาไปฟัง/วิเคราะห์บน PC ได้

สองตัวอย่างนี้คือ "วัตถุดิบ" ของชุดบทเรียน: ตัวหนึ่งย่อเสียงเป็นตัวเลข (VU) อีกตัวเก็บเสียงดิบ (WAV) เราจะหยิบทั้งคู่มาต่อกับ IMU บนเส้นเวลาเดียว

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก Edge AI Developer (รศ.วิรุฬห์ ศรีบริรักษ์, BUU) · CC BY-NC 4.0

ปัญหาการ sync — สองสัญญาณคนละจังหวะ

ถ้าเก็บเสียงกับ IMU แยกกัน แต่ละตัวมีจังหวะของมันเอง เวลาเอามารวม เราจะไม่รู้ว่า "ค่าเสียงตัวนี้เกิดพร้อมกับการขยับตัวไหน" — ข้อมูลจับคู่ไม่ได้

ไม่ sync — เก็บแยกไฟล์ IMU MIC จุดไม่ตรงกัน -> จับคู่ "เสียงนี้คู่กับท่านี้" ไม่ได้
  • ทางแก้ที่ ผิด: เก็บสองไฟล์แยกแล้วหวังว่าจะจับคู่ตอนหลังด้วยลำดับ — พลาดง่ายมากเมื่ออัตราต่างกัน
  • ทางแก้ที่ ถูก: อ่านทั้งสองเซนเซอร์ใน ลูปเดียว รอบเดียว แล้วเขียนลง แถวเดียว พร้อมเวลาประทับร่วม

นี่เป็นปัญหาคลาสสิกของงาน DAQ: ไม่ใช่แค่ "เก็บให้ได้" แต่ "เก็บให้ตรงเวลา" ต่างหากที่ทำให้ dataset ใช้งานได้จริง

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก Edge AI Developer (รศ.วิรุฬห์ ศรีบริรักษ์, BUU) · CC BY-NC 4.0

ทางออก — เส้นเวลาเดียว (shared timeline)

วิธีของเราเรียบง่ายและได้ผล: ในหนึ่งรอบของลูป อ่าน IMU และ MIC ติดกัน แล้วเขียนหนึ่งแถวที่ทั้งคู่แชร์ค่าเวลา t_ms เดียวกัน

sync — หนึ่งรอบ = หนึ่งแถว = หนึ่ง t_ms t=0 t=20 t=40 t=60 IMU MIC แต่ละหลัก = อ่านสองเซนเซอร์แล้วเขียนแถวเดียว บน t_ms ร่วม
  • ผล: ทุกแถวใน CSV บอกได้แน่ว่า "ค่าเสียง db กับค่า IMU นี้เกิดในช่วงเวลาเดียวกัน (t_ms)"
  • ทีหลังเราจะจับคู่/ตัดหน้าต่าง/ลากกราฟตามเวลาได้ทันที เพราะทุกอย่างอ้างเส้นเวลาเดียว

นี่คือแก่นของชุดบทเรียน ถ้าจำได้เรื่องเดียว ให้จำว่า "อ่านพร้อมกัน เขียนแถวเดียว ประทับเวลาร่วม"

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก Edge AI Developer (รศ.วิรุฬห์ ศรีบริรักษ์, BUU) · CC BY-NC 4.0

ประทับเวลา — ticks_ms / ticks_diff

MicroPython ให้นาฬิกามิลลิวินาทีมา เราจับเวลาเริ่มไว้ที่ t0 แล้วทุกแถวเก็บ "ผ่านไปกี่ ms จาก t0"

t0 = time.ticks_ms()                        # จุดศูนย์ของชุดนี้
# ... ในลูป:
t_ms = time.ticks_diff(time.ticks_ms(), t0) # เวลาผ่านไปเทียบ t0
  • ใช้ time.ticks_diff(a, b) แทนการลบตรงๆ (a - b) เพราะตัวนับ ms มี wrap-around (วนกลับ 0) ticks_diff จัดการให้ถูก
  • t_ms เริ่มที่ ~0 ในแถวแรก แล้วเพิ่มขึ้นเรื่อยๆ — เป็นแกนเวลาของทั้งชุด
  • ทุกเซนเซอร์ในรอบเดียวกันใช้ t_ms ค่าเดียวกัน จึงถือว่า "เวลาเดียวกัน"

อย่าใช้ time.time() หรือเลขรอบมานับเวลา — ticks_ms/ticks_diff คือคู่มาตรฐานของ MicroPython สำหรับวัดช่วงเวลาสั้นๆ อย่างแม่นและปลอดภัยจาก wrap

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก Edge AI Developer (รศ.วิรุฬห์ ศรีบริรักษ์, BUU) · CC BY-NC 4.0

อัตราสุ่มกับความคลาดเคลื่อน (jitter)

เราตั้งใจให้แต่ละแถวห่างกัน RATE_MS = 20 (50 Hz) ด้วย time.sleep_ms(20) แต่ในความจริงงานอ่านเสียง/เขียนไฟล์ก็กินเวลา ทำให้ช่วงจริงเพี้ยนได้เล็กน้อย — ค่านี้เรียก jitter

~20 ms ~20 ms 28 ms (ช้าไป) jitter = ช่วงจริงเบี่ยงจากเป้า — เก็บ t_ms ไว้ เราจึงตรวจได้ว่าเพี้ยนแค่ไหน
  • เพราะเราเก็บ t_ms จริงไว้ทุกแถว เราจึง ตรวจสอบย้อนหลังได้ ว่าช่วงจริงใกล้ 20 ms แค่ไหน
  • ฉบับเต็มวัด dt เฉลี่ยจริงและ jitter (ช่องว่างที่ใหญ่สุด) แล้วโชว์บนจอ — ถ้า jitter สูงมาก แปลว่า dataset ช่วงนั้นเชื่อถือไม่ได้
  • วิธีลด jitter: ลด CHUNK เสียง, ลดงานในลูป, อย่าพิมพ์ console ถี่

บทเรียน DAQ: การเก็บ "เวลาจริง" ไว้ ไม่ใช่แค่สมมติว่าตรง 20 ms เป๊ะ ทำให้เราตรวจคุณภาพ dataset ได้ — วิศวกรที่ดีไม่เชื่อ ตรวจเสมอ

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก Edge AI Developer (รศ.วิรุฬห์ ศรีบริรักษ์, BUU) · CC BY-NC 4.0

รูปแบบไฟล์ dataset — CSV schema

ไฟล์ที่เราจะได้หน้าตาแบบนี้ หนึ่งบรรทัดต่อหนึ่งแถวเวลา ทุกคอลัมน์ของแถวเดียวกันอ้าง t_ms เดียวกัน

t_ms,label,ax,ay,az,gx,gy,gz,db
0,shaking,0.02,-0.98,0.11,1.4,-0.7,0.3,-41.2
20,shaking,0.31,-0.88,0.25,8.9,-3.1,1.2,-38.5
40,shaking,-0.12,-1.02,0.07,-5.2,2.4,-0.9,-37.9
คอลัมน์ มาจาก ความหมาย
t_ms ticks_diff เส้นเวลาร่วม (ms จากต้นชุด)
label ปุ่มที่กด ท่า/คลาสของชุดนี้ (idle/circle/shaking)
ax..gz bmi270.motion() เซนเซอร์ 1 — accel 3 แกน + gyro 3 แกน
db dbfs(buf) เซนเซอร์ 2 — ระดับเสียงของเฟรมนั้น

สังเกตว่า label อยู่ในทุกแถว — เราเก็บไปพร้อมข้อมูลเลย จะได้ไม่ต้องมานั่งใส่ label ทีหลัง นี่คือ dataset ที่ "พร้อม train" ตั้งแต่ออกจากบอร์ด

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก Edge AI Developer (รศ.วิรุฬห์ ศรีบริรักษ์, BUU) · CC BY-NC 4.0