2025年电赛E题简易自行瞄准装置(基础发挥全完成)
1 系统功能介绍
本系统为“2025年电赛E题简易自行瞄准装置基础”的设计与实现方案,核心目标是在移动小车平台上完成自动寻迹行驶、激光瞄准与击靶任务,并在部分扩展任务中实现激光光斑沿指定轨迹运动的功能。系统以天猛星 MSPM0G3507 开发板为主控,结合二位云台步进电机驱动模块、5 路灰度传感器、OLED 显示屏、AT8236 双路直流电机驱动模块、编码器测速模块、OpenMV 摄像头控制模块以及激光笔控制电路等,构成了完整的运动控制与自动瞄准系统。
在功能实现上,系统满足以下要求:
2 系统电路设计
系统电路设计采用模块化思想,将运动驱动、位置检测、视觉处理、显示、通信与激光控制等部分分离设计,既方便调试,也便于后期功能扩展。
2.1 主控模块——天猛星 MSPM0G3507 开发板
MSPM0G3507 是德州仪器推出的高性能 ARM Cortex-M0+ 内核单片机,主频高、功耗低、外设资源丰富,能够同时处理运动控制与图像数据通信任务。本系统中,主控板主要功能包括:
- 接收 5 路灰度传感器数据,实现循迹控制算法;
- 控制 AT8236 驱动模块,实现直流电机正反转与调速;
- 读取编码器反馈信号,进行速度闭环与位移计算;
- 与 OpenMV 摄像头通过 UART 通信,接收目标偏差信息;
- 控制步进电机驱动模块,实现云台二维旋转定位;
- 控制继电器实现激光笔通断。
2.2 灰度传感器模块
灰度传感器由 5 路 TCRT5000 红外反射式光电开关组成,依次排列在小车前端,负责检测赛道黑白反差。接线关系如下(从左到右):
- VCC → 5V
- GND → GND
- PA30 → L2
- PA28 → L1
- PA1 → M0
- PA0 → R1
- PA25 → R2
该布局可实现对赛道中心线的偏离量判断,通过 PID 算法动态调整电机输出,保证小车平稳循迹。
2.3 OLED 显示模块
OLED 屏幕用于显示小车运行状态、圈数设定、速度信息等。接线方式:
- VCC → 5V 或 3.3V
- GND → GND
- SCL → PB9
- SDA → PB8
OLED 模块采用 I2C 通信方式,显示刷新快、功耗低,适合在比赛中实时提供状态信息。
2.4 电机驱动模块——AT8236 双路直流驱动
直流驱动模块负责控制小车两侧驱动轮的直流减速电机。接线方式如下:
- PWMA、PWMB → 5V
- PB12 → AIN1
- PB14 → AIN2
- PA7 → BIN1
- PA17 → BIN2
- STBY → 5V
该驱动芯片具备 PWM 调速、方向控制等功能,配合主控 PWM 输出实现平稳加减速与灵活转向。
2.5 编码器测速模块
小车两侧驱动轮各配备一组光电编码器,用于精确测量车轮转速与行驶里程。接线方式:
- PB5 → 左编码器 A 相 E1A
- PA10 → 左编码器 B 相 E1B
- PB4 → 右编码器 A 相 E2A
- PB15 → 右编码器 B 相 E2B
编码器信号输入主控后,结合定时器输入捕获功能,可实现速度闭环控制。
2.6 二位云台步进电机驱动模块
云台结构采用双步进电机驱动,分别实现激光笔的上下和左右方向调整,驱动模块为 D36A 双路步进电机驱动器,供电与直流驱动模块共用电池电源。接线方式:
- P5 → PA21
- P7 → DIR2
- P6 → ST1
- P9 → DIR1
- EN1、EN2 → 步进电机驱动 3.3V
2.7 OpenMV 摄像头模块
OpenMV 主要用于目标检测与偏差计算,将识别到的靶心位置偏差数据发送给主控,用于云台定位控制。摄像头采用独立 5V 稳压模块供电,确保图像处理稳定。
2.8 激光笔继电器控制模块
激光笔由继电器实现通断控制,继电器输入端由摄像头 P3 控制,确保仅在发射阶段开启激光。
3 程序设计
程序设计分为运动控制、视觉识别、云台控制和激光控制四大部分。系统采用模块化编程,每个功能模块独立调试,最终在主程序中统一调度。
3.1 主程序框架
主程序负责初始化各硬件模块,进入主循环后根据任务模式执行对应功能。
int main(void)
{
SYSCFG_DL_init(); //系统资源配置初始化
hard_init(); //硬件配置初始化
OLED_Init(); //显示屏初始化
GPIO_Init(); //蜂鸣器初始化
params_init(); //控制参数初始化
Encoder_Init(); //编码器资源初始化
Button_Init(); //板载按键初始化
timer_irq_config(); //定时器中断配置
usart_irq_config(); //串口中断配置
while(1)
{
TrackControl();
}
}
3.2 PID算法
float pid_control_dt_run(controller *ctrl,float dt)
{
float _dt=0;
get_systime(&ctrl->_time);
_dt=ctrl->_time.period/1000.0f;
if(_dt>1.05f*dt||_dt<0.95f*dt||isnan(_dt)!=0) _dt=dt;
if(_dt<0.0001f) return 0;
/*******偏差计算*********************/
for(uint16_t i=19;i>0;i--)
{
ctrl->error_backup[i]=ctrl->error_backup[i-1];
}
ctrl->error_backup[0]=ctrl->error;
ctrl->last_error=ctrl->error;//保存上次偏差
ctrl->error=ctrl->expect-ctrl->feedback;//期望减去反馈得到偏差
ctrl->dis_error=ctrl->error-ctrl->error_backup[ctrl->dis_error_gap_cnt-1];//微分 ctrl->dis_error=ctrl->error-ctrl->last_error;//原始微分
if(ctrl->error_limit_flag==1)//偏差限幅度标志位
{
if(ctrl->error>= ctrl->error_limit_max) ctrl->error= ctrl->error_limit_max;
if(ctrl->error<=-ctrl->error_limit_max) ctrl->error=-ctrl->error_limit_max;
}
/*******积分计算*********************/
if(ctrl->integral_separate_flag==1)//积分分离标志位
{
//只在偏差比较小的时候引入积分控制
if(ABS(ctrl->error)<=ctrl->integral_separate_limit) ctrl->integral+=ctrl->ki*ctrl->error*_dt;
}
else
{
ctrl->integral+=ctrl->ki*ctrl->error*_dt;
}
/*******积分限幅*********************/
if(ctrl->integral>=ctrl->integral_limit_max) ctrl->integral=ctrl->integral_limit_max;
if(ctrl->integral<=-ctrl->integral_limit_max) ctrl->integral=-ctrl->integral_limit_max;
ctrl->dis_error_lpf=LPButterworth(ctrl->dis_error,&ctrl->lpf_buffer,&ctrl->lpf_params);
/*******总输出计算*********************/
ctrl->last_output=ctrl->output;//输出值递推
ctrl->output=ctrl->kp*ctrl->error//比例
+ctrl->integral//积分
+ctrl->kd*ctrl->dis_error_lpf;//微分
/*******总输出限幅*********************/
if(ctrl->output>= ctrl->output_limit_max) ctrl->output= ctrl->output_limit_max;
if(ctrl->output<=-ctrl->output_limit_max) ctrl->output=-ctrl->output_limit_max;
/*******返回总输出*********************/
return ctrl->output;
}
3.3 OpenMV 查找图像中的矩形框,绘制中心点
# 查找图像中的矩形
for r in img.find_rects(threshold = rect_threshold):
bos = 1
bili = r[2]/r[3]
print("bili==",bili)
if bili >1.5-0.4 and bili <1.5+0.1:
if(r[2] < 110 and r[3] < 70 and r[2] > 13 and r[3] > 10 ):
img.draw_rectangle(r.rect(), color = (255, 0, 0)) # 绘制矩形
# ===== 添加画中心点 =====
center_x = int((r.x() + r.x() + r.w()) / 2)
center_y = int((r.y() + r.y() + r.h()) / 2)
img.draw_circle(center_x, center_y, 3, color=(0, 0, 255))
i=0
# 获取并绘制四个顶点
for p in r.corners():
img.draw_circle(p[0], p[1], 2, color = (0, 255, 0))
points[3-i][0] = p[0]*2
points[3-i][1] = p[1]*2
i = i + 1
r_jiguang_piancha_x = int(jiguang_piancha_x/biaoding_50cm_rect_kuan*r[2])
r_jiguang_piancha_y = int(jiguang_piancha_y/biaoding_50cm_rect_kuan*r[2])
pan_error = center_x - img.width() / 2 - r_jiguang_piancha_x # 水平误差
tilt_error = center_y - img.height() / 2 - r_jiguang_piancha_y # 垂直误差
pan_output = pid_control(pan_error, pan_pid_params, pan_state)
tilt_output = pid_control(tilt_error, tilt_pid_params, tilt_state)
speed_1 = abs(int(pan_output))
speed_2 = abs(int(tilt_output))
direction_1 = "cw"
direction_2 = "cw"
if pan_error > 3 :
flag_x = 0
direction_1 = "cw"
jiguang_pin.low()
elif pan_error < -3 :
flag_x = 0
direction_1 = "ccw"
jiguang_pin.low()
else:
flag_x = 1
if tilt_error > 4 :
direction_2 = "ccw"
flag_y = 0
jiguang_pin.low()
elif tilt_error < -4 :
direction_2 = "cw"
flag_y = 0
jiguang_pin.low()
else:
flag_y = 1
if flag_x and flag_y:
jiguang_pin.high()
motor_control(1, speed_1, direction_1)
motor_control(2, speed_2, direction_2)
print(pan_error," -- ",tilt_error," -- ","Motor1: speed =", speed_1, ", dir =", direction_1," Motor2: speed =", speed_2, ", dir =", direction_2)
else:
bos_num = bos_num + 1
if bos_num >30:
bos_num = 0
bos = 0
4、历年电赛试题解析专栏
- 点击查看:电赛试题解析汇总:https://blog.csdn.net/m0_51061483/category_10443456.html
5、资料下载
- 点击下载资料:https://download.csdn.net/download/m0_51061483/91617547
-
47