ทำไมเราถึงเรียก sensors.bmi270.acceleration() แล้วได้ตัวเลขมาเลย ทั้งที่ชิปอยู่คนละก้อนกับ CPU
bmi270 ห่อรายละเอียด I2C ทั้งหมดไว้ให้แล้ว เราแค่เรียกฟังก์ชันเดียว
accelerometer (BMI270) เป็น digital sensor — มี ADC อยู่ในชิปแล้ว ส่งค่าออกมาเป็นตัวเลข (ถ้าเป็น analog sensor ดิบ เช่น potentiometer ต้องพึ่ง ADC ของ MCU เอง)
0x68)0x68 ผ่าน SCL+SDA เพียง 2 เส้น แล้ว BMI270 (slave) ตอบตัวเลขความเร่งกลับมา — นี่คือสิ่งที่ sensors.bmi270.acceleration() ทำให้เบื้องหลัง
ด้านล่างคือ schematic จริง ของบอร์ด — สังเกตตาราง SDO=0 → I2C address = 0x68 ที่มุมขวาบน

เวลาเราอ่านค่าเอียงจากเซนเซอร์ เบื้องหลังคือคุย I2C ไปถาม address 0x68 แล้วได้เลขกลับมา
DS เปิดยุค input แบบใหม่บนเครื่องพกพา — touchscreen + ไมโครโฟน กลายเป็นวิธีเล่นใหม่ ไม่ใช่แค่ปุ่ม
เชื่อมกับวันนี้: การ เอียงบอร์ด อ่านค่าจาก accelerometer ของน้อง คือ input modality ใหม่แบบเดียวกัน — เซนเซอร์กลายเป็นปุ่ม
ที่มา: "Nintendo-DS-Fat-Blue" — Evan-Amos, Public domain, Wikimedia Commons · เนื้อหา: en.wikipedia.org/wiki/Nintendo_DS
| คำสั่ง | ทำอะไร |
|---|---|
sensors.init() |
ปลุกเซนเซอร์ทั้งชุด (รวม BMI270) — เรียก ครั้งเดียวพอ |
sensors.bmi270.acceleration() |
คืนค่า 3 แกน (ax, ay, az) หน่วย m/s² — หารด้วย 9.81 เพื่อแปลงเป็น g |
game.Box(x, y, w, h, color=...) |
สร้างกล่องบนจอ |
box.move(dx, dy) |
เลื่อนกล่อง คุมขอบจอให้แล้ว ไม่หลุดออกนอกจอ |
game.keys() |
อ่านปุ่มระบบ (.back = ออก) |
จุดสำคัญ: ต้อง
sensors.init()ก่อน เรียกsensors.bmi270.acceleration()เสมอ ไม่งั้นเซนเซอร์ยังไม่ตื่นacceleration()จะ โยน OSError (โปรแกรมหยุด) ไม่ใช่คืนค่าศูนย์เงียบ ๆ
หัวใจของคาบนี้คือสมการ map สั้น ๆ แค่บรรทัดเดียวต่อแกน:
ax, ay มาจากเซนเซอร์ (หาร 9.81 แปลงเป็น g แล้ว) อยู่ในช่วงราว −1.0 ถึง +1.0SPEED คือตัวคูณ — ยิ่งมาก เอียงนิดเดียวกล่องก็พุ่งdx, dy คือ "ขยับกี่พิกเซลในเฟรมนี้"deadzone — ค่าเอียงเล็ก ๆ จากมือสั่นหรือวางไม่ราบสนิท เราตัดทิ้งให้เป็น 0 ก่อน

dx = ax × SPEED เป็นลบ → กล่องไปซ้ายเราไม่ต้องเขียน if เลือกซ้าย/ขวา/บน/ล่างเลย — แค่คูณค่าเซนเซอร์ ทั้งทิศและความเร็วโผล่มาเองจากตัวเลข

อยากให้มัน ไหลต่อ เหมือนลูกแก้วจริง ต้องเก็บ "ความเร็ว" ไว้ — นั่นคือเรื่องของ physics simulation ที่รอให้น้องไปต่อยอด
อยากเห็นว่า ข้างในชิป BMI270 ทำงานยังไงตอนเราเอียง ดูคลิปนี้จากผู้ผลิตชิปเอง (Bosch):
Bosch MEMS sensors: Working principle of an accelerometer — Bosch Sensortec
โค้ด map สั้น ๆ ที่น้องเพิ่งเขียน คือหัวใจเดียวกับเกมพวกนี้:
ที่มา: "PuzzleOfDexterity" — Andreas.Roever, CC BY-SA 3.0, Wikimedia Commons
Game Simulation Machine: บอร์ดในมือน้องกลายเป็น ตัวคุมจริง ๆ ของโลกในจอ — เอียงมือ = เอียงโลกในเกม นี่คือก้าวแรกของการทำ simulator
ลองจินตนาการ: ถ้าเปลี่ยนกล่องเป็น "ลูกแก้ว" แล้วเติม "หลุม" ที่ห้ามตก — เกมเขาวงกตของน้องก็เกิดขึ้นแล้ว
game.start() — ล้างจอ + เตรียมระบบgame.Box / game.Text — กล่อง + ป้ายwhile True + อ่านปุ่ม BACKtime.sleep_ms(30)sensors.init()sensors.bmi270.acceleration()box.move() ด้วยค่า mapของใหม่จริง ๆ ของคาบนี้คือ การคุยกับเซนเซอร์ — 3 บรรทัดที่เชื่อมฮาร์ดแวร์เข้ากับเกม
ไฟล์ฝึก: practise_codes/s09_tilt.py↗ — มองหาคำว่า "เติม:" ทุกจุด
import bentogame as game
import sensors # ชุดเซนเซอร์บนบอร์ด (ต้อง init ก่อนใช้)
# bmi270 อยู่ใต้ sensors แล้ว — เรียก sensors.bmi270.acceleration() ได้เลย
import time
game.start() # เคลียร์จอ + เตรียมระบบ
# เติม: ปลุกเซนเซอร์ทั้งชุดก่อนใช้งาน ด้วย sensors.init()
pass
# สร้างกล่องไว้กลางจอ ขนาด 40x40
box = game.Box(game.WIDTH // 2, game.HEIGHT // 2, 40, 40, color=game.CYAN)
label = game.Text("เอียงบอร์ดเพื่อขยับกล่อง", 20, 20)
DEADZONE = 0.10 # ค่าเอียงเล็ก ๆ ถือว่ายังนิ่ง กันกล่องสั่น
SPEED = 120.0 # ตัวคูณ: ยิ่งเอียงมาก กล่องยิ่งวิ่งไว
บรรทัดที่เขียนว่า
# เติม:กับpassคือจุดที่รอน้อง ๆ เติม อย่าลืมลบpassออกตอนเติมเสร็จ
while True:
keys = game.keys() # อ่านปุ่มระบบ (BACK = ออก)
if keys.back:
game.clear()
break
# เติม: อ่านความเร่ง 3 แกนด้วย sensors.bmi270.acceleration() -> คืน (ax, ay, az) หน่วย m/s²
ax, ay, az = (0.0, 0.0, 0.0)
ax, ay, az = ax/9.81, ay/9.81, az/9.81 # แปลงเป็น g (วางราบ แกนหนึ่ง ≈ 1.0)
# ตัด noise ในช่วง deadzone ให้เป็น 0 (กล่องจะนิ่งเมื่อวางราบ)
if abs(ax) < DEADZONE:
ax = 0.0
if abs(ay) < DEADZONE:
ay = 0.0
# เติม: map ค่าความเอียงเป็นระยะเลื่อนต่อเฟรม แล้วเรียก box.move(dx, dy)
# ใบ้: dx = ax * SPEED, dy = ay * SPEED (box.move คุมขอบจอให้แล้ว)
pass
time.sleep_ms(30) # ~30 fps
game.start() # เคลียร์จอ + เตรียมระบบ
sensors.init() # ปลุกเซนเซอร์ทั้งชุด (รวม BMI270) ครั้งเดียวพอ
box = game.Box(game.WIDTH // 2, game.HEIGHT // 2, 40, 40, color=game.CYAN)
label = game.Text("เอียงบอร์ดเพื่อขยับกล่อง", 20, 20)
DEADZONE = 0.10
SPEED = 120.0
while True:
keys = game.keys() # อ่านปุ่มระบบ (BACK = ออก)
if keys.back:
game.clear()
break
ax, ay, az = sensors.bmi270.acceleration() # เซนเซอร์คืน m/s²
ax, ay, az = ax/9.81, ay/9.81, az/9.81 # แปลงเป็น g (วางราบ แกนหนึ่ง ≈ 1.0)
if abs(ax) < DEADZONE: # ตัด noise ในช่วง deadzone
ax = 0.0
if abs(ay) < DEADZONE:
ay = 0.0
box.move(ax * SPEED, ay * SPEED) # map ค่าเอียง -> ระยะเลื่อนต่อเฟรม
time.sleep_ms(30) # ~30 fps
ลองเติมเองให้สุดก่อนนะ — เดี๋ยวเฉลยพร้อมกันในห้องเรียน แล้วเทียบกับที่ตัวเองเติมดูว่าตรงไหม
az ทั้งที่ไม่ได้ใช้เซนเซอร์คืนความเร่ง 3 แกนพร้อมกันเป็น tuple เดียว จะหยิบมาแค่ 2 แกนไม่ได้ ต้อง unpack ให้ครบ:
ax, ay, az = sensors.bmi270.acceleration() # s09_tilt.py:34 — คืน 3 ค่าเสมอ
ax, ay, az = ax/9.81, ay/9.81, az/9.81 # :36 — หาร 9.81 ทั้ง 3 ค่า
...
box.move(ax * SPEED, ay * SPEED) # :45 — ใช้แค่ ax, ay — az ถูกทิ้ง
move()az ไว้เพราะต้องมีชื่อรับค่าตัวที่ 3 (ถ้าเขียน ax, ay = ... จะ ValueError: too many values to unpack) — ใช้ _ แทนก็ได้ แต่ตั้งชื่อ az ไว้อ่านง่ายและเผื่อต่อยอดต่อยอด (ไม่มีในไฟล์เฉลย) — ทำค่าเซนเซอร์ให้นิ่งด้วย EMA: ค่าดิบจาก BMI270 สั่นเป็น noise ทุกเฟรม เรากรองให้เนียนได้ด้วย low-pass filter (EMA) ใส่ หลังบรรทัดอ่านค่า
:36ก่อน deadzone:filt_x = ALPHA * ax + (1 - ALPHA) * filt_x # ALPHA ~ 0.2 : ยิ่งน้อยยิ่งนิ่ง(หน่วงกว่า)สูตร EMA คือ
filt = α·new + (1−α)·filt— ผสมค่าใหม่นิดเดียวกับค่าเก่าที่สะสมไว้ ทำให้การกระตุกหายไป กล่องลื่นขึ้น (แต่ตอบสนองช้าลงเล็กน้อย) นี่คือจุดต่อยอดของDEADZONEที่ตัด noise แบบ on/off ให้กลายเป็นการกรองแบบนุ่ม
ทุกเฟรม (~30 Hz) ข้อมูลวิ่งจาก เอียงบอร์ด → ค่าเซนเซอร์ → deadzone → ระยะเลื่อน → กล่องบนจอ แล้ววนกลับ:
box.move()เป็นตัว clamp ขอบจอ ให้ในตัว (engine 70%) เราแค่ป้อนax·SPEED,ay·SPEEDเข้าไป — สังเกตว่าไม่มีifเลือกทิศเลย ทิศ (+/−) และความเร็ว (ขนาด) โผล่มาเองจากค่าเซนเซอร์
s09_tilt.py ใน BENTO IDE
ปุ่ม Program to Device (สีเขียว มุมซ้ายบน) ใน BENTO IDE — คือปุ่มเดียวที่ข้อ 1-2 พูดถึง
เรารันด้วยปุ่ม Program to Device เท่านั้น ไม่ต้องไปพิมพ์
exec(open(...))ที่ REPL นะ
ลองปรับ ทีละค่า แล้ว Program to Device ใหม่ สังเกตความต่าง:
| ลองเปลี่ยน | ผลที่เห็น |
|---|---|
SPEED = 60.0 |
กล่องวิ่งช้า ต้องเอียงเยอะกว่าจะขยับ |
SPEED = 240.0 |
เอียงนิดเดียวกล่องก็พุ่ง คุมยากขึ้น |
DEADZONE = 0.02 |
ไวมาก วางราบก็ยังขยับเล็กน้อย (สั่น) |
DEADZONE = 0.30 |
ต้องเอียงเยอะกว่ากล่องถึงจะเริ่มขยับ |
นี่คือหัวใจของงานเซนเซอร์จริง — ค่าดิบจากชิปไม่เคย "พอดี" ในครั้งแรก เราต้องจูน
SPEEDกับDEADZONEให้เข้ากับมือคนเล่น
| ค่า | ความหมาย |
|---|---|
| 9.81 | m/s² ต่อ 1g — หารค่าดิบให้เป็นหน่วย g |
| ±2 g | ช่วงที่เซนเซอร์ตั้งไว้ (เอียงปกติไม่เกินนี้) |
| 1.0 g | วางราบ แกน Z อ่านได้ราวนี้ (ใช้เป็นตัวเช็ก) |
| DEADZONE 0.10 | ต่ำกว่านี้ถือว่ายังนิ่ง (กันกล่องสั่น) |
| SPEED 120 | ตัวคูณ ax → พิกเซลต่อเฟรม (จูนได้) |
| 0x68 | address ของ BMI270 บนบัส I2C |
| ~30 fps | time.sleep_ms(30) หนึ่งรอบลูป |
ตัวเลขพวกนี้ไม่ใช่เวทมนตร์ — มาจาก ฟิสิกส์ (9.81, 1g) บวกกับ การจูนให้เข้ามือคนเล่น (DEADZONE, SPEED)
sensors.init() — sensors.bmi270.acceleration() จะ โยน OSError แล้วโปรแกรมหยุดทันที (ไม่ใช่คืนค่าศูนย์เงียบ ๆ) — เห็น error ที่คอนโซลแบบนี้ให้ย้อนไปเช็กว่าลืม init รึเปล่าinit ผิดที่ — ต้อง init ก่อนเข้าลูป ไม่ใช่ในลูป (สิ้นเปลือง + เซนเซอร์รีเซ็ตซ้ำ)pass — ในไฟล์ฝึกมี pass ค้างไว้ ถ้าไม่ลบ บรรทัดที่เราเติมจะไม่ทำงานตามตั้งใจDEADZONE เล็กไป กล่องสั่นตลอดเวลาแม้วางราบ — เพิ่มค่าขึ้นSPEED มากไป เอียงนิดเดียวกล่องหายไปชนขอบ — ลดค่าลงax/ay ใน box.move()ถ้าค่าที่อ่านได้ดูแปลก ๆ ลองพิมพ์
print(sensors.bmi270.acceleration())ในลูปดูค่าจริงก่อน แล้วค่อยจูน
sensors.bmi270.acceleration()game.Boxpractise_codes/s09_tilt.py↗ ให้ครบ 3 ช่องSPEED + DEADZONE ให้คุมง่ายที่สุด แล้วจดค่า + เหตุผลต่อยอด: เปลี่ยนสีกล่องตามความแรงที่เอียง (เอียงมาก = สีแดง) หรือเอาค่า
azมาทำอะไรสักอย่าง — ลองคิดเล่นดูนะน้อง ๆ
เฉลยนี้มีไว้เทียบกับที่น้องเติมเอง ไม่ได้มีไว้ copy วาง คะแนนของคาบอยู่ที่ใบงานกับการอธิบายด้วยคำพูดตัวเอง ไม่ใช่ที่โค้ดตรงเฉลย วิธีใช้ให้ได้ผลจริง: อ่านทีละส่วนให้เข้าใจว่าทำไมต้องเป็นแบบนี้ แล้วปิดไฟล์ กลับไปพิมพ์ใหม่ด้วยมือ ตอนพิมพ์เองนี่แหละที่สมองจำ pattern การคุยกับเซนเซอร์ได้จริง เราจะแกะทีละก้อน ไม่ดูรวดเดียว เพราะทุกบรรทัดมีเหตุผลของมัน
ก้อนแรก — เปิดเครื่องมือ แล้วปลุกเซนเซอร์ก่อนใช้:
import bentogame as game
import sensors # ชุดเซนเซอร์บนบอร์ด (ต้อง init ก่อนใช้)
# bmi270 อยู่ใต้ sensors แล้ว — เรียก sensors.bmi270.acceleration() ได้เลย
import time
game.start() # เคลียร์จอ + เตรียมระบบ
sensors.init() # ปลุกเซนเซอร์ทั้งชุด (รวม BMI270) ครั้งเดียวพอ
sensors.init() (:18) ต้องมา ก่อน เข้าลูป ไม่ใช่ในลูป — เหมือนสไลด์ "กับดักที่เจอบ่อย" เตือนไว้ ปลุกเซนเซอร์ครั้งเดียวพอ ถ้า init ซ้ำทุกเฟรมจะสิ้นเปลืองและเซนเซอร์รีเซ็ตวนไม่หยุดinit แล้วเรียก sensors.bmi270.acceleration() เลย มันจะ โยน OSError โปรแกรมหยุดทันที ไม่ใช่คืน 0 เงียบ ๆ (ตรงกับสไลด์ "API ที่ใช้คาบนี้") — นิสัยที่สร้างคือ "อุปกรณ์ต้องเปิดก่อนอ่าน"import sensors แยกจาก import bentogame as game เพราะเกมกับเซนเซอร์เป็นคนละโมดูล วันนี้เราต่อสะพานสองโลกนี้เข้าหากันเป็นครั้งแรกของคอร์สก่อนสั่งอ่านค่าจากอุปกรณ์ใด ให้ถามตัวเองก่อนว่า "มันตื่นหรือยัง" —
sensors.init()คือการปลุกให้ตื่น
# สร้างกล่องไว้กลางจอ ขนาด 40x40
box = game.Box(game.WIDTH // 2, game.HEIGHT // 2, 40, 40, color=game.CYAN)
label = game.Text("เอียงบอร์ดเพื่อขยับกล่อง", 20, 20)
DEADZONE = 0.10 # ค่าเอียงเล็ก ๆ ถือว่ายังนิ่ง กันกล่องสั่น
SPEED = 120.0 # ตัวคูณ: ยิ่งเอียงมาก กล่องยิ่งวิ่งไว
game.WIDTH // 2, game.HEIGHT // 2 (:21) คือ กึ่งกลางจอ ไม่ hard-code เลขพิกเซล — บอร์ดจอคนละขนาดก็ยังกลางจอเสมอ นิสัย "ถามค่าจริงจากระบบ" ที่ย้ำมาตั้งแต่คาบต้น ๆDEADZONE กับ SPEED ขึ้นมาเป็น ค่าคงที่ชื่อชัด ไว้บนสุด (:24-25) ไม่ใช่ตัวเลขลอย ๆ กลางลูป เวลาจูนเราแก้ที่เดียวจบ ตรงกับสไลด์ "จูนค่าให้เป็นความรู้สึกของเรา"0.10 กับ 120.0 ไม่ใช่เวทมนตร์ มาจากฟิสิกส์ (ช่วง ax ≈ −1..1 ในสไลด์ "ฟิสิกส์เบื้องหลัง") บวกการลองจนเข้ามือ ตั้ง SPEED ใหญ่ไปกล่องพุ่งชนขอบ ตั้ง DEADZONE เล็กไปกล่องสั่นตลอดเวลาตัวเลขที่ต้องจูนบ่อย ให้ยกขึ้นมาตั้งชื่อไว้บนสุดเสมอ ตัวเราในอีกสิบนาทีจะขอบคุณ
while True:
keys = game.keys() # อ่านปุ่มระบบ (BACK = ออก)
if keys.back:
game.clear()
break
# อ่านความเร่ง 3 แกน เซนเซอร์คืน m/s²
ax, ay, az = sensors.bmi270.acceleration()
# แปลงเป็น g (วางราบ แกนหนึ่ง ≈ 1.0, ช่วงเอียงราว -1..1)
ax, ay, az = ax/9.81, ay/9.81, az/9.81
# ตัด noise ในช่วง deadzone ให้เป็น 0 (กล่องจะนิ่งเมื่อวางราบ)
if abs(ax) < DEADZONE:
ax = 0.0
if abs(ay) < DEADZONE:
ay = 0.0
# map ค่าความเอียง -> ระยะเลื่อนต่อเฟรม (box.move คุมขอบจอให้แล้ว)
box.move(ax * SPEED, ay * SPEED)
time.sleep_ms(30) # ~30 fps
ax, ay, az = sensors.bmi270.acceleration() (:34) รับค่า 3 แกนพร้อมกันเป็น tuple เดียว ต้อง unpack ให้ครบ ถ้าเขียน ax, ay = ... จะ ValueError (สไลด์ "ทำไมอ่าน az") เกม 2 มิติใช้แค่ ax, ay แต่ az ต้องมีที่รับ9.81 (:36) แปลง m/s² → g ทั้ง 3 ค่า ค่า ax ที่ได้ คือ sinθ ของมุมเอียงโดยตรง ตามสไลด์ "ฟิสิกส์เบื้องหลัง" เราจึงคูณ SPEED ได้เลย ไม่ต้องเรียก math.asin() หามุมจริง:39-42) ตัดค่าสั่นเล็ก ๆ ให้เป็น 0 ด้วย abs() วางราบกล่องจึงนิ่งสนิท ถ้าไม่ตัด มือสั่นนิดเดียวกล่องก็ไถลตลอดbox.move(ax * SPEED, ay * SPEED) (:45) คือสมการ map จากสไลด์ "จาก ค่าเอียง ไปเป็น การขยับ" — ทิศ (+/−) กับความเร็ว (ขนาด) โผล่มาเองจากค่าเซนเซอร์ ไม่มี if เลือกทิศเลย และ box.move clamp ขอบจอให้แล้ว (engine 70%)time.sleep_ms(30) (:47) คุมให้วน ~30 fps นี่คือ game loop โครงเดิม read → update → wait ที่เจอมาทุกคาบทั้งเกมย่อลงเหลือสี่จังหวะ: อ่านค่า → แปลงหน่วย → กรอง noise → เอาไปขยับ จำสี่จังหวะนี้ไว้ เซนเซอร์ตัวไหนก็ท่าเดียวกัน
โค้ดสั้น ๆ นี้ไม่ได้เรียงมั่ว แต่ละบรรทัดต่อยอดของเดิมที่เรามีอยู่แล้ว แล้วเติมแนวคิดใหม่ทีละอย่าง เหมือนวิธีที่เราจะค่อย ๆ ต่อเกมทั้งเครื่องขึ้นมา:
| ชิ้นส่วน | บรรทัด | แนวคิดใหม่ที่เพิ่ม | ของเดิมที่เอากลับมาใช้ |
|---|---|---|---|
| ปลุกเซนเซอร์ | :18 |
peripheral ต้อง init ก่อนอ่าน | game.start() จากคาบก่อน |
| ตั้งค่าปรับมือ | :24-25 |
แยกค่า (DEADZONE/SPEED) ออกจากโครง | ตัวแปร constant ชื่อชัด |
| อ่าน 3 แกน | :34 |
I2C read คืน tuple → unpack | อ่าน game.keys() ท่าเดียวกัน |
| หาร 9.81 | :36 |
หน่วยฟิสิกส์ m/s²→g, ax ≈ sinθ |
คูณ/หารเลขธรรมดา |
| deadzone | :39-42 |
ตัด noise แบบ on/off | if + abs() |
| map → move | :45 |
ax·SPEED รวมทิศ+ความเร็วในเลขเดียว |
box.move clamp ขอบ (engine) |
| หน่วงเฟรม | :47 |
คุมเฟรมเรต ~30 fps | while True game loop |
:18, :34-36, :45 — ที่เหลือคือโครงเกมเดิมที่เราคุ้นแล้ว (ตรงกับ 70/30 ในสไลด์ก่อน)ถ้าจับจังหวะไต่ระดับนี้ได้ น้องจะต่อเซนเซอร์ตัวใหม่เองได้โดยไม่ต้องรอเฉลย เพราะโครงมันซ้ำเดิมเสมอ
ที่มา — วันนี้ต่อยอดจากตรงไหน ย้อนไปสไลด์ "accelerometer คืออะไร" เราบอกว่าก้อนตัวเลขจากเซนเซอร์คือ analog ของจริงในโลก แล้วสไลด์ "ฟิสิกส์เบื้องหลัง" อธิบายว่าทำไมค่ามันแทนมุมเอียงได้ (ax ≈ sinθ) ปัญหาดั้งเดิมที่เรากำลังแก้คือ "จะเอาปริมาณจริงในโลก (แรงโน้มถ่วง) มาเป็นคำสั่งในโปรแกรมได้ยังไง" — สไลด์ "จาก ค่าเอียง ไปเป็น การขยับ" คือคำตอบสั้น ๆ: อ่าน → map → ขยับ
ที่ไป — ทักษะเล็กนี้จะโตเป็นอะไร สี่จังหวะ อ่าน→แปลง→กรอง→ขยับ ที่น้องเพิ่งเขียน คือ แม่แบบของการอ่าน peripheral ทุกชนิด เปลี่ยน BMI270 เป็นไมโครโฟน แสง หรือปุ่มอนาล็อก โครงก็เหมือนเดิม คาบหน้า (Shooter: บังคับมีน้ำหนัก) เราจะเอา input แบบนี้ไปคุมยานที่ มีแรงเฉื่อย ไม่หยุดทันทีเหมือนกล่องวันนี้ ตรงกับที่สไลด์ "ดูแบบเคลื่อนไหว" ทิ้งท้ายไว้ว่าอยากให้ "ไหลต่อเหมือนลูกแก้ว"
ลองตอบสามข้อนี้ในใจ นี่แหละคือการเชื่อมจุดด้วยตัวเอง:
ax ≈ sinθ จากสไลด์ "ฟิสิกส์เบื้องหลัง" ได้ไหม ถ้าค่านั้นคือ "มุมเอียง" แล้วเราเอาไปคูณ SPEED มันกลายเป็น "ความเร็ว" ได้ยังไง (ใบ้: ตัวคูณเปลี่ยนความหมายของตัวเลข)sensors.bmi270.acceleration() แล้ว map ไป box.move มันคือเรื่องเดียวกับ game.keys() แล้วเลือกทิศจากคาบปุ่ม ต่างแค่ input มาจากเซนเซอร์แทนนิ้วถ้าตอบได้ว่า "อ๋อ เซนเซอร์ก็คือปุ่มอีกแบบ" — นั่นคือการหยั่งรู้ที่อยากให้เกิด: input ทุกชนิดคือ "ตัวเลขที่เราเอาไป map"
โค้ด map ค่าความเร่งสั้น ๆ ที่น้องเพิ่งเขียน คือหัวใจเดียวกับของพวกนี้ ทุกอันมีเครื่องจริงในโลก:
box.move(ax * SPEED, ay * SPEED) (:45) เอียงเครื่องเป็นพวงมาลัยDEADZONEread → update → wait อันเดียวกับ time.sleep_ms(30) (:47) แค่ actuator เป็นมอเตอร์แทนกล่องสี่กรณีนี้ ไม่มีอันไหนเป็นของสมมติเลย ทุกอันมีอุปกรณ์จริงในกระเป๋าน้องที่ทำงานด้วยหลักการเดียวกับโค้ดวันนี้อยู่
ลองหยิบสักข้อไปคิดต่อ ไม่มีคำตอบเดียวตายตัว ทุกข้อโยงกลับไปหาโปรเจกต์จริงได้:
:36 แล้วหา ALPHA ที่ลื่นแต่ไม่หน่วงจนคุมยาก เขียนค่าที่เลือก + เหตุผลลงใบงานvx (ความเร็ว) ไว้ แล้วให้ความเอียง บวกเพิ่ม vx แทนกำหนดตำแหน่งตรง ๆ กล่องจะเคลื่อนต่างจากวันนี้ยังไงเลือกมาสักข้อ แล้วเขียนลงใบงานว่า "ถ้าเป็นเรา จะออกแบบยังไง" ไม่ต้องถูกตั้งแต่แรก ขอแค่คิดต่อจากสี่จังหวะที่พิมพ์เองวันนี้ — ตรงนั้นแหละที่น้องเริ่มเป็นวิศวกรเซนเซอร์ ไม่ใช่แค่คนพิมพ์ตามเฉลย
fit-css