简介:本资源为自由摆平板控制系统完整设计文档,面向参加电子设计竞赛或从事嵌入式运动控制学习的学生与工程师,帮助解决摆动过程中平板保持水平、硬币不掉落及激光笔定向打靶等动态平衡控制问题。压缩包内仅1个docx文件,约36KB,内容涵盖系统方案论证、硬件选型与软件实现思路。文档以STM32F103VET6为核心控制器,搭配TFT液晶显示、高精度WDD35D4导电塑料角位移传感器与四宏细分驱动器驱动的步进电机,详细比较了控制器、角度检测、电机及电源模块的多种备选方案,并给出摆架框架、检测系统与控制驱动系统三大部分的结构设计。读者可从中获取完整的赛题实现方案、关键器件选型依据、A/D采样与电机控制逻辑,以及性能、成本与实用性之间的权衡思路,适合作为嵌入式动态控制项目的参考范例。目前已有305人学习下载。
1. 自由摆平板控制系统:从硬币不掉到激光打靶的工程拆解
第一次看到“自由摆上的平板控制系统”这个题目,很多人会以为是个简单的平衡装置,实际动手才发现——摆杆在重力作用下做非线性摆动,平板还要在动态中保持水平,硬币不能滑落,激光笔还得精准命中靶心。这套基于 STM32F103VET6 的方案,核心就是用高精度 WDD35D4 导电塑料角位移传感器实时捕捉摆杆和平板的角度偏差,通过 A/D 采样回传给单片机,再由四宏细分驱动器驱动步进电机做反向补偿。它适合参加过电子设计竞赛的在校学生、做嵌入式控制入门的工程师,以及需要复现动态平衡平台的开发者。如果你正被“摆动中平板怎么稳、硬币怎么不掉、激光怎么不跑偏”这三个问题卡住,这份设计拆解能让你少走几晚通宵的弯路。
2. 硬件选型:为什么是 STM32F103VET6 + WDD35D4 + 步进电机
2.1 主控芯片的算力账:51 单片机为什么扛不住
自由摆系统的控制周期通常要求在 5ms 以内完成一次“采样—解算—输出”的闭环。摆杆摆动时,角度变化率在过零点附近最大,如果控制器算力不够,等你想明白该往哪转,平板已经歪到硬币滑出去了。
51 单片机的主频一般 12MHz 到 24MHz,单周期指令少,做一次带浮点的角度解算和步进脉冲规划,时间经常超过 10ms。STM32F103VET6 是 ARM Cortex-M3 内核,主频 72MHz,自带硬件除法器和 8 通道 10 位 ADC,做同样的解算加脉冲输出,余量足够再跑一个 TFT 刷新。选它不是为了“性能过剩”,而是给控制周期留出安全边界。
常见做法是:ADC 用规则通道扫描模式,配合 DMA 搬运,CPU 只读结果不做等待;步进脉冲用定时器 PWM 输出,改频率就是改转速,改占空比就是改细分步进的有效脉冲宽度。这两条路走通,控制周期能压到 2ms 以内。
2.2 角度传感器的选择:电位器、ADXL345 和 WDD35D4 的差别
角度检测是这套系统里最容易被低估的环节。普通电位器便宜,但线性度差,碳膜磨损后输出跳变,摆杆来回摆几十次,角度读数就开始飘。ADXL345 是加速度计,测的是重力分量,静态倾角还行,一旦摆杆有动态加速度,它分不清是倾斜还是运动产生的惯性力,数据直接变成“玄学”。
WDD35D4 是导电塑料角位移传感器,本质是精密电位器,但导电塑料膜层的线性度和耐磨性比碳膜高一个量级。它的输出是电阻分压,接一个稳定的参考电压,ADC 读到的电压和角度基本成线性。关键参数:独立线性度可达 0.1%,旋转寿命标称 5000 万次以上,工作温度范围覆盖一般实验室环境。
接线时注意:传感器两端接参考电压和地,中间抽头接 ADC 输入。参考电压必须和 STM32 的 ADC 参考源一致,否则算出来的角度会有固定偏差。我一般会在软件里做两点校准:摆杆垂直向下时记一个 ADC 值,水平时记另一个,用这两点算斜率和截距。
2.3 步进电机与细分驱动:开环控制够不够用
伺服电机闭环性能好,但价格高,调试门槛也高。步进电机开环控制,配合细分驱动器,在自由摆这个场景下精度够用。四宏细分驱动器把整步切成更多微步,比如 16 细分,原来一步 1.8°,现在一步 0.1125°,平板角度调整更细腻,低频振动也明显减弱。
步进电机的扭矩随转速上升而下降,选型时要看“矩频特性曲线”。摆杆摆动时,平板要克服的惯量不大,但要求响应快。常见做法是选 42 步进电机,保持扭矩 0.4N·m 左右,配 16 细分,脉冲频率在 5kHz 到 20kHz 之间调。脉冲频率太高,扭矩掉得厉害,平板反而跟不上;太低,调整动作一顿一顿的,硬币照样滑。
驱动器接线:PUL 接脉冲,DIR 接方向,ENA 接使能。STM32 这边用一个定时器输出 PUL,另一个 GPIO 控制 DIR。每次要调整平板角度时,先算目标角度和当前角度的差值,再算需要发多少个脉冲、方向是正还是反。
3. 控制逻辑落地:从 ADC 采样到步进脉冲的完整链路
3.1 角度采样与滤波:ADC 读回来怎么变成可信角度
ADC 读回来的原始值有噪声,直接拿来算控制量,步进电机会频繁正反转,平板抖得像筛糠。常见做法是:连续采样 8 次,去掉最大最小值,剩下 6 次取平均。这个滤波窗口不能太大,否则引入相位滞后,摆杆过零点时平板反应慢半拍。
// 角度采样与滑动平均滤波 #define SAMPLE_NUM 8 uint16_t adc_buf[SAMPLE_NUM]; uint16_t adc_filtered; uint16_t get_filtered_adc(void) { uint32_t sum = 0; uint16_t max = 0, min = 0xFFFF; for (int i = 0; i < SAMPLE_NUM; i++) { adc_buf[i] = ADC_GetConversionValue(ADC1); if (adc_buf[i] > max) max = adc_buf[i]; if (adc_buf[i] < min) min = adc_buf[i]; sum += adc_buf[i]; } // 去掉最大最小各一个,剩下取平均 sum = sum - max - min; adc_filtered = sum / (SAMPLE_NUM - 2); return adc_filtered; }这段代码的关键在SAMPLE_NUM和去掉极值的策略。8 次采样在 72MHz 主频下耗时不到 100μs,对控制周期影响可忽略。去掉最大最小是为了抑制偶发尖峰干扰,比如电机换向时电源波动耦合到传感器上。如果你发现滤波后角度还是跳,先查参考电压是否稳定,再查传感器抽头接线是否松动。
3.2 角度换算与目标偏差计算
ADC 值转角度需要两个校准点。假设摆杆垂直向下时 ADC 读数为adc_down,平板水平时 ADC 读数为adc_level,那么当前角度可以用线性映射算出来。但自由摆的平板目标角度不是固定值——题目要求硬币与平板保持相对静止,意味着平板要实时补偿摆杆的倾斜。
// 角度换算与偏差计算 float adc_to_angle(uint16_t adc_val) { // 两点校准:adc_down 对应 0°,adc_level 对应 90° float angle = (float)(adc_val - adc_down) * 90.0f / (adc_level - adc_down); return angle; } // 目标角度:平板需要转到的位置 // 摆杆角度为 swing_angle 时,平板应补偿到水平 float target_angle = 90.0f - swing_angle; // 具体公式按机械结构定 float current_angle = adc_to_angle(get_filtered_adc()); float error = target_angle - current_angle;这里的target_angle公式取决于摆杆和平板的机械连接方式。如果平板直接固定在摆杆末端,摆杆倾斜 θ,平板也倾斜 θ,要让它水平,就得反向转 θ。如果中间有减速机构,还要乘传动比。参数adc_down和adc_level必须实测,不能照抄别人的值,因为每个传感器的安装角度和机械零位都不一样。
3.3 步进电机脉冲生成:定时器怎么配
步进电机的转速由脉冲频率决定,转动角度由脉冲数量决定。STM32 的通用定时器可以输出 PWM,但步进电机需要的是“发完 N 个脉冲就停”,所以更常见的做法是用定时器更新中断,在中断里翻转 GPIO,同时计数。
// 步进电机脉冲生成(定时器中断方式) volatile int32_t step_remain = 0; // 剩余脉冲数,正负表示方向 volatile uint8_t step_pulse = 0; void TIM3_IRQHandler(void) { if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) { TIM_ClearITPendingBit(TIM3, TIM_IT_Update); if (step_remain > 0) { step_pulse = !step_pulse; GPIO_WriteBit(GPIOA, GPIO_Pin_6, step_pulse ? Bit_SET : Bit_RESET); if (step_pulse == 0) step_remain--; // 完整脉冲计数 } else if (step_remain < 0) { step_pulse = !step_pulse; GPIO_WriteBit(GPIOA, GPIO_Pin_6, step_pulse ? Bit_SET : Bit_RESET); if (step_pulse == 0) step_remain++; } } } // 设置运动:脉冲数正负代表方向,频率决定速度 void move_steps(int32_t steps, uint16_t freq_hz) { // 设置方向引脚 if (steps > 0) GPIO_ResetBits(GPIOA, GPIO_Pin_7); else GPIO_SetBits(GPIOA, GPIO_Pin_7); step_remain = steps; // 定时器频率 = 72MHz / (prescaler+1) / (period+1) // 要得到 freq_hz 的脉冲频率,period 对应半个周期 uint16_t period = 36000000 / freq_hz - 1; TIM_SetAutoreload(TIM3, period); TIM_Cmd(TIM3, ENABLE); }step_remain用有符号数,正负区分方向,省掉一个方向变量。定时器中断频率是脉冲频率的两倍,因为每次中断翻转一次电平,一个完整脉冲需要两次翻转。period的计算里 36000000 是 72MHz 的一半,因为中断频率是脉冲频率的两倍。实际调试时,如果电机丢步,先降频率,再查驱动器细分设置和电流拨码。
4. 避坑与排查:调试自由摆系统时最容易翻车的五个点
4.1 现象:硬币在摆杆过零点附近滑落
原因:过零点时摆杆角速度最大,平板需要快速反向补偿,但步进电机从收到指令到实际转动有响应延迟,加上滤波引入的相位滞后,补偿动作慢了一拍。
解决:在过零点附近提前预判。用摆杆角速度乘以一个前馈系数,叠加到目标角度上。角速度可以通过相邻两次角度采样差分得到。前馈系数从小往大调,直到硬币不再滑落。注意前馈不能太大,否则平板会过冲,硬币往反方向滑。
4.2 现象:激光笔打靶时光斑在靶心附近画圈
原因:步进电机开环控制,每次调整都是“发脉冲—停—再发脉冲”,平板在目标角度附近来回振荡。细分驱动器虽然减小了单步角度,但没有位置反馈,振荡不会自动收敛。
解决:在控制量里加死区。当角度误差小于某个阈值(比如 0.5°)时,不发脉冲,让平板保持不动。死区大小要实测:太小,振荡依旧;太大,激光偏出靶心。常见做法是死区取步进电机单步角度的一半左右。
4.3 现象:ADC 读数在电机转动时跳变
原因:电机驱动器的电源线和传感器信号线捆在一起走线,电机换向时电流突变,通过线间耦合干扰了传感器信号。或者电机和传感器共用一组电源,电机启动瞬间把电源电压拉低,ADC 参考电压跟着波动。
解决:传感器供电和电机供电分开走线,传感器用 L7805 单独稳压,电机用 LM2576 另一路。信号线远离电机线,最好双绞屏蔽。如果还跳,在 ADC 输入脚加一个 0.1μF 电容到地,硬件低通滤波。
4.4 现象:TFT 液晶刷新时控制周期变长
原因:TFT 刷新占用 SPI 或 FSMC 总线,如果刷新函数里用了阻塞延时,主循环被拖慢,步进脉冲更新不及时。
解决:TFT 刷新放在主循环空闲时做,不要放在定时器中断里。刷新频率降到 10Hz 到 20Hz 足够看,没必要 60Hz。如果用了 DMA 刷屏,确认 DMA 传输不占用控制定时器的中断优先级。
4.5 现象:步进电机发热严重甚至失步
原因:细分驱动器电流拨码设置过大,或者脉冲频率超过电机矩频特性的拐点,电机扭矩不够,转子跟不上脉冲,表现为失步和发热。
解决:先查驱动器电流拨码,按电机额定电流设置,一般 42 步进电机设 1.2A 到 1.5A。再查脉冲频率,如果超过 20kHz 还要求大扭矩,要么换电机,要么加减速机构。调试时用手摸电机外壳,温热正常,烫手就是电流大了。
5. 进阶技巧:用摆杆角速度做前馈补偿的实操细节
前馈补偿是让硬币在快速摆动中不掉的关键。原理不复杂:平板需要补偿的角度不仅取决于摆杆当前角度,还取决于摆杆角速度——角速度越大,平板需要提前转到的位置越超前。
具体实现:每 2ms 采一次角度,存到环形缓冲区。当前角度和上一次角度的差值除以采样周期,就是角速度的近似值。前馈量 = 角速度 × 前馈系数。前馈系数通过实验确定:先把系数设为 0,慢慢增大摆幅直到硬币刚好要滑,记下此时的角度误差;然后逐步增大前馈系数,直到硬币在最大摆幅下也不滑。
// 前馈补偿实现 #define FEEDFORWARD_GAIN 0.15f // 前馈系数,实测调整 float angle_history[2] = {0}; float swing_velocity = 0; void update_feedforward(float current_angle) { // 差分求角速度 swing_velocity = (current_angle - angle_history[0]) / 0.002f; // 2ms 周期 angle_history[0] = current_angle; // 前馈量叠加到目标角度 float feedforward = swing_velocity * FEEDFORWARD_GAIN; target_angle += feedforward; }FEEDFORWARD_GAIN的单位是“度每度每秒”,物理意义是:摆杆每秒钟转 1°,平板额外补偿多少度。这个值取决于摆杆长度、平板惯量和电机响应速度,换一套机械结构就得重新调。我一般从 0.05 开始试,每次加 0.05,直到硬币在最大摆幅下稳定不滑,再往回退一点留余量。
验证前馈是否生效的方法:用激光笔打靶,观察光斑轨迹。没有前馈时,光斑在靶心附近画椭圆;加了合适的前馈,椭圆缩小成一个小点。如果光斑变成一条直线来回扫,说明前馈系数太大,平板过冲了。
还有一个细节:前馈补偿只在摆杆角速度较大时起作用,角速度接近零时前馈量也接近零,不会影响静态平衡。所以不用担心前馈把静态精度带偏。调试时先把静态平衡调好,再加前馈,顺序不能反。
从那以后我每次调这种动态补偿系统,都强制先跑一遍“纯比例控制 + 死区”,确认静态能稳住,再逐步加前馈和滤波。跳过这一步直接上复合控制,大概率是参数互相打架,调一天也调不出来。希望帮到你。
本文还有配套的精品资源,点击获取