<?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%8a%b6%e6%80%81%e6%9c%ba/feed/" rel="self" type="application/rss+xml" />
	<link>https://cloudlay.cn</link>
	<description>life</description>
	<lastBuildDate>Sun, 14 Jun 2026 17:08:33 +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带你打电赛·小车电控篇(十一)：把一切串起来——状态机、控制环时序与整车软件架构</title>
		<link>https://cloudlay.cn/nuedc-car-11-architecture-fsm/</link>
					<comments>https://cloudlay.cn/nuedc-car-11-architecture-fsm/#respond</comments>
		
		<dc:creator><![CDATA[云间辞]]></dc:creator>
		<pubDate>Sun, 14 Jun 2026 17:08:33 +0000</pubDate>
				<category><![CDATA[嵌入式]]></category>
		<category><![CDATA[FreeRTOS]]></category>
		<category><![CDATA[实时控制]]></category>
		<category><![CDATA[智能小车]]></category>
		<category><![CDATA[状态机]]></category>
		<category><![CDATA[电赛]]></category>
		<category><![CDATA[软件架构]]></category>
		<guid isPermaLink="false">https://cloudlay.cn/nuedc-car-11-architecture-fsm/</guid>

					<description><![CDATA[📚 本文是 「从 0 到 1 带你打电赛 · 小车电控篇」 系列（共 12 篇）第 11 篇。 第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 篇）第 11 篇。</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"><a href="https://cloudlay.cn/nuedc-car-05-sensing/">第5篇 · 感知：灰度/电磁/编码器/IMU</a></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"><strong>第11篇 · 状态机与整车软件（本篇）</strong></li>
<li style="margin:.15em 0"><a href="https://cloudlay.cn/nuedc-car-12-field-manual/">第12篇 · 现场作战+避坑+开源</a></li>
</ol>
</div>
<p>电赛小车系列前面十篇，我们把电控的零件一个个磨出来了：电机能听话转、传感器看得见路、PID 能把速度和方向调稳、视觉和主控也能对上话。但这些都还只是&#8221;零件&#8221;。这一篇，我们要做的是把所有零件焊成一台真正能在赛道上跑的车。</p>
<p>你会发现一个有意思的现象：很多队伍每个模块单测都好好的——电机转得欢、灰度读得准、PID 波形漂亮——可一旦合到一起跑整车，就开始抽风：要么卡死不动，要么时灵时不灵，要么过个十字就冲出去了。问题几乎都不在某个模块本身，而在&#8221;怎么把它们组织起来、谁先跑谁后跑、出了岔子怎么兜底&#8221;。这就是整车软件架构要解决的事。</p>
<p>这一篇我们讲清四件事：<strong>分层</strong>（代码怎么摆放）、<strong>调度</strong>（谁在什么时候跑）、<strong>控制环时序</strong>（一拍里按什么顺序干活）、<strong>状态机</strong>（整车行为怎么管），最后再钉一遍贯穿全系列的保护逻辑和那条&#8221;机械定上限、软件定下限&#8221;的硬道理。吃透这几件事，你的车就从&#8221;一堆能动的零件&#8221;变成&#8221;一台能比赛的车&#8221;。</p>
<h2>一、分层架构：让你的算法&#8221;换芯片不换脑子&#8221;</h2>
<p>先讲个大白话比喻。写整车软件像点外卖：</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>你（<strong>应用层 App</strong>）只管对着 App 下单，从不冲进后厨。App（<strong>中间件层</strong>）把你的订单翻译成&#8221;做一份番茄炒蛋、骑手送到 3 号楼&#8221;。真正干脏活累活的是商家灶台和骑手（<strong>驱动层 / 硬件抽象层</strong>）。 妙处在于：你换个城市（换芯片），点外卖的方式一点不变，变的只是当地的骑手和店家（驱动层）。</p>
</div>
<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/38c16a37cfcf.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>以逐飞 SeekFree 库为例，它的真实分层是这样的：</p>
<table>
<thead>
<tr>
<th>层</th>
<th>逐飞里叫什么</th>
<th>放什么</th>
<th>芯片相关吗</th>
</tr>
</thead>
<tbody>
<tr>
<td>公共层</td>
<td><code>zf_common</code></td>
<td>时钟、中断、调试配置</td>
<td>弱相关</td>
</tr>
<tr>
<td>驱动层</td>
<td><code>zf_driver</code></td>
<td>PWM、PIT 定时中断、编码器、ADC、GPIO、UART、SPI</td>
<td><strong>强相关</strong></td>
</tr>
<tr>
<td>设备层</td>
<td><code>zf_device</code></td>
<td>摄像头、陀螺、编码器、屏的初始化和应用函数</td>
<td>弱相关</td>
</tr>
<tr>
<td>应用层</td>
<td>你自己写的 <code>code</code></td>
<td>状态机、循迹决策、串级 PID 调用</td>
<td>无关</td>
</tr>
</tbody>
</table>
<p>中间件层（PID、滤波、协议、元素识别）这些纯算法，本质上和芯片没有任何关系——它们只是数学，搬到任何平台上都一样跑。</p>
<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;" /> 上层只调下层，下层绝不反调；设备层禁止直接碰芯片 API</p>
<p>设备层（<code>zf_device</code>）只能调驱动层（<code>zf_driver</code>）的接口，<strong>绝不能直接去调 <code>HAL_GPIO_WritePin</code> 这种最底层的芯片 API</strong>。逐飞为了彻底解耦，连设备层都全部通过驱动层拿接口，连 <code>extern</code> 都不用。</p>
</div>
<p>这条铁律还是用打比方理解最快：</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>下属向上级汇报（对外暴露接口），老板不会替下属干活，下属也别越级跑去机房乱动设备（直接调 HAL）。一旦有人绕过流程，整个公司就乱套了——你换个机房（换芯片），就得满地找哪儿被人偷偷动过。</p>
</div>
<p><strong>为什么值得花这个力气分层？</strong> 因为它直接决定了你前面那些功夫能不能&#8221;白嫖&#8221;过来。整车搭建那一篇讲过&#8221;STM32 练手、MSPM0 比赛&#8221;的策略，分层就是这套策略能成立的根。看一组实打实的证据：同一套逐飞库，社区已经移植到了 RT1064、英飞凌 TC264、STC32G144K，甚至 2025 年 TI 板电赛用的 <a href="https://github.com/woai66/SeekFree_MSPM0G3507_Opensource_Library" target="_blank" rel="noopener noreferrer">MSPM0G3507</a>。这些版本的 App 层和算法层<strong>几乎一字不改</strong>，差异全部集中在底层驱动。</p>
<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>
<p>你在 STM32 上调通的串级 PID、写好的状态机、磨好的丢线找回逻辑，换到 TI MSPM0 比赛时，<strong>只动驱动层就能整段平移过去</strong>。不分层，换芯片就是重写；分层做好，换芯片只是换驱动。</p>
</div>
<p>想找范本直接抄结构的，推荐 <a href="https://github.com/Sakuramdd/SeekFree_RT1064_Opensource_Library" target="_blank" rel="noopener noreferrer">Sakuramdd/SeekFree_RT1064_Opensource_Library</a>，标准三层加上 <code>code</code> 目录（image / control / init），是研究分层与解耦最干净的样本。STM32 与 MSPM0 之间具体怎么对应 API、平移时要注意什么，整车搭建那一篇已经给过对照心法，这里不展开。</p>
<h2>二、调度：电赛首选&#8221;前后台裸机&#8221;，别动不动就上 RTOS</h2>
<p>代码摆好了，下一个问题是：这么多任务——读传感器、跑 PID、刷屏、收视觉数据——谁在什么时候跑？这叫<strong>调度</strong>。常见的有三档，我们从简单到复杂排一下。</p>
<h3>第一档：前后台裸机（电赛首选）</h3>
<p>这是最常见、最可预测的方案，也是绝大多数电赛车的选择。结构极简：</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><strong>主循环</strong>是&#8221;后台&#8221;，像值班大爷慢悠悠扫地、刷屏、收通信，干那些不急的慢活。<strong>定时器中断</strong>是&#8221;前台&#8221;，像门铃——一响立刻放下扫帚去开门（跑控制环），开完门接着扫地。 关键纪律：门铃要<strong>快进快出</strong>，开个门别在门口跟人唠嗑（中断服务函数别太长），不然下一个客人（下一个控制周期）就堵在门口了。</p>
</div>
<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/b4d08705fbeb.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>
<h3>第二档：时间片轮询</h3>
<p>如果你有好几个不同周期的任务（控制环 5ms、读视觉 20ms、刷屏 100ms），可以用一个简单的时间片调度器：用一个 1ms 的定时器统一递减计时，到点就给任务打个&#8221;该跑了&#8221;的标记，主循环看到标记再去执行。</p>
<pre><code class="language-c">// 时间片轮询调度器：一个 1ms 的 tick 管多周期任务
typedef struct { uint16_t period, timer; uint8_t run; void(*task)(void); } Task_t;

Task_t tasks[] = {
    {5,   5,   0, Control_Loop},  // 5ms：  控制环
    {20,  20,  0, Read_Vision},   // 20ms： 收视觉
    {100, 100, 0, Update_OLED},   // 100ms：刷屏
};
#define N (sizeof(tasks) / sizeof(tasks[0]))

void SysTick_Handler(void) {          // 1ms tick
    for (int i = 0; i &lt; N; i++) {
        if (tasks[i].timer == 0) { tasks[i].timer = tasks[i].period; tasks[i].run = 1; }
        else tasks[i].timer--;
    }
}

int main(void) {
    Init();
    while (1)
        for (int i = 0; i &lt; N; i++)
            if (tasks[i].run) { tasks[i].run = 0; tasks[i].task(); }
}</code></pre>
<p>这其实是前后台的&#8221;加强版&#8221;，介于裸机和 RTOS 之间——能管多个不同周期的任务，但仍然透明可控、没有调度器的黑盒。</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;的写法，慢任务（比如刷屏）拖久了会顺延后面的任务。真正死磕时序的控制环，最稳的还是直接放进定时器中断里跑（也就是第一档的&#8221;前台&#8221;），别和慢任务挤在主循环里抢时间。</p>
</div>
<h3>第三档：RTOS（FreeRTOS / RT-Thread）</h3>
<p>只有当任务真的多、真的需要并发的时候才上 RTOS。它的代价不小：</p>
<table>
<thead>
<tr>
<th>RTOS</th>
<th>内核占用</th>
<th>特点</th>
</tr>
</thead>
<tbody>
<tr>
<td>FreeRTOS</td>
<td>4~9 KB</td>
<td>抢占/协作/时间片可选，支持 30+ 架构，tickless 低功耗</td>
</tr>
<tr>
<td>RT-Thread</td>
<td>最小 3K ROM / 1K RAM</td>
<td>基于优先级全抢占、256 优先级 O(1) 调度、自带 TCP/IP</td>
</tr>
<tr>
<td>µC/OS-III</td>
<td>6~24K 代码 + 1K 数据</td>
<td>任务数无限、文档全</td>
</tr>
</tbody>
</table>
<p>电赛如果真要上 RTOS，主流就是 FreeRTOS 和 RT-Thread 这两个。</p>
<h3>那到底该不该上 RTOS？</h3>
<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>去楼下小卖部（三五个任务）凭记忆走最快；跨城多点配送（多任务真并发）才值得开导航（RTOS）。但导航本身也耗油（几 KB 开销）。任务就三五个，硬上 RTOS 纯属给自己添堵。</p>
</div>
<p>更具体地说，<strong>宁可用周期性裸机也不上 RTOS</strong> 的理由有三条，都很实在：</p>
<ul>
<li><strong>确定性</strong>：定时器中断直接管任务，没有 RTOS 那套调度算法和优先级反转，固定周期任务给你严格的时序保证，最坏执行时间也好分析。</li>
<li><strong>低开销</strong>：省掉内核那几 KB 的 ROM/RAM，省掉每个任务的控制块（TCB）。</li>
<li><strong>易验证</strong>：任务少（三五个）时，状态机比多线程透明得多，不会有死锁、没有同步 bug。</li>
</ul>
<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>任务才三五个还非要上 FreeRTOS，徒增几 KB 开销，引入优先级反转和同步 bug，时序还更难验证。<strong>用最小的复杂度完成需求</strong>，这是工程上的硬道理。比赛时一个莫名其妙的死锁，能让你整夜睡不着还找不到原因。</p>
</div>
<p>记住一句话：RTOS 是给&#8221;任务多到管不过来&#8221;准备的解药，不是给整车软件撑场面的装饰品。</p>
<h2>三、控制环时序：5ms 一拍，按死顺序干活</h2>
<p>调度定好了，现在聚焦最核心的那个&#8221;前台&#8221;——控制环。它跑在定时器中断里，是整车的心跳。</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;" /> 5ms 控制周期 = 机器人的心跳</p>
<p>每跳一次，完整做一遍：看路（采集）→ 判断（融合）→ 打方向、踩油门（串级 PID + 输出）→ 检查安全（保护）。心跳必须稳，<strong>每一拍的活必须在一拍之内干完</strong>，绝不能拖到下一拍。</p>
</div>
<p>为什么是 5ms（也就是 200Hz）？这是智能车社区多年试出来的甜点：够快，能保证控制平滑；又不至于太快，给中断里的计算留足时间。背后有个定量依据——<strong>采样定理（Nyquist）</strong>：采样频率要大于被控信号变化频率的 2 倍，才不会&#8221;漏看&#8221;信号的变化。小车的动态没那么快，5ms 一拍绰绰有余。</p>
<div class="ds-callout ds-callout-note" style="border-left:4px solid #448aff;background:#448aff14;padding:.6em 1em;margin:1.2em 0;border-radius:6px">
<p style="margin:0 0 .45em;font-weight:700;color:#448aff"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4dd.png" alt="📝" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 哈工大紫丁香三队的实测节拍</p>
<p>电磁/舵机处理 2.2ms（约 454Hz）、舵机输出 5ms、电机控制 5ms、陀螺采样 5ms，部分队伍把编码器读取放到 10ms。你看，核心控制基本都压在 5ms 这个量级。</p>
</div>
<p>STM32 上配一个 5ms 中断很简单：在 72MHz 主频下，预分频 <code>PSC = 72</code>（把计数时钟降到 1MHz，即 1µs 一格）、重装载 <code>ARR = 5000</code>（数 5000 格就是 5ms），就是精确的 5ms。</p>
<h3>一拍里的标准流程</h3>
<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/df9e9ae0bbc5.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>落成代码骨架就是这样（前后台架构里的&#8221;前台&#8221;）：</p>
<pre><code class="language-c">// PIT / TIM 5ms 中断 —— 固定周期控制环（前台）
void TIM3_IRQHandler(void) {
    if (TIM_GetITStatus(TIM3, TIM_IT_Update)) {
        TIM_ClearITPendingBit(TIM3, TIM_IT_Update);

        // 1) 采集：读编码器测速，读完立刻清零
        int16_t el = Read_Encoder(L); Clear_Encoder(L);
        int16_t er = Read_Encoder(R); Clear_Encoder(R);
        IMU_Read(&amp;gyro, &amp;acc);

        // 2) 融合：互补滤波出姿态角
        float ang = Complementary_Filter(acc, gyro, 0.005f);

        // 3) 串级 PID：外环速度 → 内环转向
        int16_t ps = Speed_PI(target, (el + er) / 2);   // 外环：速度
        int16_t pt = Turn_PD(line_err, gyro.z);         // 内环：转向

        // 4) 输出：差速 + 限幅后写 PWM
        Set_Motor(LIMIT(ps - pt, -7200, 7200),
                  LIMIT(ps + pt, -7200, 7200));

        // 5) 保护：视觉超时则降级，绝不盲冲
        if (++vision_timeout &gt; VTO) Set_Motor_Safe();
    }
}</code></pre>
<h3>内环必须比外环快</h3>
<p>讲串级 PID 时埋过一个伏笔，这里再钉一遍：</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;" /> 串级 PID 像开车</p>
<p>外环（大脑）决定&#8221;我要开 60 码&#8221;，内环（脚）精确控制油门把速度顶上去。大脑别管太细，<strong>脚的反应必须更快</strong>。摄像头循迹车里，方向环用位置式 PD、速度环用增量式 PI，直道加速、弯道减速。</p>
</div>
<p>所以控制环里的铁律是：<strong>内环周期 ≤ 外环周期，且内环先调稳</strong>。外环的输出是内环的目标，内环都晃晃悠悠，外环再准也没用。具体的调参顺序（先内后外、先 P 后 I 再 D、看波形治百病）在 PID 入门、PID 进阶和 PID 调参实战那几篇已经讲透了，这里只强调时序关系。</p>
<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>
<p>在 5ms 控制中断里塞显示刷新、串口打印、图像处理这种耗时操作，会直接挤爆控制周期、破坏固定时序，严重时触发看门狗复位。<strong>控制环里只留：采集 + PID + 输出 + 保护。</strong> 显示、打印、通信、图像处理一律甩到主循环或单独的低频中断。</p>
<p>有个队伍踩过的真实坑：边处理图像边在屏幕上画点，结果屏幕一直闪烁、还拖慢了节拍。正确做法是所有显示函数集中到图像处理完成后一次性执行——显示永远别插进控制热路径。</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;" /> 坑二：把控制周期和 PWM 频率混为一谈</p>
<p>这俩根本不是一回事： &#8211; <strong>PWM 频率</strong>是 20kHz 级——为了超过人耳听觉、避免电机啸叫（电机驱动那一篇讲过）。 &#8211; <strong>控制环频率</strong>是 200Hz 级（5ms 一拍）——这是你算 PID 的节奏。</p>
<p>一个是&#8221;电机驱动信号有多细腻&#8221;，一个是&#8221;你多久重新决策一次&#8221;，别搞混。</p>
</div>
<h3>顺手补一个：传感器进 PID 前先滤波</h3>
<p>采集到的编码器、陀螺数据有高频噪声，直接喂给 PID 的 D 项会被 Kd 放大成持续抖动。一个又便宜又好用的办法是一阶低通：</p>
<pre><code class="language-c">// 一阶低通：新值占 0.3，旧值占 0.7，平滑又便宜
Encoder = Encoder * 0.7f + new_value * 0.3f;</code></pre>
<p>融合姿态角同理。互补滤波是工程上最常用的：</p>
<pre><code class="language-c">// dt = 0.005s；0.98 越大 =&gt; 越信陀螺（高通）
float Complementary_Filter(float acc_ang, float gyro_rate, float dt) {
    static float angle = 0;
    angle = 0.98f * (angle + gyro_rate * dt) + 0.02f * acc_ang;
    return angle;
}</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>陀螺像短跑选手——短期很准，但越跑越偏（零漂会随时间累积）；加速度计像指南针——长期方向对，但一抖一抖的（受振动干扰）。互补滤波把两人的优点加权拼起来：短期信陀螺、长期信加计，得到又稳又准的姿态。精度要求更高时上卡尔曼，原理一样是融合两者，进阶控制那一篇有更细的取舍判断。</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;" /> 滤波也有代价</p>
<p>滤波会引入相位滞后，系数下得太狠会拖慢响应、削弱 D 的效果，需要折中。</p>
</div>
<h2>四、状态机：把整车行为拆成&#8221;一次只演一个角色&#8221;</h2>
<p>控制环管的是&#8221;这一拍怎么打方向&#8221;，但整车在赛道上还有更宏观的行为切换：在起跑线待命、起步加速、跑直道、过弯道、到了十字路口要决策、到终点要精确停靠、丢线了要找回、出意外要急停……这些行为怎么管？答案是<strong>有限状态机（FSM）</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>车在任何时刻只演<strong>一个</strong>角色（待命 / 直道 / 弯道 / 十字 / 丢线 / 急停），剧情（条件）触发了才换角色。每段戏只管自己的台词，出了 bug 一眼就能定位是哪个角色演砸了，不会一锅乱炖。</p>
</div>
<p>这就是状态机最大的价值：<strong>降耦合、易调试</strong>。把复杂行为拆成独立状态，每个状态只管单一功能。智能车国赛代码多年都用状态机管理直道、弯道、十字、丢线、急停；自动驾驶的行为规划也用分层有限状态机（HFSM），状态名都很像，比如 <code>FORWARD_DRIVE</code>、<code>STOP_SIGN_WAIT</code>、<code>CROSS_INTERSECTION</code>。</p>
<h3>整车主状态机骨架</h3>
<figure class="ds-diagram" style="text-align:center;margin:1.3em 0"><img decoding="async" src="https://cloudlay.cn/wp-content/uploads/dianseiche/618fc94ce74a.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>注意急停（ESTOP）能从<strong>任何</strong>状态进入——这是最高优先级，下面代码里会体现（图里只画了几条代表线，实际是所有状态都能转）：</p>
<pre><code class="language-c">typedef enum {
    ST_IDLE, ST_START, ST_STRAIGHT, ST_CURVE,
    ST_CROSS, ST_PARK, ST_LOST, ST_ESTOP
} CarState;

CarState st = ST_IDLE;

void State_Machine(void) {
    if (estop_flag) { st = ST_ESTOP; }   // 急停最高优先级，先判
    switch (st) {
        case ST_IDLE:     if (start_btn) st = ST_START;       break;
        case ST_START:    if (moving)    st = ST_STRAIGHT;    break;
        case ST_STRAIGHT: if (is_cross())      st = ST_CROSS;
                          else if (is_curve()) st = ST_CURVE;
                          else if (line_lost)  st = ST_LOST;  break;
        case ST_CURVE:    if (!is_curve())     st = ST_STRAIGHT;
                          else if (line_lost)  st = ST_LOST;  break;
        case ST_CROSS:    Cross_FillLine();
                          if (passed) st = ST_STRAIGHT;       break;
        case ST_LOST:     Recover_ByLastError();
                          if (line_found) st = ST_STRAIGHT;   break;
        case ST_PARK:     Precise_Stop();                     break;
        case ST_ESTOP:    Set_Motor(0, 0);                    break;
    }
}</code></pre>
<div class="ds-callout ds-callout-note" style="border-left:4px solid #448aff;background:#448aff14;padding:.6em 1em;margin:1.2em 0;border-radius:6px">
<p style="margin:0 0 .45em;font-weight:700;color:#448aff"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4dd.png" alt="📝" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 状态机放在哪跑？</p>
<p>状态机本身不是高频热路径，它做的是&#8221;宏观决策&#8221;，可以放在主循环里、或者用比控制环慢一档的节奏跑（比如配合视觉数据的更新频率）。真正每 5ms 死磕的是控制环；状态机只负责告诉控制环&#8221;现在该用什么目标速度、什么循迹策略&#8221;。</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>
<p>两个高发 bug： 1. <strong>急停不是最高优先级</strong>——藏在某个 case 里，结果某些状态下急停根本触发不了。一定要像上面那样，在 switch 之前先判 <code>estop_flag</code>。 2. <strong>丢线找回没有超时退出</strong>——车进了 <code>ST_LOST</code> 状态，找半天找不回来又没有退出条件，就卡死在那儿原地打转。每个&#8221;等待型&#8221;状态（丢线找回、十字通过、精确停靠）都该配一个超时兜底，超时了就降级或急停。</p>
</div>
<h2>五、保护逻辑：能拿分的车，先得是&#8221;摔不死&#8221;的车</h2>
<p>开篇那一篇讲过电赛的拿分逻辑——有一堆&#8221;失败即 0 分&#8221;的红线（冲出赛道、超时等等）。所以整车软件里，保护逻辑不是锦上添花，而是保命。前面控制环代码里那行 <code>if (++vision_timeout &gt; VTO) Set_Motor_Safe();</code> 就是保护段的入口。这里展开两个最关键的。</p>
<h3>视觉超时降级：信号断了别闭眼猛踩</h3>
<p>这是新手最容易忽略、又最容易翻车的地方。注意一个事实：<strong>摄像头帧率（60~100Hz）远低于控制环（200Hz）</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>开车进隧道，导航（摄像头）突然黑屏。聪明的做法是：松油门、按记忆方向慢慢直行，等信号恢复；而不是闭着眼按原速猛踩。</p>
</div>
<p>具体做法：设一个看门狗 / 超时计数器，每来一帧新数据就清零，控制环每拍 +1。超过阈值就降级——保持上一个有效打角、减速或限速直行；严重时直接急停。阈值各队按自己车的特性定。视觉那一篇也讲过同款思路：通信超时（比如超过 100ms 没收到包）就降级到灰度或 IMU 兜底，两处其实是一回事。</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>摄像头帧率低于控制环却不做帧同步、不做超时处理，用过期或无效图像盲控，丢线那一刻直接冲出去——这是社区里反复出现的&#8221;冲出赛道&#8221;原因之一。</p>
</div>
<h3>丢线找回：记住&#8221;最后看见路的方向&#8221;</h3>
<p>丢线之后怎么回到赛道？核心思路是<strong>记忆最后一次有效偏差</strong>：</p>
<ul>
<li>设一个 <code>last_error</code>，没丢线时持续更新它；</li>
<li>一旦判定丢线，就<strong>冻结</strong>这个值；</li>
<li>用冻结的偏差判断车是往哪边出去的，据此朝相反方向打角，把车拽回赛道。</li>
</ul>
<p>判丢线常用&#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>如果丢线瞬间偏差归零或乱跳，车就彻底失去方向感，原地乱转或直接飞出去。一定要冻结 <code>last_error</code>，靠它把车找回来。条件允许的话，视觉 + 电磁双传感器互补，丢线时切到电磁兜底也是好招。</p>
</div>
<h2>六、一条贯穿始终的硬道理：机械定上限，软件定下限</h2>
<p>最后，讲一个比任何代码都重要的认知，来自哈工大紫丁香队的血泪经验：</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;" /> 机械影响上限，软件影响下限</p>
<p>机械没做好，车速会被死死限制——高速甩尾侧滑对机械要求极高，主销后倾/内倾、前束、底盘高度（降重心）直接影响回正稳定性。<strong>软件再强，也救不了垃圾机械。</strong></p>
</div>
<p>他们有两个特别值得记的故事：</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>他们的电磁越野组一开始用纯神经网络控制，低速效果好，但高速抖动过大、甚至直接冲出赛道。事后复盘出两个根因：一是偏差范围太大，量化成整型时损失了精度；二是用来做时序预测的网络（TCN）预测值偏小，还出现了&#8221;提前预测&#8221;的现象，导致打角时机不对。最终方案是混合控制——用神经网络提取与时间无关的信息，叠加传统 PID 处理时间相关的部分，按预测角度动态切换两者权重，才稳下来。</p>
<p><strong>教训</strong>：高速控制对反馈精度和动态响应极其敏感；经典 PID 常常是高速稳定的&#8221;压舱石&#8221;。进阶算法该不该上、怎么上，进阶控制那一篇有更细的判断。</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>同一支队伍发现车速一高就明显甩尾侧滑，软件怎么调都压不住，最后被迫回去改机械——调主销后倾内倾、前束、降低底盘重心，问题才解决。</p>
<p><strong>教训</strong>：调参遇到天花板，别在软件里死磕，先回头看看是不是机械到极限了。</p>
</div>
<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>
<p>线上比赛真有队伍电磁没标定就莫名其妙冲出赛道——而国赛赛道边缘是黑海绵条，是否冲出由人工判罚，赛场上还无从复现。<strong>起跑前一定留一个标定 / 自检环节</strong>，别带着一台没标定的车上场。</p>
</div>
<h2>想找完整工程当范本？</h2>
<p>光看片段不过瘾，下面几个开源仓库可以拿来研究整车是怎么组织的：</p>
<ul>
<li><a href="https://github.com/Sakuramdd/SeekFree_RT1064_Opensource_Library" target="_blank" rel="noopener noreferrer">Sakuramdd/SeekFree_RT1064_Opensource_Library</a>：逐飞 RT1064 视觉组开源库，标准三层 + <code>code</code>(image/control/init)，研究分层与解耦的最佳范本。</li>
<li><a href="https://github.com/woai66/SeekFree_MSPM0G3507_Opensource_Library" target="_blank" rel="noopener noreferrer">woai66/SeekFree_MSPM0G3507_Opensource_Library</a>：逐飞库移植到 TI MSPM0G3507，直接对应 TI 赛道，是芯片解耦平移的活样本。</li>
<li><a href="https://github.com/Jerrysupreme/XJTU-VISON_SMARTCAR_2025" target="_blank" rel="noopener noreferrer">Jerrysupreme/XJTU-VISON_SMARTCAR_2025</a>：西安交大 2025 智能视觉组完整参赛级工程，底盘 RT1064 + 摄像头，学整车怎么组织。</li>
<li><a href="https://github.com/Blight001/SmartCarCameraTrackingSimulation" target="_blank" rel="noopener noreferrer">Blight001/SmartCarCameraTrackingSimulation</a>：Unity 做的赛道仿真，10 款赛道含国赛道，还能逐帧回溯复盘&#8221;为什么冲出赛道&#8221;——没硬件时调循迹、调参、复盘的神器。</li>
<li><a href="https://github.com/hxk55668/PID_CAR-STM32-FREERTOS-" target="_blank" rel="noopener noreferrer">hxk55668/PID_CAR-STM32-FREERTOS-</a>：想用 FreeRTOS 组织控制/通信/显示任务的，可以参考这个。</li>
</ul>
<p>更全的开源清单和现场调试速查表，留到下一篇集中给。</p>
<h2>小结：把这一篇钉进脑子</h2>
<p>到这里，整车软件的骨架就立起来了。回顾一下这几条心法：</p>
<ul>
<li>☐ <strong>分层</strong>：App / 中间件 / Driver / HAL 四层，上层只调下层、下层不反调，设备层禁碰芯片 API——换芯片只改驱动层。</li>
<li>☐ <strong>调度</strong>：电赛首选前后台裸机（主循环跑慢任务 + 定时器中断跑控制环），任务真多了再考虑 RTOS。</li>
<li>☐ <strong>控制环</strong>：5ms 一拍（200Hz），按&#8221;采集→融合→串级 PID→输出→保护&#8221;死顺序走，内环快于外环，ISR 只做核心、耗时操作甩主循环。</li>
<li>☐ <strong>状态机</strong>：把整车行为拆成独立状态，急停最高优先级、等待型状态都要超时退出。</li>
<li>☐ <strong>保护</strong>：视觉超时降级、丢线记忆找回、出界急停——保命优先。</li>
<li>☐ <strong>认知</strong>：机械定上限、软件定下限；传感器上场前必标定。</li>
</ul>
<p>软件框架搭好了，但真正决定你能不能拿奖的，是四天三夜里在赛场上把它跑稳、调好、不翻车。下一篇现场作战手册，我们就聊聊四天三夜怎么安排、调试怎么避坑、以及哪些开源仓库值得你现在就 star。</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 篇）第 11 篇。</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"><a href="https://cloudlay.cn/nuedc-car-05-sensing/">第5篇 · 感知：灰度/电磁/编码器/IMU</a></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"><strong>第11篇 · 状态机与整车软件（本篇）</strong></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-11-architecture-fsm/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
