| คำถาม | คำตอบของชุดบทเรียนนี้ | อยู่ช่วงไหน | |
|---|---|---|---|
| Why | ในเมื่อบอร์ดมี IMU อยู่แล้ว ทำไมยังต้องมานั่งคำนวณมุมเอง | เพราะ ไม่มีชิปตัวไหนวัด "องศา" มาให้ มันวัดความเร่งกับความเร็วเชิงมุม ส่วนมุมเป็นปริมาณที่เราคิดขึ้นมาจากตัวเลขดิบ · และคำนวณผิดลำดับหรือผิดหน่วย โปรแกรมไม่พัง มันแค่ ตอบผิดอย่างเงียบ ๆ ซึ่งเป็นบั๊กที่แพงที่สุด | ครึ่งแรก · จากความเร่งสู่องศา · สไลด์ลำดับค่าที่คืนกลับมา |
| What | มีอะไรให้ใช้บ้าง | ตระกูล IMU ทั้งสิบสี่ชื่อ — sensors.bmi270 5 · sensors.bmm350 5 · ฝั่ง IMU ของ dsp อีก 4 คือ tilt() compass() Madgwick Pedometer |
สไลด์บัญชี 14 ชื่อ |
| How | ประกอบยังไงให้ใช้งานได้จริง | อ่านหกแกนจากการอ่านครั้งเดียวด้วย motion() → dsp.tilt() → กรองด้วย dsp.EMA → หักค่าอ้างอิงที่ปุ่ม "ตั้งศูนย์" เก็บไว้ → ขึ้นแถบสองแกนบนไม้บรรทัด + Seg7 |
ห้าไฟล์ตัวอย่าง + ไฟล์ฝึก |
ปลายทางที่จับต้องได้ — เครื่องวัดระดับสองแกน วางราบอ่านได้ราว 0° เอียงแล้วแถบทั้งสองแกนวิ่งถูกทิศ ไฟบอกได้ว่าเกินเกณฑ์ที่ตั้งไว้หรือยัง และกดปุ่มตั้งศูนย์เมื่อไรก็ประกาศจุดอ้างอิงใหม่ได้ทันที
บทเรียน 2.7–2.9 คือค่าดิบหนึ่งตัว แปลงเป็นหน่วยที่คนอ่านออก · ชุดบทเรียนนี้คือค่าดิบสามตัว แปลงเป็นปริมาณที่ไม่มีเซนเซอร์ตัวไหนวัดตรง ๆ
sensors.bmi270.motion() และบอกได้ว่าทำไมต้องอ่านทีเดียวdsp.tilt() โดยจำลำดับค่าที่มันคืนได้ถูกเกินเกณฑ์ผ่าน แต่อยู่ในชุดบทเรียนนี้ เพราะเป็นของกลุ่มเดียวกันและถ้าไม่พูดที่นี่ก็ไม่มีที่ไหนพูด: sensors.bmm350 (เข็มทิศ ห้าคำสั่ง) · dsp.compass() · dsp.Madgwick · dsp.Pedometer — สี่หัวข้อนี้มีสไลด์ของตัวเองและมีไฟล์ตัวอย่างให้ลอง ไม่ได้อยู่ในเกณฑ์ผ่านของชุดบทเรียนนี้
กฎเหล็ก 5 ข้อของ ui ยังบังคับใช้เหมือนเดิม — ui.poll() ทุกลูป, time.sleep_ms(200) สำหรับงานหนัก, งบ 32 widget ต่อหน้า (เพดานเฟิร์มแวร์ 64), ใช้ ui.* ครั้งแรกแล้ว sensor auto-task หยุด จึงต้องอ่านเซนเซอร์เองในลูป, ลูปเร็วเกินเฟรมจะหายเงียบ ๆ

หน้าจอของเฉลยปัจจุบัน (s06_digital_level.py ของบทเรียน 3.3) แบ่งเป็นสามการ์ด
dsp.tilt() คืนค่ามาพอดี แต่ละแกนเป็น ui.Bar วางทับ ui.Scale ที่มีขีดและตัวเลข -90 ถึง 90 พร้อม ui.Seg7 อ่านค่าละเอียดอยู่ข้าง ๆui.Led สองดวง บอกว่าตอนนี้อยู่ในเกณฑ์หรือเกินui.Spinbox ที่ตั้งเองได้ด้วยปุ่มเพิ่ม/ลด เพราะงานวางกล้องกับงานวางตู้เย็นยอมได้ไม่เท่ากัน · ขวาคือปุ่ม "ตั้งศูนย์" กับ "จบการวัด" · ใต้สุดคือบรรทัดอ้างอิง R +0.0 P +0.0 ที่เก็บค่าศูนย์ล่าสุดไว้ค่าที่อ่านได้ต้องเทียบกับอะไรสักอย่างเสมอ ปุ่มตั้งศูนย์คือการประกาศว่า "ตรงนี้คือศูนย์" และไม้บรรทัดใต้แถบคือการประกาศว่า "เต็มสเกลคือเท่านี้"
คำตอบยอดนิยมคือ "วัดความเร่ง" ซึ่งถูกครึ่งเดียว วางบอร์ดนิ่งสนิทบนโต๊ะ มันไม่ได้เร่งไปไหนเลย แต่ค่าที่อ่านได้คือ
อ่านเป็นภาษาคน: เซนเซอร์ไม่ได้วัดว่าเราเคลื่อนที่เร็วขึ้นแค่ไหน มันวัด แรงที่ตัวเรือนดันมวลเล็ก ๆ ข้างในไว้ไม่ให้ตกอิสระ — โต๊ะดันบอร์ดขึ้น บอร์ดดันมวลขึ้น จึงอ่านได้ +1 g ในทิศขึ้น ปริมาณนี้มีชื่อว่า proper acceleration
ตัวเลขจากบอร์ดจริง: ปล่อยบอร์ดตกอิสระ (อย่าทำ) ทั้งสามแกนจะอ่านได้ ~0 เพราะไม่มีใครดันมวลนั้นแล้ว

แรงโน้มถ่วงอยู่ในค่าที่อ่านได้ ตลอดเวลา — คนอื่นเรียกมันว่าสัญญาณรบกวน วันนี้เราจะเรียกมันว่าสัญญาณ



โครงสร้างข้างใน BMI270 คือ มวลพิสูจน์ (proof mass) เล็กกว่าเม็ดฝุ่น แขวนบนสปริงซิลิคอนคดเคี้ยว พอบอร์ดถูกเร่ง มวลตามไม่ทันเพราะความเฉื่อย มันจึงเลื่อนไปในทางตรงข้าม
แล้ววงจรอ่าน "ระยะที่มวลเลื่อน" ได้ยังไง — มวลมีซี่หวียื่นออกมา สอดสลับกับซี่หวีที่ยึดกับตัวเรือน แต่ละคู่ซี่คือ ตัวเก็บประจุ หนึ่งตัว
วงจรวัด ผลต่างของความจุ ส่งเข้า ADC ในชิป ออกมาเป็นตัวเลขบน I2C

การวัดความเร่งจริง ๆ แล้วคือ การวัดอัตราส่วนความจุ — นี่คือเหตุผลที่มันเล็กและกินไฟน้อยได้ขนาดนี้
เพราะขนาดรวมคงที่ เรารู้ค่าสองแกนก็เดามุมได้ นี่คือเหตุผลที่ สามตัวเลขกลายเป็นสององศา ได้


| ชื่อ | คือการเอียงแบบไหน | ทดสอบด้วยการ | มาจากแกน |
|---|---|---|---|
| roll | ซ้าย-ขวา | ยกขอบด้านที่ทำให้ roll เพิ่ม — ดูว่าขอบไหนของบอร์ดทีม (Eva Kit: ขอบซ้าย · Dev Kit: ยังไม่ได้วัด จดลงบันทึกการเรียน) | ay กับ az |
| pitch | หน้า-หลัง | ยกขอบด้านที่ทำให้ pitch เพิ่ม — ขอบที่ตั้งฉากกับขอบของ roll (Eva Kit: ขอบบน) | ax เทียบ y-z |
| yaw | หันซ้าย-ขวา | หมุนบอร์ดบนโต๊ะ | หาจาก accel ไม่ได้ |
ทำไม yaw หาไม่ได้จาก accel — การหันบอร์ดบนโต๊ะไม่ทำให้เงาของแรงโน้มถ่วงบนแกนใดเปลี่ยนเลย accel จึงมองไม่เห็นมัน ต้องใช้ magnetometer (บทเรียน 3.7–3.9) หรือ gyro
dsp.tilt()ให้เราสองแกน ไม่ใช่สาม — และนั่นเป็นข้อจำกัดทางฟิสิกส์ ไม่ใช่ข้อจำกัดของเฟิร์มแวร์
dsp.tilt()ตรวจสุขภาพเซนเซอร์ก่อนเชื่อค่าอะไรทั้งนั้น
ไทย: ไม่ว่าเอียงบอร์ดยังไง ถ้าวางนิ่งค่านี้ต้องได้ราว 9.81 m/s² เสมอ · ตัวเลขจาก Eva Kit (บอร์ดที่ใช้เขียนสไลด์): ax 0.02, ay −0.05, az 9.79 → ผ่าน
สูตรที่แปลงเงาเป็นองศา
ไทย: เวกเตอร์แรงโน้มถ่วงชี้ลงคงที่เสมอ พอบอร์ดเอียง เงาของมันบนแกนก็เปลี่ยน · ตัวเลขจาก Eva Kit ตอนยกขอบซ้าย: ay 4.90, az 8.49 →
ทำไมต้อง atan2 ไม่ใช่ atan — atan2 รับตัวตั้งกับตัวหารแยกกัน จึงแยกควอดรันต์ได้ครบสี่ และไม่ระเบิดเมื่อตัวหารเป็นศูนย์ตอนบอร์ดตั้งฉาก 90°

รู้สูตรไว้ไม่ใช่เพื่อพิมพ์เอง แต่เพื่อ เดาถูกว่าจะพังตรงไหน เมื่อค่าออกมาแปลก
dsp.tilt() คืน (roll, pitch) — roll มาก่อน pitch เสมอ ตำราและไลบรารีจำนวนมากเขียนเรียงว่า "pitch, roll" จนติดปาก
ถ้าเผลอเขียนสลับ โปรแกรมจะ ไม่ error เลย จอขึ้นตัวเลขสวยงามครบทั้งสองแกน แต่เอียงซ้าย-ขวาแล้วแถบ PITCH ดันวิ่ง ส่วนแถบ ROLL นิ่ง — เหมือนภาพขวา
บั๊กที่ไม่ทำให้โปรแกรมพัง คือบั๊กที่แพงที่สุด เพราะไม่มีใครรู้ว่ามันอยู่ตรงนั้น
motion() — หกแกนจากการอ่านครั้งเดียว และหน่วยที่ต้องอ่านให้ถูกax, ay, az, gx, gy, gz = sensors.bmi270.motion()
หกค่านี้มาจาก การอ่านครั้งเดียวกัน — กลไกต่างกันตามบอร์ด: บน Eva Kit motion() หยิบจาก snapshot ชุดเดียวที่คอร์จออ่านค้างไว้ (ไม่มีการจองบัสฝั่ง Python) · บน Dev Kit CM33 จองบัส I2C ของตัวเอง (lock) อ่านหกแกนแล้วปล่อย · ถ้าเรียก acceleration() แล้ว gyroscope() แยกสองครั้ง ระหว่างสองครั้งนั้น snapshot อาจถูกเปลี่ยนชุด (Eva) หรือบัสถูกปล่อยว่าง (Dev Kit) และถ้าตอนนั้นบอร์ดกำลังขยับ ค่า accel กับ gyro ที่ได้จะมาจาก คนละท่าของบอร์ด
| ค่า | หน่วย | จุดอ้างอิงที่ควรจำ |
|---|---|---|
ax, ay, az |
m/s² | 1 g = 9.81 · วางราบ az ≈ +9.81 · ตกอิสระ = 0 |
gx, gy, gz |
deg/s | วางนิ่ง ≈ 0 · หมุนด้วยมือ 30-90 · สะบัดข้อมือ > 500 |
อย่าสับสน — accel บอก ตำแหน่งเชิงมุมเทียบกับแนวดิ่ง ส่วน gyro บอก อัตราการเปลี่ยนมุม ไม่ใช่ตัวมุมเอง
ข้อมูลที่จะเอาไปรวมกัน (fuse) ต้องมาจากเวลาเดียวกัน ไม่งั้นเราไม่ได้รวมข้อมูล เรากำลังรวมความมั่ว
จากชุดบทเรียนนี้ไป เราไม่ได้แค่ "แสดงค่าที่เซนเซอร์ให้มา" แต่เริ่มคำนวณปริมาณใหม่ขึ้นมาเอง