内容介绍
内容介绍
一、项目介绍和创意介绍
1. 项目背景与任务要求
题目核心任务为:方向五"教育与创意互动":设计并制作一套双轴姿态可调节的平板平衡系统,通过自动控制方式,使小球在平板上实现自动平衡、定点稳定、指定轨迹运动、抗扰动保持等功能。系统要求响应快、精度高、运行稳定,能在外界轻微扰动下快速恢复平衡,在此过程中了解并学习舵机控制的流程,并系统的学习PID算法,有助于我们更好的了解控制原理。
2. 项目功能与目标
本系统最终实现以下功能:
- 小球自动归中(模式1、2):小球放置在板面任意位置,可自动回到中心并稳定。
- 定点定位控制(模式3、4、5):小球可稳定停在板面指定坐标点,误差极小。
- 圆周轨迹跟踪(模式6):小球沿设定圆形轨迹匀速平稳运动,不脱轨、不抖动。
- 多点顺序巡线(模式7):按预设路径依次到达多个目标点,完成复杂轨迹。
- 抗干扰平衡:受到外力扰动后,可在 1 秒内快速恢复平衡状态。
- 人机交互:支持按键切换模式、OLED 实时显示坐标、误差、运行状态。
3. 项目创新点与特色
- 视觉闭环方案:放弃传统 IMU 姿态检测,改用OpenMV 机器视觉直接检测小球位置,检测更直观、精度更高。
- 双闭环级联 PID:采用位置外环 + 角度内环双 PID 结构,兼顾定位精度与动态响应。
- 解耦控制设计:X、Y 双轴独立控制、独立参数整定,消除机械耦合带来的抖动。
- 多模式一键切换:支持归中模式、定点模式、画圆模式、路径模式,调试与演示便捷。
- 软硬件安全保护:具备通信超时保护、舵机角度限位、电源异常保护,防止机械与电路损坏。
- 模块化架构:硬件分层、软件分函数,便于调试、移植与扩展,适合教学与竞赛复用。
二、使用到的硬件详细介绍
系统采用 “主控 + 视觉 + 驱动 + 电源 + 结构” 模块化硬件架构,主要器件与功能如下:
1. 主控模块:STM32F103C8T6 最小系统
- 内核:ARM Cortex-M3,最高主频 72MHz
- 资源:64KB Flash,20KB SRAM
- 接口:3 路 USART、2 路 SPI、2 路 I2C、多路 16 位定时器
- 作用:负责视觉数据解析、PID 运算、PWM 输出、按键处理、OLED 显示、逻辑调度。
2. 视觉采集模块:OpenMV4 H7 Plus
- 处理器:STM32H743,Cortex-M7
- 摄像头:OV5640,500 万像素
- 帧率:QVGA 模式下可达 50–60FPS
- 功能:图像采集、灰度化、二值化、轮廓查找、质心计算、坐标串口输出。

3. 执行机构:双路数字舵机
- 型号:DS3115 大扭矩数字舵机
- 扭矩:15–20kg・cm,满足平板驱动需求
- 控制方式:20ms 周期 PWM,脉宽 500–2500μs 对应 0–180°
- 作用:分别控制平板X 轴、Y 轴倾斜角度,改变小球受力方向。

4. 电源管理模块
- LM2596S:12V 输入→5V/3A 输出,给舵机供电
- MP1584:5V 输入→3.3V 输出,给 STM32、OpenMV、OLED 供电
- 配置大电容滤波、防反接、防浪涌电路,保证供电稳定。
5. 人机交互模块
- 矩阵键盘:8–12 键,用于模式切换、目标点设置、PID 参数微调
- OLED 显示屏:0.96 寸 I2C 接口,显示:小球坐标 (X,Y)、目标坐标、误差、模式、电压状态。
6. 机械结构系统
- 平衡平板:亚克力 / 铝基板,尺寸约 20cm×20cm
- 双轴支撑:中心球铰支撑,保证两轴自由倾斜
- 传动机构:舵机摇臂 + 金属连杆 + 鱼眼轴承,减小机械间隙与死区
- 支架:固定 OpenMV 于平板正上方,保证视野全覆盖。
三、方案框图和项目设计思路
1. 系统总体方案框图

2. 整体设计思路
系统采用实时闭环负反馈控制,流程如下:
- 位置检测:OpenMV 摄像头俯视拍摄平板,通过图像处理提取小球中心坐标 (X,Y)。
- 数据传输:通过 UART 串口按固定帧格式发送给 STM32。
- 误差计算:STM32 将实际位置与目标位置做差,得到位置误差。
- PID 运算:双闭环 PID 控制器根据误差输出舵机角度控制量。
- 执行动作:主控输出两路 PWM 控制舵机,推动平板倾斜。
- 闭环修正:小球位置随平板姿态改变,视觉持续采集新位置,不断修正,直至误差趋近于 0。
3. 控制算法设计思路
- 外环(位置环 PID):根据小球位置误差,计算平板期望倾角。
- 内环(角度环 PID):根据期望倾角与实际倾角,计算舵机 PWM 输出。
- 解耦控制:X、Y 轴完全独立,互不影响,提升稳定性。
- 抗积分饱和:设置积分限幅,避免小误差累积导致过冲。
- 微分滤波:对微分项做一阶低通滤波,抑制高频噪声。
四、原理图和 PCB 介绍
1. 原理图设计说明
(1)STM32F103C8T6 最小系统原理图
- 电源:VDD 接 3.3V,VSS 接地,配置 0.1μF 去耦电容。
- 复位:NRST 引脚接上拉电阻 + 按键复位。
- 时钟:外部 8MHz 晶振 + 22pF 负载电容。
- 下载:SWDIO、SWCLK、GND 组成调试下载接口。
(2)电源电路原理图
- 12V 输入插座,串联自恢复保险丝 + 防反接二极管。
- LM2596:12V→5V
- MP1584:5V→3.3V,给数字电路供电,纹波更小。
(3)舵机驱动电路
- 舵机信号线接 STM32 定时器通道 PA0、PA1。
- 舵机电源直接由 5V 大功率模块供电,不经过 3.3V。
- PWM 输出口串联小电阻,保护 IO 口,防止浪涌损坏。
(4)串口通信电路
- OpenMV 的 TX 接 STM32 的 RX,RX 接 TX,共地。
- 配置电平匹配(3.3V 兼容),无电平转换芯片。
(5)按键与 OLED 电路
- 矩阵按键采用行列扫描,上拉电阻输入。
OLED 采用 I2C 通信,SDA、SCL 接 PB7、PB6。

五、软件流程图和关键代码介绍
1. 软件系统整体流程图
霍夫变换视觉与通信方案
2. 软件模块划分
- 初始化模块:时钟、外设、中断、PID 参数初始值。
- 视觉通信模块:串口接收、数据帧解析、校验。
- PID 控制模块:位置环、角度环、积分限幅、输出限幅。
- PWM 输出模块:舵机角度映射、双轴独立输出。
- 人机交互模块:按键扫描、菜单切换、OLED 显示。
- 保护模块:超时判断、角度限位、异常回中。
3. 关键代码详细说明
(1)PID 结构体与初始化
typedef struct {
float target; // 目标值
float measure; // 测量值
float Kp; // 比例系数
float Ki; // 积分系数
float Kd; // 微分系数
float err; // 当前误差
float last_err; // 上一次误差
float integral; // 积分累加
float output; // 输出值
float MaxOut; // 最大输出限幅
float MaxIntegral; // 积分限幅
} PID_TypeDef;
PID_TypeDef PID_X, PID_Y;
(2)增量式 PID 计算函数
float PID_Calc(PID_TypeDef *pid)
{
pid->err = pid->target - pid->measure;
float increment =
pid->Kp * (pid->err - pid->last_err)
+ pid->Ki * pid->err
+ pid->Kd * (pid->err - 2*pid->last_err + pid->last_last_err);
pid->output += increment;
// 输出限幅
if(pid->output > pid->MaxOut) pid->output = pid->MaxOut;
if(pid->output < -pid->MaxOut) pid->output = -pid->MaxOut;
pid->last_last_err = pid->last_err;
pid->last_err = pid->err;
return pid->output;
}
(3)OpenMV 数据帧解析
// 通信协议:0xA3 + 0xB3 + XH + XL + YH + YL + 0xC3
void Parse_OpenMV_Data(u8 *buf)
{
if(buf[0]==0xA3 && buf[1]==0xB3 && buf[6]==0xC3)
{
Ball_X = (buf[2]<<8) | buf[3];
Ball_Y = (buf[4]<<8) | buf[5];
}
}
(4)舵机角度控制
void Servo_SetAngle(float angle_x, float angle_y)
{
// 角度映射到PWM比较值
TIM_SetCompare1(TIM2, (u16)(500 + angle_x * 11.11));
TIM_SetCompare2(TIM2, (u16)(500 + angle_y * 11.11));
}
(5)模式切换逻辑
while(1)
{
Key_scanf(); //比较草率的按键扫描,按一下容易多次触发,但耐不住它简单呀,凑合用,核心不在这
OLED_ShowNum(20,0,X_measure,2);OLED_ShowNum(20,2,Y_measure,2); //显示小球测量坐标
OLED_ShowNum(5,5,start_time/100,2); //读秒显示
switch(mode)
{
case 1:OLED_ShowNum1(75,5,mode,2);break;
case 2:OLED_ShowNum1(75,5,mode,2);break;
case 3:OLED_ShowNum1(75,5,mode,2);break;
case 4:OLED_ShowNum1(75,5,mode,2);break;
case 5:OLED_ShowNum1(75,5,mode,2);break;
case 6:OLED_ShowNum1(75,5,mode,2);break;
case 7:
{
OLED_ShowNum1(75,5,mode,2);
OLED_ShowNum(85,1 ,key_values[0],1);
OLED_ShowNum(85,2,key_values[1],1);
OLED_ShowNum(85,3,key_values[2],1);
OLED_ShowNum(85,4,key_values[3],1);
break;
}
}
if(mode_flag)
OLED_ShowStr(120,5,"*",2); //我定义为确认键,显示*的时候代表已经进入了这个模式
else
OLED_ShowStr(120,5," ",2);
err_time++; //异常时间累加
if(err_time>30) //小球可能已经脱板
{
Servo_Control(1500,1500); //回到水平位置
}
delay_ms(20 ); //适当增加这个数值,可以给按键消抖
}
}
void TIM2_IRQHandler(void) //有关时间的操作放在中断里就行了,下面写的比较潦草
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
{
start_time++; //每10ms++
if(mode_flag)
{
switch(mode)
{
case 1:
{
X_calcu=140;Y_calcu=60;
}
break;
case 2:
{
if(0<start_time&&start_time<350)
{
X_calcu=80;Y_calcu=60;
}
else if(300<start_time)
{
X_calcu=140;Y_calcu=120;
}
break;
case 3:
{
if(0<start_time&&start_time<500)
{
X_calcu=80;Y_calcu=60;
}
else if(500<start_time&&start_time<1100)
{
X_calcu=83;Y_calcu=117;
}
else if(1150<start_time)
{
X_calcu=140;Y_calcu=120;
}
}
break;
case 4:
{
if(0<start_time&&start_time<800)
{
X_calcu=80;Y_calcu=60;
}
else if(800<start_time&&start_time)
{
X_calcu=194;Y_calcu=177;
}
}
break;
case 5:
{
if(0<start_time&&start_time<950)
{
X_calcu=80;Y_calcu=60;
}
else if(950<start_time&&start_time<1650)
{
X_calcu=140;Y_calcu=60;
}
else if(1650<start_time&&start_time<2500)
{
X_calcu=195;Y_calcu=120;
}
else if(2500<start_time)
{
X_calcu=194;Y_calcu=177;
}
}
break;
case 6:
{
if(0<start_time&&start_time<500)
{
X_calcu=83;Y_calcu=117;
}
else if(500<start_time&&start_time<800)
{
X_calcu=108;Y_calcu=151;
}
else if(800<start_time&&start_time<1100)
{
X_calcu=175;Y_calcu=151;
}
else if(1100<start_time&&start_time<1400)
{
X_calcu=175;Y_calcu=93;
}
else if(1400<start_time&&start_time<1700)
{
X_calcu=108;Y_calcu=93;
}
else if(1700<start_time&&start_time<2000)
{
X_calcu=108;Y_calcu=151;
}
else if(2000<start_time&&start_time<2300)
{
X_calcu=175;Y_calcu=151;
}
else if(2300<start_time&&start_time<2600)
{
X_calcu=175;Y_calcu=93;
}
else if(2600<start_time&&start_time<2900)
{
X_calcu=108;Y_calcu=93;
}
else if(2900<start_time&&start_time<3200)
{
X_calcu=108;Y_calcu=151;
}
else if(3200<start_time&&start_time<3500)
{
X_calcu=175;Y_calcu=151;
}
else if(3500<start_time&&start_time<3800)
{
X_calcu=175;Y_calcu=93;
}
else if(3800<start_time&&start_time<4100)
{
X_calcu=108;Y_calcu=93;
}
else if(4100<start_time&&start_time<4400)
{
X_calcu=108;Y_calcu=151;
}
else if(4400<start_time)
{
X_calcu=196;Y_calcu=180;
}
break;
}
case 7:
{
for(int ab=0;ab<4;ab++)
{
switch(key_values[ab])
{
case 1: x_pos[ab]=80; y_pos[ab]=60; break;
case 2: x_pos[ab]=140; y_pos[ab]=60; break;
case 3: x_pos[ab]=198; y_pos[ab]=60; break;
case 4: x_pos[ab]=83; y_pos[ab]=117; break;
case 5: x_pos[ab]=140; y_pos[ab]=120; break;
case 6: x_pos[ab]=196; y_pos[ab]=120; break;
case 7: x_pos[ab]=83; y_pos[ab]=177; break;
case 8: x_pos[ab]=140; y_pos[ab]=177 ; break;
case 9: x_pos[ab]=195; y_pos[ab]=177; break;
}
}
if(0<start_time&&start_time<800)
{
X_calcu=x_pos[0];Y_calcu=y_pos[0];
}
else if(800<start_time&&start_time<1600)
{
X_calcu=x_pos[1];Y_calcu=y_pos[1];
}
else if(1600<start_time&&start_time<2400)
{
X_calcu=x_pos[2];Y_calcu=y_pos[2];
}
else if(2400<start_time&&start_time<3200)
{
X_calcu=x_pos[3];Y_calcu=y_pos[3];
}
break;
}
}
}
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}
六、功能展示图及说明(具体功能看视频展示)
电路实物图片
1. 小球自动回归中心功能
- 现象:小球放在平板边缘,系统迅速调整双轴角度,小球平稳滑向中心并稳定。
- 指标:稳态误差≤±2 像素,调整时间≤1 秒。
- 说明:验证基础闭环平衡能力。

2. 定点定位功能
- 现象:通过按键设定目标点,小球快速移动到指定位置并保持不动。
- 说明:可设置九宫格点位,定位准确,无明显漂移。


3. 圆周轨迹跟踪功能
- 现象:小球沿圆形轨迹匀速平稳运动,轨迹圆滑,无卡顿、无脱轨。
- 指标:一圈时间约 8–9 秒,轨迹偏差≤5 像素。
小球上半圆A-B运行轨迹图
小球下半圆C-D运行轨迹图
4. 多点顺序巡线功能
- 现象:按设定路径依次到达 A→B→C→D→A 等点位,完成复杂路径控制。
5. 抗干扰测试
- 现象:用手轻推小球或轻晃平板,系统快速响应,1 秒内恢复平衡。
- 说明:体现系统响应速度与鲁棒性。
6. OLED 显示界面
- 显示内容:小球 X、Y 坐标、目标坐标、控制模式、系统状态。
- 作用:便于现场调试与参数观察。


七、设计中遇到的难题和解决方法
难题 1:视觉数据抖动大,小球位置跳变
- 原因:光照变化、图像噪声、小球运动模糊导致坐标跳变。
- 解决:
- OpenMV 端使用滑动平均滤波。
- STM32 端做限幅滤波 + 死区处理。
- 固定 ROI 区域,减少背景干扰。
难题 2:PID 整定困难,系统易震荡或响应慢
- 原因:双轴耦合、机械死区、参数不匹配。
- 解决:
- 采用先 P→再 D→最后 I的整定顺序。
- 双轴独立整定参数,不共用一套参数。
- 加入积分限幅,防止积分饱和。
难题 3:舵机启动时电源电压被拉低,系统重启
- 原因:舵机启动电流大,电源瞬间压降。
- 解决:
- 增加1000μF+100μF并联滤波电容。
- 舵机与主控分开供电,不共用地线。
- 增加续流二极管吸收反向电动势。
难题 4:双轴耦合,一动 X 轴 Y 轴偏移
- 原因:机械结构不对称、连杆长度不一致。
- 解决:
- 调整连杆长度与安装角度,保证对称。
- 软件上 X、Y 轴完全解耦,独立控制。
难题 5:OpenMV 与 STM32 串口通信丢包
- 原因:波特率不匹配、数据无帧格式。
- 解决:
- 统一波特率 115200。
- 采用帧头 + 数据 + 帧尾校验机制。
- 设置超时判断,丢失一帧不影响系统。
八、心得体会
1.收获与体会
在专业能力上,我对闭环控制、PID 算法、嵌入式开发、机器视觉、电源设计、抗干扰处理有了更深刻的理解,不再停留在课本理论,而是真正落地到硬件与代码中。同时,我也深刻体会到:控制类题目不仅是写代码,更是机械、电路、算法、调试能力的综合比拼。任何一个小细节,如机械间隙、电源纹波、接地处理、滤波参数,都会直接影响系统成败。
在团队协作方面,我们分工明确、互相配合,遇到问题一起分析、轮流调试,培养了高效沟通、抗压应变、快速排错的能力。竞赛过程非常辛苦,经常熬夜调试,但每当系统稳定实现功能时,所有付出都变得非常值得。
2.存在的不足
- 硬件抗干扰经验不足,前期因电源与接地问题浪费大量时间。
- PID 算法调优效率不高,依赖多次试凑,理论分析能力有待加强。
- 复杂故障排查速度慢,对系统整体运行逻辑的熟练度仍需提升。
3. 总结
这次活动不仅提升了我的专业技能,更培养了严谨、耐心、坚持、迎难而上的工程素养。
附件下载
滚球平衡系统.zip
STM32代码和OpenMV4 H7 Plus的视觉代码
Gerber_PCB2_2026-04-29.zip
嘉立创eda专业版PCB
团队介绍
无
评论
0 / 100
查看更多
猜你喜欢
省二【F题—北京林业大学】B21188队—智能送药小车本组为实现智能送药小车的功能,采用四轮小车的结构,使用 LM2596S 降 压模块将小车电源电压稳定输出。以 arduino UNO 为主控板, 首先为实现寻径功能,我们选择采用三个灰度传感器来返回给当前传感器检测到的值。
北京林业大学
4125
国二【F题—南京大学金陵学院】颜武丁队—智能送药小车小车采用四轮驱动,以 STM43F407VET6 作为主控,以 Sipeed K210 采集 分析行进中的场景信息,以红外传感器感知药品装载,部通过红、绿、蓝、黄、 白等色 LED 显示状态,通过 nrf24l01 无线通信实现两车通信。
金陵学院
4752
制作FPGA电子琴1. 存储一段音乐,并可以进行音乐播放,
2. 可以自己通过板上的按键进行弹奏,支持两个按键同时按下(和弦)并且声音不能失真,板上的按键只有13个,可以通过有上方的“上“、”下”两个按键对音程进行扩展
john
2657


