这次我们来看一个经典的电子设计竞赛项目:2017年全国大学生电子设计大赛的B题“滚球控制系统”。这个项目不是最新的AI模型,而是一个考验硬件设计、控制算法和系统集成的综合工程题目。对于电子、自动化、嵌入式方向的学生和工程师来说,它至今仍是学习PID控制、图像处理、机械结构设计和系统调试的绝佳案例。
项目的核心目标是设计并制作一套能够控制平板上的小球,使其按照预设轨迹运动的系统。它考察了从传感器选型、执行机构驱动、控制算法实现到上位机软件设计的全链路能力。如果你正在准备电赛、课程设计,或者想深入理解闭环控制系统,这个项目的思路和实现细节非常有参考价值。
本文会带你拆解“滚球控制系统”的完整实现方案。我们将从系统方案设计开始,分析核心的硬件选型(如摄像头、电机、平板材质),重点讲解PID控制算法的整定过程,并给出一个可运行的上位机软件框架。最后,我们会讨论实际调试中遇到的常见问题及解决方法,帮助你快速复现或优化自己的系统。
1. 核心能力速览
| 能力项 | 说明 |
|---|---|
| 项目类型 | 电子设计竞赛综合系统(硬件+软件+算法) |
| 核心功能 | 通过控制平板倾角,使平板上的小球跟踪预设轨迹运动 |
| 控制对象 | 一个小球(通常为钢球或乒乓球) |
| 执行机构 | 两个舵机或步进电机,控制平板在X、Y轴方向倾斜 |
| 传感方式 | 摄像头视觉识别(主流)或激光测距/角度传感器 |
| 核心算法 | PID控制算法(位置式或增量式) |
| 上位机 | 通常使用MATLAB、Python(OpenCV)或C#(WinForm)开发,用于图像处理、轨迹设定和监控 |
| 适合场景 | 全国大学生电子设计竞赛备赛、自动控制原理课程实践、嵌入式系统学习、PID算法验证平台 |
| 硬件门槛 | 中等,需要基础的嵌入式开发板(如STM32)、电机驱动、摄像头、机械结构制作能力 |
| 关键难点 | 机械结构稳定性、图像识别实时性与精度、PID参数整定、系统抗干扰能力 |
2. 适用场景与使用边界
这个项目主要适用于以下几类人群和场景:
- 电赛备赛学生:2017年B题是经典题目,其解题思路、方案选型和调试经验对后续竞赛有直接借鉴意义。
- 自动化/嵌入式学习者:提供了一个从理论(控制算法)到实践(硬件驱动、系统集成)的完整练手项目。
- 课程设计与毕业设计:作为“自动控制系统”或“计算机控制技术”等课程的综合性实践课题,具有明确的输入、输出和控制目标。
- PID算法验证平台:可以非常直观地观察P、I、D三个参数对系统动态性能(超调、稳态误差、响应速度)的影响。
使用边界与注意事项:
- 非通用AI工具:这不是一个开箱即用的软件或AI模型,而是一个需要从零搭建的硬件系统。
- 依赖机械精度:系统的最终性能极大程度上受限于机械结构的加工精度、平整度和摩擦系数。粗糙的机械结构会让再优秀的算法也无用武之地。
- 实时性要求高:视觉处理和控制周期需要足够快(通常要求几十毫秒内完成一次“图像采集-处理-控制输出”的循环),否则小球容易失控。
- 环境光影响:若采用视觉方案,环境光的强弱变化会直接影响图像二值化效果,进而影响小球定位精度,需要在算法上增加鲁棒性处理或使用可控光源。
3. 环境准备与前置条件
在开始具体实现前,需要准备好软硬件环境。
3.1 硬件清单
一套典型的滚球控制系统硬件包括:
- 控制核心:
- 微控制器:STM32F4系列(如F407,带FPU便于浮点运算)、STM32H7系列(性能更强)或树莓派(直接运行Linux和OpenCV)。STM32是电赛常用平台。
- 开发板:对应的最小系统板或核心板。
- 感知单元:
- 摄像头:OV7725、OV7670(带FIFO)等数字摄像头模块,或USB摄像头(连接上位机)。前者需单片机采集图像,后者将处理任务放在上位机。
- 镜头:广角镜头,以确保能拍摄到整个平板区域。
- 可选辅助传感器:MPU6050等姿态传感器,用于测量平板实际倾角,可与视觉定位做数据融合或作为备份。
- 执行机构:
- 舵机:大扭矩舵机(如MG996R),控制简单,但精度和分辨率有限。
- 步进电机+丝杆滑台:控制精度高,能实现更精确的倾角控制,但驱动和控制电路更复杂。
- 电机驱动模块:根据电机类型选择,如舵机直接PWM驱动,步进电机需要专用的步进驱动板(如A4988、DRV8825)。
- 被控对象与平台:
- 平板:亚克力板、木板或铝板,表面需平整光滑。尺寸常见为60cm x 60cm左右。为减少摩擦,有时会在表面贴一层光滑的薄膜。
- 小球:钢球、玻璃球或乒乓球。钢球惯性大,受空气阻力影响小,但需要更强的电机扭矩。
- 机械结构:
- 用于固定平板和电机的支架。要求稳固、无晃动。通常设计成“十”字结构,平板中心通过万向节或球铰与两个电机的推杆连接。
- 电源:
- 12V/5A以上的开关电源,为电机和整个系统供电。注意电机启动瞬间电流较大。
- 调试工具:
- USB转TTL串口模块,用于单片机与上位机通信。
- 万用表、示波器(用于调试PWM信号、电源)。
3.2 软件清单
- 下位机开发环境:
- Keil MDK或STM32CubeIDE:用于STM32程序的编写、编译和下载。
- STM32CubeMX:用于图形化配置STM32的时钟、引脚、外设(如定时器产生PWM、串口、DCMI接口接摄像头等),生成初始化代码。
- 上位机开发环境(视觉处理与控制):
- 方案一(推荐,分离式):上位机(PC)负责图像处理和轨迹计算,通过串口发送控制指令给下位机(STM32)。上位机可用Python + OpenCV + PySerial或C# + EmguCV(OpenCV .NET包装) + SerialPort快速开发。
- 方案二(一体式):所有处理都在嵌入式端完成,如使用树莓派运行Python+OpenCV,或STM32H7配合轻量级图像处理库。开发难度较大,但系统更紧凑。
- 调试与可视化:MATLAB或Python (Matplotlib)可用于绘制小球运动轨迹、跟踪误差曲线,辅助PID参数整定。
- 通信协议:
- 需要自定义一套简单的串口通信协议,用于上下位机间的数据交换。例如:
[帧头][指令类型][X坐标或误差][Y坐标或误差][校验和][帧尾]。
- 需要自定义一套简单的串口通信协议,用于上下位机间的数据交换。例如:
4. 系统方案设计与核心算法
4.1 系统总体框架
系统通常采用“上位机视觉处理 + 下位机实时控制”的双层结构,如下图所示(此处用文字描述):
- 感知层:摄像头拍摄包含小球和平板的图像,上传至上位机。
- 处理与决策层(上位机):
- 图像预处理:灰度化、高斯滤波去噪。
- 小球识别:颜色阈值分割(如果小球颜色特异)或背景差分法,结合轮廓查找,定位小球中心像素坐标
(x_img, y_img)。 - 坐标变换:将图像像素坐标
(x_img, y_img)通过透视变换或标定,转换为平板上的实际物理坐标(x_real, y_real)。 - 轨迹生成:根据题目要求(如直线、圆、定点),生成目标坐标
(x_target, y_target)。 - 误差计算:计算小球当前位置与目标位置的偏差
e_x = x_target - x_real,e_y = y_target - y_real。 - 控制律计算:将
e_x,e_y输入两个独立的PID控制器,分别计算出控制平板X轴和Y轴倾角所需的控制量u_x,u_y(通常是PWM的占空比或步进电机的步数)。 - 指令下发:将
u_x,u_y通过串口发送给下位机。
- 执行层(下位机):
- 接收上位机指令,解析出
u_x,u_y。 - 通过定时器输出对应占空比的PWM波驱动舵机,或发送脉冲序列驱动步进电机,改变平板倾角。
- 接收上位机指令,解析出
- 被控对象:平板倾角改变,小球在重力分力作用下开始滚动,其位置被摄像头再次捕获,形成闭环。
4.2 核心PID控制算法
PID控制是该项目的大脑。其离散位置式公式为:
u(k) = Kp * e(k) + Ki * ∑[e(j)] + Kd * [e(k) - e(k-1)]其中:
u(k):第k次控制周期的输出量。e(k):第k次控制周期的偏差(目标位置 - 实际位置)。Kp:比例系数,决定系统响应速度。过大会导致振荡甚至发散,过小会使响应迟缓。Ki:积分系数,消除稳态误差。过大会引起积分饱和,导致超调加大和振荡。Kd:微分系数,抑制超调,提高稳定性。过大会放大噪声,使系统对干扰敏感。
在滚球系统中的应用:需要两个独立的PID控制器,分别控制X轴和Y轴。由于两个轴动力学特性相似,参数通常可以设置成相同或相近。
参数整定步骤(经验法):
- 初始化:将
Ki和Kd设为0,Kp设为一个较小值。 - 纯比例调节:逐渐增大
Kp,直到小球能跟随目标运动,但出现明显的等幅振荡或围绕目标点来回摆动。记下此时的Kp为Ku,振荡周期为Tu。 - 加入积分:引入
Ki,设置为0.5 * Ku / Tu左右。观察稳态误差是否减小。积分作用太强会导致系统“迟钝”或低频振荡,需回调。 - 加入微分:引入
Kd,设置为0.125 * Ku * Tu左右。观察超调量是否被抑制,响应是否更平滑。微分对高频噪声敏感,如果摄像头坐标抖动厉害,需谨慎使用或对误差做低通滤波。 - 微调:在真实系统上反复微调三个参数,以达到“响应快、超调小、稳态准”的平衡。X轴和Y轴要分开调试,先调好一个轴,再调另一个。
5. 上位机软件实现(Python + OpenCV 示例)
这里给出一个使用Python和OpenCV实现图像处理、PID计算和串口通信的简化框架。
5.1 环境安装
# 创建虚拟环境(可选) python -m venv venv source venv/bin/activate # Linux/Mac # venv\Scripts\activate # Windows # 安装核心库 pip install opencv-python pip install pyserial pip install numpy5.2 核心代码框架
import cv2 import numpy as np import serial import time import threading class BallBalancingSystem: def __init__(self, com_port='COM3', baudrate=115200): # 1. 初始化串口通信 try: self.ser = serial.Serial(com_port, baudrate, timeout=1) print(f"串口 {com_port} 打开成功") except Exception as e: print(f"串口打开失败: {e}") self.ser = None # 2. PID 控制器参数 (需要实际调试) self.kp_x, self.ki_x, self.kd_x = 1.2, 0.05, 0.3 self.kp_y, self.ki_y, self.kd_y = 1.2, 0.05, 0.3 # PID 状态变量 self.last_error_x, self.integral_x = 0, 0 self.last_error_y, self.integral_y = 0, 0 # 3. 图像处理参数 self.cap = cv2.VideoCapture(0) # 打开摄像头 self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) # 小球的颜色范围 (HSV空间,根据小球颜色调整) self.lower_color = np.array([0, 100, 100]) self.upper_color = np.array([10, 255, 255]) # 4. 目标轨迹 self.target_x, self.target_y = 320, 240 # 初始目标为图像中心 self.trajectory = self.generate_circle_trajectory(center=(320,240), radius=100, points=50) # 5. 控制周期 self.control_interval = 0.05 # 50ms self.running = True def generate_circle_trajectory(self, center, radius, points): """生成圆形轨迹点""" trajectory = [] for i in range(points): angle = 2 * np.pi * i / points x = int(center[0] + radius * np.cos(angle)) y = int(center[1] + radius * np.sin(angle)) trajectory.append((x, y)) return trajectory def detect_ball(self, frame): """识别小球并返回其中心像素坐标""" # 转换到HSV颜色空间 hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) # 根据颜色阈值创建掩膜 mask = cv2.inRange(hsv, self.lower_color, self.upper_color) # 形态学操作去除噪声 kernel = np.ones((5,5), np.uint8) mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel) mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel) # 查找轮廓 contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) if contours: # 假设面积最大的轮廓是小球 largest_contour = max(contours, key=cv2.contourArea) if cv2.contourArea(largest_contour) > 100: # 面积阈值,过滤噪声 (x, y), radius = cv2.minEnclosingCircle(largest_contour) center = (int(x), int(y)) radius = int(radius) # 在图像上画出来 cv2.circle(frame, center, radius, (0, 255, 0), 2) cv2.circle(frame, center, 2, (0, 0, 255), 3) return center, frame # 未找到小球 return None, frame def pid_control(self, current_pos, target_pos): """计算PID控制量""" if current_pos is None: return 0, 0 # 未检测到小球,停止输出 curr_x, curr_y = current_pos tar_x, tar_y = target_pos # X轴PID error_x = tar_x - curr_x self.integral_x += error_x derivative_x = error_x - self.last_error_x output_x = self.kp_x * error_x + self.ki_x * self.integral_x + self.kd_x * derivative_x self.last_error_x = error_x # Y轴PID error_y = tar_y - curr_y self.integral_y += error_y derivative_y = error_y - self.last_error_y output_y = self.kp_y * error_y + self.ki_y * self.integral_y + self.kd_y * derivative_y self.last_error_y = error_y # 输出限幅 (例如,映射到PWM的0-1000范围) output_x = np.clip(output_x, -500, 500) output_y = np.clip(output_y, -500, 500) return int(output_x), int(output_y) def send_control_command(self, output_x, output_y): """通过串口发送控制指令给下位机""" if self.ser and self.ser.is_open: # 自定义协议示例: `$X[output_x]Y[output_y];` command = f"$X{output_x:04d}Y{output_y:04d};\n" self.ser.write(command.encode('ascii')) def control_loop(self): """主控制循环""" traj_index = 0 last_control_time = time.time() while self.running: ret, frame = self.cap.read() if not ret: break # 1. 检测小球 ball_center, processed_frame = self.detect_ball(frame) # 2. 更新目标点 (按轨迹) self.target_x, self.target_y = self.trajectory[traj_index] traj_index = (traj_index + 1) % len(self.trajectory) cv2.circle(processed_frame, (self.target_x, self.target_y), 5, (255, 0, 0), -1) # 3. 按固定周期进行PID计算和控制 current_time = time.time() if current_time - last_control_time >= self.control_interval: output_x, output_y = self.pid_control(ball_center, (self.target_x, self.target_y)) self.send_control_command(output_x, output_y) last_control_time = current_time # 在图像上显示控制量 cv2.putText(processed_frame, f'Ctrl X:{output_x}, Y:{output_y}', (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255,255,255), 2) # 4. 显示图像 cv2.imshow('Ball Balancing Control', processed_frame) if cv2.waitKey(1) & 0xFF == ord('q'): self.running = False break self.cap.release() cv2.destroyAllWindows() if self.ser: self.ser.close() if __name__ == "__main__": system = BallBalancingSystem(com_port='COM3') # 修改为你的串口号 system.control_loop()6. 下位机程序要点(STM32 + 舵机)
下位机主要负责接收指令并驱动电机。以下是基于STM32和标准库的PWM驱动舵机核心逻辑(使用定时器TIM1的通道1和通道2)。
// 1. 串口接收与解析 (使用串口1,中断方式) // 假设指令格式为: "$X+0123Y-0456;" (X轴控制量+123, Y轴控制量-456) char rx_buffer[64]; uint8_t rx_index = 0; int16_t target_pwm_x = 0, target_pwm_y = 0; // 接收到的目标PWM值 void USART1_IRQHandler(void) { if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) { char ch = USART_ReceiveData(USART1); if(ch == '$') { rx_index = 0; // 开始接收新帧 } rx_buffer[rx_index++] = ch; if(ch == ';') { // 帧尾 rx_buffer[rx_index] = '\0'; parse_command(rx_buffer); // 解析指令 rx_index = 0; } if(rx_index >= sizeof(rx_buffer)-1) rx_index = 0; // 防止溢出 } } void parse_command(char* cmd) { // 简单解析示例,实际应用需要更健壮的校验 if(sscanf(cmd, "$X%hdY%hd;", &target_pwm_x, &target_pwm_y) == 2) { // 解析成功,更新PWM update_pwm_output(); } } // 2. PWM输出更新 (舵机控制) // 舵机控制PWM周期通常为20ms,高电平宽度0.5ms-2.5ms对应角度0-180度 // 假设定时器时钟72MHz,预分频72-1,则计数器频率为1MHz,周期20000对应20ms。 // 计数值500对应0.5ms,2500对应2.5ms。 #define PWM_PERIOD 20000 #define PWM_MIN 500 #define PWM_MAX 2500 #define PWM_MID ((PWM_MIN+PWM_MAX)/2) // 1500, 对应90度(平放) void update_pwm_output(void) { int16_t pwm_x = PWM_MID + target_pwm_x; // 将控制量偏移到中点 int16_t pwm_y = PWM_MID + target_pwm_y; // 限幅保护 if(pwm_x < PWM_MIN) pwm_x = PWM_MIN; if(pwm_x > PWM_MAX) pwm_x = PWM_MAX; if(pwm_y < PWM_MIN) pwm_y = PWM_MIN; if(pwm_y > PWM_MAX) pwm_y = PWM_MAX; // 更新定时器比较寄存器值 TIM_SetCompare1(TIM1, pwm_x); // 通道1控制X轴舵机 TIM_SetCompare2(TIM1, pwm_y); // 通道2控制Y轴舵机 } // 3. 主函数初始化 int main(void) { // 初始化系统时钟、GPIO、定时器、串口... RCC_Configuration(); GPIO_Configuration(); TIM1_PWM_Init(PWM_PERIOD); // 初始化PWM,周期为20000 USART1_Init(115200); // 初始化串口,波特率115200 // 设置舵机初始位置为中间 TIM_SetCompare1(TIM1, PWM_MID); TIM_SetCompare2(TIM1, PWM_MID); delay_ms(1000); // 等待舵机回中 while(1) { // 主循环可以处理其他任务,如读取传感器、状态指示灯等 // 舵机控制由串口中断触发更新 } }7. 系统调试与性能优化
7.1 调试步骤
机械与电气检查:
- 确保平板水平放置时,两个舵机均处于中位(PWM占空比7.5%)。
- 手动倾斜平板,检查小球能否自由滚动,有无卡顿。
- 测量电源电压,确保电机动作时电压稳定,避免因电压跌落导致单片机复位。
通信测试:
- 上下位机分别编写简单的测试程序。上位机发送固定指令,下位机收到后点亮LED或回传数据,确保串口通信畅通,协议解析正确。
视觉识别单独测试:
- 运行上位机程序,用小球在平板上移动,观察OpenCV窗口是否能稳定、准确地识别并标注出小球中心。调整颜色阈值、形态学操作参数,确保在各种光照下都能可靠识别。
开环测试:
- 将PID输出断开,改为手动给定固定或缓慢变化的PWM值。观察平板倾角变化是否符合预期,小球是否向预定方向滚动。这一步验证了“控制指令->电机->平板->小球”这个前向通道是正常的。
单轴闭环调试:
- 固定一个轴(如Y轴)的目标位置,只对X轴进行PID控制。从一个固定点控制小球运动到目标点。先调P,再调I,最后调D。观察响应曲线,目标是在不过冲的前提下尽快到达目标点。
双轴闭环联调:
- 两个轴同时投入控制。由于可能存在耦合(X轴倾斜会影响Y轴方向的小球位置),可能需要微调参数。从简单的定点控制开始,然后测试直线运动,最后测试圆形等复杂轨迹。
7.2 性能优化技巧
- 图像处理加速:
- 降低分辨率:将摄像头分辨率从1080p降至480p或更低,大幅减少处理数据量。
- 设定ROI:只对平板所在的图像区域进行处理。
- 优化算法:使用更快的颜色空间(如YCrCb),或利用背景固定这一特点,使用背景差分法代替颜色分割。
- 控制算法增强:
- 积分抗饱和:当误差持续较大时,限制积分项的增长,防止系统“反应迟钝”。
- 微分先行:只对测量值微分,而不对误差微分,可以减少设定值突变引起的输出冲击。
- 变参数PID:根据误差大小采用不同的PID参数,大误差时用大P快速响应,小误差时用小P和加入I、D提高精度和稳定性。
- 系统抗干扰:
- 软件滤波:对摄像头识别到的小球坐标进行滑动平均滤波或卡尔曼滤波,平滑数据,减少抖动。
- 机械减震:在电机与平板连接处增加橡胶垫,吸收振动。
- 电源隔离:电机驱动电源与单片机控制电源分开,避免干扰。
8. 常见问题与排查方法
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 小球检测不稳定,时有时无 | 1. 环境光变化大 2. 颜色阈值设置不当 3. 小球反光或与背景颜色相近 | 1. 查看原始图像和掩膜图像。 2. 使用颜色选取工具获取精确的HSV范围。 3. 尝试背景差分法。 | 1. 增加恒定光源。 2. 动态调整阈值或使用自适应阈值。 3. 更换小球颜色或平板背景。 |
| 小球能检测,但控制时剧烈振荡 | 1. PID参数Kp过大2. 控制周期太短 3. 机械结构松动,有回程间隙 | 1. 观察误差曲线,看是否等幅振荡。 2. 检查控制周期是否远小于系统机械响应时间。 3. 手动晃动机械结构,检查是否有明显晃动。 | 1. 大幅降低Kp。2. 适当增加控制周期(如从30ms增至50ms)。 3. 紧固螺丝,消除间隙。 |
| 小球总是向一个方向跑偏,无法稳定 | 1. 平板初始不水平 2. 舵机中位不准 3. PID积分项累积(积分饱和) | 1. 使用水平仪校准平板。 2. 上电初始化时,给舵机发送中位PWM信号并确认。 3. 检查误差和积分项数值。 | 1. 机械调平或软件补偿零点。 2. 重新标定舵机中位PWM值。 3. 加入积分限幅或积分抗饱和算法。 |
| 串口通信时好时坏,控制指令丢失 | 1. 波特率不匹配 2. 接线松动 3. 未处理接收缓冲区溢出 4. 协议无校验,受干扰出错 | 1. 上下位机核对波特率。 2. 检查TX、RX、GND连接。 3. 在中断服务程序中及时读取数据。 4. 发送测试数据,用串口助手监控。 | 1. 统一波特率(常用115200)。 2. 重新焊接或插紧。 3. 优化接收中断程序。 4. 在协议中加入校验和(如CRC8)。 |
| 上位机画面卡顿,控制延迟大 | 1. 图像处理耗时过长 2. OpenCV的 imshow函数本身较慢3. 未使用多线程 | 1. 打印每个处理步骤的时间。 2. 注释掉 imshow看帧率是否提升。 | 1. 采用前述的图像处理加速方法。 2. 降低显示帧率,或仅在调试时显示。 3. 将图像采集、处理、控制、显示分到不同线程。 |
| 电机动作时单片机复位 | 1. 电机反向电动势干扰 2. 电源功率不足,电压被拉低 | 1. 观察复位时电源电压波形。 2. 测量电机工作电流。 | 1. 在电机两端并联续流二极管。 2. 使用更大功率的电源,控制部分与电机部分电源加磁珠或电感隔离。 |
9. 最佳实践与备赛建议
- 模块化开发与测试:将系统清晰地划分为“视觉模块”、“通信模块”、“控制模块”、“驱动模块”。每个模块独立编写和测试,确保无误后再联调。
- 重视机械结构:“三分算法,七分机械”。在机械设计上多花时间,确保平整、稳固、低摩擦、无间隙。这是好成绩的基础。
- 参数保存与加载:将调试好的PID参数、摄像头标定参数、舵机中位值等保存在单片机的Flash或上位机配置文件中,避免每次上电重新调整。
- 设计人机交互:在上位机设计简单的UI,可以实时显示小球位置、目标位置、误差曲线、PID输出,并能动态调整PID参数和切换轨迹。这极大方便调试和演示。
- 准备备用方案:视觉方案受光照影响大,可以准备一套基于MPU6050等传感器的倾角测量备用方案。当摄像头受到强光干扰时,可以切换至传感器融合模式。
- 注意演示效果:电赛最终要评审和演示。确保系统外观整洁,线缆收纳好。可以设计一些有观赏性的轨迹,如画圆、走“2017”数字、追踪移动目标等。
- 文档与代码注释:从开始就写好代码注释,记录关键参数的调整过程和原因。整理好硬件接线图、软件流程图。这在撰写设计报告时能节省大量时间。
“滚球控制系统”是一个完美的控制理论实践项目,它把课本上的方框图变成了看得见、摸得着的物理运动。成功实现它的关键,不在于用了多高级的算法,而在于对每个环节(机械、电气、视觉、控制)细节的耐心打磨和扎实的调试。建议从最简单的定点稳定开始,逐步增加功能,每步都确保稳定,最终的系统才会可靠。把这个项目吃透,你对闭环控制、嵌入式系统和软硬件协同开发的理解会上一个大台阶。