Sensor hacking 101

Sensor hacking 101

Every smartphone sold in the last decade carries a cluster of MEMS sensors — tiny silicon structures that move when you move, bend when air pressure changes, and deflect in magnetic fields. Most owners never see the numbers. They feel a smooth glass rectangle, while underneath it a bundle of transducers samples the physical world at 50–200 Hz. Phyzix exists to put those numbers on screen, in SI units, with no smoothing you did not ask for. Before you build anything serious on sensor data (a parking walk in Wheria, a classroom lab, a vibration baseline for a failing pump) you need to know what each chip actually measures, because the spec sheet is consistently more optimistic than the silicon.

Accelerometers measure specific force, not motion

The most common misconception in introductory sensor work is that an accelerometer reports how fast you are moving. It does not. A MEMS accelerometer measures specific force: the net contact force per unit mass acting on a microscopic proof mass suspended inside the chip, expressed in m/s². Lay a phone flat on a table and the vertical axis reads about −9.81 m/s², with the sign depending on axis convention. The phone is going nowhere. The table is pushing up against gravity, and the sensor reports that reaction force. Drop the same phone in a vacuum chamber and all three axes head toward zero, because free fall involves no contact force at all. In symbols the output is f = a − g: proper acceleration minus the gravitational field, resolved in the sensor frame.

That one distinction separates working sensor fusion from code that integrates garbage into position. Double integration amplifies a constant bias as Δx = ½·b·t². A respectable 1 mg bias (0.0098 m/s²) becomes 4.4 m of position error after 30 s, and consumer chips are rarely that polite for long. This is why Wheria never double-integrates accelerometer data for position, and why the blue dot in naive IMU demos drifts across the parking lot inside 30 s. Phyzix shows the raw trace on a live graph so you can watch the physics directly. Shake the phone and oscillations ride on top of the gravity vector. Rotate it slowly and the 9.81 migrates from one axis to the next as the sensor frame turns. Students who expected zero at rest tend to go quiet for a moment.

One warning about convenience APIs. Both platforms offer a derived linear-acceleration stream with gravity already subtracted, and it is tempting to treat that as ground truth. It is the output of the OS's own fusion filter, complete with that filter's lag and assumptions. Phyzix deliberately graphs the raw stream first, so you learn what the estimate is built from before you decide to trust it.

Watching a gyroscope drift

A gyroscope outputs angular velocity in rad/s around each axis, and it needs no gravity reference. Inside the package a proof mass vibrates along one axis; rotating the chip adds a Coriolis acceleration a_c = 2·Ω×v at right angles to that vibration, and capacitive electrodes read the deflection. Direct rotation sensing makes gyros excellent at tracking fast orientation changes between steps in a parking garage. The catch is bias. Consumer MEMS gyros drift 1–3° per minute while sitting perfectly still, and the integral θ(t) = ∫ω·dt faithfully accumulates every bit of it. Phyzix ships a small experiment for exactly this: spin the phone on a table against a printed protractor, then compare the integrated angle with what the paper says. The gap is visible after 10 s. Because of that gap, every practical orientation system fuses the gyro with something absolute: the accelerometer for tilt whenever linear acceleration is small, the magnetometer for yaw whenever the field is clean. It is also why Wheria corrects heading with magnetometer updates while the field is trustworthy, then falls back to gyro-only dead reckoning the moment steel pillars make the compass lie.

The classic entry point is a complementary filter, which Phyzix exposes as a toggle on the orientation screen:

// per sample, dt in seconds
gyro_pitch = pitch + w_x * dt        // fast, drifts
acc_pitch  = atan2(a_y, a_z)         // absolute, noisy
pitch = 0.98 * gyro_pitch + 0.02 * acc_pitch

Two lines of fusion and one tuning constant. The 0.98 says: trust the gyro over fractions of a second, and let the accelerometer pull the estimate back over a few seconds. A Kalman filter is the grown-up version of the same bargain, with weights derived from noise statistics rather than tuned by eye — that derivation lives in our Kalman filter post.

The magnetometer draws an ellipsoid

The magnetometer returns a three-component vector of the local magnetic field in microtesla. Earth's field runs 25–65 µT depending on latitude and local geological anomalies; around Ho Chi Minh City it sits near 43 µT. In open air the vector points toward magnetic north with a predictable inclination angle. Inside a parking garage everything changes. Steel reinforcement and your own car distort the field until the apparent north rotates 40–90° while the phone lies motionless. Plot uncalibrated samples in 3D as you rotate the phone and they trace an off-center ellipsoid instead of a sphere centered at the origin. The constant offset is hard-iron bias from nearby ferromagnetic parts, typically tens of µT on the handsets we test. The stretching is soft-iron distortion from the surrounding steel. Phyzix draws raw and calibrated vectors side by side, so you can watch the ellipsoid recenter and round out as you walk from the street down into Landmark 81's basement. The correction itself is a figure-8 motion, or slow online fitting during straight walks; either way it is the same pipeline Wheria runs in production, documented in our compass calibration post.

Barometers, light, and the environment

The barometer reports absolute atmospheric pressure in hPa. Hydrostatics gives ΔP = ρ·g·Δh, roughly 12 Pa per meter of height near sea level, so one garage floor of 3.2 m is a 38 Pa step. Weather fronts move the absolute reading by hundreds of pascals over a few hours. Only the differential since you tapped "parked" carries floor information, high-pass filtered to reject that slow drift. Phyzix shows absolute pressure alongside the delta from a user-set baseline; the full floor-counting math, including why 0.3–1 Pa RMS of sensor noise still resolves a 38 Pa step cleanly, is in the barometer parking post.

The ambient light sensor rounds out the environmental set. It reports illuminance in lux and makes a decent inverse-square demo: measure a desk lamp at 0.5 m and again at 1 m, then check the ratio against E ∝ 1/r². Expect a factor near 4, minus whatever the wall reflections contribute. Many models saturate in direct sunlight and quantize coarsely at the dim end, which is itself a useful lesson about instrument range.

Bench notes: six phones on a granite slab

Numbers in posts like this should come from somewhere. Last spring we clamped 6 phones (3 iPhone, 3 Android) to a granite surface plate for 48 h and logged every sensor at its maximum advertised rate. Granite does not fidget. Every wiggle in those logs is the sensor's own opinion.

  • Accelerometer: 1.5–4 mg RMS of noise at 100 Hz depending on the handset, and the bias moved by up to 8 mg between cold start and thermal equilibrium some 20 min later. Thermal drift dwarfs the white noise on a timescale of minutes.
  • Gyroscope: 0.05–0.12 °/s RMS at rest, with integrated heading wandering 1–3° per minute — matching the folklore for once.
  • Magnetometer: 0.3–1.2 µT RMS, plus a 6 µT step on one device the instant a charging cable was plugged in. Current makes fields, and the chip cannot know the field is your charger.
  • Barometer: 0.3–1 Pa RMS once warm, but the first 10 min of each log showed a 4–6 Pa slope from self-heating. That is about 0.4 m of phantom altitude, which matters when a floor is worth 38 Pa.

One Android also promised 200 Hz and delivered 197 Hz with visible timestamp jitter. Integrate against per-sample timestamps, always. The nominal rate is a marketing number.

From classroom to production pipeline

The gap between reading sensor values and navigating a user back to their car is signal processing plus honest uncertainty. Phyzix is deliberately the first half of that journey: raw SI units, live graphs, exportable CSV, bilingual labels so Vietnamese students do not have to parse English error strings. Wheria is the second half: step detection from accelerometer peaks, heading fusion from gyro and magnetometer, floor hints from barometer deltas, all combined in a Kalman filter that outputs a confidence badge instead of a fake precise dot. To follow the full chain, start with step detection in our IMU odometry post, then read how the pieces fuse in the indoor navigation pipeline; the classroom side of the same instrument set is covered in the pocket lab post. The physics is the same in both apps. Phyzix just shows you the wires.

Điện thoại nào bán trong mười năm qua cũng giấu một cụm sensor MEMS — mấy cấu trúc silic tí hon rung theo tay bạn, cong theo áp suất không khí, lệch theo từ trường. Chủ máy hầu như không bao giờ thấy con số. Họ chỉ thấy tấm kính mượt. Bên dưới, cả bó transducer đang sample thế giới vật lý ở 50–200 Hz. Phyzix sinh ra để bày đúng những con số đó lên màn hình, đơn vị SI, không làm mượt gì nếu bạn không yêu cầu. Muốn xây thứ nghiêm túc từ dữ liệu sensor (walk tìm xe trong Wheria, lab trên lớp, baseline rung cho cái bơm sắp hỏng) thì phải biết từng con chip đo đúng cái gì, vì spec sheet lúc nào cũng lạc quan hơn miếng silic.

Gia tốc kế đo lực riêng, không đo chuyển động

Hiểu lầm kinh điển nhất khi mới học sensor: gia tốc kế báo bạn đang di chuyển nhanh cỡ nào. Sai. Chip MEMS đo lực riêng — lực tiếp xúc ròng trên đơn vị khối lượng tác lên khối proof mass treo trong chip, đơn vị m/s². Đặt máy nằm phẳng trên bàn, trục dọc đọc quanh −9,81 m/s², dấu tùy quy ước trục. Máy chẳng gia tốc đi đâu cả. Cái bàn đang đẩy ngược trọng lực và sensor ghi đúng phản lực đó. Thả máy trong buồng chân không thì cả ba trục tiến về 0, vì rơi tự do không có lực tiếp xúc. Viết gọn: f = a − g, gia tốc thật trừ trường trọng lực, tính trong hệ quy chiếu của sensor.

Phân biệt được điểm này thì fusion mới chạy. Tích phân hai lần khuếch đại bias hằng theo Δx = ½·b·t². Bias 1 mg (0,0098 m/s²) thành 4,4 m sai số vị trí sau 30 s, mà chip consumer hiếm khi ngoan được lâu vậy. Nên Wheria không bao giờ tích phân gia tốc hai lần để lấy vị trí. Cũng vì vậy chấm xanh trong mấy demo IMU ngây thơ trôi hết bãi xe trong nửa phút. Mở Phyzix, đồ thị live đơn vị SI cho xem tận mắt. Lắc máy: dao động cưỡi lên vector trọng lực. Xoay chậm: con số 9,81 bò từ trục này sang trục kia theo khung máy. Học sinh nào đinh ninh đứng yên là đọc 0 thường im lặng một lúc.

Cảnh giác với API tiện lợi. Cả hai nền tảng đều phát stream linear acceleration đã trừ sẵn trọng lực, nghe rất đáng tin. Thực ra đó là output của bộ lọc fusion trong OS, kèm luôn độ trễ và giả định của chính bộ lọc đó. Phyzix cố tình vẽ stream thô trước, để bạn biết ước lượng kia được dựng từ cái gì rồi hãy tin.

Nhìn con quay drift theo thời gian thực

Con quay xuất vận tốc góc rad/s quanh từng trục, không cần trọng lực làm mốc. Trong chip có khối rung dọc một trục; máy xoay thì gia tốc Coriolis a_c = 2·Ω×v đẩy khối lệch ngang, điện cực tụ đọc độ lệch. Đo quay trực tiếp nên con quay bắt thay đổi hướng nhanh giữa các bước dưới hầm rất tốt. Kẹt ở bias. Con quay MEMS consumer drift 1–3° mỗi phút dù nằm im hoàn toàn. Tích phân θ(t) = ∫ω·dt gom trung thành từng chút một. Phyzix có sẵn bài thí nghiệm nhỏ: xoay máy trên bàn cạnh thước đo góc in giấy rồi so góc tích phân với tờ giấy. 10 s là thấy lệch. Vì cái lệch đó, mọi hệ định hướng thực tế phải fusion con quay với một nguồn tuyệt đối: gia tốc kế cho độ nghiêng khi gia tốc tuyến tính nhỏ, từ kế cho yaw khi từ trường sạch. Cũng vì nó, Wheria sửa heading bằng cập nhật từ kế lúc từ trường còn đáng tin, rồi rơi về dead reckoning thuần con quay ngay khi cột thép làm la bàn nói dối.

Cửa ngõ kinh điển là complementary filter, Phyzix để hẳn một toggle trên màn orientation:

// mỗi sample, dt tính bằng giây
gyro_pitch = pitch + w_x * dt        // nhanh, drift
acc_pitch  = atan2(a_y, a_z)         // tuyệt đối, nhiễu
pitch = 0.98 * gyro_pitch + 0.02 * acc_pitch

Hai dòng fusion, một hằng số tuning. Con số 0,98 nghĩa là: tin con quay trong tích tắc, để gia tốc kế kéo ước lượng về trong vài giây. Kalman filter là bản người lớn của đúng thỏa thuận này, trọng số suy từ thống kê nhiễu chứ không chỉnh bằng mắt — dẫn xuất đầy đủ nằm trong bài Kalman filter.

Từ kế vẽ ra một ellipsoid

Từ kế trả vector ba thành phần của từ trường cục bộ, đơn vị microtesla. Từ trường Trái Đất chạy 25–65 µT tùy vĩ độ và anomaly địa chất; quanh Sài Gòn cỡ 43 µT. Ngoài trời, vector chỉ về bắc từ với góc chúi dự đoán được. Xuống hầm xe thì khác hẳn. Cốt thép và chính chiếc xe của bạn méo từ trường tới mức hướng "bắc" xoay 40–90° trong khi máy nằm im. Vẽ mẫu chưa hiệu chuẩn lên 3D lúc xoay máy sẽ ra một ellipsoid lệch tâm thay vì mặt cầu quanh gốc. Phần offset hằng là hard-iron bias từ linh kiện sắt từ gần chip, thường cỡ vài chục µT trên mấy máy bọn mình test. Phần kéo giãn là soft-iron từ khối thép xung quanh. Phyzix vẽ vector thô và vector đã hiệu chuẩn cạnh nhau, nên bạn nhìn được ellipsoid tự kéo về tâm và tròn lại khi đi bộ từ mặt đường xuống hầm Landmark 81. Cách sửa là múa số 8, hoặc fit online chậm lúc đi thẳng; đằng nào cũng đúng pipeline Wheria chạy production, kể kỹ trong bài hiệu chuẩn la bàn.

Áp kế, ánh sáng và môi trường

Áp kế báo áp suất khí quyển tuyệt đối, đơn vị hPa. Thủy tĩnh cho ΔP = ρ·g·Δh, cỡ 12 Pa mỗi mét gần mực nước biển, nên một tầng hầm 3,2 m là một bậc 38 Pa. Thời tiết đẩy giá trị tuyệt đối cả trăm pascal trong vài giờ. Chỉ phần sai phân từ lúc bấm "đã đỗ" mới mang thông tin tầng, lọc high-pass để bỏ drift chậm. Phyzix hiện áp tuyệt đối cạnh delta so với baseline bạn tự đặt; toán đếm tầng đầy đủ, kể cả vì sao nhiễu sensor 0,3–1 Pa RMS vẫn phân giải sạch bậc 38 Pa, nằm trong bài toán áp kế hầm xe.

Cảm biến ánh sáng chốt bộ môi trường. Nó báo lux và làm demo nghịch đảo bình phương khá ổn: đo đèn bàn ở 0,5 m rồi 1 m, so tỉ lệ với E ∝ 1/r². Kỳ vọng ra gần 4, trừ hao phản xạ từ tường. Nhiều model bão hòa dưới nắng trực tiếp và lượng tử hóa thô ở vùng tối — bản thân chuyện đó cũng là một bài học về dải đo của dụng cụ.

Sổ bench: 6 máy trên bàn đá granite

Số liệu trong bài phải có xuất xứ. Mùa xuân rồi bọn mình kẹp 6 máy (3 iPhone, 3 Android) lên bàn máp granite suốt 48 tiếng, log mọi sensor ở tốc độ tối đa nhà sản xuất quảng cáo. Đá granite không nhúc nhích. Mọi gợn trong log là ý kiến riêng của sensor.

  • Gia tốc kế: nhiễu 1,5–4 mg RMS ở 100 Hz tùy máy; bias xê dịch tới 8 mg giữa lúc khởi động lạnh và lúc cân bằng nhiệt khoảng 20 phút sau. Drift nhiệt lấn át hẳn nhiễu trắng ở thang phút.
  • Con quay: 0,05–0,12 °/s RMS khi đứng yên; heading tích phân lang thang 1–3° mỗi phút, khớp đúng con số dân gian hay kể.
  • Từ kế: 0,3–1,2 µT RMS, cộng một bậc nhảy 6 µT trên một máy đúng khoảnh khắc cắm cáp sạc. Dòng điện sinh từ trường, mà chip đâu biết từ trường đó là cục sạc của bạn.
  • Áp kế: 0,3–1 Pa RMS khi đã ấm máy, nhưng 10 phút đầu mỗi log dốc xuống 4–6 Pa do máy tự nóng. Tương đương 0,4 m độ cao ảo — đáng kể khi một tầng chỉ đáng 38 Pa.

Một máy Android hứa 200 Hz nhưng giao 197 Hz kèm jitter thấy rõ trên timestamp. Tích phân thì bám timestamp từng sample. Tần số danh định chỉ là con số marketing.

Từ lớp học tới pipeline production

Khoảng cách giữa đọc được giá trị sensor và dẫn user về đúng chỗ đỗ xe là xử lý tín hiệu cộng uncertainty thật thà. Phyzix chủ đích làm nửa đầu chặng đường: đơn vị SI thô, đồ thị live, export CSV, nhãn song ngữ để học sinh Việt khỏi phải dịch chuỗi lỗi tiếng Anh. Wheria là nửa sau: phát hiện bước từ peak gia tốc kế, fusion heading từ con quay và từ kế, gợi ý tầng từ delta áp kế, tất cả gom vào Kalman filter xuất badge tin cậy thay vì một chấm chính xác giả. Muốn theo trọn chuỗi, bắt đầu với phát hiện bước trong bài IMU odometry, rồi đọc cách các mảnh ghép fusion trong pipeline indoor navigation; phía lớp học của cùng bộ dụng cụ nằm trong bài pocket lab. Vật lý hai app giống hệt nhau. Phyzix chỉ cho bạn thấy dây nối.