从赛题到系统:剖析2017年电赛滚球控制系统的核心设计与实现
2026/7/15 3:21:01 网站建设 项目流程

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进行图像处理。具体实现步骤如下:

  1. 摄像头架设在平板正上方约80cm处,确保能完整拍摄整个平板
  2. 图像采集后先进行二值化处理,区分小球和背景
  3. 使用连通域分析算法提取小球质心坐标
  4. 通过坐标系转换,将像素坐标映射为实际物理坐标
# 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_threshold=100, area_threshold=100) if blobs: max_blob = max(blobs, key=lambda 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以上,而且不受光照影响。实现原理很简单:

  1. 将电阻屏作为平板表面
  2. 通过ADC读取小球接触点的电压值
  3. 根据电压值计算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参数的整定是个经验活,我们总结了一套实用的调试方法:

  1. 先将Ki和Kd设为0,逐渐增大Kp直到系统开始振荡
  2. 取振荡时Kp值的60%作为最终比例系数
  3. 加入微分控制,从Kp的10%开始,逐步增大Kd直到消除振荡
  4. 最后加入积分项,Ki值通常设为Kp的1/100左右

在调试过程中,我们发现x轴和y轴的参数需要分别调整,因为机械结构在两个方向上的特性可能不同。另外,不同区域的最佳参数也不一样——中心区域可以承受更大的Kp,而边缘区域需要更保守的参数以避免小球飞出平板。

提示:调试时可以先用手拨动小球,观察系统恢复平衡的过程。理想的响应应该是快速但不超调,就像用手接住一个滚落的鸡蛋一样。

5. 系统集成与调试

5.1 软硬件联调策略

当机械、感知和控制三个子系统都准备好后,真正的挑战才开始。系统集成阶段最常见的问题是各模块的时序配合。我们的解决方案是:

  1. 建立统一的时间基准,所有关键操作都放在定时器中断中执行
  2. 控制周期设置为20ms(50Hz),与摄像头帧率同步
  3. 采用生产者-消费者模式,图像处理和控制计算分开线程
// 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 常见问题排查

在最后的测试阶段,我们遇到了几个典型问题:

  1. 小球在特定区域会突然加速

    • 原因:平板表面不平整,有轻微凹陷
    • 解决:更换平板并重新校准摄像头坐标映射
  2. 系统在边缘区域容易振荡

    • 原因:PID参数过于激进
    • 解决:实现参数调度,在不同区域使用不同的PID参数
  3. 偶尔出现控制延迟

    • 原因:图像处理耗时不稳定
    • 解决:优化算法,限制最大处理时间

经过反复调试,我们的系统最终能够稳定完成所有基本要求和发挥部分的要求。特别是在路径规划任务中,我们实现了一个简单的轨迹生成器,让小球可以平滑地从起点移动到目标区域:

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)

这套滚球控制系统虽然是为竞赛而设计,但其技术原理在工业领域有广泛应用,如平衡车、卫星姿态控制等。通过这个项目,我们不仅锻炼了工程实践能力,更深刻理解了控制系统设计的精髓——在多个相互制约的因素中找到最佳平衡点。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询