外觀
TMR3109 磁編碼器全鏈路:協議、驅動、標定,以及一個"裝錯磁鐵"的坑
TMR3109 是一顆 23-bit 磁旋轉編碼器,SPI 讀絕對角度,裝在夾爪手指轉軸上測開合。 從驅動、資料採集到標定,全鏈路已上線:MCU 50Hz 採集 → AP → 網頁九點分段標定。 最大的教訓不在軟體:讀數異常的頭號根因不是裝配偏心,而是裝錯了磁鐵的充磁方向。
成果速覽
換掉錯誤磁鐵後,量程從"個位數~二十幾度、臺間差 10 倍"恢復到 ~40°:
| 能力 | 現狀 |
|---|---|
| 協議解析 | SPI Mode 1,28-bit 幀(23-bit 角度 + CRC + error)✅ |
| 採集 | MCU 50Hz 採集 → 共享記憶體 → AP → 網頁/標定 ✅ |
| 標定 | 兩點線性 + 九點分段標定共存(治具 0/5/…/40°)✅ |
| 讀數 | 晶片角度 / 標定角度 / 開合百分比 / 量程,四種口徑 ✅ |
| 失敗模式 | 磁鐵型別、感測器掉線、無磁鋼、量程退化——全部有判據 ✅ |
感測器與安裝要求
TMR3109(多維科技)基於隧道磁阻:晶片測的是正上方磁場向量的方向,不是零件角度。 磁鋼隨手指轉軸旋轉,面內場向量跟隨,晶片讀出絕對角度。
原廠對安裝的硬性要求(產品規格書 §3.6):
| 項 | 規格 |
|---|---|
| 磁鋼 | Ø9 × 2.5 mm,釹鐵硼 N35SH,徑向對半充磁 |
| 安裝距離(氣隙) | 1 ~ 3 ~ 10 mm |
| 晶片表面場強 | 100 ~ 1000 Gs |
| 中心偏差 | ≤ 0.5 mm |
| 中心傾斜 | ≤ ±3° |
⚠️ 場強範圍兩處寫法不一致:規格書 §3.6 寫 100~1000 Gs,官網產品頁寫 200~800 Gs,建議向原廠確認。
⚠️ 頭號前提:磁鋼的充磁方向(本篇主角)
"徑向對半充磁"是字面意思:圓盤的 N、S 沿直徑左右分佈。 本批結構裝的是軸向充磁(一個圓面是 N、一個圓面是 S)——這就是災難的起點:

驅動與資料鏈路
SPI 協議與位提取
讀角度指令返回 28-bit 資料(4 位元組 SPI 幀,MSB 優先):
| 位 | 內容 |
|---|---|
| d[27:5] | 23-bit 角度碼 |
| d[4:1] | 4-bit CRC(x⁴+x³+x²+1,初值 0011) |
| d[0] | error 位 |
c
// 换算:θ = raw / 8388608 × 360°(raw 为 23-bit 角度码)
rt_uint8_t tx[4] = {0x60, 0x00, 0x00, 0x00}, rx[4] = {0};
spi_xfer(s, tx, rx, 4);
uint32_t v = (rx[0]<<24) | (rx[1]<<16) | (rx[2]<<8) | rx[3];
uint32_t raw = (v >> 5) & 0x7FFFFF; // d[27:5]
float angle = raw / 8388608.0f * 360.0f; // 0~360°坑:datasheet 時序圖沒寫,但實測 MISO 有 4 個時鐘的流水線延遲—— 按手冊位對齊提取會把讀數鉗在 337.5°~360°。上程式碼已按實測修正(
>>5取 d[27:5])。
資料鏈路
- 讀數經
raw/8388608×360換算成角度,四條路徑(MCU 序列埠 / 共享記憶體 / HTTP / 網頁)數值一致 - 採集完整性已驗證:60s 3012 幀無丟幀、無毛刺、無位錯誤特徵
遇到的問題:同型機量程差 10 倍
現象:同一結構、同一 80° 開合行程(預期感測器量程 ~40°), 本批 9 臺實測讀數量程 2.6°~25.1°,且同一側機器出現正/反兩種讀數方向。
| 機號 | 量程 | 機號 | 量程 | 機號 | 量程 |
|---|---|---|---|---|---|
| 機1 | 25.1° | 機4 | 11.2° | 機7 | 4.5° |
| 機2 | 13.9° | 機5 | 11.0° | 機8 | 3.8° |
| 機3 | 11.7° | 機6 | 8.0° | 機9 | 2.6° |
當時的分析走了兩條線:
教訓:"橫向偏心"模型在數學上完全自洽,也擬合了觀測,但擬合到的原因不一定是真因。 最終是結構+原廠確認才定案。排查順序應當是:先驗磁鐵充磁方向,再談裝配公差。
定案:裝錯了磁鐵
軸向充磁磁鋼在晶片軸線上產生的面內旋轉分量 ≈ 0(磁場沿軸向走), 讀數只剩磁鋼偏心/晃動帶來的小殘餘分量——所以量程小、臺間差大、方向隨機翻轉。 這與觀測完全吻合;換正確磁鐵(徑向對半充磁)後,面內旋轉分量佔主導,量程立即恢復 ~40°。
快速鑑別兩種充磁(來料/裝配自檢):
| 方法 | 徑向對半充磁(正確) | 軸向充磁(錯誤) |
|---|---|---|
| 鐵片靠近圓盤邊緣 | 兩側(N、S 各一半)都吸(側面是磁極面) | 邊緣一圈吸力弱且均勻 |
| 鐵片靠近上下圓面 | 不吸 / 弱 | 一面 N、一面 S 都吸(圓面就是磁極) |
| 裝轉軸上轉一週測讀數 | 量程正常(~40°) | 量程很小、不穩定、方向隨機 |
怎麼救殘餘非線性:兩點 + 九點分段標定
兩點標定(全閉/全開)只能做線性對映,修正不了中段非線性(讀數被壓扁的曲線不是直線)。 所以在應用層加了九點分段標定:治具給出 9 個(治具角度, 原始值)點對,分段線性插值把非線性掰回來。

兩種標定方式共存
兩套資料都保留,mode 欄位決定當前生效方式(哪個後做用哪個):
| 你做的操作 | 生效對映 |
|---|---|
| 九點「確認標定」 | 分段線性(points) |
| 「標定全閉/全開」 | 兩點線性(two-point) |
| 「啟用九點標定」按鈕 | 顯式切回分段 |
操作(網頁「感測器」頁)
- 手指放到治具某卡位(如 20°)→ 點對應按鍵(採 2 秒均值)→ 重複點即覆蓋
- 九點齊全(
9/9 已采集)→ 點「確認標定」(校驗:九點齊備 + 讀數隨角度單調) - 生效後即時顯示 開合百分比(給演算法的口徑)、標定角度、量程
介面:/api/v1/angle/calib(GET/POST)、/calib/{point,commit,reset,mode}、/angle/regress。
讀數口徑(容易混)
| 顯示 | 含義 |
|---|---|
| 即時 | 晶片原始角度(0~360°)+ raw,與標定無關 |
| 標定角度 | raw 經當前標定換算的治具角度(0~40°),超程顯示 ⚠超程 |
| 開合 | raw 經當前標定換算的百分比(給演算法) |
| 量程 | 當前標定的量程 = span_raw / span_deg(晶片角度) |
口徑坑:端點欄位(
closed_deg/open_deg/span_deg)一律是晶片角度,不是治具角度—— 之前九點確認曾把治具角度寫進端點欄位,導致"量程"顯示成 40°,已統一修正。
經驗與坑檔案
| 現象 | 根因 | 判據 / 解決 |
|---|---|---|
| 量程個體差異巨大、方向隨機 | 磁鋼裝錯(軸向充磁) | 換徑向對半充磁;來料/裝配按規格書 §8 驗充磁方向 |
讀數恆為 0x7FFFFF(359.99996°) | 感測器未響應(未供電/未連線/掉線),MISO 上拉讀全 1 | 真值在 360° 附近會抖 ±幾 LSB,釘死 = 假值;檢查供電與 J57 連線;驅動應補 CRC/error 位校驗 |
| 讀數滿量程隨機跳(幾 ms ±300°) | 無磁鋼或磁鋼不在位(裸板) | 這不是"噪聲大",是沒訊號;裝磁鋼 |
| 標定後百分比是天文數字 | 兩次採在同一位置,端點量程退化 | 已加 2000 raw 保護;在兩個真實位置重新標定 |
| 兩點面板「量程」顯示 40° | 治具角度被寫進端點欄位 | 端點欄位統一為晶片角度 |
MSH 單跑 tmr3109 觸發斷言 | SPI 未單獨 init | 先 angle_init |
| 部署後功能沒變 | 構建後漏了 docker cp 產物,推的是舊二進位制 | 用 md5 比對板上二進位制 vs 產物,必做 |
| 靜止噪聲 | 健康整機 std ≈ 0.001°(~18 LSB) | >0.5° 查機械晃動或磁場;測噪聲要直讀共享記憶體按時間戳去重,HTTP 輪詢會重複計數低估噪聲 |
復現與出處
| 項 | 位置 |
|---|---|
| 驅動 | MCU:rtos/components/angle_sensor/angle_sensor.c(SPI Mode 1,位提取含 4clk 延遲修正) |
| 採集 | MCU:applications/gripper/sensor_acq.c(50Hz → 共享記憶體) |
| 標定 | AP:app/gripper/v2/cmd/gripper-app/angle_calib.go、angle_calib_http.go、web/index.html |
| 資料手冊 | 多維科技 TMR3109 產品規格書 v1.0.1(§3.6 磁場規格、§4.1 SPI、§8 機械角度方向) |
| 知識庫 | 團隊 skill:magnetic-angle-sensor、rv1126b-gripper-angle-calib |
寫於 2026-09 · 基於夾爪專案實測 · 全部資料來自真機