Skip to content

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°,且同一侧机器出现正/反两种读数方向。

机号量程机号量程机号量程
机125.1°机411.2°机74.5°
机213.9°机511.0°机83.8°
机311.7°机68.0°机92.6°

当时的分析走了两条线:

教训:"横向偏心"模型在数学上完全自洽,也拟合了观测,但拟合到的原因不一定是真因。 最终是结构+原厂确认才定案。排查顺序应当是:先验磁铁充磁方向,再谈装配公差。

定案:装错了磁铁 ​

轴向充磁磁钢在芯片轴线上产生的面内旋转分量 ≈ 0(磁场沿轴向走), 读数只剩磁钢偏心/晃动带来的小残余分量——所以量程小、台间差大、方向随机翻转。 这与观测完全吻合;换正确磁铁(径向对半充磁)后,面内旋转分量占主导,量程立即恢复 ~40°。

快速鉴别两种充磁(来料/装配自检):

方法径向对半充磁(正确)轴向充磁(错误)
铁片靠近圆盘边缘两侧(N、S 各一半)都吸(侧面是磁极面)边缘一圈吸力弱且均匀
铁片靠近上下圆面不吸 / 弱一面 N、一面 S 都吸(圆面就是磁极)
装转轴上转一周测读数量程正常(~40°)量程很小、不稳定、方向随机

怎么救残余非线性:两点 + 九点分段标定 ​

两点标定(全闭/全开)只能做线性映射,修正不了中段非线性(读数被压扁的曲线不是直线)。 所以在应用层加了九点分段标定:治具给出 9 个(治具角度, 原始值)点对,分段线性插值把非线性掰回来。

九点分段标定示意

两种标定方式共存 ​

两套数据都保留,mode 字段决定当前生效方式(哪个后做用哪个):

你做的操作生效映射
九点「确认标定」分段线性(points)
「标定全闭/全开」两点线性(two-point)
「启用九点标定」按钮显式切回分段

操作(网页「传感器」页) ​

  1. 手指放到治具某卡位(如 20°)→ 点对应按键(采 2 秒均值)→ 重复点即覆盖
  2. 九点齐全(9/9 已采集)→ 点「确认标定」(校验:九点齐备 + 读数随角度单调)
  3. 生效后实时显示 开合百分比(给算法的口径)、标定角度、量程

接口:/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 · 基于夹爪项目实测 · 全部数据来自真机

这篇里有不清楚的地方,或者想聊类似的东西?私信我 →