简介本资源是一个基于STM32F10x系列微控制器的上拉式磁悬浮系统完整开发包面向嵌入式初学者、电子设计竞赛学生及磁悬浮技术实践者解决电磁力闭环控制、传感器反馈处理与实时悬浮稳定等核心工程问题。压缩包共88个文件含39个C源文件实现ADC采样、PID控制、PWM驱动、串口调试等关键逻辑、40个H头文件封装外设寄存器定义与函数声明、1个Keil工程配置文件.uvprojx、1个hex可执行镜像及批处理清理脚本等总大小仅365KB轻量但结构完整涵盖CORE内核启动、SYSTEM系统时钟与延时、HARDWARE按键/LED/ADC/LCD/平衡模块等驱动、USER主控逻辑与悬浮算法四大功能层级。已有1495人学习下载读者可直接导入Keil MDK编译运行获取从电路原理理解、固件库调用、USMART串口调试到物理系统联调的全流程实践能力特别适合掌握嵌入式控制闭环设计与磁悬浮物理建模的进阶学习。 很多人第一次接触磁悬浮第一反应都是“底下有个线圈把东西推起来”。但真正玩过这个项目之后你会发现用上方电磁铁把悬浮体“拉”起来的方案反而更紧凑也更适合入门。这套基于STM32的上拉式磁悬浮系统本质上就是用一个线性霍尔传感器测量悬浮体位置然后用PID闭环控制电磁铁的电流让悬浮体稳定悬停在指定高度。我最初做这个项目的时候也踩了一堆坑从一上电就吸死、到持续抖动、再到玩一会儿就飘走过程相当折腾。这篇就把整个系统的原理、硬件设计、控制代码和调试经验一次讲透给想复现的朋友一条能直接走通的路。1. 上拉式磁悬浮到底是怎么“飘”起来的原理拆解与拓扑对比1.1 “上拉”和“下推”的核心区别常见的磁悬浮摆件大多是下推式底部线圈通电后对悬浮体通常是一个含有永磁体的结构产生向上的斥力和重力平衡。但下推式有个天然麻烦——悬浮体一旦偏离中心水平方向也很容易失稳整个结构避震和机械对位都得做得很讲究。上拉式则是把电磁铁放在顶部悬浮体挂在下面。电磁铁通电产生吸力把悬浮体往上吸而重力把它往下拉。只需要控制吸力的大小等于重力悬浮体就能在空中悬停。由于悬浮体受到的是“吸”而不是“推”它在水平方向上更倾向于被吸到电磁铁正下方天然自带一点自对中能力结构上简单不少。另外一个很重要的差异是传感器安装。下推式系统如果想做闭环传感器得放在线圈旁边去测悬浮体距离很容易受到线圈磁场的干扰。上拉式系统里线性霍尔传感器可以直接安装在电磁铁底部中心悬浮体就在正下方磁路非常清晰。我实测下来同样价位和尺寸的传感器在上拉式结构里输出更干净线性度也更好。1.2 受力模型与控制目标为什么必须闭环上拉式磁悬浮的核心受力关系可以简化成重力方向向下F_g m * g恒定不变。电磁铁吸力方向向上F_m k * (I / x)^2其中 I 是线圈电流x 是悬浮体到电磁铁底部的距离k 是跟线圈匝数、磁芯截面积、悬浮体磁性强弱有关的系数。也就是说吸力同时受电流和距离影响而且是非线性关系。如果只给一个固定电流悬浮体很快就会被吸上去贴死或者因为电流不足直接掉下去。要让悬浮体悬停在某个高度必须实时测量 x根据 x 的变化动态调整 I。从控制角度理解这个系统是一个典型的“开环不稳定”系统。悬浮体在平衡点附近时如果距离变小吸力会急剧增大进一步把悬浮体拉得更近形成正反馈。只有通过快速、准确的闭环控制才能让它稳定下来。这也是为什么做磁悬浮离不开PID而不是像普通灯具那样“通电就行”。1.3 稳定悬浮点的线性化简化虽然上面的受力公式是非线性的但在实际工程中我们只需要在目标悬浮高度附近做小范围调节。把吸力和距离的关系在平衡点 x0 附近一阶展开会得到一个线性的近似模型F_m ≈ F_0 k1 * (x - x0) k2 * (i - i0)其中 k1 是距离变化引起的吸力变化k2 是电流变化引起的吸力变化。这样一来整个闭环就可以用位置式PID去处理不需要上什么高级控制算法。很多朋友一听到磁悬浮就觉得得上卡尔曼滤波、状态空间、模糊控制其实对于单自由度上拉式结构PID再加上合理采样和滤波完全能把悬浮体稳住抖动幅度控制在毫米以内。2. 动手前必须先想清楚的硬件架构传感器、功率驱动、主控2.1 位置反馈传感器线性霍尔传感器的选型与标定上拉式磁悬浮最推荐的位置传感器是线性霍尔传感器比如 SS495A、A1304、OH49E。它的原理是感应磁场强度并输出对应的模拟电压磁场越强电压越高。悬浮体本身带永磁体离霍尔传感器越近磁场越强输出电压也就越高这就是位置测量的基础。选型时主要看三个指标灵敏度mV/Gauss毫伏/高斯灵敏度越高同样位移变化带来的电压变化越大分辨率越高。线性范围要保证悬浮体在上极限和下极限之间运动时霍尔输出电压仍然是线性的。温漂SS495A 的温漂系数通常比某些低成本国产霍尔更小长时间上电后位置漂移更小。我自己用的是 SS495A供电电压 5V静态输出电压约 2.5V灵敏度约 2.5mV/Gauss。下方悬浮体用直径 20mm、厚度 5mm 的钕铁硼圆片磁铁实测在 5mm 到 30mm 的悬浮范围内霍尔输出电压变化大约在 1.2V 到 3.8V之间线性度不错。标定的方法非常重要。第一次调试时我把悬浮体放在电磁铁下方不同距离用游标卡尺量距离同时用串口打印ADC读取到的霍尔电压值整理成一个距离-电压对应表。这样做的目的是确定目标悬浮高度对应的目标电压值。比如我希望悬浮体悬停在 15mm 高度查表得到对应的霍尔电压是 2.78V那么ADC采样后换算成电压和目标值比较就能得到误差信号。一定要把机械参考点定好。我用的机械结构是电磁铁底部固定一块亚克力板板上留一个小孔让霍尔传感器嵌入霍尔传感器的感应面基本和板子底面齐平。这样标定出来的距离才准确如果霍尔传感器陷在板子内部那它感受到的磁场和悬浮体距离就不是完全对应的。2.2 电磁铁与驱动电路设计要点电磁铁是这个项目的执行机构也是最容易想当然的部分。很多人直接用现成的继电器线圈或者电磁锁线圈效果往往不理想因为那些线圈的工作电压、电流和电感量都是按“吸合保持”设计的不适合连续高速PWM调节。我建议自己绕电磁铁。参数可以参考铁氧体磁芯直径 18mm长度 25mm用 0.3mm 漆包线绕 220 到 300 匝绕完后的直流电阻约 5 到 8 欧姆。供电电压 12V 时最大电流约 1.5A 到 2A。这个规格在短时间工作下吸力足够又不会因为匝数太多导致电感过大、电流响应太慢。驱动电路部分很多人第一反应是用继电器那是绝对不行的PWM 频率下继电器触点根本来不及响应。正确的做法是用 MOSFET 做开关控制再加一个续流二极管保护。我用的是 IRLZ44N 这种逻辑电平 MOSFET可以直接用 STM32 的 PWM 信号驱动不需要额外的 MOSFET 驱动芯片。电路要点MOSFET 的 D 极接电磁铁一端S 极接地G 极接 STM32 的 PWM 输出。电磁铁另一端接 12V 电源同时在电磁铁两端反向并联一个续流二极管比如 1N5819 或 SS34 肖特基否则关断瞬间会产生很高的反向电动势烧掉 MOSFET。PWM 频率建议选 20kHz 左右。太低的话电磁铁会发出可听见的“吱吱”声特别烦人太高的话MOSFET 开关损耗增大。我实测 20kHz 下电磁铁噪音基本消失驱动电路也没明显发热。电源部分要特别注意单片机和驱动电路最好分开供电。我是用一个 12V/3A 的电源适配器给电磁铁供电然后用一个 LM2596 降压模块把 12V 转成 5V 给 STM32 板和霍尔传感器供电。不要用同一个稳压芯片同时给系统控制和电磁铁供电否则电磁铁启动瞬间电流拉伸会让电压波动导致 STM32 复位。2.3 主控最小系统与外设分配主控我用的是 STM32F103C8T6也就是大家常说的“蓝Pill”核心板。选择它不是因为性能有多强而是资料多、便宜、硬件I2C/SPI/ADC/PWM齐全这个项目用到的外设它都有而且 HAL 库和标准库都有大量现成例程。外设分配建议如下外设资源功能引脚ADC1 通道0采样线性霍尔传感器输出电压PA0TIM2 通道1输出 PWM控制 MOSFET 驱动电磁铁PA0和上面引脚可能冲突时用 PA1USART1串口打印调试信息距离、误差、PWM占空比PA9/PA10普通GPIO启动/停止按钮指示灯PB0/PB1如果你用的是标准库ADC 单通道采样加 DMA 也很方便。建议把 ADC 采样率设高一点比如 12 位分辨率下配置成连续采样模式采样结果通过 DMA 不断搬运到内存。控制循环里每次读最新一次采样值即可不用每次轮询等待转换完成。这样可以让控制节奏更稳定不会因为ADC转换时序抖动导致控制周期不均匀。3. 让系统动起来的控制闭环从采样到PID输出3.1 控制周期设计1kHz 是个合适的起点磁悬浮系统的动态响应很快控制周期太长会感觉悬浮体“发呆”之后突然猛抽一下控制周期太短又会暴露传感器噪声和PWM分辨率不足的问题。我实验下来1kHz 是一个比较稳的起点也就是每 1ms 执行一次“采样—算误差—算PID—更新PWM占空比”的循环。这个循环最好放在定时器中断里做而不是直接写在 main 的 while(1) 里。原因是 while 循环的执行时间会受到串口打印、LED闪烁等操作的干扰控制周期就会忽长忽短。我把 SysTick 配置成 1ms 中断一次在中断回调函数里完成整个控制流程实测下来悬浮稳定性明显提升。串口打印调试信息时要注意直接把打印放在 1ms 中断里会严重拖慢中断处理时间。我一般是在控制中断里只更新一个共享变量main 主循环每隔 100ms 打印一次这个变量的值这样既能看到实时数据又不会影响控制周期。3.2 PID 公式理解与代码实现控制算法的核心就是一个位置式PID虽然代码简单但每一部分的物理含义要想清楚P比例当前误差越大输出越强。从上拉式结构来看悬浮体偏低了就要增加电流增大吸力悬浮体偏高了就要减小电流让重力把它拉回去。D微分抑制变化速度。如果悬浮体正在快速靠近电磁铁D项会提前减小输出避免它冲过头。D项本质上是在给系统加阻尼。I积分消除静态误差。如果电磁铁发热导致吸力下降系统可能会出现悬停位置偏低的情况I项会不断累积误差并补偿这个偏差。代码实现上我用的是位置式PID输出直接对应PWM占空比。以下是我实测过的核心控制代码伪代码风格但基本可以直接套用typedef struct { float Kp; float Ki; float Kd; float integral; float last_error; float integral_limit; float output_limit; } PID_t; float PID_Process(PID_t *pid, float target, float current, float dt_ms) { float error target - current; float derivative (error - pid-last_error) * 1000.0f / dt_ms; // 转换为每秒误差变化 pid-integral error * dt_ms / 1000.0f; // 积分限幅防止积分过深导致大超调 if (pid-integral pid-integral_limit) pid-integral pid-integral_limit; if (pid-integral -pid-integral_limit) pid-integral -pid-integral_limit; float output pid-Kp * error pid-Ki * pid-integral pid-Kd * derivative; // 输出限幅防止占空比到100%或0% if (output pid-output_limit) output pid-output_limit; if (output 0) output 0; pid-last_error error; return output; }需要注意一个细节电磁铁的PWM占空比应该和电流近似成正比但实际有死区。PWM占空比低于某个阈值大概5%到10%时线圈电流太小吸力几乎为零悬浮体只会掉下去。所以输出限幅时我会把下限设成一个“最小保持占空比”比如8%再低就不让了。这个值可以通过手动给电磁铁通电观察悬浮体能不能刚好被吸起来实测确定。3.3 调参的完整流程从“抽风”到“稳如老狗”调PID参数是一个工程活也是有固定套路可循的。我最开始调的时候上来就乱改参数结果悬浮体要么猛地砸向电磁铁要么直接掉下来。后来我总结了一套流程效率高很多。第一步只保留PI和D都设为0。慢慢增大Kp观察悬浮体的状态。当Kp到了一定程度你会看到悬浮体在目标位置附近来回振荡但不会掉下来这就是“临界振荡”。记下这个时候的Kp值大概是最终Kp的50%到70%。第二步加入D抑制振荡。D项的作用是给系统加阻尼所以你会看到振荡幅度变小逐渐收敛。Kd从小的值比如0.1开始加加到振荡消失、悬浮体能够稳定悬浮在目标高度上。但注意Kd不要贪大过大的Kd会把传感器噪声放大表现为尖锐的高频抖动。第三步加入I消除静差。如果悬浮体稳定下来后悬停位置比目标高度低了一些比如目标15mm实际14.5mm就需要加I项把它拉回去。Ki从0.001开始小步加加到悬浮高度准确匹配目标值。第四步微调整体参数。用手轻轻按压悬浮体观察它能不能快速回到目标位置然后突然松开观察是否出现振荡。理想情况是悬浮体在1到2次衰减振荡后即恢复稳定。我最终调出来的一套参考参数Kp0.8Kd0.12Ki0.005控制周期1ms。但不同线圈、不同磁铁、不同结构下最优值肯定不同只能作为参考起点。在调参过程中串口打印绝对是最重要的助手。我打印的数据格式是“目标电压, 实际电压, PWM占空比”。当悬浮体抖动时从打印数据里能明显看到实际电压在高频振荡这就是噪声或D项过大如果实际电压和目标电压长时间相差一个固定值那就是I项不够或目标值估不准。4. 实测中最容易翻车的几个坑抖动、发热、温漂4.1 高频抖动与电磁铁啸叫的根源调试到一定阶段悬浮体可能看起来是稳定悬浮的但你能听到电磁铁发出“吱——”的高频声音用手摸还能感觉到轻微振动。这说明系统里存在高频振荡根源往往不只是PID参数。我碰到过三个具体原因一是传感器噪声被PD项放大。STM32的ADC虽然分辨率是12位但如果供电纹波偏大霍尔输出电压本身就会带噪声。解决办法是在霍尔传感器供电端加一个100nF去耦电容在ADC采样值上做一次简单的滑动平均滤波比如取最近4次采样的平均值。注意滤波窗口不能太长否则控制滞后会增加悬浮体会变得很“肉”。二是PWM分辨率不够。如果PWM频率设成20kHz重载值是899即72000000/20000-1。在这个分辨率下每增加1个单位占空比对应的电流变化可能已经大于悬浮体稳定所允许的微小调节量于是系统误差在“调多了”和“调少了”之间来回跳宏观上表现为抖动。解决方法是把PWM频率适当降低到10kHz或者把PWM重载值调高一些提升分辨率。三是控制周期和PWM频率没有对齐。如果控制周期是1kHz而PWM周期是20kHz那么每20次PWM周期里只有一次占空比被更新其余19次都用旧占空比。这种情况下悬浮体的动态响应会有一点点“阶跃感”。虽然影响不大但如果想彻底解决可以把PWM频率也设成1kHz缺点是电磁铁噪音会明显出现。我最后选择折中控制周期1msPWM频率20kHz抖动基本不可闻。4.2 电磁铁发热与电流裕量的物理过程长时间上电后电磁铁线圈温度会上升。铜线的电阻温度系数大约是0.0039/℃如果线圈室温下电阻是6欧姆温升30℃后电阻变为约6.7欧姆。在12V供电下最大电流就由2A降到了1.79A最大吸力下降了大约20%。这个物理过程会导致两个问题一是悬浮体在启动后十几分钟内悬停位置会缓慢下移二是如果电流裕量本来就不够温度上来后可能出现“吸不住”导致悬浮体跌落。解决思路分两个方面。硬件上线圈尽量选粗一点的漆包线0.35mm以上或者采用间歇工作方式不要长时间满功率。我把系统的最大占空比限在80%实测电流约1.3A线圈温升在30分钟稳定后再也没有出现过吸不住的情况。软件上I项的积分补偿就是为这种慢变扰动准备的只要Ki不是0系统会自动增加占空比去补偿电磁铁吸力下降。4.3 霍尔温漂与机械共振以及如何排查霍尔传感器的温漂相比线圈发热要隐蔽得多。SS495A的温漂参数虽然在同级别里算好的但长时间工作后输出电压仍然会有零点漂移表现就是悬浮体高度缓慢变化。我干过一件傻事调参调得差不多了结果第二天开机发现悬浮高度完全不对排查半天才知道是晚上气温变化导致霍尔静态输出电压变了。解决办法有两个一是在每次启动时做一次“零点校准”让悬浮体放在机械底座上采集此时的霍尔电压作为零点参考然后以这个零点计算目标高度对应的电压二是选温漂更小的传感器比如更高端一点的A1304或者给霍尔传感器加一个微型恒温块。对于DIY项目零点校准性价比最高代码也简单main函数里加个按钮触发校准就行。机械共振是另一个容易被忽略的坑。如果你发现悬浮体在某个特定高度附近总是不稳定即使加大D项也没用那很可能是机械结构本身的固有频率和控制系统的带宽耦合了。我遇到过的情况是支架是一根细长的亚克力柱系统稳定悬浮时总有点轻微但持续的低频晃动。后来把支架换成更粗的铝柱然后配重块加重了悬浮体的质量问题立刻消失。这个故障排查有点反直觉因为软件上你已经把PID调到很理想了问题其实出在机械刚度不足上。4.4 排查问题时的系统化思路这里必须提一个排查问题的方法论。我自己最开始一看到异常就去调参数经常把系统越调越乱。后来养成一个习惯把问题分成“软件仿真”和“硬件实测”两层先确认哪一层出了问题。具体方法是断开电磁铁驱动给STM32烧一个测试程序定时改变PWM占空比同时读回霍尔电压并打印。用手轻轻移动悬浮体观察霍尔电压变化是否平滑、无跳变再观察固定PWM占空比下线圈电流是否稳定。如果这些底层反馈都没问题再接入PID闭环调参。这个过程听着繁琐但能帮你避免“硬件噪声本来很大、你却以为是PID参数不对”这种徒劳无功的调参。5. 从“能飘”到“飘得稳”再到“能玩出花样”进阶方向5.1 悬浮高度动态调节与上位机联动系统稳定悬浮后一个很自然的需求是动态改变悬浮高度。实现方式非常直接把PID的目标值从固定值改成一个可调变量比如通过串口接收指令。我试过用STM32串口接收“H15”设置悬浮到15mm“H20”设置到20mm实测变化过程非常有趣悬浮体不会一次性跳到新高度而是平滑地滑过去整个动态过程有微弱但明显的“缓冲感”。进一步可以加一块OLED屏实时显示目标高度、当前高度和PWM占空比曲线。虽然只是锦上添花但你盯着曲线看的时候会比我对着串口终端猜状态更直观也更“酷”。OLED用I2C接口和STM32连接SSD1306驱动库是现成的网上例程一大把我不重复讲了。如果想玩WiFi控制可以用ESP8266或者ESP32的AT指令和STM32串口通信在手机浏览器里通过网页滑块控制悬浮高度Lag不算特别明显大概100ms级别体验已经相当可玩。5.2 扩展为双路磁悬浮二维平衡的思路上拉式磁悬浮如果只做单路本质上是一维控制老实说在控制层面不算特别难。真正进阶一点的做法是做一个双路系统两个电磁铁并排悬浮体是一个长条形磁铁左右两端分别受到吸力。这样系统从一维变成了类似跷跷板的两维耦合控制需要两路PID且参数要协调难度上了一个台阶。但我要先泼一盆冷水双路系统机械结构复杂是两个单路系统的并排放置而这个“跷跷板”的耦合非常强。我刚搭完的时候把两路PID分别调好分别悬停都没问题但放上同一个悬浮体后立刻失衡。后来我才意识到第二路的误差会通过悬浮体的倾斜传递到第一路形成交叉耦合。解决方法是把两个传感器信号做差分处理后分别送入两个PID控制器而不是各自独立闭环。这已经有点接近现代控制里的解耦控制思想了。如果你只是玩玩单路做稳已经很了不起真想挑战可以在有一路稳定经验之后再上双路否则会非常容易上头。5.3 最终稳定性的三个操作技巧最后分享三个我在实际调试中觉得最有用的操作技巧随便哪一条拿出来都可能帮你省下半天调参时间。第一电流采样和位置采样一定要分开滤波。我见过很多朋友把霍尔电压和电源电流的噪声混在一起分析导致滤波参数越调越乱。正确的做法是霍尔位置信号用短的滑动平均4次电源电流信号用一阶低通alpha0.1两者独立处理互不污染。第二给PWM输出加一个“加速度限制”。不要让PID输出的占空比直接一步跳变。比如本次输出相对上次输出的变化量限制在最大5%以内。这样虽然控制响应慢了一点点但能极大减少电磁铁和悬浮体之间的冲击感对悬浮稳定性提升很明显。第三调参失败时先把积分清零再重新调。很多时候悬浮体越调越乱不是因为Kp和Kd不对而是I项已经累积了一个比较大的值导致系统在错误的工作点上运行。我调试时每次修改几组参数都会先把I项的累积值清零再观察新的阶跃响应。这个细节帮我避免了很多“参数越改越乱”的恶性循环。本文还有配套的精品资源点击获取