1. 滚球控制系统的赛题解析2017年全国大学生电子设计竞赛B题的滚球控制系统是一个典型的多变量非线性控制问题。题目要求在一个边长为65cm的正方形平板上通过控制平板的倾斜角度让直径不超过2.5cm的小球完成指定动作。这个看似简单的题目实际上考察了参赛队伍在机械结构设计、传感器选型、控制算法实现等多个方面的综合能力。我当年第一次看到这个题目时第一反应是这不就是个平衡板吗但实际操作起来才发现远没有想象中简单。平板面积越大机械结构的稳定性就越难保证小球直径越小位置检测的精度要求就越高。题目中要求的9个圆形区域相邻中心距20cm这意味着我们需要实现厘米级的位置控制精度。从系统设计的角度来看这道题目可以分解为三个核心模块机械结构、感知系统和控制系统。机械结构需要保证平板能够快速、平稳地倾斜感知系统需要实时准确地获取小球位置控制系统则需要根据目标位置和实际位置的偏差计算出合适的平板倾斜角度。这三个模块环环相扣任何一个环节出现问题都会影响整体性能。2. 机械结构设计与实现2.1 支撑结构选型机械结构是整个系统的基础也是最容易踩坑的地方。根据比赛规则平板的支撑结构必须自制不能使用商品化产品。常见的方案有三种十字万向节结构、双舵机结构和四连杆结构。我最初尝试的是十字万向节结构这种结构简单直接用一个万向节连接平板中心点和底座。但实测发现当平板尺寸达到65cm时万向节承受的扭矩太大容易产生晃动。后来改用双舵机结构两个舵机分别控制x轴和y轴的倾斜稳定性明显提升。但要注意舵机的选型普通9g舵机扭矩太小根本无法驱动这么大的平板。最终我们选择了MG996R金属齿轮舵机这款舵机扭矩达到10kg·cm价格也相对合理。安装时要注意舵机摆臂的长度会影响控制精度——摆臂越长控制分辨率越高但响应速度会降低。我们经过多次测试最终确定摆臂长度为5cm这个长度在精度和速度之间取得了不错的平衡。2.2 平板材质选择平板材质直接影响小球的滚动特性。题目允许使用木质、金属、有机玻璃等材料我们测试了多种材质后发现细木工板成本低但容易变形铝合金板平整度好但重量大亚克力板重量适中但静电明显PVC发泡板重量轻、平整度好最终选择了3mm厚的PVC发泡板表面喷涂哑光黑漆。这种组合既保证了足够的刚度又控制了整体重量而且黑色背景有利于视觉识别。需要注意的是喷涂时要确保漆面均匀避免出现反光点影响摄像头识别。3. 感知系统方案对比3.1 摄像头方案视觉定位是当前最主流的方案常用的有OpenMV、Pixy等开源摄像头。我们尝试了正点原子的OV2640模块配合STM32F407进行图像处理。具体实现步骤如下摄像头架设在平板正上方约80cm处确保能完整拍摄整个平板图像采集后先进行二值化处理区分小球和背景使用连通域分析算法提取小球质心坐标通过坐标系转换将像素坐标映射为实际物理坐标# OpenMV上的简单质心检测代码 import sensor, image, time sensor.reset() sensor.set_pixformat(sensor.RGB565) sensor.set_framesize(sensor.QVGA) sensor.skip_frames(time 2000) while(True): img sensor.snapshot() blobs img.find_blobs([(0, 0, 0)], pixels_threshold100, area_threshold100) if blobs: max_blob max(blobs, keylambda b: b.pixels()) img.draw_cross(max_blob.cx(), max_blob.cy()) print(x:%d, y:%d % (max_blob.cx(), max_blob.cy()))这种方案的优点是灵活性高可以适应不同颜色的小球。但缺点是计算量较大帧率通常只能达到20-30fps而且对光照条件敏感。3.2 电阻屏方案另一种思路是使用电阻屏检测小球位置。我们在前期测试中尝试过7寸四线电阻屏效果出乎意料的好。电阻屏的采样率可以达到100Hz以上而且不受光照影响。实现原理很简单将电阻屏作为平板表面通过ADC读取小球接触点的电压值根据电压值计算x、y坐标// STM32读取电阻屏的示例代码 uint16_t Read_X(void) { GPIO_InitTypeDef GPIO_InitStruct; // 设置X为输出高X-为输出低 GPIO_InitStruct.Pin X_PLUS_PIN; GPIO_InitStruct.Mode GPIO_MODE_OUTPUT_PP; HAL_GPIO_Init(GPIOX, GPIO_InitStruct); HAL_GPIO_WritePin(GPIOX, X_PLUS_PIN, GPIO_PIN_SET); GPIO_InitStruct.Pin X_MINUS_PIN; HAL_GPIO_Init(GPIOX, GPIO_InitStruct); HAL_GPIO_WritePin(GPIOX, X_MINUS_PIN, GPIO_PIN_RESET); // 设置Y和Y-为模拟输入 GPIO_InitStruct.Pin Y_PLUS_PIN | Y_MINUS_PIN; GPIO_InitStruct.Mode GPIO_MODE_ANALOG; HAL_GPIO_Init(GPIOY, GPIO_InitStruct); // 读取Y电压值 HAL_ADC_Start(hadc1); return HAL_ADC_GetValue(hadc1); }电阻屏方案的优点是响应速度快、精度高但缺点是成本较高而且对小球重量有要求通常需要20g以上。由于比赛规定平板尺寸为65cm市面上没有这么大尺寸的电阻屏这个方案最终没能用在正式比赛中。4. 控制算法设计与调参4.1 PID控制原理滚球控制系统本质上是一个位置伺服系统PID控制器是最合适的选择。PID控制器的输出由比例项、积分项和微分项组成u(t) Kp*e(t) Ki*∫e(t)dt Kd*de(t)/dt其中e(t)是目标位置与实际位置的偏差Kp是比例增益决定系统对当前误差的反应强度Ki是积分增益用于消除稳态误差Kd是微分增益抑制系统振荡在实际调试中我们采用了增量式PID算法这样可以避免积分饱和问题typedef struct { float Kp, Ki, Kd; float error, last_error, prev_error; float integral, output; } PID_Controller; float PID_Update(PID_Controller* pid, float setpoint, float measurement) { pid-error setpoint - measurement; float p_out pid-Kp * (pid-error - pid-last_error); float i_out pid-Ki * pid-error; float d_out pid-Kd * (pid-error - 2*pid-last_error pid-prev_error); pid-output p_out i_out d_out; pid-prev_error pid-last_error; pid-last_error pid-error; return pid-output; }4.2 参数整定技巧PID参数的整定是个经验活我们总结了一套实用的调试方法先将Ki和Kd设为0逐渐增大Kp直到系统开始振荡取振荡时Kp值的60%作为最终比例系数加入微分控制从Kp的10%开始逐步增大Kd直到消除振荡最后加入积分项Ki值通常设为Kp的1/100左右在调试过程中我们发现x轴和y轴的参数需要分别调整因为机械结构在两个方向上的特性可能不同。另外不同区域的最佳参数也不一样——中心区域可以承受更大的Kp而边缘区域需要更保守的参数以避免小球飞出平板。提示调试时可以先用手拨动小球观察系统恢复平衡的过程。理想的响应应该是快速但不超调就像用手接住一个滚落的鸡蛋一样。5. 系统集成与调试5.1 软硬件联调策略当机械、感知和控制三个子系统都准备好后真正的挑战才开始。系统集成阶段最常见的问题是各模块的时序配合。我们的解决方案是建立统一的时间基准所有关键操作都放在定时器中断中执行控制周期设置为20ms50Hz与摄像头帧率同步采用生产者-消费者模式图像处理和控制计算分开线程// STM32中的多任务调度示例 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim htim3) // 20ms定时器 { static uint8_t stage 0; switch(stage) { case 0: // 图像采集 CAM_TriggerCapture(); stage; break; case 1: // 图像处理 if(CAM_DataReady()) { ProcessImage(); stage; } break; case 2: // 控制计算 PID_Update(); stage 0; break; } } }5.2 常见问题排查在最后的测试阶段我们遇到了几个典型问题小球在特定区域会突然加速原因平板表面不平整有轻微凹陷解决更换平板并重新校准摄像头坐标映射系统在边缘区域容易振荡原因PID参数过于激进解决实现参数调度在不同区域使用不同的PID参数偶尔出现控制延迟原因图像处理耗时不稳定解决优化算法限制最大处理时间经过反复调试我们的系统最终能够稳定完成所有基本要求和发挥部分的要求。特别是在路径规划任务中我们实现了一个简单的轨迹生成器让小球可以平滑地从起点移动到目标区域typedef struct { float x, y; // 当前位置 float target_x, target_y; // 目标位置 float vx, vy; // 当前速度 float max_speed; // 最大速度 float accel; // 加速度 } TrajectoryPlanner; void UpdateTrajectory(TrajectoryPlanner* planner) { // 计算到目标的距离 float dx planner-target_x - planner-x; float dy planner-target_y - planner-y; float dist sqrt(dx*dx dy*dy); if(dist 0.5f) // 到达目标 { planner-vx 0; planner-vy 0; return; } // 计算期望速度方向 float dir_x dx / dist; float dir_y dy / dist; // 加速度限制 planner-vx dir_x * planner-accel * 0.02f; // 0.02s 20ms planner-vy dir_y * planner-accel * 0.02f; // 速度限制 float speed sqrt(planner-vx*planner-vx planner-vy*planner-vy); if(speed planner-max_speed) { planner-vx planner-vx * planner-max_speed / speed; planner-vy planner-vy * planner-max_speed / speed; } // 更新位置 planner-x planner-vx * 0.02f; planner-y planner-vy * 0.02f; }6. 竞赛经验与优化建议回顾整个开发过程有几个关键点值得特别注意。机械结构的稳定性是基础我们前两天的调试不顺利很大程度上是因为机械结构刚度不足。建议在机械设计阶段就进行有限元分析确保关键部位不会变形。传感器选择直接影响系统性能。如果条件允许建议同时准备视觉和电阻屏两套方案。比赛中我们遇到强光干扰导致视觉失效的情况幸好有备用方案。控制算法方面单纯的PID虽然能完成任务但更先进的方法如模糊PID或者状态反馈控制可能会带来更好的性能。我们在赛后尝试了LQR控制响应速度比PID提高了约30%。最后系统校准非常重要。我们开发了一套半自动校准程序可以快速建立摄像头像素坐标与实际物理坐标的映射关系大大节省了调试时间# 校准程序伪代码 def calibrate(): known_points [(0,0), (32.5,0), (65,0), (0,32.5), (32.5,32.5), (65,32.5), (0,65), (32.5,65), (65,65)] image_points [] for point in known_points: move_ball_to(point) time.sleep(1) img capture_image() center detect_ball_center(img) image_points.append(center) # 计算透视变换矩阵 H cv2.findHomography(np.array(image_points), np.array(known_points)) save_calibration(H)这套滚球控制系统虽然是为竞赛而设计但其技术原理在工业领域有广泛应用如平衡车、卫星姿态控制等。通过这个项目我们不仅锻炼了工程实践能力更深刻理解了控制系统设计的精髓——在多个相互制约的因素中找到最佳平衡点。