STM32下推式磁悬浮项目源码解析:从硬件接线到PID调参实战
发布时间:2026/10/20 19:38:34 锦皓数字建站

简介基于STM32F103的下推式磁悬浮完整工程包面向嵌入式开发爱好者、自动化专业学生以及备战电子设计竞赛的开发者可用于系统学习磁悬浮控制器的软硬件设计与调参方法。资源共239个文件压缩包约4MB涵盖C语言源码.c/.h、Keil工程配置.uvprojx、可直接烧录的hex固件、电路原理图.pdsprj以及物料清单.txt从硬件电路规划到程序编译烧录形成完整闭环便于对照学习。目前已有2950人学习下载。下推式结构通过底部电磁场排斥实现稳定悬浮对传感器采样与PID控制实时性要求较高借助原理图可梳理电源、驱动、传感器接口等硬件链路对照源码则可深入分析磁场检测、PID参数整定、PWM功率输出等核心逻辑再结合物料清单进行实物搭建与异常排查非常适合课程设计、竞赛备赛或个人项目二次开发。1. 这盒磁悬浮源码到底能干什么从一份 rar 到稳定的悬浮球很多人一看到磁悬浮第一反应是高铁或是那些悬在展柜里的地球仪。而这份 stm32f103 下推式磁悬浮项目源码做的是另一件事用一枚 STM32F103 芯片让一个永磁体稳稳地“飘”在一个电磁铁上方。电磁铁布置在下方永磁体在上方靠磁场反向托举所以叫“下推式”。它不是玩具品牌送的演示 Demo而是一套可以自己编译、自己接线、自己调参的裸机控制工程。对于想入门闭环控制、搞清楚 ADC 采样、PID 运算和 PWM 驱动三者怎么配合的开发者来说这个项目提供了完整且能跑通的样例。整包代码量不大但硬件选型、控制逻辑、调参手感和后续改造空间都覆盖到了是那种适合拆开看、也能下手改的工程包。这个项目最吸引人的地方是用最便宜的蓝药丸板子做出一个看起来高科技的实物效果。你不需要买专用电机驱动板不需要浮点 DSP也不需要复杂的外部电路。一个 STM32F103C8T6、一个线性霍尔传感器、一个 N-MOS 管和一个线圈就构成全部核心。对于做过一点单片机但没碰过“让物体稳定悬浮”这种高带宽实时控制的开发者它能让你在几个小时内体会到稳准狠的调试过程。接下来我把整个工程从原理到跑通到改造一条线拆开讲每一步都能对应到源码和硬件的具体动作。2. 系统架构与控制闭环霍尔传感器、PWM 和磁场的三角关系2.1 下推式磁悬浮的工作原理与稳定条件下推式磁悬浮的结构并不复杂底座中心固定一个绕制好的电磁线圈线圈上方悬着一个带永磁体的悬浮物。线圈通电后产生磁场与永磁体相互作用产生一个向上的托举力。永磁体稍微偏离目标高度时磁场力与重力平衡被打破如果没有外部调节物体会直接掉下来或者被吸死。所以核心任务不是“把磁铁推上去”而是“不断感知位置改变电磁力维持一个动态平衡”。这里的关键变量是电磁力与距离的关系。当永磁体高于目标点距离增大磁场力减弱物体下落当永磁体低于目标点距离减小磁场力增强物体被推高。这个系统的开环特性是不稳定的必须靠闭环控制来持续修正。常用的位置传感器是线性霍尔传感器它感应永磁体漏磁场的强度输出电压跟随磁场变化。STM32 内置的 ADC 以每秒几千次的频率采样霍尔电压经过数字 PID 运算输出对应的 PWM 占空比再通过 MOSFET 控制线圈电流从而精确控制电磁力。整个闭环链路的带宽决定了悬浮稳定性。我一般会把 ADC 采样率放在 4kHz 左右PID 计算也在同一频率下执行PWM 频率则拉到 20kHz 以上。这样输出电流纹波小线圈不会发出人耳可闻的啸叫悬浮体也不会因为控制周期太长而振荡。源码工程里的定时器配置基本就是按照这个思路来做的改起来也很直接。2.2 硬件选型与引脚接线不提原理图的走通方案这份 rar 解压后里面包含的是 Keil 工程文件、硬件部分通常只提供引脚定义说明。结合我自己的组装经验推荐一套稳定且便宜的选型主控STM32F103C8T6 最小系统板72MHz 主频内置 2 路 12 位 ADC带高级定时器 TIM1 和 TIM8 可输出互补 PWM。位置传感器推荐使用 SS495 或 A1302 这类线性霍尔传感器输出与磁场线性相关的电压。注意供电用 5V输出信号需要串联电阻分压到 3.3V再进 STM32 的 ADC 引脚。驱动电路用一颗 N-MOSFET比如 IRFP540 或 IRLZ44N线圈一端接电源正极另一端接 MOSFET 漏极源极接地。线圈两端并联续流二极管防止关断瞬间的反向电动势击穿管子。电磁线圈电感量建议在 1mH 到 5mH 之间线径 0.4mm 左右绕制 200 到 400 匝。这个参数不要求严格但电感太大会导致电流响应慢太小则吸力不足。引脚接线可以按下面这个表来对照这也是我实际试过能稳跑的配置单片机外设引脚连接目标ADC1 通道 0PA0霍尔传感器输出分压后TIM1 PWM 通道 1PA8通过栅极电阻接 MOSFET 栅极3.3V / GND3.3V / GND最小系统板电源5V 输入5V 引脚霍尔传感器和驱动电路上拉注意霍尔传感器的输出一定要确认不带反向信号。如果接反了PID 会表现为正反馈悬浮物一上电就往上冲或者往下吸死。代码里做了一步符号切换的宏定义但在硬件上先确认电压随距离增大而增大的方向能省下很多调试时间。3. 核心代码拆解ADC 采样、PID 计算和 PWM 输出的具体实现3.1 霍尔传感器位置数据的读取ADC 采样 滑动滤波源码里位置采样的核心是配置 ADC1 为连续扫描模式并用定时器触发保证采样周期稳定。我把关键的初始化部分整理成下面这段static void ADC_Init(void) { GPIO_InitTypeDef gpio; ADC_InitTypeDef adc; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE); gpio.GPIO_Pin GPIO_Pin_0; gpio.GPIO_Mode GPIO_Mode_AIN; GPIO_Init(GPIOA, gpio); adc.ADC_Mode ADC_Mode_Independent; adc.ADC_ScanConvMode DISABLE; adc.ADC_ContinuousConvMode DISABLE; adc.ADC_ExternalTrigConv ADC_ExternalTrigConv_T1_CC1; adc.ADC_DataAlign ADC_DataAlign_Right; adc.ADC_NbrOfChannel 1; ADC_Init(ADC1, adc); ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_39Cycles5); ADC_ResetCalibration(ADC1); ADC_StartCalibration(ADC1); }这段代码把 ADC1 的通道 0 接到 PA0并设置了外部触发源为 TIM1 的捕获比较事件。这意味着每次 TIM1 更新或者比较匹配时ADC 自动开始转换无需 CPU 干预。转换完成后读取数据寄存器即可。为了减小电磁干扰工程里还加了一个一阶滑动滤波数组每次取最近 8 次采样的平均值。这个滤波在调参时非常有用能让 PID 输入信号平滑很多但要注意会增加相位延迟所以滤波窗口不宜过大。3.2 数字 PID 控制器增量式算法与输出限幅核心控制代码是一个典型的增量式 PID输出的是 PWM 占空比的增量而不是绝对值。这样在手动切换目标高度时不会产生过大的跳变。源码里的关键实现类似这样typedef struct { float kp; float ki; float kd; float target; float err_acc; int last_err; int last_last_err; } PID_TypeDef; int pid_update(PID_TypeDef *pid, int cur_pos, int out_min, int out_max) { int err (int)(pid-target) - cur_pos; int p (int)(pid-kp * err); int d (int)(pid-kd * (err - pid-last_err)); pid-err_acc err; int i (int)(pid-ki * pid-err_acc); pid-last_last_err pid-last_err; pid-last_err err; int output p i d; if (output out_max) output out_max; if (output out_min) output out_min; return output; }这段 pid_update 函数在每次 ADC 转换完成后被调用。kp 负责快速恢复偏差kd 负责抑制过冲和振荡ki 用来消除静态误差但积分项要特别小心。电磁悬浮系统本身是强不稳定的如果 ki 过大输出占空比会持续累积导致悬浮物低频大幅震荡。工程默认把 ki 设得非常小甚至接近 0等调好 p 和 d 之后再加一点积分。输出限幅是必须做的否则 PWM 满幅运行时线圈电流过大发热严重。3.3 PWM 驱动与主循环让 PID 结果变成线圈电流PWM 部分使用 TIM1 的通道 1配置为 PWM 模式输出频率由预分频和自动重载寄存器决定。在主循环里只需要不停调用 pid_update并把结果写入 TIM1 的捕获比较寄存器。核心主循环如下while (1) { if (adc_ready_flag) { cur_pos read_filtered_adc(); pwm_val pid_update(pid, cur_pos, 0, 799); TIM_SetCompare1(TIM1, pwm_val); adc_ready_flag 0; } }注意这里的 PWM 计数器范围是 0 到 799也就是 7200 万 / 800 90kHz。实测 20kHz 以上就能避开听觉频段90kHz 更有优势但需要注意 MOSFET 的开关损耗。这里的 adc_ready_flag 由 ADC 转换完成中断置位保证每个采样周期只更新一次 PWM。整体代码结构很清晰中断采集、后台计算、主循环输出。为了避免首次上电直接冲出去源码里还有一个软启动逻辑主循环第一次运行前PWM 占空比从 0 开始缓慢加到初始值。这个过程就像把悬浮物从地面慢慢“扶”起来比直接给满占空比容易成功。4. 悬浮调试避坑指南五个让我翻过车的细节4.1 现象悬浮物一启动就上下震荡幅度越来越大原因PID 的 kd 太小甚至设成 0阻尼不够。电磁悬浮系统的固有特性是负阻尼没有微分项系统必然振荡。解决先增大 kd让阻尼效果压住速度变化。通常 kd 的数值要比 kp 大一个数量级。如果震荡从 100Hz 以上的微小颤振变成几赫兹的大幅摆动说明 kd 过大会带来噪声放大需要配合滤波。4.2 现象霍尔传感器读数在接线后只有 0V 或者 3.3V 满量程原因供电电压超出线性霍尔的工作范围或者输出没有分压直接接 STM32 引脚可能导致内部钳位。我之前把 5V 供电的霍尔输出直接接 PA0ADC 读到 4095永远无法反映真实位置。解决将霍尔输出串一个 4.7kΩ 电阻到 PA0再并联一个 10kΩ 电阻到地相当于把 5V 满幅拉到约 1.6V符合 STM32 的 3.3V 量程。同时电源引脚加 0.1uF 去耦电容。4.3 现象上电瞬间 MOS 管冒烟或发烫原因PWM 占空比在初始化时输出满占空比线圈电流过大导致 MOS 管过流。如果题目代码给的初始占空比是 50%有时也会因为电感饱和而烧管。解决主循环初始化时先把 TIM 比较值清 0再按每毫秒加 1 的速率上升到初始值并且全局输出限幅限制在总占空比的 60% 以内。同时检查续流二极管的极性接反会直接短路电源。4.4 现象悬浮物在某个高度反复抖动调整 PID 效果不明显原因PWM 频率只有 1kHz 或 2kHz线圈电流纹波大磁场力波动直接体现在悬浮物上。人耳能听到嗡嗡声就是频率过低的表现。解决把 TIM1 的预分频设为 1重载值设为 799让 PWM 频率到 90kHz。如果 MOSFET 开关损耗太大可以降到 20kHz但不要低于 10kHz。4.5 现象用 ST-Link 烧录后程序偶尔进入 HardFault原因ADC 多通道没有禁用扫描模式导致 DMA 指针错乱或者 TIM1 中断优先级高于系统调用造成不可重入。解决在初始化里把 ADC_ScanConvMode 设为 DISABLE只选一个通道同时把 TIM1 中断优先级设为最低通过 NVIC 配置把位置更新放在主循环而不是中断服务函数里。这样即使中断嵌套也不会破坏 PID 状态。5. 从烧录到悬浮一步一步跑通整个工程5.1 需要用到的软件和工程环境工程源码是在 Keil MDK 上写的所以想跑起来最省事的方式是直接用 Keil MDK 5 打开目录里的工程文件。如果电脑上装了 STM32CubeIDE也可以导入源码但要注意外设库文件版本推荐还是用 Keil。烧录器我建议用 ST-Link V2便宜稳定在设备管理器里能识别到 SWD 接口。接线完成后把编译生成的 hex 文件通过 ST-Link 烧进板子板子供电用 USB 线。5.2 硬件接线检查清单在烧录之前先做一整套硬件自检不然程序跑起来你无法判断是设计问题还是接线问题确认霍尔传感器和 STM32 共地不共地读到的 ADC 值会漂移。给电磁铁供电的 12V 电源负极也要和 STM32 的 GND 接在一起否则 MOSFET 栅极悬空PWM 无法控制回路。用万用表量霍尔输出手动上下移动永磁体确认电压变化范围在 0.5V 到 2.5V 之间且方向正确。空载状态下给 MOS 管栅极一个 3.3V 高电平线圈通电永磁体靠近时能感到吸力说明驱动链路通。5.3 调参步骤从完全发散到稳定悬浮调参是这类项目最关键的体验也是一开始的难点。我的经验是先把积分项清零只留比例和微分把 kp 设置到 5 以内kd 设 0占空比初始值设 30%。观察悬浮物会瞬间被吸死或弹飞但不要紧这是开环状态。逐步加大 kp每次加 2直到系统出现明显的快速抖动。记录这个 kp 值。从这个 kp 值往回退 30%然后开始增大 kd。kd 从 10 开始加每 5 个数值观察一次抖动是否减小。当悬浮物能维持 2 到 3 秒不跌落且无高频振颤说明临界稳定已经达到。这时加一点 ki每次加 0.02用来消除高度误差。最后微调 target 值让悬浮物稳定在目标高度可以明显看到物体像被“钉”在半空中。我建议在调试时用串口打印 ADC 原始值、PID 输出值和 PWM 占空比这样能更快看清控制过程。源码里如果没加串口打印可以在主循环里每隔 200ms 通过 USART1 发送调试信息改动量不大。5.4 验证稳定性的三个标准判断一个磁悬浮系统是否调好不应该只看“能不能浮起来”。我发现有三个更实用的标准用手轻压悬浮物它能在一秒内回到原高度且不产生持续振荡。把目标高度从 10mm 改到 12mm悬浮物能平滑跟过去没有明显过冲。连续运行十分钟线圈表面温度不超过 60 摄氏度MOS 管手摸不烫。如果这三个标准都满足说明 PID 参数基本合格可以进入下一步的二次开发了。6. 二次开发实战改造成自适应 PID 和上位机实时调参这个项目最值得玩的地方是它在二次开发上留了很大的口子。我第一个想建议的改造方向是给工程加一个串口上位机调参工具。简单做法就是用写一个 Python 脚本通过串口读取 STM32 的 PID 参数并返回当前悬浮状态。这样你不需要反复烧录固件就能实时在电脑上调整 kp、kd、ki甚至切换目标高度。步骤是先定义一个简单的串口协议比如用四个字节分别表示命令类型和三个参数STM32 在串口中断里解析后更新 PID 结构体的成员。另一个更进阶的方向是自适应 PID。因为悬浮物重量改变、线圈温度升高导致电阻变化时固定参数会失效。我最近在尝试的做法是在运行过程中检测悬浮物的振荡频率如果检测到持续的低频振荡就自动减小 kp 并增大 kd如果悬浮物响应变得迟钝就反向调整。这个逻辑不需要复杂算法可以用简单的梯度下降思路每秒根据误差方差做一次参数微调。实测下来可以让调试窗口从原来的 20% 余量扩展到 50% 以上。如果不想碰控制算法也可以做外观和交互上的改造加一个 OLED 显示当前高度和 PWM 占空比用两个按键手动调节目标高度或者接一个蓝牙透传模块用手机发指令控制悬浮物上升下降。这些都是小工程量的改动但会让作品完整度大大提升。我自己第一次调这个项目时完全照着源码默认参数烧录结果悬浮物一上电就撞顶。后来一点一点改参数才发现 PWM 频率设置成了 1kHz线圈啸叫刺耳悬浮物根本稳不住。换了高频 PWM 之后世界瞬间安静了。从那以后我每做一个电磁铁控制项目都会先确认驱动频率再谈 PID 调试。希望这篇文章能让你少走这段弯路拿到工程后顺利把悬浮球“盘”起来。本文还有配套的精品资源点击获取
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。