完全指南:從I2C讀取到航向角校準(zhǔn)與傾斜補(bǔ)償)
簡(jiǎn)介面向電子開(kāi)發(fā)與嵌入式愛(ài)好者的HMC5883L三軸數(shù)字羅盤模塊資料包聚焦霍尼韋爾高集成弱磁傳感器芯片的原理與應(yīng)用。HMC5883L采用各向異性磁阻AMR技術(shù)內(nèi)置12位ADC與自檢功能可在±8高斯磁場(chǎng)中實(shí)現(xiàn)5毫高斯分辨率適合手機(jī)、汽車導(dǎo)航及個(gè)人導(dǎo)航系統(tǒng)等低成本磁場(chǎng)檢測(cè)場(chǎng)景。資料對(duì)芯片的3.0×3.0×0.9mm LCC表面封裝、16引腳定義、I2C總線接口、低電壓2.16-3.6V與微功耗100uA特性進(jìn)行了梳理并介紹了羅盤精度控制在1°~2°時(shí)的校準(zhǔn)與消磁思路可作為選型評(píng)測(cè)和驅(qū)動(dòng)開(kāi)發(fā)的參考。壓縮包為RAR格式大小約543KB目前已有612人學(xué)習(xí)下載上手快速可作為HMC5883L入門與項(xiàng)目驗(yàn)證的基礎(chǔ)資料。1. HMC5883L 是什么一顆三軸磁力計(jì)帶你從“讀磁場(chǎng)”走到“算方向”第一次拿到 HMC5883L 模塊的人多半是在做小車導(dǎo)航或者飛控。插上電I2C 掃描出 0x1E 地址讀回一串 XYZ 原始值然后對(duì)著 datasheet 里的公式算出一個(gè)角度結(jié)果角度在屋里亂飄——以為是模塊壞了其實(shí)是沒(méi)搞清楚這顆芯片的作用邊界。HMC5883L 是一個(gè)三軸磁力計(jì)也就是電子羅盤核心它能感知地磁場(chǎng)在芯片三個(gè)軸向上的分量配合加速度計(jì)做傾斜補(bǔ)償后輸出相對(duì)磁北的航向角。它解決的是“方向從哪來(lái)”的問(wèn)題不是“位置在哪”的問(wèn)題跟 GPS 互補(bǔ)。適合做電子羅盤、機(jī)器人定向、天線云臺(tái)指北這類需要絕對(duì)航向參考的場(chǎng)景。這一篇就照著“原理 → 接線 → 讀數(shù) → 校準(zhǔn) → 排障 → 融合”的順序把 HMC5883L 從模塊資料到落地調(diào)試講透。2. 把 HMC5883L 當(dāng)黑匣子之前先摸清數(shù)據(jù)鏈路2.1 關(guān)鍵參數(shù)與選型理由HMC5883L 是典型的低成本三軸磁阻傳感器接口只有 I2C芯片內(nèi)部有 12 位 ADC測(cè)量范圍默認(rèn) ±1.3 高斯分辨率到 5 毫高斯級(jí)別。功耗很低3.3V 供電時(shí)工作電流不到 1mA這對(duì)電池供電的小車和手持設(shè)備非常友好。它的數(shù)據(jù)更新率從 0.75Hz 到 75Hz 可配日常低成本航向參考取 15Hz 夠用做動(dòng)態(tài)姿態(tài)解算可以放到 30Hz。選它而不是直接買一個(gè)“串口輸出航向角”的成品模塊原因很現(xiàn)實(shí)成品模塊把算法封裝死了校準(zhǔn)參數(shù)不透明安裝方向和你的機(jī)械結(jié)構(gòu)一旦對(duì)不上很難從外部調(diào)整。HMC5883L 給的是原始磁場(chǎng)分量校準(zhǔn)、坐標(biāo)變換、濾波都在你手里控制。同樣的模塊在不同項(xiàng)目里可以接不同的解算邏輯這也是社區(qū)里流傳的資料大多數(shù)圍繞它而不是成品模塊的原因。實(shí)際拿到的模塊板上可能多焊了一顆穩(wěn)壓芯片和上拉電阻有些版本還把電平轉(zhuǎn)換做進(jìn)去了5V 單片機(jī)和 3.3V 單片機(jī)都能直連這一點(diǎn)在選型時(shí)比芯片本身更值得確認(rèn)。2.2 接線與 I2C 地址探測(cè)接線圖很簡(jiǎn)短VCC 接 3.3VGND 接地SCL 和 SDA 分別接主控的 I2C 時(shí)鐘和數(shù)據(jù)線。大多數(shù)現(xiàn)成模塊自帶 4.7kΩ 上拉不需要再加如果是自己畫(huà)的板子記得給 SCL 和 SDA 各加一顆上拉電阻否則總線波形爬升慢高速通信時(shí)容易讀到錯(cuò)數(shù)據(jù)。HMC5883L 的固定 7 位地址是 0x1E很多國(guó)產(chǎn)替代芯片會(huì)把地址改成 0x0D所以拿到模塊第一步不是讀數(shù)據(jù)而是先掃描總線。import smbus bus smbus.SMBus(1) for addr in range(0x08, 0x78): try: bus.read_byte(addr) print(found device at 0x%02X % addr) except Exception: pass這段代碼用 smbus 遍歷 I2C 總線上所有可能的 7 位地址讀一個(gè)字節(jié)試探是否有設(shè)備應(yīng)答。參數(shù)上要注意SMBus(1)對(duì)應(yīng)樹(shù)莓派這類主控的 I2C1 總線不同板子的總線號(hào)可能不同用i2cdetect -l查一下再填。如果掃描出來(lái)的是 0x0D 而不是 0x1E不用慌后續(xù)所有寄存器操作把地址替換成掃描結(jié)果即可寄存器布局是一樣。2.3 寄存器布局與配置選項(xiàng)HMC5883L 的寄存器不多核心就六個(gè)。配置寄存器 A0x00控制采樣平均數(shù)和數(shù)據(jù)輸出速率配置寄存器 B0x01控制增益模式寄存器0x02控制連續(xù)測(cè)量、單次測(cè)量和睡眠數(shù)據(jù)寄存器 0x03 到 0x08 依次存放 X、Y、Z 軸的高位和低位。配置寄存器 A 默認(rèn)值 0x10 對(duì)應(yīng) 8 次采樣平均、15Hz 輸出速率這個(gè)默認(rèn)值對(duì)大多數(shù)場(chǎng)景夠用。如果電機(jī) PWM 干擾明顯可以把平均次數(shù)提到 4 或 8代價(jià)是輸出延遲變大。增益的選擇要看應(yīng)用場(chǎng)景。默認(rèn)配置 B 是 0x20滿量程 ±1.3 高斯分辨率最高。問(wèn)題在于 HMC5883L 的 ADC 只有 12 位如果周圍磁場(chǎng)偏強(qiáng)讀數(shù)會(huì)溢出表現(xiàn)為數(shù)值突然跳到負(fù)滿量程或正滿量程。遇到這種情況就要把增益調(diào)低比如配 0x1C±1.9 高斯或 0x18±2.5 高斯。我一般先保持默認(rèn)采集一輪數(shù)據(jù)看最大最小值有沒(méi)有貼近 ±2048如果貼近了就降增益別一上來(lái)就調(diào)。2.4 讀原始數(shù)據(jù)的最小代碼import smbus import time bus smbus.SMBus(1) addr 0x1E bus.write_byte_data(addr, 0x02, 0x01) # 模式寄存器0x01 表示單次測(cè)量 def read_mag(): bus.write_byte_data(addr, 0x02, 0x01) # 每次讀取前重新觸發(fā)單次測(cè)量 time.sleep(0.01) data bus.read_i2c_block_data(addr, 0x03, 6) # 從 0x03 連續(xù)讀 6 字節(jié) x (data[0] 8) | data[1] y (data[2] 8) | data[3] z (data[4] 8) | data[5] if x 32768: x - 65536 if y 32768: y - 65536 if z 32768: z - 65536 return x, y, z for _ in range(10): x, y, z read_mag() print(x, y, z) time.sleep(0.1)這段代碼做了三件事把模式寄存器寫成單次測(cè)量從數(shù)據(jù)寄存器連續(xù)讀 6 字節(jié)再把高低字節(jié)合成有符號(hào) 16 位整數(shù)。注意 HMC5883L 的數(shù)據(jù)寄存器是 16 位左對(duì)齊讀出來(lái)的原始值范圍在 ±2048 左右不是 ±32768。代碼里if x 32768的符號(hào)擴(kuò)展是為了應(yīng)對(duì)某些兼容芯片輸出范圍更大的情況對(duì)原廠芯片不會(huì)觸發(fā)。單次測(cè)量模式比連續(xù)模式省電而且每次讀取都是完整的一次采樣不會(huì)拿到新舊混拼的數(shù)據(jù)這在后面排障章節(jié)會(huì)展開(kāi)說(shuō)。3. 從原始值到航向角單位換算、atan2 與濾波參數(shù)3.1 LSB 到磁通量的換算原始值本身沒(méi)有物理意義要把它換算成毫高斯才有用。HMC5883L 的輸出 磁場(chǎng)強(qiáng)度 × 增益對(duì)應(yīng)的 LSB/Gauss。不同增益下的換算系數(shù)不同這是最容易抄錯(cuò)的地方。默認(rèn) 1.3 高斯量程對(duì)應(yīng) 1090 LSB/Gauss那么每個(gè) LSB 約等于 0.92 毫高斯如果把增益調(diào)到 0.88 高斯換算系數(shù)變成 1370 LSB/Gauss。增益寄存器值滿量程Ga分辨率LSB/Gauss每 LSB 對(duì)應(yīng)mGauss0x20默認(rèn)±1.310900.920x1C±1.98201.220x18±2.56601.520x14±4.04402.270x10±4.73902.560x0C±5.63303.030x08±8.12304.35實(shí)際工程中除非你要把測(cè)量結(jié)果和別的磁場(chǎng)數(shù)據(jù)做對(duì)照否則航向角計(jì)算可以直接用原始值。因?yàn)楹较蚪怯玫氖?X 和 Y 的比值換算系數(shù)會(huì)約掉。但校準(zhǔn)時(shí)必須用同一單位的數(shù)值不能混用。另一個(gè)細(xì)節(jié)如果你是在飛控里做數(shù)據(jù)融合建議先換算成毫高斯再進(jìn)狀態(tài)估計(jì)器這樣初始化的協(xié)方差矩陣物理意義明確調(diào)起來(lái)不用猜。3.2 水平放置時(shí)的航向角公式把模塊水平放平X 軸指向車頭或機(jī)頭此時(shí)航向角就是地磁場(chǎng)水平分量相對(duì)于 X 軸的夾角。用 atan2 而不是 atan因?yàn)?atan 會(huì)丟象限0° 到 90° 和 180° 到 270° 無(wú)法區(qū)分。import math def heading_from_xy(x, y): angle math.degrees(math.atan2(y, x)) if angle 0: angle 360.0 return angle這個(gè)函數(shù)把 atan2 的結(jié)果從 (-180°, 180°] 映射到 [0°, 360°)。話說(shuō)回來(lái)角度 0° 表示 X 軸當(dāng)前指向磁北。如果模塊安裝時(shí) X 軸轉(zhuǎn)了 90 度直接把這個(gè)角度加一個(gè)安裝偏置即可不要在代碼里反復(fù)交換 X 和 Y。實(shí)際測(cè)試時(shí)你會(huì)發(fā)現(xiàn)數(shù)值在 0° 和 360° 交界處會(huì)來(lái)回跳那是后續(xù)要處理的臨界抖動(dòng)問(wèn)題。3.3 濾波讓角度不亂串HMC5883L 的數(shù)據(jù)在車內(nèi)、樓內(nèi)測(cè)試時(shí)容易有一兩個(gè)點(diǎn)的跳變直接進(jìn)控制環(huán)會(huì)讓舵機(jī)跟著抖。常見(jiàn)做法是先做滑動(dòng)平均再算角度或者先算角度再做一階低通兩者表現(xiàn)不太一樣先平均原始值對(duì)高頻噪聲更友好先算角度再低通對(duì)偶發(fā)跳變更友好。我習(xí)慣先對(duì) XYZ 各做滑動(dòng)平均再進(jìn) atan2因?yàn)檫@樣能順帶壓低磁場(chǎng)向量的指向噪聲。from collections import deque class MagFilter: def __init__(self, window15): self.window window self.buf deque(maxlenwindow) def update(self, x, y, z): self.buf.append((x, y, z)) sx sum(v[0] for v in self.buf) sy sum(v[1] for v in self.buf) sz sum(v[2] for v in self.buf) n len(self.buf) return sx / n, sy / n, sz / n窗口大小的選擇要看輸出頻率。15Hz 輸出時(shí)窗口取 15 意味著約 1 秒的滯后低速小車夠用如果做 75Hz 輸出、窗口 15滯后約 0.2 秒室內(nèi)飛行器也能接受。窗口越大越平滑但轉(zhuǎn)向時(shí)航向角會(huì)拖尾轉(zhuǎn)彎后要 1 秒多才能收斂到真實(shí)方向這是新手容易忽略的取舍。一階低通的系數(shù) α 在 0.1 到 0.2 之間比較合適太大等于沒(méi)濾波太小轉(zhuǎn)彎后回正速度慢得讓人著急。4. 校準(zhǔn)是繞不過(guò)去的門檻硬磁軟磁偏置與擬合4.1 為什么出廠的原始讀數(shù)不能直接用HMC5883L 出廠時(shí)確實(shí)做過(guò)靈敏度校正但那是針對(duì)芯片本身的你手里的模塊裝到 PCB 上、焊上排針、旁邊再放幾顆螺絲情況就變了。焊錫和 PCB 走線會(huì)產(chǎn)生固定的偏置磁場(chǎng)這叫硬磁干擾周圍的鐵磁性材料會(huì)扭曲磁場(chǎng)方向這叫軟磁干擾。硬磁干擾直接讓測(cè)量圓偏離原點(diǎn)軟磁干擾讓圓變成橢圓。不加校準(zhǔn)就拿原始值算角度結(jié)果就是轉(zhuǎn)動(dòng)模塊時(shí)航向角非線性某個(gè)方向偏 5 度另一個(gè)方向偏 20 度毫無(wú)規(guī)律。做校準(zhǔn)不需要轉(zhuǎn)臺(tái)也不需要標(biāo)準(zhǔn)磁場(chǎng)源。地磁場(chǎng)在固定位置是均勻的你拿著模塊在水平面轉(zhuǎn)一整圈X 軸和 Y 軸的讀數(shù)在理想情況下應(yīng)該落在一個(gè)圓上。圓偏離原點(diǎn)是硬磁圓被壓成橢圓是軟磁。校準(zhǔn)就是把這組數(shù)據(jù)恢復(fù)成“圓心在原點(diǎn)的正圓”。4.2 采集一圈數(shù)據(jù)過(guò)程與代碼校準(zhǔn)采集的要點(diǎn)是慢、均勻、覆蓋全。把模塊水平放置從正北開(kāi)始逆時(shí)針勻速轉(zhuǎn) 360 度轉(zhuǎn)一圈大約 30 秒采樣 300 到 500 個(gè)點(diǎn)。轉(zhuǎn)速太快會(huì)導(dǎo)致每圈覆蓋不完整轉(zhuǎn)兩圈能消除偶發(fā)抖動(dòng)的影響。不要只在桌面上轉(zhuǎn)手持模塊在不同高度轉(zhuǎn)一圈更好能覆蓋到不同傾角下的 Z 軸數(shù)據(jù)雖然水平航向角只用 X 和 Y但 Z 軸偏置在后續(xù)傾斜補(bǔ)償里用得上。import smbus import time bus smbus.SMBus(1) addr 0x1E samples [] def read_raw(): bus.write_byte_data(addr, 0x02, 0x01) time.sleep(0.01) data bus.read_i2c_block_data(addr, 0x03, 6) x (data[0] 8) | data[1] y (data[2] 8) | data[3] z (data[4] 8) | data[5] if x 32768: x - 65536 if y 32768: y - 65536 if z 32768: z - 65536 return x, y, z print(start rotating the board...) start time.time() while time.time() - start 30: x, y, z read_raw() samples.append((x, y, z)) time.sleep(0.05) xs [s[0] for s in samples] ys [s[1] for s in samples] zs [s[2] for s in samples] x_off (max(xs) min(xs)) / 2 y_off (max(ys) min(ys)) / 2 z_off (max(zs) min(zs)) / 2 x_scale (max(xs) - min(xs)) / 2 y_scale (max(ys) - min(ys)) / 2 print(offsets:, x_off, y_off, z_off) print(scales:, x_scale, y_scale)這段采集代碼跑 30 秒每次讀取間隔 50ms得到約 600 個(gè)點(diǎn)。參數(shù)上 0.05 秒的間隔配合 20Hz 實(shí)際采樣率不會(huì)丟數(shù)據(jù)也不會(huì)因?yàn)榭偩€上有其他設(shè)備搶占而卡死。采集完成后代碼直接算出 XYZ 三軸的最大值和最小值取中點(diǎn)作為偏置取半跨度作為縮放比例。X 和 Y 的縮放比例如果差異超過(guò) 10%說(shuō)明軟磁干擾明顯下面要用縮放校正差異在 5% 以內(nèi)可以直接忽略只做偏置校正。4.3 最小二乘圓擬合更穩(wěn)的替代方式極值法簡(jiǎn)單但容易受少數(shù)離群點(diǎn)影響旋轉(zhuǎn)時(shí)手抖一下某個(gè)軸的最大值被一個(gè)壞點(diǎn)拉偏校準(zhǔn)結(jié)果就變了。更穩(wěn)的做法是最小二乘擬合圓。把 X 和 Y 的采樣點(diǎn)看成平面上的點(diǎn)集擬合出一個(gè)圓圓心坐標(biāo)就是硬磁偏置半徑就是理想磁場(chǎng)強(qiáng)度。這個(gè)方法對(duì)少量離群點(diǎn)不敏感而且能直接給出橢圓比作為軟磁干擾程度的量化指標(biāo)。圓擬合的計(jì)算量不大直接寫進(jìn)啟動(dòng)校準(zhǔn)流程的腳本里一次校準(zhǔn)只需要算一次不用擔(dān)心嵌入式平臺(tái)的算力。核心思想是構(gòu)建線性方程組求圓心 (a, b) 和半徑 R使得所有點(diǎn)到圓心的距離平方與 R2 的誤差最小。偏移后的 X 和 Y 再除以各自半跨度就把橢圓拉回正圓。校準(zhǔn)結(jié)果存到文件或?qū)懭?Flash 的固定區(qū)域運(yùn)行階段讀出來(lái)做實(shí)時(shí)校正不要在每次啟動(dòng)時(shí)重新采集。5. HMC5883L 常見(jiàn)問(wèn)題排查五個(gè)高頻坑的記錄5.1 現(xiàn)象I2C 掃描不到設(shè)備或讀回全 0、全 0xFF原因分三種模塊供電電壓不對(duì)SCL/SDA 沒(méi)有上拉地址不是 0x1E。前兩種最常見(jiàn)。有些模塊板載穩(wěn)壓芯片要求 5V 供電才能給內(nèi)部芯片提供穩(wěn)定的 3.3V你用 3.3V 直接供電也能跑但某些批次穩(wěn)壓芯片壓差不夠芯片上電不完整I2C 從設(shè)備不響應(yīng)。解決先看模塊絲印標(biāo)注 3.3V-5V 的用 5V 試一下標(biāo)注只支持 3.3V 的別強(qiáng)行上 5V會(huì)燒芯片。上拉問(wèn)題用示波器看 SCL 有沒(méi)有波形、有沒(méi)有明顯圓角沒(méi)有示波器就直接在 SDA 和 SCL 各加一顆 4.7kΩ 電阻到 VCC。還有就是我上面提過(guò)的地址可能不是 0x1E。某國(guó)產(chǎn)兼容芯片默認(rèn)地址是 0x0D寄存器布局和 HMC5883L 基本一致很多模塊商直接拿來(lái)當(dāng) HMC5883L 賣但實(shí)際上 datasheet 里的識(shí)別寄存器內(nèi)容和原廠不同。解決用本章前面給的掃描腳本全地址掃一遍掃到哪個(gè)用哪個(gè)。5.2 現(xiàn)象水平靜止放置航向角在 0° 和 360° 附近來(lái)回跳原因很直接角度映射到 [0°, 360°) 時(shí)359.5° 和 0.5° 的真實(shí)差異只有 1 度但數(shù)值上一個(gè)是 359 一個(gè)是 0滑動(dòng)平均把這兩個(gè)數(shù)放一起算結(jié)果直接跳到 180° 附近。這不是 HMC5883L 的錯(cuò)是后處理的問(wèn)題。解決濾波之前先判斷角度跳變?nèi)绻陆嵌群蜕弦淮屋敵鼋嵌戎畛^(guò) 300°就把新角度加上或減去 360° 再參與平均。prev_angle 0.0 def smooth_angle(new_angle, alpha0.15): global prev_angle diff (new_angle - prev_angle 540) % 360 - 180 prev_angle alpha * diff if prev_angle 0: prev_angle 360 elif prev_angle 360: prev_angle - 360 return prev_angle這段代碼把角度差限制在 (-180°, 180°] 范圍內(nèi)再去做一階低通從原理上消滅了跨零點(diǎn)跳變。alpha 取 0.15 時(shí)轉(zhuǎn)向 90° 大約需要 0.5 秒收斂低速巡檢小車完全夠用。如果你用的是滑動(dòng)平均而不是一階低通處理方式是在入隊(duì)前把角度統(tǒng)一偏移讓它們處于連續(xù)區(qū)間算完平均值再映射回 [0°, 360°)。5.3 現(xiàn)象校準(zhǔn)完方向是準(zhǔn)的換個(gè)房間或過(guò)幾天又偏了原因校準(zhǔn)是在固定環(huán)境做的你身邊如果有鐵磁物體比如鋼架桌子、金屬屏風(fēng)、帶磁扣的背包磁場(chǎng)分布會(huì)被改變。校準(zhǔn)數(shù)據(jù)里包含了這些物體的硬磁偏置換個(gè)環(huán)境偏置變了角度自然就偏了。這不是芯片漂移HMC5883L 本身溫漂很小是環(huán)境磁場(chǎng)變了。解決安裝位置固定后在最終使用環(huán)境里重新校準(zhǔn)不要在校準(zhǔn)后再挪動(dòng)模塊附近的金屬物體。如果需要跨環(huán)境使用啟動(dòng)時(shí)加一個(gè)“開(kāi)機(jī)后水平轉(zhuǎn)一圈自動(dòng)校準(zhǔn)”的流程轉(zhuǎn)圈時(shí)用圓擬合算出偏置寫入 RAM。另一個(gè)隱蔽原因是校準(zhǔn)后修改了增益寄存器。校準(zhǔn)數(shù)據(jù)是某個(gè)增益下采集的增益一變ADC 的 LSB 對(duì)應(yīng)物理量變了同樣的偏置換算到原始 LSB 數(shù)值對(duì)不上。解決先定增益再做校準(zhǔn)校準(zhǔn)后不要再動(dòng)配置寄存器 B。5.4 現(xiàn)象模塊焊在開(kāi)發(fā)板上讀數(shù)異常手拿模塊反而正常原因開(kāi)發(fā)板上的大電流走線產(chǎn)生了磁場(chǎng)特別是 DC-DC 電感、電機(jī)驅(qū)動(dòng)線、鋰電池放電回路這些在模塊附近形成的磁場(chǎng)強(qiáng)度可能比地磁場(chǎng)還大。地磁場(chǎng)大約 500 毫高斯一個(gè) 1A 電流的走線在 1cm 處產(chǎn)生的磁場(chǎng)大約 200 毫高斯這已經(jīng)足夠明顯扭曲讀數(shù)了。解決把模塊盡量遠(yuǎn)離大電流路徑和電感如果空間受限只能拉開(kāi)距離或者加磁屏蔽罩。供電線不要從模塊正下方穿過(guò)信號(hào)線也別繞成圈。這個(gè)現(xiàn)象也解釋了為什么很多人做的小車方向控制莫名偏航。電機(jī)電流變化時(shí)磁場(chǎng)偏置跟著變化航向角就跟著飄。把磁力計(jì)放到遠(yuǎn)離電機(jī)的機(jī)臂末端比任何軟件濾波都有效。我在某圖像處理 Demo 的輪式小車上就吃過(guò)這個(gè)虧校準(zhǔn)做了三遍靜態(tài)方向準(zhǔn)一跑起來(lái)就偏最后把模塊從底盤中心挪到車頭伸出的支架上問(wèn)題才消失。5.5 現(xiàn)象連續(xù)讀數(shù)據(jù)時(shí)XYZ 偶爾出現(xiàn)一個(gè)軸明顯跳變?cè)蜻B續(xù)測(cè)量模式下數(shù)據(jù)寄存器更新是異步的。你讀 X 低字節(jié)時(shí)芯片可能正在寫入新數(shù)據(jù)結(jié)果高低字節(jié)來(lái)自前后兩次采樣拼出來(lái)的數(shù)就飛了。原廠芯片有數(shù)據(jù)鎖存邏輯讀取數(shù)據(jù)寄存器時(shí)鎖存當(dāng)前值但如果你用read_i2c_block_data一次讀 6 字節(jié)總線上是兩個(gè)事務(wù)完成整塊讀取中間窗口內(nèi)更新就可能導(dǎo)致錯(cuò)位。解決讀模式寄存器0x09的 DRDY 位確認(rèn)數(shù)據(jù)就緒后再讀或者干脆用單次測(cè)量模式每次觸發(fā)后延時(shí)等數(shù)據(jù)更新完成再讀這也是本文前面代碼采用的方式。單次測(cè)量模式在 15Hz 輸出頻率下完全夠用延時(shí) 10ms 保證數(shù)據(jù)寄存器刷新完成。需要注意的是單次測(cè)量模式每次讀完必須重新寫模式寄存器觸發(fā)下一次轉(zhuǎn)換否則芯片回到睡眠狀態(tài)再讀就是老數(shù)據(jù)。很多新手在連續(xù)讀取模式下踩了錯(cuò)位坑換成單次測(cè)量后一切正常就是這個(gè)道理。6. 讓航向更可信傾斜補(bǔ)償解算與收斂參數(shù)水平放置時(shí)航向角只用 X 和 Y 就夠了但小車爬坡、無(wú)人機(jī)傾斜時(shí)地磁場(chǎng)在 X 和 Y 上的投影會(huì)隨姿態(tài)變化直接算角度誤差會(huì)到十幾度甚至更大。這時(shí)需要引入加速度計(jì)求出的俯仰角 pitch 和橫滾角 roll把磁場(chǎng)矢量先投影回水平面再算航向角。投影公式是磁場(chǎng)領(lǐng)域常見(jiàn)的標(biāo)準(zhǔn)形式推導(dǎo)過(guò)程不復(fù)雜核心是讓水平分量恢復(fù)出來(lái)。import math def tilt_compensated_heading(mx, my, mz, pitch, roll): x_h mx * math.cos(pitch) mz * math.sin(pitch) y_h mx * math.sin(roll) * math.sin(pitch) my * math.cos(roll) - mz * math.sin(roll) * math.cos(pitch) angle math.degrees(math.atan2(y_h, x_h)) return angle if angle 0 else angle 360這里 pitch 和 roll 的單位是弧度從加速度計(jì)數(shù)據(jù)用 atan2 求得。這個(gè)公式有個(gè)邊界條件當(dāng) pitch 或 roll 接近 ±90° 時(shí)水平分量趨近于零航向角數(shù)值變得敏感任何一點(diǎn)噪聲都會(huì)讓角度大幅擺動(dòng)。所以傾斜補(bǔ)償更適合用在姿態(tài)角小于 60° 的場(chǎng)景垂直方向的姿態(tài)解算要交給陀螺儀和磁力計(jì)更深的融合算法。最后說(shuō)一個(gè)經(jīng)驗(yàn)HMC5883L 這個(gè)模塊的精度上限就在那里靜態(tài)方向重復(fù)性可以做到 ±1° 以內(nèi)但動(dòng)態(tài)場(chǎng)景受電機(jī)磁場(chǎng)和加速度干擾能穩(wěn)定在 ±3° 就是不錯(cuò)的成績(jī)。想要更好的效果要么加磁屏蔽要么用更高端的磁力計(jì)芯片。就拿這個(gè)模塊練手把校準(zhǔn)、濾波、補(bǔ)償整順了換任何傳感器都能快速上手。希望這篇 HMC5883L 模塊資料筆記能幫你少走點(diǎn)彎路。本文還有配套的精品資源點(diǎn)擊獲取