<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>电磁循迹 &#8211; Cloudlay</title>
	<atom:link href="https://cloudlay.cn/tag/%e7%94%b5%e7%a3%81%e5%be%aa%e8%bf%b9/feed/" rel="self" type="application/rss+xml" />
	<link>https://cloudlay.cn</link>
	<description>life</description>
	<lastBuildDate>Sun, 14 Jun 2026 17:08:32 +0000</lastBuildDate>
	<language>zh-Hans</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.0</generator>

<image>
	<url>https://cloudlay.cn/wp-content/uploads/2026/01/avatar.ico</url>
	<title>电磁循迹 &#8211; Cloudlay</title>
	<link>https://cloudlay.cn</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>从0到1带你打电赛·小车电控篇(五)：给小车装眼睛和平衡感——灰度、电磁、编码器与 IMU</title>
		<link>https://cloudlay.cn/nuedc-car-05-sensing/</link>
					<comments>https://cloudlay.cn/nuedc-car-05-sensing/#respond</comments>
		
		<dc:creator><![CDATA[云间辞]]></dc:creator>
		<pubDate>Sun, 14 Jun 2026 17:08:32 +0000</pubDate>
				<category><![CDATA[嵌入式]]></category>
		<category><![CDATA[IMU]]></category>
		<category><![CDATA[互补滤波]]></category>
		<category><![CDATA[智能小车]]></category>
		<category><![CDATA[灰度循迹]]></category>
		<category><![CDATA[电磁循迹]]></category>
		<category><![CDATA[电赛]]></category>
		<category><![CDATA[编码器]]></category>
		<guid isPermaLink="false">https://cloudlay.cn/nuedc-car-05-sensing/</guid>

					<description><![CDATA[📚 本文是 「从 0 到 1 带你打电赛 · 小车电控篇」 系列（共 12 篇）第 5 篇。 第1篇 · 拿奖 [&#8230;]]]></description>
										<content:encoded><![CDATA[<div class="ds-series" style="border:1px solid #4488ff33;background:#4488ff0d;border-radius:8px;padding:.8em 1.1em;margin:1.2em 0">
<p style="margin:0 0 .5em"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4da.png" alt="📚" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 本文是 <strong>「从 0 到 1 带你打电赛 · 小车电控篇」</strong> 系列（共 12 篇）第 5 篇。</p>
<ol style="margin:.2em 0 0;padding-left:1.4em">
<li style="margin:.15em 0"><a href="https://cloudlay.cn/nuedc-car-01-how-to-score/">第1篇 · 拿奖逻辑：把比赛拆成小目标</a></li>
<li style="margin:.15em 0"><a href="https://cloudlay.cn/nuedc-car-02-history/">第2篇 · 赛题进化史与押题</a></li>
<li style="margin:.15em 0"><a href="https://cloudlay.cn/nuedc-car-03-build-and-architecture/">第3篇 · 整车搭建与代码框架</a></li>
<li style="margin:.15em 0"><a href="https://cloudlay.cn/nuedc-car-04-motor-power/">第4篇 · 电机驱动与电源地基</a></li>
<li style="margin:.15em 0"><strong>第5篇 · 感知：灰度/电磁/编码器/IMU（本篇）</strong></li>
<li style="margin:.15em 0"><a href="https://cloudlay.cn/nuedc-car-06-pid-basics/">第6篇 · PID 入门：搞懂 P/I/D</a></li>
<li style="margin:.15em 0"><a href="https://cloudlay.cn/nuedc-car-07-pid-advanced/">第7篇 · PID 进阶：串级+工程补丁</a></li>
<li style="margin:.15em 0"><a href="https://cloudlay.cn/nuedc-car-08-pid-tuning/">第8篇 · PID 调参实战(核心)</a></li>
<li style="margin:.15em 0"><a href="https://cloudlay.cn/nuedc-car-09-advanced-control/">第9篇 · 进阶控制：几时该上</a></li>
<li style="margin:.15em 0"><a href="https://cloudlay.cn/nuedc-car-10-vision-comm/">第10篇 · K230 视觉与通信协议</a></li>
<li style="margin:.15em 0"><a href="https://cloudlay.cn/nuedc-car-11-architecture-fsm/">第11篇 · 状态机与整车软件</a></li>
<li style="margin:.15em 0"><a href="https://cloudlay.cn/nuedc-car-12-field-manual/">第12篇 · 现场作战+避坑+开源</a></li>
</ol>
</div>
<p>上一篇《电机驱动与电源地基》解决的是&#8221;小车的腿能不能听话地动起来&#8221;。可问题来了：腿动得再稳，要是脑子不知道&#8221;我现在偏左了还是偏右了&#8221;&#8221;我跑多快&#8221;&#8221;车头朝哪个方向&#8221;，那也只是一辆瞎跑的车。</p>
<p>这一篇我们就来给小车装上四样&#8221;感官&#8221;——灰度、电磁、编码器、IMU。它们的共同任务只有一个：<strong>给后面的控制环(PID)送上一份又干净又准确的反馈信号</strong>。打个比方，PID 是司机，感知层就是司机的眼睛、耳朵和平衡感。眼睛进了沙子，再老练的司机也得撞墙。所以这一篇虽然不讲控制算法，却是整车成败的地基里非常硬核的一块。</p>
<p>我们分四块讲：循迹的两条路线(灰度 / 电磁)、测速度的编码器、测姿态的 IMU。每一块都给你能照抄的算法和代码，最后说一句&#8221;路上怎么在它们之间切换&#8221;。</p>
<h2>一、循迹路线一：灰度——小车最常用的&#8221;看路&#8221;方式</h2>
<p>灰度循迹是绝大多数电赛/智能车小车的第一选择：在车头底部横排一组朝下的光电/灰度传感器，地面是白底黑线，传感器看到黑线就报告&#8221;我这儿压线了&#8221;。把所有传感器的信息汇总，就能算出&#8221;黑线现在偏车身中线多远&#8221;。这个偏差，就是喂给转向 PID 的核心输入。</p>
<h3>数字量阵列：加权位置法(行业标准算法)</h3>
<p>最经典、被全网开源项目反复抄的算法，叫<strong>加权位置法</strong>，来自 Pololu 的 QTR 传感器库 <a href="https://www.pololu.com/docs/0J19/3" target="_blank" rel="noopener noreferrer">readLine 算法</a>。它的思路特别好懂：</p>
<div class="ds-callout ds-callout-example" style="border-left:4px solid #7c4dff;background:#7c4dff14;padding:.6em 1em;margin:1.2em 0;border-radius:6px">
<p style="margin:0 0 .45em;font-weight:700;color:#7c4dff"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f9e9.png" alt="🧩" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 比喻：同学按学号站一排报到</p>
<p>把 8 个传感器想象成班里 8 个同学，按学号(0~7)站成一排。谁看到黑线谁&#8221;举手&#8221;，举手的力度(传感器读数)当权重。我们把&#8221;学号 × 力度&#8221;全加起来，再除以&#8221;总力度&#8221;，得到的就是一个平均学号——黑线现在大概压在第几号同学附近。比起只数&#8221;有没有人举手&#8221;，这能算出黑线卡在两个同学<strong>中间</strong>的位置，精细得多。</p>
</div>
<p>写成公式就是：</p>
<p class="ds-math" style="overflow-x:auto">$$\text{position} = \frac{\sum_{i} (i \times 1000) \times value_i}{\sum_{i} value_i}$$</p>
<p>其中 $i$ 是传感器索引。对 8 路传感器，这个值的范围是 $0 \sim 7000$，正中心就是 $(8-1)\times500 = 3500$。这个数随黑线左右移动单调变化，<strong>直接拿来当 PID 输入</strong>：设定值 setpoint = 3500，误差 error = position − 3500。</p>
<p>下面是改写自 QTR 库的核心代码，数字量、模拟量阵列都能用：</p>
<pre><code class="language-c">// N 路灰度阵列加权位置法，返回 0~(N-1)*1000，中心 (N-1)*500
// value[i]: 第 i 路校准后读数(压线=高值)，已做归一化
uint16_t readLinePosition(uint16_t *value, uint8_t N) {
    uint32_t avg = 0;   // 加权和 Σ(i*1000*value)
    uint32_t sum = 0;   // 权重和 Σ(value)
    static uint16_t lastPos = 0;
    bool onLine = false;
    for (uint8_t i = 0; i &lt; N; i++) {
        uint16_t v = value[i];
        if (v &gt; 200) onLine = true;        // 判定压线
        if (v &gt; 50) {                       // 噪声门限，&gt;50 才计入加权
            avg += (uint32_t)v * (i * 1000);
            sum += v;
        }
    }
    if (!onLine) {                          // 全丢线：朝上次方向找线(下面细讲)
        if (lastPos &lt; (uint32_t)(N - 1) * 1000 / 2) return 0;
        else return (N - 1) * 1000;
    }
    lastPos = avg / sum;
    return lastPos;                         // PID 输入：error = lastPos - (N-1)*500
}</code></pre>
<p>注意里面两个门限：读数 <code>&gt;50</code> 才计入加权和(滤掉噪声)，<code>&gt;200</code> 才算&#8221;真的压线了&#8221;。这是 QTR 库的经验值，能让位置计算更干净。</p>
<h3>数字量 vs 模拟量：要不要那么精细？</h3>
<p>刚才代码里的 <code>value[i]</code> 可以是两种来源：</p>
<table>
<thead>
<tr>
<th>类型</th>
<th>怎么读</th>
<th>优点</th>
<th>缺点</th>
</tr>
</thead>
<tbody>
<tr>
<td>数字量</td>
<td>每路经比较器输出 0/1(压线/没压)</td>
<td>简单、直接读 IO、抗环境光稍好、运算量小</td>
<td>分辨率低，偏差呈阶梯状</td>
</tr>
<tr>
<td>模拟量</td>
<td>直接读 ADC 原值</td>
<td>分辨率高，能算出黑线在两路中间的&#8221;亚像素&#8221;位置</td>
<td>运算量大，更怕环境光和反光</td>
</tr>
</tbody>
</table>
<div class="ds-callout ds-callout-tip" style="border-left:4px solid #00bfa6;background:#00bfa614;padding:.6em 1em;margin:1.2em 0;border-radius:6px">
<p style="margin:0 0 .45em;font-weight:700;color:#00bfa6"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4a1.png" alt="💡" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 比喻</p>
<p>数字量像只问&#8221;踩到线了吗？是/否&#8221;；模拟量像问&#8221;你离线多近？打个 0~100 分&#8221;。后者能算出更细的偏差，但也更容易被路灯、反光骗到。</p>
</div>
<p>模拟量有个必做的预处理：<strong>归一化</strong>。每一路传感器单独记下自己在纯黑、纯白时的最大最小值，运行时把读数折算到 0~100。这能消除各路传感器的个体差异，换场地也只要重测一遍。</p>
<pre><code class="language-c">// 标定阶段：扫黑白记录每路 min[i]/max[i]
// 运行阶段：归一化到 0~100
uint8_t normalize(uint16_t raw, uint16_t mn, uint16_t mx) {
    if (raw &lt;= mn) return 0;
    if (raw &gt;= mx) return 100;
    return (uint8_t)((uint32_t)(raw - mn) * 100 / (mx - mn));
}</code></pre>
<h3>工程四件套：&#8221;实验室能跑、赛场翻车&#8221;的分水岭</h3>
<p>灰度循迹真正拉开差距的，不是那个加权公式，而是下面这四件事。</p>
<p><strong>① 黑白阈值，一定要现场标定。</strong> 别用上次的、别用实验室的。赛场的光照、地面材质、纸张反光都和你家不一样，固定阈值一换场就误判。</p>
<div class="ds-callout ds-callout-warning" style="border-left:4px solid #ff9100;background:#ff910014;padding:.6em 1em;margin:1.2em 0;border-radius:6px">
<p style="margin:0 0 .45em;font-weight:700;color:#ff9100"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 2022 年那届的&quot;翻车现场&quot;</p>
<p>那年好几支队伍的小车在赛场识别/循迹翻车——实验室明明能跑，一到赛场就不行。根因几乎都是同一个：赛场环境和实验室差太多，参数没现场重标。(<a href="https://blog.csdn.net/qq_47652105/article/details/127036370" target="_blank" rel="noopener noreferrer">来源</a>)</p>
<p>基础做法：上电后让阵列扫过真实赛道的纯黑线和纯白底，采集每路 min/max，阈值取中值。</p>
</div>
<p>进阶一点，可以上 <strong>OTSU 大津法</strong>做动态阈值，免去手动标定：</p>
<pre><code class="language-c">// 非线性映射，增强黑线区分度(减小传感器个体差异)
adc[a] = pow((adc[a] / 10.0), 3) / 15000;
// OTSU：遍历灰度级找"最大类间方差"对应的灰度作阈值
// 类间方差 = p0 * p1 * (m0 - m1)^2   (p0/p1 两类概率，m0/m1 两类均值)
// 阈值限幅 100 &lt;= threshold &lt;= 800
output[i] = (input[i] &lt;= threshold) ? 1 : 0;     // 二值化</code></pre>
<p>有作者用 OTSU + 三次方非线性映射，在 <strong>9 片不同材质、不同光照、带污渍褶皱的场地</strong>上测试全部正常，还省掉了上电黑白标定这一步(<a href="https://blog.csdn.net/2301_78814366/article/details/150100225" target="_blank" rel="noopener noreferrer">来源</a>)。</p>
<div class="ds-callout ds-callout-tip" style="border-left:4px solid #00bfa6;background:#00bfa614;padding:.6em 1em;margin:1.2em 0;border-radius:6px">
<p style="margin:0 0 .45em;font-weight:700;color:#00bfa6"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4a1.png" alt="💡" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 比喻：标定就像考试前定及格线</p>
<p>不同试卷(场地)难度不同，你得先看看这次黑题白题大概多少分，把及格线划在中间，而不是死守上次的 60 分。OTSU 就是自动帮你找&#8221;最能把黑、白分成两堆&#8221;的那条线。</p>
</div>
<p><strong>② 全丢线，绝不能把 error 清零。</strong> 这是新手最容易踩的命案。</p>
<div class="ds-callout ds-callout-danger" style="border-left:4px solid #ff1744;background:#ff174414;padding:.6em 1em;margin:1.2em 0;border-radius:6px">
<p style="margin:0 0 .45em;font-weight:700;color:#ff1744"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f525.png" alt="🔥" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 全丢线清零 = 直冲出弯</p>
<p>丢线大多发生在急弯切出赛道的那一瞬间。此时最后那个偏差方向，恰恰就是你该回去找线的方向。如果把 error 归零，车就以为&#8221;前方笔直&#8221;，一头冲出去。</p>
<p>正确做法(就是上面代码里那段)：丢线时<strong>记住上一周期的偏差极性</strong>，朝原来弯道的方向打死方向去重新抓线。再补一条超时保护：长时间持续丢线就减速甚至停车倒找，别让车原地打转。</p>
</div>
<div class="ds-callout ds-callout-example" style="border-left:4px solid #7c4dff;background:#7c4dff14;padding:.6em 1em;margin:1.2em 0;border-radius:6px">
<p style="margin:0 0 .45em;font-weight:700;color:#7c4dff"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f9e9.png" alt="🧩" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 比喻</p>
<p>好比你过弯时眼睛突然闭了一下，正确做法是&#8221;刚才在往左拐就继续往左找路&#8221;，而不是&#8221;看不见了就直着冲&#8221;——直冲必然冲出赛道。</p>
</div>
<p><strong>③ 十字/直角路口，必须特判。</strong> 经过十字时多路传感器会同时压线，加权位置会突然跳变、把偏差算飞，车就猛打一下方向。处理办法：8 路里有 <strong>≥3 路同时压线</strong>就判为路口，进入&#8221;路口模式&#8221;——这时候不用常规偏差了，而是靠状态机让车直行/盲走固定一段(配合编码器或惯导计程)穿过去，过完再恢复巡线。一位省赛一等奖选手就是这么干的，并强调&#8221;换赛道、换车都得重标重调，没有万能参数&#8221;(<a href="https://blog.csdn.net/zhengnianli/article/details/106684237" target="_blank" rel="noopener noreferrer">来源</a>)。</p>
<p><strong>④ 安装高度与前瞻，是个三方妥协。</strong> 前瞻 = 传感器有效检测点离车头多远。</p>
<ul>
<li>前瞻<strong>远</strong> → 高速直道更稳，能提前预判弯道，但急弯容易提前切弯、切到弯内；</li>
<li>前瞻<strong>近</strong> → 急弯跟得紧，但高速时容易发抖。</li>
</ul>
<p>经验做法：<strong>直道用远前瞻，弯道用近前瞻</strong>，可以动态切换(灰度阵列靠&#8221;用哪几路算偏差&#8221;来实现切换，切换点要带迟滞，否则在直弯交界来回切会抖)。</p>
<div class="ds-callout ds-callout-warning" style="border-left:4px solid #ff9100;background:#ff910014;padding:.6em 1em;margin:1.2em 0;border-radius:6px">
<p style="margin:0 0 .45em;font-weight:700;color:#ff9100"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 恩智浦智能车光电组的纠结</p>
<p>一份技术报告里，底盘高度反复纠结：太低上坡蹭赛道、过障被顶住；太高重心高、高速过弯侧翻；前瞻调太远又有噪点。最后只能在&#8221;不蹭赛道&#8221;的前提下尽量压低，再配俯仰角控制前瞻。(<a href="https://bj.bcebos.com/cdstm-hyetecforthesmartcar-bucket/d641c155ba4444649c662287c241d3a9.pdf" target="_blank" rel="noopener noreferrer">来源</a>)</p>
<p>一句话：高度和前瞻都不是越大越好，是<strong>重心稳定性、过障能力、识别准确率</strong>三者的折中。</p>
</div>
<h3>关于 TCRT5000 红外光电</h3>
<p>入门玩具车上最常见的就是 TCRT5000 这种离散红外反射光电。它是红外发射二极管(波长 950nm) + 光敏三极管，集成了日光阻断滤光片(<a href="https://www.vishay.com/docs/83760/tcrt5000.pdf" target="_blank" rel="noopener noreferrer">Vishay 官方手册</a>)。关键参数：</p>
<ul>
<li>峰值工作距离 <strong>2.5mm</strong>，有效范围 0.2~15mm，最佳 0.2~6.5mm；</li>
<li>供电 3.3~5V，配 LM393 比较器整形输出数字量。</li>
</ul>
<div class="ds-callout ds-callout-danger" style="border-left:4px solid #ff1744;background:#ff174414;padding:.6em 1em;margin:1.2em 0;border-radius:6px">
<p style="margin:0 0 .45em;font-weight:700;color:#ff1744"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f525.png" alt="🔥" class="wp-smiley" style="height: 1em; max-height: 1em;" /> TCRT5000 必须贴地，这是它最大的硬约束</p>
<p>离地太远，反射光变弱，会把白地误判成&#8221;黑&#8221;。所以循迹时模块要贴到 <strong>2~8mm</strong>(贴近峰值 2.5mm 最好)。调试口诀：<strong>没遇黑线时指示灯应长亮，一旦灯灭就是遇到黑线了。</strong> 安装高度不一致会导致各路灵敏度不同，要逐路调电位器。</p>
</div>
<div class="ds-callout ds-callout-tip" style="border-left:4px solid #00bfa6;background:#00bfa614;padding:.6em 1em;margin:1.2em 0;border-radius:6px">
<p style="margin:0 0 .45em;font-weight:700;color:#00bfa6"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4a1.png" alt="💡" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 比喻</p>
<p>TCRT5000 就像用手电照地面看反光，离太远光散了，白地黑地都看不清，必须凑很近(几毫米)才分得清黑白。</p>
</div>
<p><strong>选型小结</strong>：TCRT5000 离散红外便宜、易上手，但单点少、贴地要求严、抗环境光差，适合入门；集成灰度阵列(如 8 路 IIC 数字阵列、模拟量阵列)一致性好、分辨率高、部分带自标定，是电赛/智能车的主流；摄像头方案前瞻最远、能识别十字数字等复杂元素，但算力和调试成本高(留到《K230 视觉与通信协议》一篇展开)。<strong>2024/2025 电赛 H 题(自动行驶小车)的主流一等奖方案，就是 8 路灰度 + 惯导(主控 MSPM0G3507)</strong>，不是电磁，这点后面会再强调。</p>
<h3>灰度循迹的调参口诀(转向环)</h3>
<p>偏差信号算出来后，喂给转向 PID。这里先给经验，完整的调参流程见《PID 调参实战》一篇。</p>
<div class="ds-callout ds-callout-tip" style="border-left:4px solid #00bfa6;background:#00bfa614;padding:.6em 1em;margin:1.2em 0;border-radius:6px">
<p style="margin:0 0 .45em;font-weight:700;color:#00bfa6"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4a1.png" alt="💡" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 灰度转向调参铁律</p>
<ol>
<li><strong>先把基础速度降到很低</strong>(最大的 30~50%)，Ki = Kd = 0，只加 Kp 直到能跟线；</li>
<li>升速，加 Kd 抑制振荡和过弯超调；</li>
<li><strong>Ki 在循迹转向环通常保持 0</strong>——转向是&#8221;位置跟随&#8221;，P 就能消静差，Ki 多余且容易引发振荡和过冲。</li>
</ol>
</div>
<p>有个 Arduino 5 路 PD 循迹的实测起点可以参考：Kp=15、Ki=0、Kd=200、基础速度 200(PWM 0~255)，5.14m 的赛道跑约 5 秒(<a href="https://znzcqy.github.io/post/I9lb3ilye/" target="_blank" rel="noopener noreferrer">来源</a>)。</p>
<p>还有一条容易被忽视：<strong>偏差信号要先低通滤波再算微分</strong>。灰度原始读数有抖动，直接微分会被 Kd 放大，车就发抖。经验做法：</p>
<pre><code class="language-c">adc_err = 0.4 * err + 0.6 * adc_err;   // 一阶低通，新值权重 α≈0.3~0.5</code></pre>
<p>注意滤波别太狠：α 太小会引入滞后，让弯道响应变慢——这是前瞻和滤波之间的又一个权衡。</p>
<div class="ds-callout ds-callout-danger" style="border-left:4px solid #ff1744;background:#ff174414;padding:.6em 1em;margin:1.2em 0;border-radius:6px">
<p style="margin:0 0 .45em;font-weight:700;color:#ff1744"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f525.png" alt="🔥" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 灰度循迹必查的几个坑</p>
<ul>
<li><strong>偏差极性 / 电机加减号接反</strong> → 车不回正反而越跑越偏，直接冲出去。上电第一件事：手推车体，确认 error 符号和 left/right 混合方向匹配。</li>
<li>用实验室/上次的阈值上赛场 → 误判。每次新场地重标或用 OTSU。</li>
<li>十字不特判 → 偏差算飞，急打方向。</li>
<li>偏差不滤波直接微分 → 高速发抖。</li>
<li>Ki 盲目加在转向环 → 过弯过冲、画龙。</li>
</ul>
</div>
<h2>二、循迹路线二：电磁——蒙眼跟着 20kHz 小调走</h2>
<p>有些赛题(尤其智能车竞赛电磁组)不是白底黑线，而是在赛道中心埋一根漆包线，通上 <strong>20kHz、约 100mA</strong> 的正弦交变电流，产生交变磁场。车上用工字电感去感应这个磁场——离导线越近，感应出的电压越大。</p>
<div class="ds-callout ds-callout-example" style="border-left:4px solid #7c4dff;background:#7c4dff14;padding:.6em 1em;margin:1.2em 0;border-radius:6px">
<p style="margin:0 0 .45em;font-weight:700;color:#7c4dff"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f9e9.png" alt="🧩" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 比喻：蒙眼跟着哼歌的人走</p>
<p>地上那根线一直&#8221;哼着&#8221;20kHz 的歌，车的两只&#8221;耳朵&#8221;(左右电感)谁听得响就说明离谁近，靠左右耳的响度差，就能判断车往哪边偏了。</p>
</div>
<p>电磁循迹在电赛里用得不算多(主要是智能车竞赛电磁组的常态)，但思路很经典，而且 TI MSPM0 芯片在这上面有独特优势，值得了解。</p>
<h3>信号链：从 30mV 微弱交流到一个能用的数</h3>
<p>单个 10mH 工字电感在离线上方 15~20cm 处感应出的原始信号，只有大约 <strong>30mV 的 20kHz 小交流</strong>——这么弱、这么高频，单片机根本没法直接读。必须走完整的四级信号链：</p>
<figure class="ds-diagram" style="text-align:center;margin:1.3em 0"><img decoding="async" src="https://cloudlay.cn/wp-content/uploads/dianseiche/49e1d9cd30f7.png" alt="电赛小车电控 · 示意图" loading="lazy" style="max-width:100%;height:auto;background:#fff;border-radius:8px;padding:6px;box-shadow:0 1px 6px rgba(0,0,0,.25)"></figure>
<ol>
<li><strong>LC 并联谐振选频</strong>：只放大 20kHz，抑制其它频率的干扰。10mH 电感配 6.8nF 电容，谐振频率约 19.3kHz(理论上 20kHz 对应 6.33nF，工程上取就近标称值 6.8nF，刻意略低一点适配寄生电容与公差)。</li>
<li><strong>运放放大约 100 倍</strong>：把 30mV 抬到伏级(STM32 不超 3.3V)。</li>
<li><strong>二极管检波</strong>(常用肖特基 BAT54S)：把 20kHz 交流的包络/峰值整流成直流。</li>
<li><strong>RC 低通滤波</strong>：平滑后送 ADC。</li>
</ol>
<div class="ds-callout ds-callout-tip" style="border-left:4px solid #00bfa6;background:#00bfa614;padding:.6em 1em;margin:1.2em 0;border-radius:6px">
<p style="margin:0 0 .45em;font-weight:700;color:#00bfa6"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4a1.png" alt="💡" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 两个比喻串起来</p>
<ul>
<li><strong>LC 谐振选频像调收音机台</strong>：周围一堆杂音(各种磁场)，LC 只把 20kHz 这个台调清楚放大，别的拧小声。</li>
<li><strong>检波整流像把抖动的水面读成水位</strong>：电感信号是 20kHz 高速抖动的交流(像水波纹)，二极管 + 电容只看波纹有多高(包络)，变成稳定的&#8221;水位&#8221;给单片机读。</li>
</ul>
</div>
<h3>核心算法：差比和 (L−R)/(L+R)</h3>
<p>电感读到值之后，最关键、最该记住的算法就是<strong>差比和</strong>：</p>
<p class="ds-math" style="overflow-x:auto">$$\text{偏差} = \frac{L &#8211; R}{L + R}$$</p>
<p>为什么不直接用 L−R？因为整体场强会漂移——电流波动、不同赛道，会让所有电感的读数整体涨 10%，这时候位置没变，L−R 却跟着变了。而差比和的分子分母同比例缩放，比值不变，所以偏差只反映<strong>位置</strong>，不受幅值影响。</p>
<div class="ds-callout ds-callout-example" style="border-left:4px solid #7c4dff;background:#7c4dff14;padding:.6em 1em;margin:1.2em 0;border-radius:6px">
<p style="margin:0 0 .45em;font-weight:700;color:#7c4dff"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f9e9.png" alt="🧩" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 比喻：按比例打分，而不是绝对分</p>
<p>全班整体多考了 10 分(信号变强)，但你比同桌高出的相对名次没变。除以总分看相对位置，就不会被整体高低带偏——这就是抗幅值漂移。</p>
</div>
<p>工程上还会先把每个通道<strong>归一化到 0~100</strong>(value/max×100)，方便统一设阈值、识别环岛/十字等特殊元素。下面是真实电磁组代码里的双重差比和：</p>
<pre><code class="language-c">// 归一化(每通道 value/max*100)
Left_Adc  = (adc_deal_last[LEFT_1]*100) / adc_max[0];
Right_Adc = (adc_deal_last[RIGHT_1]*100) / adc_max[1];
// 双重差比和求偏差 AD_Bias
AD_Bias = ((Left_Adc - Middle_Adc)*100/(Left_Adc + Middle_Adc))
        - ((Right_Adc - Middle_Adc)*100/(Right_Adc + Middle_Adc));</code></pre>
<div class="ds-callout ds-callout-warning" style="border-left:4px solid #ff9100;background:#ff910014;padding:.6em 1em;margin:1.2em 0;border-radius:6px">
<p style="margin:0 0 .45em;font-weight:700;color:#ff9100"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 标定别贪满幅</p>
<p>12 位 ADC(0~4096)实测一般不超 3600。标定时让电感最大值落在量程的 <strong>70~90%</strong>，<strong>绝不要调到满幅</strong>——满幅会削顶饱和、丢分辨率，还给特殊元素的极值留不出空间。</p>
</div>
<h3>电感布局：成绩的隐形变量</h3>
<p>电感怎么摆，直接影响直道稳不稳、弯道灵不灵：</p>
<table>
<thead>
<tr>
<th>布局</th>
<th>特点</th>
</tr>
</thead>
<tbody>
<tr>
<td>一字横排</td>
<td>简单、直道稳，但对弯道/特殊元素弱</td>
</tr>
<tr>
<td>八字向外 V</td>
<td>两端差异大，对弯道敏感，外侧可取极值识别元素</td>
</tr>
<tr>
<td>双横 + 竖直(双 T)</td>
<td>综合直弯，竖直电感对正上方磁场敏感，用于环岛/坡道识别</td>
</tr>
<tr>
<td>双 45°</td>
<td>对称性好，但要求左右运放/电感一致性极高，否则跑偏</td>
</tr>
</tbody>
</table>
<p>布局要点：相邻电感间距 <strong>≥2cm</strong>，避免互感串扰。</p>
<div class="ds-callout ds-callout-warning" style="border-left:4px solid #ff9100;background:#ff910014;padding:.6em 1em;margin:1.2em 0;border-radius:6px">
<p style="margin:0 0 .45em;font-weight:700;color:#ff9100"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 第 16 届电磁越野组的教训</p>
<p>木地板测试时编码器脉冲不稳，根因竟是机械连杆张力过大 + 电机振动没约束——软件再调参也救不回，这是硬件缺陷。该队还放弃了 45° 布局，因为硬件对称性不足、左右运放灵敏度不一致导致左右不对称。(<a href="https://www.cnblogs.com/Tayoou/p/15257616.html" target="_blank" rel="noopener noreferrer">来源</a>)</p>
<p>一句话：机械固定、左右对称性、硬件一致性是地基。先把电感装牢、左右配对一致，再谈调参。</p>
</div>
<h3>MSPM0 的独门优势：片上运放</h3>
<p>前面那条信号链里，&#8221;运放放大&#8221;通常要在板外焊 LM358/OPA4377 之类的运放。但 <strong>TI MSPM0 芯片内部就自带运放</strong>——比如 MSPM0G3507 含 2 个 OPA，MSPM0L13xx 含 2 个零漂斩波运放 + 1 个 GPAMP，可编程增益(PGA)1~32 倍(单级不够可两级 cascade)，输出能直连片上高速 12 位 ADC。</p>
<div class="ds-callout ds-callout-example" style="border-left:4px solid #7c4dff;background:#7c4dff14;padding:.6em 1em;margin:1.2em 0;border-radius:6px">
<p style="margin:0 0 .45em;font-weight:700;color:#7c4dff"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f9e9.png" alt="🧩" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 比喻</p>
<p>MSPM0 的片上运放就像芯片自带放大镜：本来要在板外焊运放当放大镜放大微弱信号，现在 MCU 内部就有 2 个高质量(零漂、抗噪)放大镜，信号不出芯片就放大完直接给 ADC——省料、省地方，还更干净。</p>
</div>
<p>这意味着电磁前端的选频信号放大可以做进芯片里，省掉外置运放，减 BOM、减焊接、减板上噪声。零漂斩波运放尤其适合这种微弱信号(低失调、低 1/f 噪声)。配置用 TI 的 SysConfig 图形化完成，官方有 <code>opa_signal_chain_to_adc</code> 等例程。不过要注意：MSPM0 跑纯电磁的完整开源还比较少(多为灰度)，建议自己验证片内运放增益够不够，不够就级联或再外置一级。</p>
<div class="ds-callout ds-callout-important" style="border-left:4px solid #00bcd4;background:#00bcd414;padding:.6em 1em;margin:1.2em 0;border-radius:6px">
<p style="margin:0 0 .45em;font-weight:700;color:#00bcd4"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2757.png" alt="❗" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 别把 2024 电赛 H 题当电磁准备</p>
<p>H 题&#8221;自动行驶小车&#8221;限定用 MSPM0(常见 G3507)，<strong>主流一等奖方案是 8 路灰度 + MPU6050 + 位置式 PID，不是电磁循迹。</strong> 电磁 + 差比和 + 工字电感主要是智能汽车竞赛电磁组的玩法，别搞混了准备方向。</p>
</div>
<h3>电磁的几个高频坑</h3>
<div class="ds-callout ds-callout-danger" style="border-left:4px solid #ff1744;background:#ff174414;padding:.6em 1em;margin:1.2em 0;border-radius:6px">
<p style="margin:0 0 .45em;font-weight:700;color:#ff1744"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f525.png" alt="🔥" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 危险</p>
<ul>
<li><strong>电机 PWM 频率别落在 20kHz 附近</strong>——开关噪声会被 LC 选频电路一并放大，污染信号。取 13~19kHz 错开，或给模拟前端独立 LDO 供电 + 屏蔽 + 物理远离电机。</li>
<li><strong>不做归一化直接用 L−R</strong> → 信号涨落时偏差漂移，不同赛道表现不一(差比和就是解决这个)。</li>
<li><strong>相邻电感太近(&lt;2cm)</strong> → 互感串扰。</li>
<li><strong>舵机环加大 I</strong> → 积分饱和、弯道滞后画龙；微分项不滤波会放大 ADC 噪声抖舵。</li>
</ul>
</div>
<div class="ds-callout ds-callout-tip" style="border-left:4px solid #00bfa6;background:#00bfa614;padding:.6em 1em;margin:1.2em 0;border-radius:6px">
<p style="margin:0 0 .45em;font-weight:700;color:#00bfa6"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4a1.png" alt="💡" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 排查电磁链路：用示波器自下而上</p>
<p>很多&#8221;算法跑不动&#8221;其实是前端没出波形(电感虚焊/谐振没对/运放没起振)。正确顺序是：先确认信号源和示波器正常，把电感贴近电磁线量 10mH 引脚看有没有 20kHz 正弦，再逐级往后量放大、检波的输出。<strong>先怀疑硬件波形，别一上来怀疑代码。</strong> (<a href="https://blog.csdn.net/weixin_45523734/article/details/111141670" target="_blank" rel="noopener noreferrer">来源</a>)</p>
</div>
<h2>三、编码器：知道自己跑多快</h2>
<p>循迹解决了&#8221;往哪走&#8221;，编码器解决&#8221;跑多快&#8221;。增量编码器输出 A、B 两路正交方波(相位差 90°)，转得越快、脉冲越密；正转反转，A、B 的超前关系相反。</p>
<h3>首选读法：定时器编码器模式</h3>
<p>STM32 的定时器有个硬件&#8221;编码器模式&#8221;，<strong>强烈推荐用它</strong>：硬件自动加减计数、自动判方向，CPU 几乎零开销。</p>
<p>在 CubeMX 里把 TIMx 的 Encoder Mode 设成 <strong>&#8220;TI1 and TI2&#8221;</strong>(即 4 倍频，Encoder Mode 3)，Prescaler = 0，ARR 设 65535(16 位)或 0xFFFFFFFF(32 位定时器，<strong>强烈推荐用 32 位的 TIM2/TIM5 防溢出</strong>)。</p>
<div class="ds-callout ds-callout-example" style="border-left:4px solid #7c4dff;background:#7c4dff14;padding:.6em 1em;margin:1.2em 0;border-radius:6px">
<p style="margin:0 0 .45em;font-weight:700;color:#7c4dff"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f9e9.png" alt="🧩" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 比喻：4 倍频就像数火车数得更细</p>
<p>只数车头(2 倍频 = A、B 各数一边)不如车头车尾都数(4 倍频 = A、B 的上升沿 + 下降沿都数)。同样一列火车，你数出 4 倍的格子，测得更精细。</p>
</div>
<p>读速度的代码非常简洁——每个固定控制周期(常用 5ms 或 10ms)读一次计数差值：</p>
<pre><code class="language-c">// CubeMX: TIMx -&gt; Encoder Mode = "TI1 and TI2"(4倍频), Prescaler=0, ARR=65535/0xFFFFFFFF
HAL_TIM_Encoder_Start(&amp;htim2, TIM_CHANNEL_ALL);   // 启动编码器接口

// 每个固定控制周期(5/10ms)调用一次，返回"单位周期脉冲数"作速度反馈
int16_t Encoder_GetSpeed(void) {
    static int16_t last = 0;
    int16_t cur   = (int16_t)__HAL_TIM_GET_COUNTER(&amp;htim2); // 读计数
    int16_t delta = cur - last;   // signed 差值自动吃掉 65535/0 回绕(单周期增量&lt;32767)
    last = cur;
    return delta;                 // 正/负即正反转，数值即速度反馈
}</code></pre>
<h3>关键心法：单位周期脉冲数，直接当速度用</h3>
<p>很多新手卡在&#8221;怎么把脉冲换算成 m/s&#8221;。其实<strong>不用换算</strong>——那个 <code>delta</code>(单位周期内的脉冲数)本身就能直接当速度反馈喂进 PID。</p>
<div class="ds-callout ds-callout-tip" style="border-left:4px solid #00bfa6;background:#00bfa614;padding:.6em 1em;margin:1.2em 0;border-radius:6px">
<p style="margin:0 0 .45em;font-weight:700;color:#00bfa6"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4a1.png" alt="💡" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 比喻：数电线杆</p>
<p>不用换算成公里每小时，就像你每 10 秒数一次窗外过了多少根电线杆，杆数本身就代表快慢——数越多车越快，直接拿这个数去踩油门/松油门就行。</p>
</div>
<p>如果报告里需要物理速度，再换算也不迟：</p>
<p class="ds-math" style="overflow-x:auto">$$\text{RPM} = \frac{\Delta CNT \times (1000 / \text{period\_ms})}{\text{PPR} \times 4 \times \text{减速比}}$$</p>
<p>线速度再乘轮周长即可。采样周期是个权衡：太短，ΔCNT 太小，量化噪声大；太长，响应慢。智能车常用 5ms 或 10ms(弱一点的电机推荐 10ms)。</p>
<div class="ds-callout ds-callout-warning" style="border-left:4px solid #ff9100;background:#ff910014;padding:.6em 1em;margin:1.2em 0;border-radius:6px">
<p style="margin:0 0 .45em;font-weight:700;color:#ff9100"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 16 位定时器溢出，靠 signed 差值法自动解决</p>
<p>CNT 到 65535 会回绕到 0。代码里 <code>int16_t delta = cur - last;</code> 这一句，利用了无符号回绕的特性，即便跨越 65535/0 边界也能给出正确的带符号增量——<strong>只要单周期增量不超过 ±32767。</strong> 长距离里程累加要用 int32/int64。</p>
</div>
<p>还有一种读法是用 EXTI 外部中断在 A/B 边沿软件计数(M 法数脉冲 / T 法测间隔)，更灵活，但占 CPU、高速易丢脉冲。<strong>结论：有硬件编码器接口时优先用定时器模式</strong>，EXTI 只在引脚/定时器不够时备选。</p>
<div class="ds-callout ds-callout-danger" style="border-left:4px solid #ff1744;background:#ff174414;padding:.6em 1em;margin:1.2em 0;border-radius:6px">
<p style="margin:0 0 .45em;font-weight:700;color:#ff1744"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f525.png" alt="🔥" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 编码器必查</p>
<ul>
<li><strong>A/B 接反或电机正负接反</strong> → 反馈方向和控制方向相反，闭环变正反馈，越控越偏。先开环验证：手转轮子正转，读数应增大。</li>
<li><strong>dt 不固定</strong>(用 HAL_Delay 或主循环计时) → 速度 = Δ脉冲/dt 和积分项全失真，PID 怎么调都不稳。<strong>编码器采样和 PID 控制周期，要用同一个硬件定时中断严格定时。</strong> 中断里只采集 + 置标志，别做浮点/打印，计算放主循环。</li>
</ul>
</div>
<h2>四、IMU：给小车装上平衡感和方向感</h2>
<p>IMU(常用 MPU6050)是六轴传感器：三轴加速度计 + 三轴陀螺仪。它能告诉小车两件事——<strong>姿态</strong>(Pitch/Roll，平衡车直立要用)和<strong>航向</strong>(Yaw，走直线锁方向要用)。但这两件事的难度天差地别。</p>
<h3>姿态融合：互补滤波(必会)</h3>
<p>加速度计和陀螺仪各有各的毛病，得互补着用：</p>
<ul>
<li><strong>加速度计</strong>：能算出绝对的倾角，但噪声大、抖；</li>
<li><strong>陀螺仪</strong>：积分出来的角度很平滑，但会慢慢漂移。</li>
</ul>
<div class="ds-callout ds-callout-example" style="border-left:4px solid #7c4dff;background:#7c4dff14;padding:.6em 1em;margin:1.2em 0;border-radius:6px">
<p style="margin:0 0 .45em;font-weight:700;color:#7c4dff"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f9e9.png" alt="🧩" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 比喻：开会时该多听谁</p>
<p>加速度计像个诚实但话痨手抖的人(说的方向对，但一直抖)；陀螺仪像个嗓门稳但会慢慢跑题的人(短期稳，但越说越偏)。互补滤波就是：开会时多听那个稳的人(权重 0.98)，偶尔用诚实的人把话题拉回正轨(0.02)，综合出又稳又准的答案。</p>
</div>
<p>互补滤波公式：</p>
<p class="ds-math" style="overflow-x:auto">$$\text{angle} = \alpha \times (\text{angle} + \text{gyro} \times dt) + (1-\alpha) \times \text{acc\_angle}$$</p>
<p>$\alpha$ 取 0.95~0.98(陀螺权重高)。加速度算角：$\text{pitch} = \text{atan2}(a_y, \sqrt{a_x^2 + a_z^2})$。代码：</p>
<pre><code class="language-c">// 一阶互补滤波：融合加速度(绝对但抖)与陀螺(平滑但漂)，求 Pitch/Roll
// alpha=0.98(陀螺权重)，dt=控制周期(秒)
float comp_pitch = 0;
void IMU_Update(float ax, float ay, float az, float gy, float dt) {
    float acc_pitch = atan2f(ay, sqrtf(ax*ax + az*az)) * 57.2958f; // 加速度算角(度)
    comp_pitch = 0.98f*(comp_pitch + gy*dt) + 0.02f*acc_pitch;     // 互补融合
}</code></pre>
<div class="ds-callout ds-callout-tip" style="border-left:4px solid #00bfa6;background:#00bfa614;padding:.6em 1em;margin:1.2em 0;border-radius:6px">
<p style="margin:0 0 .45em;font-weight:700;color:#00bfa6"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4a1.png" alt="💡" class="wp-smiley" style="height: 1em; max-height: 1em;" /> α 怎么取</p>
<p>α 越大越信任陀螺(更平滑但跟随慢)，越小越信任加速度(更跟手但更抖)。小车机械振动大就适当加大 α 滤掉加速度噪声。一般直接取 0.95~0.98 就行。<strong>dt 千万记得用秒(0.005)，别用 ms 或采样次数</strong>，否则融合比例完全错。</p>
</div>
<p>进阶可以上<strong>一阶卡尔曼滤波</strong>(平衡车经典 TKJ 版)，效果更平滑，标准参数 Q_angle=0.001、Q_bias=0.003、R_measure=0.03、dt=0.005~0.01。它本质上就是&#8221;会自动调权重的互补滤波&#8221;——发现加速度计这会儿可信就多听它，发现它在抖就少听它。代价是计算量大。对资源受限的单片机，互补滤波通常已经够用，追求极致平滑再上卡尔曼。</p>
<div class="ds-callout ds-callout-tip" style="border-left:4px solid #00bfa6;background:#00bfa614;padding:.6em 1em;margin:1.2em 0;border-radius:6px">
<p style="margin:0 0 .45em;font-weight:700;color:#00bfa6"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4a1.png" alt="💡" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 卡尔曼 = 自适应增益的互补滤波</p>
<p>两者本质相似。互补滤波省、收敛快、对小车足够；卡尔曼平滑但费算力。别一上来就堆卡尔曼，先把互补滤波吃透。&#8221;什么时候才值得上进阶滤波&#8221;，留到《进阶控制算法》一篇细聊。</p>
</div>
<h3>Yaw(航向)：六轴 IMU 的一个大坑</h3>
<p>姿态(Pitch/Roll)有加速度计当绝对参考，能纠偏。但 <strong>Yaw 不行</strong>——水平面内的旋转，加速度计提供不了任何参考(重力始终竖直)。所以 Yaw 只能靠陀螺 Z 轴积分：<code>yaw += gyro_z * dt</code>，<strong>必然随时间漂移</strong>。这是六轴 IMU 的固有局限(想要绝对航向，得上九轴加磁力计，或者用外部基准纠偏)。</p>
<div class="ds-callout ds-callout-example" style="border-left:4px solid #7c4dff;background:#7c4dff14;padding:.6em 1em;margin:1.2em 0;border-radius:6px">
<p style="margin:0 0 .45em;font-weight:700;color:#7c4dff"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f9e9.png" alt="🧩" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 比喻：闭着眼睛数步子估方向</p>
<p>陀螺仪测的是转得多快(角速度)，要靠不停累加才知道现在朝哪。就像闭着眼睛靠数步子估自己面朝哪个方向，走久了一点点误差攒起来，最后以为朝北其实朝东北——这就是漂移。</p>
</div>
<p>对策有两步。</p>
<p><strong>第一步：开机静止采零偏。</strong> 陀螺仪静止时本该读 0，但实际总有个固定的偏心读数。上电后让车<strong>绝对静止</strong>，采 N 次(常 500~2000，1000 次很常见)陀螺 Z 读数求平均当零偏，运行时每个读数都减掉它。</p>
<div class="ds-callout ds-callout-example" style="border-left:4px solid #7c4dff;background:#7c4dff14;padding:.6em 1em;margin:1.2em 0;border-radius:6px">
<p style="margin:0 0 .45em;font-weight:700;color:#7c4dff"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f9e9.png" alt="🧩" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 比喻</p>
<p>就像体重秤没人站时也显示 0.3kg。你先记住这个 0.3，以后每次读数都减掉它，才是真实角速度。</p>
</div>
<pre><code class="language-c">// 开机静止采陀螺 Z 轴零偏 + 运行时积分 Yaw(相对航向)
float gz_bias = 0, yaw = 0;
void Gyro_CalibZ(void) {            // 上电后车保持静止时调用
    float sum = 0;
    for (int i = 0; i &lt; 1000; i++){ sum += MPU_GetGyroZ(); HAL_Delay(1); }
    gz_bias = sum / 1000.0f;        // 零偏 = 静止读数均值
}
void Yaw_Update(float dt){          // 每个控制周期调用
    float gz = MPU_GetGyroZ() - gz_bias;   // 去零偏
    yaw += gz * dt;                 // 积分得相对航向(短时漂移可忽略)
}</code></pre>
<div class="ds-callout ds-callout-danger" style="border-left:4px solid #ff1744;background:#ff174414;padding:.6em 1em;margin:1.2em 0;border-radius:6px">
<p style="margin:0 0 .45em;font-weight:700;color:#ff1744"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f525.png" alt="🔥" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 采零偏时车必须绝对静止</p>
<p>有人手扶、地面震动，都会把运动量误算进零偏，运行时车就持续缓慢自转/跑偏。而且采一次零偏<strong>也只能减小、不能消除</strong>漂移(温漂、随机游走仍在)，长时间运行 Yaw 仍会缓慢漂。</p>
</div>
<p><strong>第二步：走直线用&#8221;相对航向&#8221;，随时重置。</strong> 既然绝对 Yaw 会漂，那就不依赖它的绝对值——每段直线起步时把当前 yaw <strong>清零</strong>当目标，航向误差 err = 0 − yaw 做 PID，输出叠加到左右轮差速：</p>
<pre><code class="language-c">// 走直线锁航向：起步时 yaw=0(清零当目标)，err = 0 - yaw，差速纠偏
// out = Kp*err - Kd*gz;   pwm_L = base + out;   pwm_R = base - out;</code></pre>
<p>注意上面那行的 D 项直接用了陀螺角速度 <code>gz</code>(对测量微分，比对误差微分更干净、抗扰)。转 90°：把目标航向加/减 90，闭环自动转到位后再清零，进入下一段直线。再高级一点可以做串级：外环用航向角锁朝向、内环用角速度求跟手——不过对入门小车，上面这套单环 PD 已经够稳。</p>
<div class="ds-callout ds-callout-example" style="border-left:4px solid #7c4dff;background:#7c4dff14;padding:.6em 1em;margin:1.2em 0;border-radius:6px">
<p style="margin:0 0 .45em;font-weight:700;color:#7c4dff"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f9e9.png" alt="🧩" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 比喻</p>
<p>不管罗盘(绝对 Yaw)准不准，起步时直接把现在朝向定为 0°当基准，之后谁偏了就往回掰——就像走路时盯着正前方一个目标走，而不是依赖会跑偏的指南针。</p>
</div>
<div class="ds-callout ds-callout-warning" style="border-left:4px solid #ff9100;background:#ff910014;padding:.6em 1em;margin:1.2em 0;border-radius:6px">
<p style="margin:0 0 .45em;font-weight:700;color:#ff9100"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 2024 电赛 H 题的航向实战</p>
<p>团队发现单靠 MPU6050 积分航向 + 编码器里程，长距离会累积误差，温漂还让 Yaw 越漂越大。解决：用实时温度算比例系数做<strong>动态温漂补偿</strong>，把漂移压到 ±1 以内，并用 8 路灰度纠偏兜底。有的队甚至换成维特智能陀螺仪(约 0.05° 精度)替代 MPU6050(约 2°/s 零漂)来提升转向精度。(<a href="https://blog.csdn.net/weixin_60991529/article/details/141832409" target="_blank" rel="noopener noreferrer">来源</a>)</p>
<p>一句话：纯惯性导航(IMU + 编码器)长距离必然累积误差，要么加外部基准(灰度/视觉)纠偏，要么温漂补偿 + 定期重置，别指望积分一路准到底。</p>
</div>
<div class="ds-callout ds-callout-danger" style="border-left:4px solid #ff1744;background:#ff174414;padding:.6em 1em;margin:1.2em 0;border-radius:6px">
<p style="margin:0 0 .45em;font-weight:700;color:#ff1744"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f525.png" alt="🔥" class="wp-smiley" style="height: 1em; max-height: 1em;" /> IMU 必查</p>
<ul>
<li><strong>零偏采集没静止</strong> → 运行时持续自转。</li>
<li><strong>以为采一次零偏就永久消漂</strong> → 温漂仍在，必须相对航向 + 重置。</li>
<li><strong>dt 单位忘换成秒</strong> → 融合/积分比例全错。</li>
<li><strong>方向环只用绝对 Yaw 单环且不重置</strong> → 漂移被当成真实偏差，车被慢慢带偏。</li>
</ul>
</div>
<h2>五、把感官接到一起：分工、串级与切换</h2>
<p>四样感官各管一摊，整车里它们这样配合：</p>
<figure class="ds-diagram" style="text-align:center;margin:1.3em 0"><img decoding="async" src="https://cloudlay.cn/wp-content/uploads/dianseiche/c2cc35986a38.png" alt="电赛小车电控 · 示意图" loading="lazy" style="max-width:100%;height:auto;background:#fff;border-radius:8px;padding:6px;box-shadow:0 1px 6px rgba(0,0,0,.25)"></figure>
<p>记住两条分工原则(串级双环的落地代码与调参顺序，见《PID 进阶》和《状态机与整车软件》两篇)：</p>
<ul>
<li><strong>速度环(编码器反馈)用增量式 PID</strong>——只输出 ΔPWM、自带积分、无饱和切换冲击，适合连续调速；</li>
<li><strong>方向/航向环(灰度/电磁/IMU 反馈)用位置式 PID(实践中多为 PD)</strong>——适合&#8221;锁住某个朝向/位置&#8221;。</li>
</ul>
<div class="ds-callout ds-callout-example" style="border-left:4px solid #7c4dff;background:#7c4dff14;padding:.6em 1em;margin:1.2em 0;border-radius:6px">
<p style="margin:0 0 .45em;font-weight:700;color:#7c4dff"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f9e9.png" alt="🧩" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 比喻：开车时手脚分工</p>
<p>方向环是你的手(管方向、外环、慢一点)，速度环是你的脚(管油门、内环、快一点)。手脚分工又配合，车才能又快又直。</p>
</div>
<p>至于&#8221;什么路段用哪个感官&#8221;，核心思路是<strong>分工 + 兜底</strong>：</p>
<ul>
<li><strong>巡线</strong>主要靠灰度/电磁给偏差；</li>
<li><strong>直道锁航向</strong>用 IMU 的相对航向，防止灰度细微抖动累积成跑偏；</li>
<li><strong>十字/丢线</strong>时灰度偏差不可信，切到 IMU + 编码器&#8221;盲走&#8221;几格穿过去；</li>
<li>一旦灰度长时间丢线，<strong>有外部基准(重新抓到的灰度线)就纠偏、没有就靠惯导兜底</strong>——绝不让 error 清零直冲。</li>
</ul>
<div class="ds-callout ds-callout-success" style="border-left:4px solid #00c853;background:#00c85314;padding:.6em 1em;margin:1.2em 0;border-radius:6px">
<p style="margin:0 0 .45em;font-weight:700;color:#00c853"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 这一篇你该带走的</p>
<ul>
<li>灰度循迹核心 = <strong>加权位置法</strong> + 工程四件套(现场标定 / 全丢线记忆方向 / 十字特判 / 前瞻高度妥协)。</li>
<li>电磁循迹核心 = <strong>LC 选频→放大→检波→ADC</strong> 信号链 + <strong>差比和 (L−R)/(L+R)</strong> 抗幅值漂移；MSPM0 片上运放能省外置前端。</li>
<li>编码器 = <strong>定时器编码器模式 4 倍频</strong>，单位周期脉冲数直接当速度，signed 差值吃掉溢出。</li>
<li>IMU = 加速度 + 陀螺<strong>互补滤波</strong>求姿态；<strong>Yaw 必漂</strong>，靠开机采零偏 + 相对航向 + 随时重置来锁直线。</li>
<li>不管哪种感官，<strong>dt 要严格定时、极性先开环验证、参数现场重标并留约 20% 裕度</strong>——这是&#8221;实验室能跑、赛场也能跑&#8221;的分水岭。</li>
</ul>
</div>
<p>感官齐了、反馈干净了，下一步就是真正让控制环动起来——从最基础的&#8221;把一个电机的速度调稳&#8221;开始，一次搞懂 P、I、D 各自在管什么。我们下一篇 PID 入门见。</p>
<div class="ds-series" style="border:1px solid #4488ff33;background:#4488ff0d;border-radius:8px;padding:.8em 1.1em;margin:1.2em 0">
<p style="margin:0 0 .5em"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4da.png" alt="📚" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 本文是 <strong>「从 0 到 1 带你打电赛 · 小车电控篇」</strong> 系列（共 12 篇）第 5 篇。</p>
<ol style="margin:.2em 0 0;padding-left:1.4em">
<li style="margin:.15em 0"><a href="https://cloudlay.cn/nuedc-car-01-how-to-score/">第1篇 · 拿奖逻辑：把比赛拆成小目标</a></li>
<li style="margin:.15em 0"><a href="https://cloudlay.cn/nuedc-car-02-history/">第2篇 · 赛题进化史与押题</a></li>
<li style="margin:.15em 0"><a href="https://cloudlay.cn/nuedc-car-03-build-and-architecture/">第3篇 · 整车搭建与代码框架</a></li>
<li style="margin:.15em 0"><a href="https://cloudlay.cn/nuedc-car-04-motor-power/">第4篇 · 电机驱动与电源地基</a></li>
<li style="margin:.15em 0"><strong>第5篇 · 感知：灰度/电磁/编码器/IMU（本篇）</strong></li>
<li style="margin:.15em 0"><a href="https://cloudlay.cn/nuedc-car-06-pid-basics/">第6篇 · PID 入门：搞懂 P/I/D</a></li>
<li style="margin:.15em 0"><a href="https://cloudlay.cn/nuedc-car-07-pid-advanced/">第7篇 · PID 进阶：串级+工程补丁</a></li>
<li style="margin:.15em 0"><a href="https://cloudlay.cn/nuedc-car-08-pid-tuning/">第8篇 · PID 调参实战(核心)</a></li>
<li style="margin:.15em 0"><a href="https://cloudlay.cn/nuedc-car-09-advanced-control/">第9篇 · 进阶控制：几时该上</a></li>
<li style="margin:.15em 0"><a href="https://cloudlay.cn/nuedc-car-10-vision-comm/">第10篇 · K230 视觉与通信协议</a></li>
<li style="margin:.15em 0"><a href="https://cloudlay.cn/nuedc-car-11-architecture-fsm/">第11篇 · 状态机与整车软件</a></li>
<li style="margin:.15em 0"><a href="https://cloudlay.cn/nuedc-car-12-field-manual/">第12篇 · 现场作战+避坑+开源</a></li>
</ol>
</div>
]]></content:encoded>
					
					<wfw:commentRss>https://cloudlay.cn/nuedc-car-05-sensing/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
