ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

从TinyML到CORDIC:有限资源下的四大硬核计算方案

从TinyML到CORDIC:有限资源下的四大硬核计算方案 1. 从一份早报标题里拆出的四个硬核方向看到Hackaday 科技精选早报这个标题很多人第一反应是这不就是个资讯合集吗。但如果你真在硬件圈或者嵌入式圈待过一段时间就会知道Hackaday 这类站点每天冒出来的项目真正值得动手复现的其实就那么几个剩下的要么是概念验证、要么是博眼球。所以一份有价值的早报核心不在于全而在于筛——把当天最值得动手的几个方向挑出来讲清楚它为什么值得做、难点在哪、普通人能不能复现。这篇要聊的四个方向分别对应四个完全不同的技术栈AI 与硬核工程的交叉、IR Blaster 红外发射器、Intel 8087 浮点协处理器、CORDIC 算法。乍一看这四个东西八竿子打不着但如果你把它们放在用有限资源做精确计算这条主线上会发现它们其实是同一个问题的四种解法AI 想用算力换智能IR Blaster 想用最低成本控制家电8087 想用专用硬件加速浮点CORDIC 想用移位加法替代乘法。理解了这条主线你再看任何一个嵌入式项目判断它值不值得做会快很多。我写这篇的出发点很简单网上关于这四个方向的资料要么太学术论文式推导要么太浅今天发现一个好玩的项目。我想做的是把它们拉到一个有点基础的爱好者能不能上手这个层面上来该给公式给公式该给代码给代码该说坑说坑。适合的读者是玩过 Arduino 或树莓派、看得懂 C 语言、对底层计算有点好奇、但还没系统接触过浮点硬件和 CORDIC 的人。如果你正好在这个阶段这篇应该能帮你省下不少翻资料的时间。下面按AI 工程实践的现实边界 → IR Blaster 的完整复现链路 → Intel 8087 的历史与原理 → CORDIC 的算法本质这个顺序展开每个方向都会给到可操作的细节而不是停留在概念层面。2. AI 工程实践在硬件项目里的真实边界2.1 为什么硬件圈的 AI 落地和互联网圈完全是两回事先泼一盆冷水。现在网上关于AI 工程实践的内容绝大多数是围绕大模型 API 调用、Agent 编排、提示词工程展开的这些在纯软件场景下确实能跑通。但一旦落到硬件项目上约束条件立刻变了你的算力可能是几十 MHz 的单片机内存可能只有几十 KB功耗预算可能只有几毫安。这种条件下那些动辄几十亿参数的大模型根本无从谈起。所以硬件圈的 AI 落地实际走的是另一条路TinyML。它的核心思路不是把大模型塞进单片机而是针对具体任务训练一个极小的模型然后量化、剪枝、部署。一个典型的手写数字识别模型经过量化后可以压到 20KB 以内在 Cortex-M4 上跑一次推理只要几毫秒。这才是硬件项目里 AI 的真实形态。我见过太多人一上来就想在 ESP32 上跑语音助手结果卡在内存分配上。正确的做法是先明确任务边界你是要做关键词唤醒KWS、异常检测、还是简单分类不同任务的模型规模和算力需求差一个数量级。关键词唤醒用 10-20KB 的模型就够了异常检测甚至可以用统计方法加一个浅层网络。2.2 从训练到部署一条能跑通的 TinyML 链路如果你真想动手下面这条链路是我实测下来最省事的数据准备用公开数据集起步比如 KWS 用 Google Speech Commands异常检测用 NASA 的轴承数据集。别一上来就自己采数据采样、标注、清洗的时间成本远超你的预期。模型训练用 TensorFlow 或 PyTorch 训练一个小模型。注意训练阶段可以用浮点别急着量化。模型结构优先选深度可分离卷积MobileNet 系列思路参数量控制在 10 万以内。量化训练完后做 int8 量化。这一步是硬件部署的关键因为大多数 MCU 没有浮点单元或者浮点很慢int8 推理速度能快 3-5 倍模型体积缩小 4 倍。转换与部署用 TensorFlow Lite for Microcontrollers 或 CMSIS-NN 把模型转成 C 数组集成到你的固件里。推理验证在目标板子上跑对比 PC 端和 MCU 端的输出差异。量化会带来精度损失通常 1-3 个百分点是可接受的。这里有个容易忽略的点量化校准集的选择。很多人随便拿几张图做校准结果量化后精度掉得厉害。校准集应该覆盖你实际推理时会遇到的数据分布至少几百个样本否则量化参数scale 和 zero point会偏。2.3 硬件项目里 AI 到底该用在哪三个真实场景不是所有硬件项目都需要 AI。我总结下来AI 在硬件项目里真正有价值的场景有三个传感器数据的模式识别比如用加速度计做手势识别、用麦克风做关键词唤醒、用电流传感器做设备故障检测。这些场景传统方法阈值、FFT 特征分类器也能做但 AI 能省掉大量手工特征工程。视觉任务的边缘化比如用 ESP32-CAM 做简单的物体检测。注意这里说的是简单复杂检测还是得上专用芯片。自适应控制比如用强化学习调 PID 参数。这个方向比较前沿落地案例少但思路值得关注。反过来如果你的任务用几条 if-else 就能解决别硬上 AI。我见过有人用神经网络判断一个按钮有没有被按下这就是典型的过度设计。提示TinyML 的瓶颈往往不是模型本身而是内存。部署前一定要算清楚模型权重 中间激活值 输入缓冲区三者加起来不能超过可用 RAM 的 70%留 30% 给栈和堆。3. IR Blaster 红外发射器从原理到能用的完整链路3.1 红外遥控的编码本质为什么你的发射器总是不灵IR Blaster 这个项目看起来简单——不就是发个红外信号吗但真正做过的人都知道坑多得很。核心问题在于红外遥控没有统一标准。NEC、RC5、RC6、Sony SIRC、Samsung、Panasonic每家协议都不一样载波频率、编码方式、位时序全不同。先说最基础的物理层。红外遥控用的载波频率通常是 38kHz但也有 36kHz、40kHz、56kHz 的。载波的作用是抗干扰——接收端只对特定频率的信号敏感。如果你用 38kHz 的发射管去控制一个 40kHz 的设备距离会大幅缩短甚至完全没反应。编码层面以最常见的 NEC 协议为例一个完整的帧由引导码9ms 高 4.5ms 低、地址码8 位 8 位反码、命令码8 位 8 位反码组成。逻辑 0 是 560us 高 560us 低逻辑 1 是 560us 高 1690us 低。注意这里的高指的是 38kHz 载波调制后的高不是持续高电平。很多人第一次做 IR Blaster 失败就是因为直接用 GPIO 输出高低电平没有做载波调制。正确的做法是用定时器产生 38kHz 的 PWM然后用另一个定时器控制 PWM 的启停来编码。3.2 硬件选型发射管、驱动电路和接收头的搭配硬件部分有三个关键器件器件作用选型要点红外发射管发射红外光波长 940nm 最常见正向电流 100mA 左右驱动三极管/MOS放大电流单片机 GPIO 通常只能输出 20mA必须加驱动红外接收头接收并解调常见型号如 VS1838B输出已解调的信号发射管的驱动电路是重点。单片机 GPIO 直接驱动发射管电流不够距离只有几厘米。加一个 NPN 三极管如 S8050做开关配合限流电阻可以把电流拉到 100mA 以上距离能到 5-8 米。限流电阻的计算假设电源 5V发射管正向压降 1.2V目标电流 100mA则 R (5 - 1.2 - 0.3) / 0.1 ≈ 35Ω取 33Ω 标准值。这里的 0.3V 是三极管饱和压降。接收头这边VS1838B 这类一体化接收头内部已经做了载波解调和放大输出的是干净的数字信号直接接单片机 GPIO 就能读。但要注意接收头的输出是反相的——收到 38kHz 载波时输出低电平没有载波时输出高电平。3.3 用定时器精确复现 NEC 时序代码层面的关键细节下面是一段基于 STM32 的 NEC 编码核心逻辑用两个定时器TIM1 产生 38kHz PWMTIM2 做微秒级延时。// 38kHz PWM 初始化假设 72MHz 主频 // 周期 72MHz / 38kHz ≈ 1895取 1894 // 占空比 1/3比较值 1894 / 3 ≈ 631 void pwm_init(void) { // TIM1 配置为 PWM 模式ARR 1894, CCR 631 // 实际代码略重点是 ARR 和 CCR 的计算 } // 发送一个 NEC 位 void send_bit(uint8_t bit) { pwm_start(); // 开启载波 delay_us(560); // 560us 载波 pwm_stop(); // 关闭载波 if (bit) { delay_us(1690); // 逻辑11690us 空闲 } else { delay_us(560); // 逻辑0560us 空闲 } } // 发送完整 NEC 帧 void send_nec(uint8_t addr, uint8_t cmd) { pwm_start(); delay_us(9000); pwm_stop(); delay_us(4500); // 引导码 for (int i 0; i 8; i) send_bit((addr i) 1); for (int i 0; i 8; i) send_bit(!((addr i) 1)); for (int i 0; i 8; i) send_bit((cmd i) 1); for (int i 0; i 8; i) send_bit(!((cmd i) 1)); pwm_start(); delay_us(560); pwm_stop(); // 结束位 }这段代码有几个坑要提醒delay_us 的精度如果用循环做延时编译器优化等级会影响实际延时。建议用定时器计数或 DWT 周期计数器精度能到 1us 以内。中断干扰发送过程中如果被中断打断时序会乱。发送前关中断发完再开。载波占空比1/3 占空比是经验值太高发射管发热太低距离不够。3.4 学习型 IR Blaster如何录制并复现未知协议如果你要控制的设备协议未知就需要学习功能。思路是用接收头录下原始波形的时间序列然后原样回放。具体做法是用定时器输入捕获记录每次电平跳变的时间戳存成数组。回放时按时间戳依次翻转 GPIO。这个方法的好处是通用坏处是数据量大。一个完整的空调遥控码可能有几百个跳变占用几 KB 内存。优化方法是只存跳变间隔用 uint16_t并且对载波部分做压缩——因为载波是固定 38kHz只需要记录载波持续多久和空闲持续多久。我实测下来学习型方案对空调这种长码特别有用因为空调协议往往包含温度、模式、风速等几十位信息手动解码非常痛苦。4. Intel 8087浮点运算从软件模拟到硬件加速的转折点4.1 8087 出现的背景没有它浮点运算有多慢要理解 8087 的价值得先知道没有它的时候浮点运算有多惨。Intel 8086 是 16 位整数处理器没有浮点单元。做一次浮点加法需要用软件模拟拆解阶码和尾数、对阶、相加、规格化、舍入一套流程下来几十到上百个时钟周期。做三角函数、开方这种更是要几百上千个周期。8087 作为协处理器把这个过程硬件化了。它内部有 80 位的浮点寄存器栈支持 IEEE 754 单精度、双精度和扩展精度。一次浮点加法只需要几个周期比软件模拟快几十倍。这在当时是革命性的——CAD、科学计算、电子表格这些应用全靠它才跑得动。8087 和 8086 的配合方式也很有意思。它不是独立工作的而是监听8086 的指令流。当 8086 遇到浮点指令以 ESC 开头的转义指令8087 就接管执行8086 继续取下一条指令。这种松耦合设计让两者能并行工作提高了吞吐。4.2 80 位扩展精度格式为什么是 80 位而不是 64 位8087 内部用 80 位格式存储浮点数1 位符号 15 位阶码 64 位尾数。对比 IEEE 754 双精度的 1 11 528087 的阶码和尾数都更长。为什么这么设计核心原因是中间计算精度。浮点运算的误差主要来自舍入如果每一步都用双精度存储多次运算后误差会累积。8087 用 80 位做中间计算只在最后存储时才舍入到 32 位或 64 位这样能显著降低累积误差。这个思路后来被 IEEE 754 采纳成为扩展精度的概念。15 位阶码意味着指数范围达到 ±16383远超双精度的 ±1023。64 位尾数提供了约 19 位十进制有效数字。这些在当时都是顶配。4.3 从 8087 到现代 FPU哪些设计被继承了下来8087 的很多设计直接影响了后来的 x87 指令集和现代 FPU寄存器栈结构8087 的 8 个 80 位寄存器组成一个栈操作默认在栈顶进行。这个设计后来被 x87 继承但因为栈式编程不直观现代编译器很少直接用而是把 x87 当普通寄存器用。超越函数指令8087 内置了 FSIN、FCOS、FPTAN、FYL2X 等指令能直接算三角函数和对数。这些指令内部用的就是 CORDIC 类算法下面会讲。异常处理除零、溢出、下溢、精度损失等异常8087 都有对应的标志位和屏蔽机制。这套异常模型被 IEEE 754 标准化。如果你现在写代码可能觉得浮点运算是理所当然的。但每次你在 Python 里写math.sin(x)背后其实是几十年的硬件演进。8087 是这条路上的关键一站。5. CORDIC不用乘法器也能算三角函数的算法5.1 CORDIC 的核心思想把旋转分解成一系列固定角度CORDICCOordinate Rotation DIgital Computer是 1959 年 Jack Volder 提出的算法专门解决一个问题在没有硬件乘法器的条件下如何计算三角函数、双曲函数、开方等。它的核心思想非常巧妙把任意角度的旋转分解成一系列固定角度的旋转。这些固定角度满足 tan(θ) 2^(-i)也就是说第 i 次旋转的角度是 arctan(2^(-i))。这样一来旋转操作只需要移位和加法不需要乘法。具体来说每次迭代做三件事根据当前角度和目标角度的差值决定这次是顺时针还是逆时针旋转。用移位和加法完成旋转x x - σ·y·2^(-i)y y σ·x·2^(-i)其中 σ 是 ±1。更新累积角度z z - σ·arctan(2^(-i))。迭代足够多次后z 趋近于 0此时的 (x, y) 就是旋转后的坐标。如果初始向量是 (1, 0)那么最终的 x 就是 cos(θ)y 就是 sin(θ)。5.2 迭代公式的推导与增益因子的处理上面公式里的 2^(-i) 就是移位。但有个细节每次旋转的模长不是 1而是 sqrt(1 2^(-2i))。多次旋转后总增益是这些模长的乘积K ∏ sqrt(1 2^(-2i)) ≈ 1.646760258...这个增益是固定的与旋转角度无关。所以实际使用时要么在最后把结果除以 K要么在初始化时把 (x, y) 预乘 1/K。后者更常用因为除法比乘法慢。角度表 arctan(2^(-i)) 是预先算好的常量存在 ROM 里。对于 16 位精度通常迭代 16 次就够了。角度表的前几项iarctan(2^(-i)) (度)045.0000126.5651214.036237.125043.576351.7899注意角度范围CORDIC 的旋转模式只能覆盖 -99.7° 到 99.7°因为所有角度之和约 99.7°。要算更大角度需要先用三角恒等式做象限变换。5.3 定点实现用整数运算跑 CORDIC 的完整代码下面是一段 16 位定点 CORDIC 的 C 实现输入角度用度 × 100表示输出 sin 和 cos 用 Q15 格式即 -32768 到 32767 表示 -1 到 1。#include stdint.h // 角度表单位度 × 100 static const int16_t atan_table[16] { 4500, 2657, 1404, 713, 358, 179, 90, 45, 22, 11, 6, 3, 1, 1, 0, 0 }; // CORDIC 增益倒数Q15 格式 #define K_INV 19898 // 1/1.64676 ≈ 0.60725, × 32768 ≈ 19898 void cordic_sincos(int16_t angle_deg100, int16_t *sin_out, int16_t *cos_out) { int32_t x K_INV; int32_t y 0; int32_t z angle_deg100; int32_t x_new, y_new; for (int i 0; i 16; i) { if (z 0) { x_new x - (y i); y_new y (x i); z - atan_table[i]; } else { x_new x (y i); y_new y - (x i); z atan_table[i]; } x x_new; y y_new; } *cos_out (int16_t)x; *sin_out (int16_t)y; }这段代码有几个实操要点移位代替乘法y i就是 y × 2^(-i)编译器会优化成移位指令。角度范围输入角度要在 -90° 到 90° 之间。超出范围要先做象限变换比如 sin(120°) sin(60°)。精度16 次迭代后误差在 1-2 个 LSB 左右对大多数应用够用。要更高精度就增加迭代次数和角度表项。溢出中间结果用 int32_t 防止溢出最后再截断到 int16_t。5.4 CORDIC 在现代芯片里的位置为什么它还没过时你可能会问现在 MCU 都有硬件乘法器了CORDIC 还有用吗答案是在特定场景下依然有用。第一有些低成本 MCU 或 FPGA 里乘法器资源紧张CORDIC 能省下乘法器给其他模块用。第二CORDIC 的硬件实现非常规整——只需要移位器、加法器和一个小 ROM面积小、功耗低。第三在需要高吞吐的场合比如软件定义无线电里的坐标变换CORDIC 可以流水线化每个时钟周期出一个结果。实际上很多现代芯片里依然内置 CORDIC 硬件加速器只是不叫这个名字。比如某些电机控制芯片里的旋转变换模块本质就是 CORDIC。所以理解 CORDIC不只是学一个老算法而是理解一类硬件加速思路。6. 把这四个方向串起来看有限资源下的计算哲学写到这里四个方向都聊完了。如果你把它们放在一起看会发现一个共同的主题在资源受限的条件下如何用巧妙的方法完成精确计算。AI 工程实践在硬件上的落地本质是在算力和内存受限下做取舍——用 TinyML 替代大模型用量化替代浮点用专用模型替代通用模型。IR Blaster 是在成本受限下做取舍——用几块钱的发射管和三极管配合精确的定时器时序替代昂贵的专用遥控芯片。Intel 8087 是在芯片面积受限下做取舍——用协处理器的方式扩展浮点能力而不是把 FPU 塞进主 CPU。CORDIC 是在硬件乘法器受限下做取舍——用移位和加法替代乘法用迭代替代查表。这种受限下的取舍思维其实是硬件工程的核心。软件工程师可以假设内存无限、算力无限但硬件工程师永远在跟约束打交道。你手上的 MCU 有多少 RAM、多少 Flash、主频多少、有没有 FPU、有没有硬件乘法器这些约束直接决定了你的方案。我个人的经验是先摸清约束再选方案。很多人一上来就想用最先进的技术结果发现目标平台根本跑不动。反过来如果你先明确我只有 64KB RAM、没有 FPU、主频 48MHz那么方案空间立刻缩小到几个可行选项选择反而变简单了。最后分享一个我常用的判断方法拿到一个硬件项目先问三个问题——算力够不够、内存够不够、功耗够不够。三个都够随便选方案有一个不够就得针对性优化两个以上不够就得重新考虑需求是否合理。这个方法帮我省下了很多走弯路的时间。如果你对这四个方向里的任何一个想深入我的建议是IR Blaster 最适合入门硬件成本低、反馈直观CORDIC 最适合练算法思维纯软件就能验证8087 适合了解计算机体系结构历史TinyML 适合想往边缘 AI 方向走的人。挑一个先动手比四个都看一遍强得多。
返回列表