
简介面向电子设计竞赛与嵌入式开发学习者的滚球控制系统板球系统完整开源工程对应2017年全国大学生电子设计大赛B题。系统基于Keil MDK开发源码以C语言为主硬件驱动层覆盖PWM输出、超声波测距、编码器反馈、SPI通信、按键与激光模块等关键环节并保留多份.bak备份、中间文件和编译列表便于对比版本改动与调试复盘。资源共190个文件以.c/.h源文件、.uvprojx/.uvoptx工程配置及.o/.lst/.crf等编译产物为主附带少量图片、说明文档和Python脚本压缩包仅5.96MB目录结构清晰。目前已有1356人学习适合正在备赛、研究二维云台/小球轨迹控制或希望借鉴完整代码框架的读者可以结合源码梳理传感器采集、控制算法与执行机构输出之间的关系并在此基础上二次开发与迭代。开源工程降低了复现门槛可直接迁移到同类板球/平衡控制项目中。 2017年电赛B题“板球系统”是我带过那么多队伍里觉得最“经典”的一道控制类题目。说它经典是因为它把图像处理、运动控制和机械结构几个模块拧在一起任何一个环节掉链子小球就是滚不中你要的位置。这几天正好有学弟问我要当时的源码和笔记我干脆整理一篇完整的复盘文章出来把整个系统的拆解思路、关键代码、调参过程和踩坑记录都放这里给后面参赛或者做相关项目的小伙伴一个参考。不管你是刚好要打电赛还是单纯想用OpenCV加舵机做一个二维平衡的小玩意这篇内容都值得耐心看完。1. 项目概述与需求拆解1.1 2017年B题到底要求什么先带大家回顾一下题目本身的硬性指标。2017年电赛B题要求的核心是这个场景一块平板通过两个方向的倾斜来控制一个小球在板上滚动让小球从初始位置自动滚到指定位置并且在规定时间内稳定下来精度要求相当高。具体来说有基础部分和发挥部分基础部分要求定点停靠发挥部分则涉及圆周运动、轨迹跟踪这类动态任务。这道题的本质就是一个典型的两轴平衡控制系统。你把它抽象出来其实就是两个独立的控制环路X轴方向上一个控制环Y轴方向上一个控制环。板子绕X轴转动控制小球在Y方向上移动绕Y轴转动控制小球在X方向上移动。搞清楚这个解耦关系整个系统的设计框架就非常清晰了。1.2 核心难点在哪里这个题目说难不难说简单也绝对不简单。我总结下来几个核心难点值得每一个参赛者重视因为它们决定了你最终是拿省奖还是国奖图像识别的稳定性和实时性小球定位必须既快又准任何一帧的卡顿或者误检测都会直接反映成控制量的突变小球就会抖动、冲出边界。坐标系的统一摄像头看到的像素坐标跟实际平板上的物理坐标不是一回事必须做透视变换和比例换算否则闭环控制算出来的误差是错的。控制的稳定性PID参数调不好小球要么超调冲出去要么在目标点附近来回抖动就是停不住。机械结构的响应速度舵机或者电机的响应带宽必须够否则控制律算得再好执行机构跟不上也是白搭。我们当时把题目读了三遍最后拆解出来的结论是这是一个串级或者并列的PID控制问题外层是位置环内层是速度环或者至少需要位置环加阻尼项。当然直接用单环位置PID也能跑但动态性能会差很多后面我会详细讲。2. 系统整体架构与硬件选型2.1 硬件组成一览系统的硬件搭建直接决定了你的稳定性和控制效率。我们把整个系统分成了几个模块核心控制板、图像采集模块、执行机构云台、以及小球和平板本身。模块选型方案说明主控芯片STM32F407或树莓派3BSTM32做闭环控制响应更实时树莓派做图像处理更方便也可以两者结合图像采集USB摄像头30万像素以上免驱关键是帧率最好能达到60fps至少也要30fps执行机构双轴云台配MG996R舵机或步进电机舵机便宜但控制精度一般步进电机如42步进精度高但需要驱动器平板小球亚克力板高亮橙色乒乓球橙色识别度高不易受环境光干扰电源方案5V/3A给舵机5V/2A给树莓派或STM32分开供电避免舵机抖动造成主控掉电2.2 处理器选型教训我们当时最初方案是纯STM32F407图像处理用OV7670摄像头加上FIFO后来发现做色彩识别阈值调整太费劲帧率也上不去。后来直接换成了树莓派做视觉识别结合STM32或者直接用树莓派输出PWM控制舵机效果马上就好多了。这里我建议后来人如果你不是对嵌入式图像处理特别熟主控板加树莓派组合是最稳妥的。树莓派跑OpenCV做图像识别通过串口把坐标发到STM32STM32做闭环控制输出PWM给舵机各有分工实时性和稳定性都能兼顾。当然如果你们队伍里有人特别擅长C优化加纯ARM方案那也完全可以只是调试周期会拉长。2.3 机械结构那些事机械结构是整个系统的地基千万别忽视。我们第一次试跑的时候板子是硬固定在一个便宜的云台上的结果两个舵机的转动会互相耦合导致小球运动轨迹根本不是直线。后来换了带轴承的双轴云台支架才解决这个问题。平板的尺寸也要注意一般用50cm×50cm的亚克力板比较合适。太小小球很快就滚出去了留给控制的反应时间太少太大力矩需求更大舵机响应不过来。边框最好贴上防撞条防止小球高速冲出去砸坏东西。3. 图像识别与小球定位3.1 图像处理流程设计在小球定位这个环节我们的目标是从摄像头图像里拿到小球在板上的像素坐标。整个处理流程可以分成五步图像采集、预处理、二值化、轮廓提取、坐标计算。图像采集使用OpenCV的VideoCapture读取摄像头数据读取时不要逐帧直接处理而是放到一个缓冲区避免处理器等待摄像头I/O。预处理先高斯模糊去除噪声然后再转到HSV颜色空间。直接转HSV是因为RGB空间下颜色受光照影响太大同样的橙色在阳光下和阴影里BGR值差异巨大HSV把色调H分离出来对光照的鲁棒性就好很多。二值化根据HSV阈值将图像中符合小球颜色的区域变成白色其余变黑。这一步的关键是阈值上下界的选取后面我会专门给出注意事项。轮廓提取OpenCV里用findContours函数找到所有白色区域的外轮廓用minEnclosingCircle或者boundingRect把小球框出来。坐标计算用轮廓的矩或者外接圆圆心得到小球的像素坐标uv。3.2 HSV阈值设置与标定HSV阈值设置是最容易踩坑的环节。需要特别强调的是不同光照环境下HSV阈值差异非常大。室内开灯、窗户自然光、晚上灯光下识别效果完全是三个样子。这里给出一个参考区间如果你是橙色乒乓球的话通道最小值最大值H0或525S80255V100255实际标定时我建议你写一个小工具把摄像头的实时画面显示出来鼠标点击到小球上程序自动读取该像素的HSV值这样采样很多点再去设置阈值远比凭经验猜要准确。3.3 像素坐标到物理坐标的变换这一步非常关键也是很多队伍忽略的。摄像头不是垂直对准平板的总是有一定的倾斜角度这时候直接拿像素坐标算误差会导致越靠近边缘误差越大。解决方法是使用OpenCV的透视变换。你先在板上固定四个已知物理坐标的角点比如板的四个角然后找到这四个角点在图像中的像素坐标用getPerspectiveTransform计算出变换矩阵再通过warpPerspective或者直接对坐标点做矩阵乘法就能把任意像素坐标换算到平板的物理坐标系上。import cv2 import numpy as np # 四个角点的像素坐标手动标定或自动检测 src_points np.float32([[150, 120], [490, 150], [120, 470], [460, 500]]) # 对应的物理坐标单位cm假设板子50x50 dst_points np.float32([[0, 0], [50, 0], [0, 50], [50, 50]]) matrix cv2.getPerspectiveTransform(src_points, dst_points) # 将小球像素坐标(u, v)转换为物理坐标(x, y) pixel_point np.array([[[u, v]]], dtypenp.float32) physical_point cv2.perspectiveTransform(pixel_point, matrix) x, y physical_point[0][0]第一次跑这个变换的时候我们看到小球在板子边缘时输出的物理坐标跟实际测量的差距越来越大还以为是代码写错了后来才发现是因为收集角点的时候不够精确。所以标定角点时一定要用棋盘格或直角靠尺辅助误差在几个像素内后续物理坐标误差才能控制在毫米级。4. 运动控制算法与PID实现4.1 PID控制的基础设计控制部分是整个系统的灵魂。最基础的做法是对X轴和Y轴分别做位置PID控制。以X轴方向为例当前小球物理坐标为x目标位置为x_target误差e x_target - x控制器输出控制量u这个u映射到Y方向舵机的PWM值。float pid_update(float target, float current, float *integral, float last_error, float kp, float ki, float kd) { float error target - current; *integral error; float derivative error - *last_error; *last_error error; float output kp * error ki * (*integral) kd * derivative; return output; }这一段看起来简单但实际上容易犯两个错误一个是积分饱和如果小球长时间无法到达目标点积分一直累积输出会暴涨小球会直接冲出去。另一个是微分噪声放大如果通过图像识别到的坐标一旦有噪声微分会把噪声放大导致舵机抖动。所以实际使用中一定要给积分量加限幅微分项加低通滤波。4.2 单环PID不够试试串级PID单环位置PID在小球离目标点比较近的时候还能用但一旦距离较远你会发现小球会疯狂超调甚至画着S形轨迹冲向边界。原因很简单位置环只考虑了位置误差没有考虑小球当前的速度相当于一个人开车只看“离目的地还有多远”却不看“当前车速有多快”那肯定要冲过头。解决思路是加入速度环形成位置外环、速度内环的串级PID结构。外环输出的是期望速度内环根据当前速递去调整舵机角度。速度可以通过对位置差分得到vel_x (x_current - x_last) / dt但这里有个问题就是你用摄像头帧率去差分算速度噪声极大算出来的速度跳来跳去。所以一定要对原始位置数据做平滑处理比如用滑动窗口平均或者一阶低通滤波alpha 0.4 x_filtered alpha * x_raw (1 - alpha) * x_last_filtered我自己的实际使用感受是加了串级PID之后小球在长距离移动时明显“有刹车感”到目标附近会平滑减速而不是之前那种冲过头再倒回来的观感。串级PID代码也不复杂外环计算出的期望速度作为内环的目标值内环再计算一次PID得到舵机PWM即可。4.3 舵机PWM映射与执行舵机的控制需要20ms周期脉冲高电平时间在0.5ms到2.5ms之间对应0到180度。我们通过对每个轴独立控制舵机角度来让板子倾斜。这里要注意舵机中位对应的PWM值要先在机械上校准确保平板在舵机处于中位时是完全水平的。控制量u到PWM值的映射要限幅防止舵机打到底导致机械卡死。// 假设控制量u的范围是[-30, 30]映射到PWM范围[1000, 2000] float pwm_x 1500 u_x * (1000.0f / 30.0f); if (pwm_x 2000) pwm_x 2000; if (pwm_x 1000) pwm_x 1000;这里有一个工程上的细节两个舵机的安装方向可能相反。比如一个舵机顺时针转是让板子左倾另一个顺时针转却是让板子前倾。如果没仔细标定控制方向反了闭环直接变成正反馈小球会不降反升瞬间冲出板子。每换一次机械结构第一步就是确认控制量正负与实际倾斜方向的关系。5. 常见问题与排查技巧实录5.1 小球识别不稳定坐标跳动严重这个问题出现的频率最高。如果你发现小球坐标在静止时也会上下跳好几个像素先检查二值化后的图像中是否有多余的干扰区域比如反光的光斑、背景里的橙色物体。解决办法是加强预处理不只是高斯模糊还可以用形态学操作腐蚀膨胀把噪声区域去掉。检查HSV阈值是否过宽过宽就会把相近颜色都当成目标。给坐标加低通滤波从软件层面减小跳动。我们当时还发现一个有趣的现象如果平板表面太光滑LED灯会在板面上形成反光形成一块白色或者高亮的区域被误判成小球。后来在摄像头前面加了一个偏振片这个问题就大幅缓解了。5.2 小球总往一个方向偏回不到中心如果你发现静态时小球总是慢慢往一个固定方向滚过去首先要怀疑的不是PID参数而是平板机械水平没有调好。舵机中位时板子不一定完全水平用手或者水平仪确认一下如果确实不水平需要通过调整舵机中位值的软件偏移来补偿。另外摄像头安装如果不平行于平板平面透视变换后物理坐标也会有固定的系统误差。这两个问题都属于需要先做标定补偿的不是控制参数能完全兜住的。5.3 PID参数整定口诀与步骤调PID是我觉得整个项目中最磨人的环节但也是最有成就感的一刻。分享一下我自己的整定步骤按照这个顺序来比盲目试要快很多先把积分和微分设为0只保留比例系数Kp从小到大慢慢加直到小球开始出现等幅震荡在目标点附近来回摆动。记录下这个临界振荡增益Kp_crit此时振荡周期大约为T_crit。按照齐格勒-尼克尔斯Ziegler-Nichols经验公式给定初始参数P 0.6 * Kp_critI 1.2 * Kp_crit / T_critD 0.075 * Kp_crit * T_crit。在这个基础上微调主要是看动态跟踪时的超调量和静态时的稳定时间。如果超调大先减Kp或者加Kd如果静态有静差再适当加Ki。建议串级PID先调内环速度环让速度环稳定之后再调外环位置环。注意在整定过程中一定要先把小球放在板的中心附近并且把积分限幅调小否则小球一旦偏移过大积分饱和会让系统失控板子翻转过猛。5.4 帧率与控制频率的匹配问题树莓派做图像识别帧率一般在30到60fps之间而普通的PID控制频率理论上可以跑到几百赫兹。这里我的建议是让控制频率跟随视觉帧率而不是用固定高频。原因是最简单的如果控制频率远高于视觉更新频率那两次视觉数据之间的控制指令都是基于旧数据的没有意义还会造成舵机频繁抖动。我们在实际实现中是设置了一个定时器每收到一帧新的坐标就触发一次PID计算和PWM更新这样既自然又有实时性。如果后续有更高的控制需求可以在两帧视觉之间加入惯性测量单元IMU数据做预测但我们当时没有用到这么复杂效果也已经足够。5.5 参赛时的调试心态与流程最后说点题外话。电赛时间是有限的调试流程一定要有主次。我们当时的策略是第1天搭建机械结构完成摄像头标定确保小球能被稳定识别第2天跑通开环控制人为输入不同PWM值观察小球运动方向标定正反向第3天实现单环PID调稳定到定点停靠第4天升级为串级PID优化动态响应第5天集中精力跑轨迹跟踪和发挥部分储备各种异常处理方案。因为B题的核心就是控制精度和稳定速度其他功能都是在这个基础上叠加的所以前期一定要先把“小球能稳定停住”这个闭环跑通后面拓展轨迹、跟踪都会顺利很多。这一个多月折腾下来我对“板球系统”最大的感受是它把理论课上学的图像处理、控制理论、机械设计、嵌入式编程全部融合进了一个看起来小小的项目里每一个环节都会给你“上一课”。做这个系统收获最大的不是最后拿了什么奖而是你真正理解了“反馈”这两个字的重量——无论是代码里的PID还是调试过程中的反复试错本质上都是对误差的修正。如果你现在也正在做这个项目别怕踩坑每一步调试的经验都会变成你最终的底气。本文还有配套的精品资源点击获取