บทเรียน 3.1 — จากตัวเลขดิบสู่ปริมาณทางฟิสิกส์: มุมเอียง พลังงาน ความสูง และ dBFS

คณิต & ฟิสิกส์: แปลงตัวเลขดิบให้พูดภาษาคน

โมดูล 3 — ประมวลผลด้วยคณิตศาสตร์และฟิสิกส์

โมดูล 3 (Processing, Pillar 2) · ขั้นที่ 2 ของวงจรชีวิตข้อมูล

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

MicroPython บนบอร์ด BENTO (PSoC Edge · Cortex-M55 + Ethos-U55 NPU)

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

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

เหมือนทุกบทเรียน เราเริ่มแบบ กลับด้าน — รันของที่ทำงานได้ก่อน แล้วค่อยแกะว่ามันคำนวณอะไรอยู่ข้างใน

รัน Physics Lab 4 ปริมาณสดๆ แกะดูสูตร raw -> derived เติมสูตรเอง 4 บรรทัดคณิต remix ต่อ เกจของคุณ

เปิด s06_physics_viz_full.py รันก่อน เลือกปริมาณใน dropdown แล้วเอียงบอร์ด/เขย่า/ยกขึ้น-ลง — จอโชว์ตัวเลขที่คำนวณเปลี่ยนตามจริง นั่นแหละของที่เราจะแกะแล้วสร้างเองในชุดบทเรียนนี้

ชุดบทเรียนนี้ไม่ต้องท่องสูตรฟิสิกส์มาก่อน ขอแค่ได้เห็นว่า "ตัวเลขจากเซนเซอร์ กลายเป็นมุม/ความสูง/ระดับเสียงได้ยังไง" แล้วเริ่มอยากรู้ว่าสูตรนั้นหน้าตาเป็นแบบไหน

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

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

จบชุดบทเรียนนี้เราจะเดินครบ แล้วปิดท้ายด้วยเกจฟิสิกส์ที่คำนวณเอง:

  1. ทำไมต้อง Processing — ตัวเลขดิบจากเซนเซอร์ยัง "ไม่พูดภาษาคน" เราต้องแปลงก่อน
  2. รูปแบบหัวใจ ของทั้งขั้นนี้: raw → derived → viz (อ่านดิบ → คำนวณ → แสดงผล)
  3. สี่ปริมาณฟิสิกส์: มุมเอียง (dsp.tilt) · พลังงาน (energy) · ความสูง (dsp.altitude) · ระดับเสียง (dBFS)
  4. เลือก widget ให้เข้ากับข้อมูล — Arc / Bar / Seg7 / Chart อย่างไหนเหมาะกับอะไร
  5. ลงมือ: เติม 4 สูตรแปลง ใน s06_physics_viz.py แล้วดูเกจขยับตามการเคลื่อนไหวจริง

ปลายทางของวันนี้: เลือกปริมาณใน dropdown แล้วขยับบอร์ด — ค่าที่ เราคำนวณเอง โชว์เป็นตัวเลข + แถบ + กราฟ

วันนี้เน้น "อ่านดิบเป็น + แปลงเป็น + แสดงเป็น" ส่วนการกรองสัญญาณรบกวนและ FFT เก็บไว้ โมดูล 4 (Analysis)

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

ชุดบทเรียนนี้อยู่ตรงไหนของวงจร

จำวงจรชีวิตข้อมูล 5 ขั้นจากบทเรียน 1.1–1.3 ได้ไหม — ชุดบทเรียนนี้เราเดินถึง ขั้นที่ 2: Processing

1 · DAQ อ่านค่าดิบ (2.1–2.2) 2 · Processing คณิต+ฟิสิกส์ ชุดบทเรียนนี้ (วันนี้) 3 · Analysis DSP · FFT (4.1–4.2+) 4 · Training ฝึกโมเดล 5 · Apps อนุมาน + action DAQ เก็บค่ามาให้ (ชุดบทเรียนก่อนหน้า) → Processing แปลงให้มีความหมาย (ชุดบทเรียนนี้) → Analysis ขุดลึกต่อ (ชุดบทเรียนถัด ๆ ไป)

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

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

ตัวเลขดิบยังไม่พูดภาษาคน

ลองดูของจริง sensors.bmi270.motion() คืนอะไรมา:

ax, ay, az, gx, gy, gz = sensors.bmi270.motion()
# เช่น: (0.20, -6.97, 6.87, 1.1, -0.4, 0.3)
  • ตัวเลขชุดนี้อ่านแล้วบอกอะไรไม่ได้เลย — บอร์ดเอียงกี่องศา? นิ่งหรือขยับ?
  • แต่ถ้าเอา ax, ay, az ไปเข้าสูตรตรีโกณ จะได้ มุมเอียง ที่คนเข้าใจทันที
  • นี่คืองานของขั้น Processing: เอาตัวเลขดิบมา บีบให้เป็นปริมาณที่มีความหมาย
ค่าดิบ: (0.20, -6.97, 6.87) อ่านแล้วไม่รู้เรื่อง roll ≈ −45 องศา คนเข้าใจทันที dsp.tilt

คำถามของชุดบทเรียนนี้: "ตัวเลขจากเซนเซอร์นี้ แปลงเป็นปริมาณที่มีความหมายได้ยังไง?" — คำตอบคือคณิตกับฟิสิกส์ไม่กี่บรรทัด

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

รูปแบบหัวใจ — raw → derived → viz

ทุกปริมาณที่เราทำวันนี้เดินตามสามจังหวะเดียวกันเป๊ะ จำโครงนี้ให้ขึ้นใจ เพราะมันคือ MVP ของชุดบทเรียน (MVP ของบทเรียน 3.1–3.2)

1 · raw อ่านค่าดิบจากเซนเซอร์ 2 · derived คำนวณเป็นปริมาณจริง 3 · viz แสดงบนจอ (เกจ/แถบ/กราฟ) สูตรที่คุณเติมคือจังหวะกลาง (derived) — สองข้างเราเตรียมไว้ให้แล้ว

MVP ของบทเรียน 3.1–3.2: สัญญาณดิบ → ปริมาณที่คำนวณได้ → แสดงเห็นบนจอ ครบวง สำหรับอย่างน้อยหนึ่งปริมาณ

ที่เราให้ dropdown สลับ 4 ปริมาณ ก็เพราะทั้งสี่ใช้โครงเดียวกัน ต่างกันแค่ "สูตรตรงกลาง" — เห็นโครงร่วมแล้วจะเติมได้ทั้งสี่โดยไม่งง

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

สี่ปริมาณของวันนี้

แต่ละปริมาณมาจากเซนเซอร์ต่างกัน แต่เข้าโครง raw → derived → viz เหมือนกันหมด

ปริมาณ เซนเซอร์ (raw) สูตรแปลง (derived) หน่วย รันที่ไหน
Tilt มุมเอียง IMU bmi270.motion() dsp.tilt(ax,ay,az) องศา Emulator + บอร์ด
Energy พลังงาน IMU bmi270.motion() abs(accel) - 1g g Emulator + บอร์ด
Altitude ความสูง baro dps368 dsp.altitude(p, p0) เมตร Emulator + บอร์ด
Sound ระดับเสียง MIC (PDM) 20·log10(rms/32768) dBFS บอร์ดเท่านั้น
  • สามปริมาณแรกใช้ IMU/baro ซึ่ง Emulator จำลองให้ — ซ้อมที่บ้านได้เลย
  • ปริมาณเสียง (dBFS) ต้องใช้ไมค์ PDM จริง จึงทำ บนบอร์ด เท่านั้น (โค้ดเราเช็กให้ ถ้าไม่มีไมค์ก็ข้าม)

สังเกตว่า 2 ใน 4 มาจาก IMU ตัวเดียว — เซนเซอร์เดียวแปลงได้หลายปริมาณ ขึ้นอยู่กับ "สูตรที่เราเลือกใช้"

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

ปูจากชุดบทเรียนก่อนหน้า — เรามีอะไรอยู่ในมือแล้ว

ชุดบทเรียนนี้ไม่ได้เริ่มจากศูนย์ เรายืนบนสองชุดบทเรียนก่อนหน้า:

  • จากบทเรียน 1.4–1.5 (แกะแอปเซนเซอร์) — โครงร่วม import → สร้างครั้งเดียว → ลูป → ui.poll ที่เราจะเจอซ้ำในไฟล์วันนี้ทุกบรรทัด
  • จากบทเรียน 2.1–2.2 (DAQ) — วิธีอ่านค่าดิบจากเซนเซอร์ (sensors.bmi270.motion(), sensors.dps368...) วันนี้เราต่อยอดด้วยการ แปลง ค่าที่อ่านมา
  • ของใหม่ของวันนี้ — โมดูล dsp และ "สูตรฟิสิกส์" ที่ทำให้ค่าดิบมีความหมาย
บทเรียน 1.4–1.5 โครงร่วม บทเรียน 2.1–2.2 อ่านค่าดิบ (DAQ) บทเรียน 3.1–3.2 (วันนี้) แปลงให้มีความหมาย

ถ้าลืมโครงร่วมของบทเรียน 1.4–1.5 กลับไปเปิด s02 ทวนสัก 5 นาทีก่อน เพราะไฟล์วันนี้เดินตามโครงนั้นเป๊ะ ต่างกันแค่ในลูปเราคำนวณฟิสิกส์แทนการอ่านโมเดล

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

รู้จักโมดูล dsp

dsp (Digital Signal Processing) คือกล่องเครื่องมือคณิตของ BENTO คำนวณฝั่ง C จึงเร็ว วันนี้เราแตะสองฟังก์ชันสำเร็จรูป + เขียนสูตรเองสองอัน

กลุ่ม ตัวอย่าง ชุดบทเรียนนี้ใช้
ฟิวชัน IMU (มุม) dsp.tilt(ax,ay,az) → (roll,pitch) ใช้ (Tilt)
ความดัน → ความสูง dsp.altitude(p[, p0]) → เมตร ใช้ (Altitude)
ตัวกรองสัญญาณ dsp.EMA / Median / Kalman1D บทเรียน 4.1–4.2
สภาพอากาศ dsp.dew_point / heat_index บทเรียน 3.3–3.4
  • Tilt กับ Altitude มีฟังก์ชันสำเร็จใน dsp — เราแค่เรียกใช้ (แต่จะเข้าใจว่ามันคำนวณอะไร)
  • Energy กับ dBFS ไม่มีฟังก์ชันสำเร็จ — เราเขียนสูตรเองด้วย math.sqrt / math.log10 (นี่คือ "คณิตที่คุณเขียน")

dsp คำนวณฝั่ง C ก็จริง แต่เราจะไม่เรียกแบบกล่องดำ — ทุกฟังก์ชันที่ใช้ เราจะเปิดดูว่ามันแปลงตัวเลขด้วยฟิสิกส์อะไร จะได้ remix เองเป็นในชุดบทเรียนถัด ๆ ไป

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

Tilt (1) — ฟิสิกส์ของมุมเอียง

Accelerometer ไม่ได้วัด "มุม" ตรงๆ มันวัด เวกเตอร์แรงโน้มถ่วง ที่ชี้ลงพื้นเสมอ พอบอร์ดเอียง เวกเตอร์นี้จะกระจายลงสามแกนต่างกัน — เราถอดมุมกลับออกมาด้วยตรีโกณ

บอร์ดเอียง g (แรงโน้มถ่วง) ชี้ลงพื้นเสมอ ถอดมุมด้วย atan2: pitch = atan2(−ax, √(ay²+az²)) roll = atan2(ay, az) dsp.tilt ทำสูตรนี้ให้เรา คืน (roll, pitch) เป็นองศา อัตราส่วนของแกนจึงหักล้างหน่วย — ไม่ต้องรู้ว่า accel หน่วยอะไร ก็ได้มุม

เพราะ tilt ใช้ อัตราส่วน ของแกน (atan2) หน่วยของ accelerometer จึงหักล้างกัน — นี่คือเหตุผลที่ตัวอย่าง 02 เอา motion() ยัดเข้า dsp.tilt ได้เลยโดยไม่ต้องแปลงหน่วยก่อน

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

คณิตเบื้องหลัง Tilt — atan2 ทีละสัญลักษณ์

สูตรเต็มของมุมเอียง เขียนเป็นคณิตได้สองบรรทัด (ผลลัพธ์เป็นเรเดียน คูณ 180π\frac{180}{\pi} ให้เป็นองศา):

roll=atan2⁡(ay, az)\text{roll} = \operatorname{atan2}(a_y,\ a_z)

pitch=atan2⁡ ⁣(−ax, ay2+az2)\text{pitch} = \operatorname{atan2}\!\left(-a_x,\ \sqrt{a_y^{2}+a_z^{2}}\right)

อ่านทีละตัว:

  • ax, ay, aza_x,\ a_y,\ a_z — ความเร่งสามแกนจาก bmi270.motion() (หน่วย m/s²)
  • atan2⁡(y, x)\operatorname{atan2}(y,\ x) — อาร์กแทนเจนต์แบบ "รู้ควอดรันต์" คืนมุมเต็มช่วง −180∘-180^\circ ถึง +180∘+180^\circ (ต่างจาก atan⁡\operatorname{atan} ที่ได้แค่ครึ่งเดียว)
  • ay2+az2\sqrt{a_y^{2}+a_z^{2}} — ขนาดของแรงโน้มถ่วงที่เหลือในระนาบ yy–zz หลังหักแกน xx ออก

ทำไมสำคัญกับชุดบทเรียนนี้: เพราะสูตรเป็น อัตราส่วน ของแกน (เช่น ay/aza_y/a_z) หน่วยจึงหักล้างกันหมด นี่คือเหตุผลที่ป้อน motion() เข้า dsp.tilt ได้ตรงๆ โดยไม่ต้องแปลงหน่วยก่อน และ atan2⁡\operatorname{atan2} ยังกันปัญหาหารด้วยศูนย์ตอน az=0a_z = 0 (บอร์ดตั้งฉากพอดี) ที่ atan⁡\operatorname{atan} ธรรมดาทำไม่ได้

▸ ลองเล่นสด (GeoGebra): เปิด Interactive Math Lab — ลากเวกเตอร์แรงโน้มถ่วง ดู atan2 คำนวณมุมเอียงสด (applet 5)

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

Tilt (2) — dsp.tilt แล้วส่งขึ้นเกจ

จาก raw ของ IMU สามค่า เรียก dsp.tilt ครั้งเดียวได้มุมสองแกน แล้วเลือกแกนที่เอียงมากสุดมาโชว์:

ax, ay, az, gx, gy, gz = sensors.bmi270.motion()   # raw
roll, pitch = dsp.tilt(ax, ay, az)                 # derived — สูตรที่คุณเติม
dom = pitch if abs(pitch) >= abs(roll) else roll   # แกนที่เอียงมากสุด
seg.text("%d" % int(dom))                          # viz: ตัวเลข
norm = clamp100(abs(dom) / 90.0 * 100)             # 0..90 องศา -> 0..100
bar.value(norm)                                     # viz: แถบ
  • dsp.tilt คืน (roll, pitch) — ระวังลำดับ! roll มาก่อน pitch
  • clamp100 บีบค่าเป็น 0..100 เพราะ Bar/Chart รับช่วงนี้ (เราตั้ง min=0, max=100)
  • มุม 0..90 map เป็น 0..100 บนแถบ — ตรงไปตรงมา

dsp.tilt(ax,ay,az) -> (roll, pitch) ตรวจจากเอกสาร API แล้ว ลำดับคือ roll ก่อน ถ้าสลับ เกจสองแกนจะสลับกัน — ลองสังเกตตอนรัน

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

Energy (1) — ขนาดของการเคลื่อนไหว

ถ้าอยากรู้ว่าบอร์ด "ขยับแรงแค่ไหน" (ไม่สนทิศ) เราวัด ขนาดของเวกเตอร์ความเร่ง ตอนวางนิ่ง ขนาดนี้ ~ 1g (แรงโน้มถ่วงล้วน) พอเขย่า ขนาดจะเบนออกจาก 1g

วางนิ่ง |accel| ≈ 1.0g → energy ≈ 0 เขย่า |accel| = 1.8g → energy = 0.8 mag = √(ax² + ay² + az²) / 9.81 energy = |mag − 1.0| ลบ 1g เพื่อเอาฐานแรงโน้มถ่วงออก เหลือเฉพาะ "พลังงานส่วนเกิน" จากการขยับ

นี่ไม่มีฟังก์ชันสำเร็จใน dsp — เราเขียนสูตรเองด้วย math.sqrt ขนาดเวกเตอร์คือรากที่สองของผลรวมกำลังสอง (พีทาโกรัสสามมิติ) หาร 9.81 ให้เป็นหน่วย g แล้วลบ 1g ออกเพื่อให้ "นิ่ง = 0"

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

Energy (2) — เขียนสูตรเอง

ax, ay, az, gx, gy, gz = sensors.bmi270.motion()   # raw
mag = math.sqrt(ax*ax + ay*ay + az*az) / 9.81      # ขนาดเวกเตอร์ (m/s² -> g)
energy = abs(mag - 1.0)                             # derived — ลบฐาน 1g
seg.text("%.2f" % energy)                          # viz
norm = clamp100(energy / 2.0 * 100)                # 0..2g -> 0..100
  • ax*ax เร็วกว่า ax**2 เล็กน้อยบน MicroPython และอ่านง่ายพอกัน
  • motion() คืนความเร่งหน่วย m/s² (วางนิ่ง ≈ 9.81) เราหาร 9.81 ให้เป็นหน่วย g (วางนิ่ง = 1g) แล้วลบ 1.0 — ถ้าไม่ลบ ตอนนิ่งค่าจะค้างที่ 1 ไม่ใช่ 0
  • abs(...) กัน energy ติดลบตอน mag < 1g (เช่นตกอิสระชั่วขณะ)

ทำไมต้อง "ลบ 1g"? เพราะเราสนใจ การเปลี่ยนแปลง ไม่ใช่แรงโน้มถ่วงคงที่ นี่คือไอเดียเดียวกับ high-pass filter ที่จะเจอเต็มๆ ในบทเรียน 4.1–4.2

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

Altitude (1) — ฟิสิกส์ของความดันอากาศ

ยิ่งสูง อากาศยิ่งเบาบาง ความดันจึงลดลงตามความสูง ความสัมพันธ์นี้เป็นสูตรมาตรฐาน (barometric formula) dsp.altitude ใส่สูตรนี้มาให้แล้ว

สูง ความดัน (hPa) → ความดันลดเมื่อขึ้นสูง alt = dsp.altitude(p, p0) p = ความดันตอนนี้ p0 = ความดันตอนเริ่ม (จุด 0 ม.) เราวัด "ความสูงเทียบกับตอนเริ่ม" ยกบอร์ดขึ้น 1 ม. → alt ≈ +1.0 p0 จับครั้งเดียวก่อนลูป

ทำไมต้องมี p0? ถ้าไม่มีจุดอ้างอิง เราจะได้แต่ "ความสูงเหนือน้ำทะเล" ซึ่งขึ้นกับสภาพอากาศวันนั้น การจับ p0 ตอนเริ่มทำให้ได้ ความสูงสัมพัทธ์ ที่แม่นและเห็นผลชัดเวลายกบอร์ด

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

Altitude (2) — dsp.altitude(p, p0)

# ก่อนลูป: จับความดันอ้างอิงครั้งเดียว
p0, _ = sensors.dps368.pressure_temperature()

# ในลูป:
p, t = sensors.dps368.pressure_temperature()       # raw
alt = dsp.altitude(p, p0)                           # derived — สูตรที่คุณเติม
seg.text("%.1f" % alt)                             # viz (เมตร)
norm = clamp100((alt + 5.0) / 10.0 * 100)          # โชว์ช่วง -5..+5 ม.
  • dps368.pressure_temperature() คืน (hPa, °C) — เราใช้แค่ความดัน ส่วนอุณหภูมิทิ้ง (_)
  • ความดันเปลี่ยนช้า อ่าน 2–8 Hz ก็พอ (ในไฟล์เราหน่วง 120 ms)
  • ช่วง ±5 เมตรถูก map ลง 0..100 เพื่อให้เห็นการยกบอร์ดชัดๆ

dsp.altitude(p[, p0]) — p0 เป็น argument ที่ไม่บังคับ ถ้าไม่ใส่จะใช้ความดันมาตรฐานระดับน้ำทะเล (1013.25) เราเลือกใส่ p0 เอง เพื่อให้จุด 0 อยู่ที่ "ตอนเริ่มโปรแกรม"

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

Sound dBFS (1) — จากคลื่นเสียงเป็นตัวเลขเดียว

ไมค์คืนคลื่นเสียงเป็นตัวอย่างหลายพันจุดต่อวินาที เราย่อมันเป็น "ระดับความดัง" ตัวเลขเดียวสองก้าว: RMS (พลังงานเฉลี่ย) แล้วแปลงเป็น dBFS (สเกล log)

คลื่นเสียง (buf) RMS √(ค่ากลางกำลังสอง) dBFS 20·log10(rms/32768) คลื่นหลายพันจุด → พลังงานเฉลี่ย → ระดับความดังตัวเลขเดียว

32768 คือค่าเต็มสเกลของตัวอย่างเสียง 16-bit (2¹⁵) เราเทียบ RMS กับค่าเต็มสเกล จึงได้ "dBFS" = decibel เทียบ Full Scale ค่าจะเป็น 0 เมื่อดังสุด และติดลบเมื่อเบาลง

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

คณิตเบื้องหลัง dBFS — RMS แล้ว log

ย่อคลื่นเสียง NN จุดให้เหลือตัวเลขเดียว ด้วยสองก้าว:

rms=1N∑i=1Nsi2\text{rms} = \sqrt{\frac{1}{N}\sum_{i=1}^{N} s_i^{2}}

dBFS=20 log⁡10 ⁣(rms32768)\text{dBFS} = 20\,\log_{10}\!\left(\frac{\text{rms}}{32768}\right)

อ่านทีละตัว:

  • sis_i — ตัวอย่างเสียงจุดที่ ii (16-bit มีค่า −32768-32768 ถึง 3276732767)
  • NN — จำนวนจุดใน buffer (len(mbuf))
  • rms\text{rms} — root-mean-square คือ "พลังงานเฉลี่ย" ของคลื่น (ยกกำลังสองก่อนเฉลี่ย ค่าบวก-ลบจึงไม่หักกันเอง)
  • 32768=21532768 = 2^{15} — ค่าเต็มสเกลของ 16-bit จึงได้ decibel ที่ เทียบ Full Scale

ทำไมสำคัญกับชุดบทเรียนนี้: สัมประสิทธิ์เป็น 2020 ไม่ใช่ 1010 เพราะ RMS เป็น แอมพลิจูด ไม่ใช่กำลัง (20=2×1020 = 2\times 10 มาจาก s2s^2 ในนิยามกำลัง) ค่าจึงออกมาเป็น 00 dBFS เมื่อดังเต็มสเกลและติดลบเมื่อเบาลง ตรงกับที่หูคนรับรู้เสียงแบบ log พอดี

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

Sound dBFS (2) — โค้ด + เงื่อนไขบอร์ด

if has_mic:                                # PDM มีเฉพาะบนบอร์ด
    pdm.readinto(mbuf)                     # raw: อ่านคลื่นเข้า buffer
    acc = 0
    for s in mbuf:
        acc += s * s                       # ผลรวมกำลังสอง
    rms = math.sqrt(acc / len(mbuf))       # ค่ากลางกำลังสอง
    db = 20 * math.log10(rms / 32768.0)    # derived — สูตรที่คุณเติม
    seg.text("%d" % int(db))              # viz
  • ไมค์ PDM เป็น ฮาร์ดแวร์จริง โค้ดจึงเช็ก has_mic ก่อน (บน Emulator จะได้เสียงสังเคราะห์ ไม่ใช่เสียงจริง)
  • ต้องเช็ก rms > 0 ก่อน log10 เพราะ log10(0) ไม่มีค่า (เงียบสนิท = ให้ค่าพื้น -96 dBFS)
  • ปิดท้ายด้วย pdm.deinit() ใน finally — คืนฮาร์ดแวร์ไมค์เสมอ

ปริมาณนี้ต้องใช้ เสียงจริงบนบอร์ด — บนบอร์ดให้ลองปรบมือ/พูดใส่ไมค์ดู ส่วน Emulator เล่นสามปริมาณแรก (Tilt/Energy/Altitude) ได้เต็มที่

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

ทำไมต้องเป็นสเกล log (decibel)

ทำไม dBFS ใช้ log10 ไม่ใช่ค่าดิบตรงๆ? เพราะ หูคนรับรู้เสียงแบบ log ไม่ใช่แบบเชิงเส้น

  • เสียงดังขึ้น "สองเท่า" ในความรู้สึก จริงๆ คือพลังงานเพิ่มขึ้นราว 10 เท่า
  • ถ้าโชว์ค่าดิบ (linear) เสียงเบาๆ จะเบียดกันอยู่ล่างสุดจนแยกไม่ออก เสียงดังจะพุ่งชนเพดาน
  • สเกล decibel บีบช่วงกว้างมหาศาลให้อ่านง่าย: ตั้งแต่ ~-96 (เงียบ) ถึง 0 (ดังเต็มสเกล)
linear: เสียงเบาเบียดกันซ้ายสุด อ่านไม่ออก log (dB): กระจายทั้งช่วง เห็นความต่างของเสียงเบาชัด

log สเกลไม่ได้มีแค่ในเสียง — ความสว่าง แผ่นดินไหว (Richter) ค่า pH ล้วนใช้ log เพราะธรรมชาติหลายอย่างครอบคลุมช่วงกว้างมาก การ "บีบด้วย log" ทำให้ตัวเลขอ่านออก

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

เลือก widget ให้เข้ากับข้อมูล

ปริมาณต่างชนิดเหมาะกับการแสดงผลต่างแบบ นี่คือทักษะของขั้น viz — เลือกให้ผู้ใช้ "อ่านออกในพริบตา"

widget เหมาะกับ ใช้กับปริมาณ
ui.Seg7 ตัวเลขเด่นตัวเดียว (headline) ทุกปริมาณ (ค่าจริง)
ui.Bar ระดับเทียบกับช่วง (0..100) ทุกปริมาณ (normalized)
ui.Chart แนวโน้มย้อนหลัง ทุกปริมาณ (ประวัติ)
ui.Arc มุม/เข็มหมุน เหมาะ Tilt เป็นพิเศษ
  • ไฟล์วันนี้ใช้ Seg7 + Bar + Chart ร่วมกันทั้งสี่ปริมาณ ให้เห็นทั้ง "ค่าตอนนี้" และ "แนวโน้ม"
  • Bar และ Chart รับช่วง min..max เราตั้ง 0..100 แล้ว normalize ทุกปริมาณเข้าช่วงนี้ด้วย clamp100
  • ตัวอย่าง 02 เลือก Arc คู่กับ tilt เพราะเข็มหมุนสื่อ "มุม" ได้ตรงกว่าแถบ — ลองปรับใช้ในงานทำเองได้

viz ไม่ใช่แค่ "โชว์ตัวเลข" แต่คือการเลือกภาพที่ตรงกับปริมาณ ระดับเสียงเป็นแถบ มุมเป็นเข็ม แนวโน้มเป็นกราฟ — เลือกผิด ผู้ใช้ก็อ่านยากขึ้นทันที

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