news 2026/10/15 10:51:13

STM32自由摆平板控制系统:从角度检测到PID闭环的完整实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
STM32自由摆平板控制系统:从角度检测到PID闭环的完整实现

简介:基于自由摆的平板控制系统设计文档,面向电子设计竞赛参赛者与嵌入式控制系统初学者,解决摆杆摆动过程中平板平稳控制的问题。该方案以STM32F103VET6为核心,配备TFT液晶显示、WDD35D4角位移传感器与步进电机驱动器,并对控制器、角度检测、电机选型、电源模块等进行了多方案比较,给出最终选型理由与系统框架。文档还介绍了通过A/D采样将摆杆及平板角度反馈给单片机,再由细分驱动器控制步进电机实时调整平板姿态,从而完成摆杆摆动时保持平板水平或追踪目标的控制逻辑。资源共1个docx文件,压缩包约36KB,篇幅精炼但覆盖系统方案、硬件设计、软件编写与赛题发挥部分要求,适合用于备赛复盘、方案参考和嵌入式控制入门学习。已有305人阅读学习,可作为自由摆类控制系统的实用参考资料。

1. 自由摆平板控制系统:硬币不掉落背后的闭环逻辑

自由摆平板控制系统,核心目标是在摆杆来回摆动时,让摆杆末端的平板始终维持在水平状态——最直观的验收方式是硬币放在平板上不掉落。这类系统的难度不在硬件本身,而在于角度测量、PID运算和步进电机补偿三者之间的延时配合,角度反馈慢一拍,整个平板就会在最低点附近持续抖动。适合正在做课程设计、电子竞赛或毕业设计的嵌入式方向学生,以及想了解角度闭环控制完整落地的从业者。下面这套方案以 STM32F103VET6 为控制核心,配合高精度 WDD35D4 导电塑料角位移传感器与步进电机细分驱动,从选型到软件逐层拆开讲。

2. 硬件选型与系统搭建:STM32 主控下的三个关键决策

自由摆系统看起来是一个机械装置,实际上真正决定成败的是每个模块的选型取舍。题目里最容易走弯路的地方在于:角度检测器件选型、电机选型、电源方案。这三个决策直接决定了后续控制算法的复杂度上限。

2.1 系统结构:摆架框架、检测系统与控制驱动系统

系统在物理上分成三部分:

摆架框架:摆架座、摆杆、轴承及弹性元件。平板固定在摆杆末端,通过电机轴连接。这部分看着简单,实际上是系统误差的主要来源——轴承间隙稍大,角度数据就会带有周期性噪声。

检测系统:WDD35D4 导电塑料角位移传感器装在摆杆转轴处,摆杆和平板各装一只。一只测摆杆相对大地的角度,一只测平板相对摆杆的角度。角度信息经分压电路转成电压,进入 STM32 的 ADC。

控制驱动系统:STM32 根据两个角度值计算误差,输出脉冲和方向信号给细分驱动器,驱动器驱动步进电机转动,带动平板反向补偿。

我一般会在装配时把轴承预紧力调松一点点,不要压死。压死会让角度传感器在小角度区间出现迟滞,这种现象在高精度控制里非常致命。

2.2 控制器:为什么绕过 51 直接上 STM32F103VET6

51 单片机的优势是便宜、上手快,但要面对三个现实问题:ADC 通道少,难以同时采集两路角度传感器;主频低,PID 运算占用大量时间;片内资源小,滤波和状态机代码放不下。题目要的是"实时监测摆杆和平板角度",两个角度都必须在同一控制周期内完成采样和运算。

STM32F103VET6 的优势在于:

  • 72 MHz 主频,PID 在中断内可在微秒级完成;
  • 片内 ADC 可配置为 10 位或 12 位精度,项目里按 10 位使用;
  • 多个定时器可同时做 PWM 脉冲输出和输入捕获,步进电机控制和传感器采样可以并行;
  • 512 KB Flash、64 KB RAM,存得下菜单、滤波表、标定参数。

选型对比可以简化成一张表:

维度51 单片机STM32F103VET6
ADC资源少,扩展复杂片内多通道 ADC,双路采样简单
运算速度12T 周期,PID 吃力72MHz,PID 在中断内完成
PWM靠定时器模拟硬件 PWM,细分驱动直接对接
性价比低但可学高,具备完整调试接口

注意一点:如果用的是库函数版本,GPIO 初始化和 ADC 初始化建议全部走标准库,方便调试时对照参考手册查寄存器。

2.3 角度检测:WDD35D4 为什么替代电位器和加速度计

普通电位器做角度传感器的问题在于线性性能差。导电塑料电位器的电阻体是平滑的导电塑料膜而非绕线结构,因此角度与电压之间的线性关系更好,寿命也长。WDD35D4 的有效电气角度可以达到约 345°,单圈输出,符合性误差低,足够覆盖摆杆从竖直方向向两侧摆动的全部范围。

对比另外两个候选方案:

  • 普通电位器:线性度差、机械噪声大,误差在小角度区间会被控制算法放大。
  • ADXL345 加速度传感器:基于加速度原理,静态时能测倾角,但在自由摆这种持续动态运动环境中,摆杆加速度本身就包含摆动的切向与向心分量,传感器输出的信号混在一起,很难分离出真正的角度。

所以 WDD35D4 这种导电塑料角位移传感器成为刚需。接线方式很简单:VCC 接 5V,GND 接地,VOUT 接到 STM32 的 ADC 引脚。它本质上是电位器分压输出,角度与电压成线性关系。

上电前先量一遍 VOUT 与 GND 之间的静态电压,确认没有短路。这个习惯能规避很多"玄学"问题。

2.4 执行器:步进电机加细分驱动器

伺服电机的性能当然好,但价格高,而且伺服本身是闭环系统,在竞赛场景里还要额外配套驱动器,控制复杂度陡增。步进电机是开环角度控制设备,给定多少个脉冲,电机就转多少个步距角。选步进电机的主要原因是可以依靠开环特性把系统做成简单确定性模型。

步进电机有一个明显缺点:低频时振动大。低速转动时,步进电机会产生明显的抖动,这在机械系统上会直接反映到平板位置。解决方法是细分驱动。细分驱动器把每个整步分割成多个微步,比如 16 细分下,一个 1.8° 步距角的电机,每个脉冲对应 0.1125°。细分后低频振动明显减弱,平板运动更平滑。

电机选型时注意两个指标:步距角和保持转矩。步距角常用 1.8°,保持转矩要根据平板重量和摆杆长度选,一般思路是留 1.5~2 倍余量。如果平板过重导致电机堵转,可以适当调高驱动器电流,但不要超过电机额定值。

2.5 电源:L7805 与 LM2576 的分工

整机用 12V 电池供电。如果直接用线性稳压到 3.3V 给控制器供电,转化效率低且发热大。合理的做法是:

  • 12V 经 L7805 稳压到 5V,给 STM32 的 5V 供电、WDD35D4 传感器和 TFT 液晶使用;
  • 12V 经 LM2576 降压到电机驱动器所需电压,供步进电机功率部分使用。

不要用同一个稳压源同时带控制器和电机。步进电机运行时电流波动大,会把 5V 电源拉低,严重时导致单片机复位。两块电源地线在单一接地点汇合,能减少地线干扰对 ADC 采样值的污染。

3. 软件与控制算法:从角度采样到脉冲输出的完整链路

硬件搭好后,软件是自由摆系统的重头。控制链路可以拆成四层:ADC 采样层、角度转换层、PID 计算层、脉冲输出层。每一层都要在 10ms 级控制周期内完成,否则就会出现肉眼可见的滞后。

3.1 双通道 ADC 采样:摆杆角度与平板角度并行读取

两路角度传感器分别接在 STM32 的两个 ADC 通道上。为减少时间差,我建议在 ADC 扫描模式下一次启停同时完成两路采样,而不是先后读两个通道。先用标准库初始化:

void ADC_Config(void) { ADC_InitTypeDef ADC_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE); RCC_ADCCLKConfig(RCC_PCLK2_Div6); /* ADC 时钟 = 72MHz / 6 = 12MHz */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_Init(GPIOA, &GPIO_InitStructure); ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; ADC_InitStructure.ADC_ScanConvMode = ENABLE; /* 扫描模式,同时采集两个通道 */ ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStructure.ADC_NbrOfChannel = 2; ADC_Init(ADC1, &ADC_InitStructure); ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_239Cycles5); ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_239Cycles5); ADC_Cmd(ADC1, ENABLE); ADC_ResetCalibration(ADC1); while (ADC_GetResetCalibrationStatus(ADC1)); ADC_StartCalibration(ADC1); while (ADC_GetCalibrationStatus(ADC1)); }

说明:RCC_ADCCLKConfig(RCC_PCLK2_Div6) 把 ADC 时钟压到 12MHz,这是 STM32 ADC 的推荐工作上限。采样时间设置为 239.5 周期,执行原因是角度传感器输出有微小噪声,延长采样时间相当于对信号做了一次硬件平均。

通道分配上,GPIO_Pin_0 接摆杆角度传感器,GPIO_Pin_1 接平板角度传感器。后续读取时用 ADC_GetConversionValue 按转换顺序取结果。

3.2 角度数据滤波与零位标定

WDD35D4 是模拟输出,ADC 转换结果在机械振动和电磁噪声下会跳动。直接拿原始值算角度,会导致平板不停修正、抖动。我一般做两层处理:先做 5 次滑动平均,再做一次限幅滤波。

#define ADC_BUF_LEN 5 uint16_t adc_filtered(uint8_t channel) { static uint16_t buf[2][ADC_BUF_LEN]; static uint8_t idx = 0; uint32_t sum = 0; buf[channel][idx] = adc_read(channel); for (uint8_t i = 0; i < ADC_BUF_LEN; i++) { sum += buf[channel][i]; } return (uint16_t)(sum / ADC_BUF_LEN); }

滑动平均的窗口长度是 5,含义是滞后 5 个采样周期。窗口越长越平滑,相位滞后越大。在摆杆快速摆动时,滞后会直接转化为角度误差,所以 5 是比较折中的值。

零位标定在上电后做。摆杆静止在竖直位置时,把当前 ADC 值记为零位基准。系统运行时,每个角度 =(当前ADC - 零位ADC) × 分辨率。

3.3 PID 控制:比例加微分为主,积分项慎选

自由摆系统是周期性往复运动,摆杆每经过最低点一次,平板就要做一次快速反向修正。这种场景下比例项提供主要矫正力,微分项提供阻尼,积分项要慎重。积分项会把历史误差累积起来,在往复运动下容易产生过冲,我认为初期调试可以先把 Ki 设为 0。

关键计算量是"每周期需要补偿的角度"。平板的目标是相对大地水平,所以目标角度是 0,当前角度是滤波后的平板角度,误差就是两者之差。PID 输出是电机应转的角度值:

typedef struct { float kp; float ki; float kd; float integral; int16_t last_error; } PID_TypeDef; float pid_update(PID_TypeDef *pid, float current, float target) { float error = target - current; float derivative = error - pid->last_error; pid->integral += error; if (pid->integral > 300) pid->integral = 300; /* 积分限幅,防止长时间饱和 */ if (pid->integral < -300) pid->integral = -300; float out = pid->kp * error + pid->ki * pid->integral + pid->kd * derivative; pid->last_error = error; return out; }

参数说明:kp 控制响应速度,kd 控制阻尼。初值可以设为 kp=8.0、ki=0、kd=12.0 这种量级,具体值跟机械结构有关。有一个简单规则参考:当平板在最低点附近轻微振荡时,先降 kp;当平板明显滞后于水平位置时,先升 kd。

3.4 脉冲输出与步进电机转角换算

PID 输出是角度值,步进电机需要的是脉冲数和方向。换算关系由细分倍数决定。以 1.8° 步距角、16 细分为例,每个脉冲对应 0.1125°。假设 PID 输出为 2.25°,即需要输出 20 个脉冲。

#define STEP_ANGLE_DEG 1.8f #define MICRO_STEP 16 #define DEG_PER_PULSE (STEP_ANGLE_DEG / MICRO_STEP) /* 0.1125° */ void step_motor_run(float angle) { int pulse = (int)(angle / DEG_PER_PULSE + 0.5f); uint8_t dir = (pulse >= 0) ? 1 : 0; if (pulse < 0) pulse = -pulse; if (dir) { GPIO_SetBits(GPIOC, GPIO_Pin_0); /* DIR 引脚置高 */ } else { GPIO_ResetBits(GPIOC, GPIO_Pin_0); } for (int i = 0; i < pulse; i++) { GPIO_SetBits(GPIOC, GPIO_Pin_1); /* PUL 引脚 */ delay_us(50); GPIO_ResetBits(GPIOC, GPIO_Pin_1); delay_us(50); } }

这段代码有两个实际工程点:第一,脉冲宽度与间隔各 50µs,即 10kHz 脉冲频率,这适合多数 42 步进电机的响应区间;如果频率太高,电机会丢步。第二,dir 方向的确定标准是正角度对应电机正向补偿,实际接线后如果发现方向反了,只需要把 dir 判定取反。

在实际控制中,脉冲输出不应在主循环里用这种 for 循环延时方式,更好的做法是放到定时器中断中按计数完成。示例代码优先保证逻辑清晰,适合先跑通再优化。

3.5 TFT 液晶显示:把控制状态可视化

TFT 显示模块用于运行时参数显示。启动时显示摆杆角度、平板角度、PID 输出和目标角度。角度数据实时刷新,进入调试模式后可以显示波形页面。

常见接口是 FSMC 或 SPI,视具体屏而定。STM32F103VET6 有 FSMC 接口,可以驱动 16 位并口 TFT,速度可观。初始化时按屏厂提供的驱动代码跑一遍点亮之后,再封装显示函数。

调试过程中有一件事值得提前做:在屏幕上同时显示目标角度和实际角度。若两个数值的差值呈周期性变化,说明控制参数或采样滞后有问题,而不是传感器坏了。

4. 常见问题排查:五个高频翻车点与对应修复

自由摆系统最容易翻车的是控制回路不收敛。下面五条是我实际调试中遇到的典型问题,每条按现象、原因、解决展开。

4.1 平板在最低点高频抖动

现象:摆杆静止时,平板能够保持稳定,但摆杆一开始摆动,平板就在最低点附近持续高频抖动,幅度肉眼可见。

原因:摆杆经过最低点时角速度最大,平板需要在极短时间内完成大角度补偿。此时 PID 的比例项输出过大,微分项没有提供足够的阻尼,形成"补偿过头-反向修正-再过头"的振荡。

解决:把 kp 降下来,kd 提上去。一般做法是把 kp 减半,kd 增加 1.5 倍,观察两个摆动周期内的抖动幅度变化。如果抖动仍然明显,把控制周期从 10ms 降到 5ms,缩短从采样到输出的总延时。

4.2 平板角度始终滞后于摆杆运动

现象:慢速摆动时,平板动作明显"慢半拍",摆杆已经到最高点了,平板还没恢复水平。

原因:最大的嫌疑是角度滤波窗口太大,或者控制周期太长。滑动平均窗口如果是 20 次以上,在快速摆动场景下就有几十毫秒的纯滞后,直接体现为角度跟不上。

解决:把滑动平均窗口从 20 缩小到 5,必要时直接使用 3 点平滑。同时确认 ADC 采样是在扫描模式下完成的,两路角度的时间差控制在微秒级。如果还没有改善,检查电机脉冲频率,频率太低会导致单次补偿时间过长。

4.3 运行一段时间后零位漂移

现象:系统刚上电时能保持平衡,运行五分钟后平板逐渐偏转,且方向固定。

原因:WDD35D4 是导电塑料电位器,长期在同一个角度区间工作,会出现电阻体表面的微小磨损,加上机械装配的预紧松动,零位缓慢变化。

解决:把传感器转轴联轴器改用紧定螺钉锁死,并在每次上电时做一次零位校准。更稳妥的做法是在程序里加一个"静置校准"功能:上电 5 秒内摆杆保持自由垂下,这段时间内连续采集 50 次 ADC 值取平均作为零位基准。这个处理不增加任何硬件成本。

4.4 摆杆大幅摆动时硬币滑落

现象:小幅度摆动下系统正常,加大摆幅后硬币在平板上滑落。

原因:摆幅变大后,摆杆末端加速度增大,平板的水平稳定误差虽然只增加了一两度,但硬币受到的切向分力已超过静摩擦力。

解决:三步走。第一步机械上在平板表面贴防滑材料;第二步把平板侧面的挡边高度压低,避免影响激光笔安装;第三步软件把控制周期缩短,保证在大角速度区间也能及时补偿。如果换更大的电机,可以直接提升保持转矩。

4.5 启动瞬间步进电机丢步

现象:每次启动时电机会发出"咔哒"声,平板位置明显不对,运行几个周期后误差才慢慢消失。

原因:启动瞬间脉冲频率过高,超过电机的启动力矩-频率特性。步进电机在无加减速直接从静止跳到 10kHz 脉冲频率时,力矩往往不足,导致丢步。

解决:加梯形加减速。启动时脉冲频率从 500Hz 逐步升到目标频率,用定时器控制每秒步数递增。代码里加一个简单的斜坡函数,启动期间每 10ms 把脉冲间隔缩短几个微秒,确保电机平稳起步。这条经验同样适用于停止阶段。

5. 标定与整定技巧:三分钟摸清系统边界

系统跑通之后,真正考验人的是参数整定。有个非常实用的技巧:把激光笔固定在平板侧面,让激光照射到远处的白纸或墙上,整个平板的微小角度变化会通过光斑移动放大几十倍,肉眼能明显看到抖动方向。这个装置在调试最初阶段比任何上位机都直观。

整定可以按步骤做。第一步,摆杆静止,只加 kp,从小到大增加直到平板出现等幅振荡,记下此时的临界值 kp_critical。第二步,把 kp 设为临界值的 60%,加入 kd 消除振荡。第三步,摆杆摆动起来观察光斑轨迹,如果光斑在最高点位置有偏差但稳定,说明 kp 够了;如果光斑在最低点明显画弧,说明微分不足或控制周期偏长。

在记录数据时我习惯用这样一张表:

参数组合摆动幅度光斑最大偏移抖动频率结论
kp=8, kd=12小±2cm无正常
kp=16, kd=12小±5cm高频kp 过大
kp=8, kd=20小±1cm低频kd 偏大
kp=10, kd=16大±3cm无可用

整定完成后再做硬币测试,注意硬币要放在平板中心,放偏了会导致平板重心偏移,加大平衡难度。从那以后我每次调试这种摆动平衡系统,都强制自己先做一次激光光斑标定,再跑一遍上电自动校零,最后才碰 PID 参数。这个习惯帮我省掉了不少"黑匣子"问题,希望帮到你。这套系统的完整设计文档、源码工程和装配图纸,可以直接作为课程设计或竞赛复现的完整参考,下载后对照这套步骤搭硬件、烧程序、跑参数,基本能一路顺畅复现出水平稳定的效果。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/15 10:49:39

第22篇-聚合商平台安全技术体系-等保二级关键控制项与差距映射

聚合商平台安全技术体系:等保二级关键控制项与差距映射 文章目录 聚合商平台安全技术体系:等保二级关键控制项与差距映射 引言:等保不是可选项,是准入证 一、安全物理环境(略) 二、安全通信网络 三、安全区域边界 四、安全计算环境 五、安全管理中心 六、一个诚实的差距清…

作者头像 李华
网站建设 2026/10/15 10:49:13

AnythingLLM 功能详解:工作区、文档与向量库的 RAG 实践

简介&#xff1a;这份PDF文档系统梳理了AnythingLLM这款大型语言模型应用的核心功能&#xff0c;面向希望搭建私有化AI服务、集成嵌入式聊天或进行多用户权限管理的开发者与运维人员。内容围绕代理功能、API访问与密钥管理、外观自定义、嵌入式聊天小部件、工作空间聊天日志导出…

作者头像 李华
网站建设 2026/10/15 10:49:03

配电网韧性提升关键:移动电源MPS动态调度建模与优化求解

这篇是配电网韧性系列的下篇&#xff0c;如果还没看上篇&#xff0c;也不影响阅读&#xff0c;上篇讲的移动电源预配置解决的是“灾前把车停在哪”的问题&#xff0c;这篇讲的动态调度解决的是“灾害过程中和恢复阶段&#xff0c;车该怎么走、怎么接、怎么充放电”的问题。把时…

作者头像 李华
网站建设 2026/10/15 10:48:39

工业企业数据治理:三区一循环架构实践指南

1. 工业企业数据治理现状与挑战当前工业企业在数字化转型过程中普遍面临数据管理混乱、价值挖掘不足的困境。根据我们团队在30多家制造企业的实地调研&#xff0c;发现几个典型问题&#xff1a;数据孤岛现象严重&#xff1a;生产设备、质量检测、供应链等系统各自为政&#xff…

作者头像 李华
网站建设 2026/10/15 10:47:58

玩转 OpenClaw,你需要这些 Skills:从 ClawHub 到 MCP 的实战配置

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/15 10:47:54

HIVE LAG/LEAD窗口函数实战避坑指南:分区、排序与NULL处理

1. 这不是“查文档”&#xff0c;是搞懂LAG/LEAD怎么在真实业务里不翻车刚接触HIVE窗口函数时&#xff0c;我对着LAG(col, 1, 0)这行代码盯了半小时——参数里那个“1”到底指上一行还是上一帧&#xff1f;默认值填0真能兜住所有空值场景&#xff1f;线上跑着的订单延迟分析任务…

作者头像 李华