2019年电赛H题电磁炮实录
- 前言
- 硬件
- 电磁炮部分
- 控制部分
- 理论模型
- 整体结构
- 软件
- 交互操作
- OpenMV
- STM32
- 相关资料
前言
你好学弟!这里是学长向你展示他们在2019年夏天搭出来的破烂是如何运行的。老师让我们把这堆破烂留给你们(有些零件缺了你们要自己买 -_-),这边怕你们不懂,完全自愿的给你们写了一篇教程,规避一些我们走过的弯路,也希望你们能把这份精神传承下去,替下下届的接班人们开山铺路。话不多说,先上个实物图。
硬件
我们搭的这个玩意主要分为两个部分,电磁炮电路部分和控制电路部分,还有一块电源部分,当然其实测试现场是提供学生电源的,我们纯粹是吃饱了撑的。
- 电磁炮部分:包括两个开关(一个充电,一个放电),一个高压电容,一个升压模块,一个线圈与炮管。
- 控制部分:两个舵机+云台,一块F407(带触控屏),两个继电器,一块OpenMV,一个超声波模块
电磁炮部分
我们基本是照着这个电路搭建的:
原理很简单,主要就是控制一个电容的充放电,电磁炮用的是450V/1000uF的高压电容,所以需要用一个升压模块,充电开关就接在电池与升压模块之间。至于放电,把线圈接在电容两边就行了,但是放电开关不能直接接回路里(会烧的),所以加了一个可控硅,也就是一个开关管,当G置高的时候,K&A端就导通了,最后加了一个续流二极管给线圈放电。
现在说起来很简单,当初可是想破了头。。。
上实物图:
这边贴出详细物料清单:
- 高压绝缘导线:这边一定要用这种线,用的时候截一段两边套热缩管。 淘宝链接
- 开关两个: 就普通开关 淘宝链接
- 航模电池:我买的是3S 2200mah的,额定电压11.1V 淘宝链接
- 可控硅:型号BTA12-600B 淘宝链接
- 升压模块:DC-DC直流升压模块 45-390V可调 淘宝链接
- 电容:450V1000UF 电解电容 淘宝链接
注意事项:
- 线圈:其实比赛最主要的就是绕线圈,因为比赛是规定了管子直径的,所以线圈要自己拿漆包线( ϕ \phi ϕ 0.5~0.8)绕,绕的厚度,长度太短太薄都不行。线圈底部用棍子什么的封住,让炮弹头正好与线圈尾部重合,这样可以获得最长的加速距离。
- 充电开关和放电开关不能同时按,会短路!!还有每次充电之前都要先按下放电开关给电容放电,确保是从头开始充电的!!
- 升压模块:其实这个升压模块还是比较容易坏的,那个保险丝比较容易烧,要多备一些保险丝,模块上面有一个可调电阻可以调节输出电压,一般来讲在相同充电时间下充电电压越高电磁炮平射的距离越远,我们一般调节在100V左右。
- 涉及到电磁炮射出炮弹的距离,有好几个可以调节的量。例如电容的充电时间,充电电压,炮口仰角。其实电容充电时间和充电电压就是决定的炮口初速度大小,根据我们的计算,电容充电在零点几秒之内就能完成,所以主要就是旋那个调节电阻改变充电电压,然后根据经验得到一个合适的出射速度V。当然这边其实可以在炮口加一个光电门测速,这样更精确一些。
- 炮弹:关于炮弹,当初实验室里用各种各样的都有,最基本的是小钢珠,还有小磁铁块,经过验证磁铁块是比小钢珠射的更远一些。不过最后我们选的是无头螺钉,效果也不错。
- 续流二极管:我们是直接焊在线圈两端,方向一定不能弄反,挑大规格的买就行。 淘宝链接
控制部分
理论模型
其实基本模型就是中学的抛体公式:
假设炮弹初速度为V,炮口相对于地面倾角为α,炮口相对于地面的竖直落差为h,重力加速度为g,取g = 9.8N/kg。
将炮弹的速度分解为水平与竖直两个方向上的分速度,分别为Vx=V *Cosα和Vy = V * Sinα。此时炮弹落地所需时间为:
t = − V y + ( V y 2 + 2 g h ) g t= \frac{-V_y+\sqrt {(V_y^2+2gh)}}{g} t=g−Vy+(Vy2+2gh)
炮弹在水平方向的距离方程为:
s = V ∗ c o s α ∗ t s =V *cosα * t s=V∗cosα∗t
炮弹在竖直方向的距离方程为:
y = V s i n α − 1 2 g t 2 y=V sinα-\frac{1}{2} gt^2 y=Vsinα−21gt2
由此可得出射角度α与距离s的关系式:
− h = s ∗ t a n α − 1 2 g ( s V c o s α ) 2 ( ∗ ) -h=s*tanα- \frac{1}{2} g(\frac{s}{V cosα })^2(*) −h=s∗tanα−21g(Vcosαs)2(∗)
当时我们通过调节升压模块上的可调电阻得到一个确定的电压(90~100V左右)与炮口初速,然后通过调节仰角α来改变距离s。
这边有两种思路:
- 一种是通过纯数学公式模拟整个模型,得出α(x)与s(fx)的关系式,进而确定每次的仰角。用matlab数值模拟将( ∗ * ∗)式解出,V取平射能达到两米时的初速,h由题目给定。
- 另一种是直接枚举出所有距离下对应的炮口仰角,然后存在程序里调用。这种方式精确度很高,但需要大量的测试,一遍遍的调整角度然后记录距离。在比赛过程中如果时间充裕的话一般都是用枚举,老师可不会关心你用了什么先进的算法模型,而是只关心结果!!
整体结构
控制系统的总体框图如下:
- 主控模块:选用一块STM32F407带一块触控屏作为交互
- 执行模块:用了两个舵机和一个双轴云台架
- 测距模块:测距与识别模块用到的是openMV和超声波,openMV用于识别红色目标靶,超声波用于测量与靶子的距离。
- 继电器模块:通过两路继电器模块控制充电与放电开关,这里需要选导通电压12V的
- 电源模块:选用充电宝+USB集线器给OpenMV与F407供电,一块额定电压11V的电池输出两路分别给继电器供电以及到一块降压模块中转成舵机使用的7V
- 降压模块的特写:输入12.31V,输出6.63V,按下on/off开启输出,开启输出后舵机不能再用手硬拨了!
器件清单:
- STM32F407:由于这边用的是正点原子的例程,需要能兼容正点原子屏的开发板 淘宝链接
- 舵机云台:U型梁支架x2,金属舵盘x2,杯式轴承x2,脚板支架x1,长U支架x1,短U支架x1,多功能支架x2 淘宝链接 淘宝链接
- OpenMV与超声波:壳与镜头要另买 淘宝链接 淘宝链接
- 电源模块:淘宝链接 淘宝链接 淘宝链接 淘宝链接
软件
代码分为OpenMV部分和STM32部分,OpenMV用于进行色块识别,STM32用于主控以及交互。这边先讲解如何交互,然后再简略讲解一下代码结构。整个工程我也会放上来。
交互操作
所有操作都在这个正点原子电阻屏上完成。一开始进入时需要进行屏幕校准,一般情况下屏幕校准只需要进行一次即可,结果会存到Flash里。这边有一个我们一直没能解决的BUG,开发板上默认是不带24C02的,所以我们焊了一个上去,然而这个24C02在代码里始终无法初始化成功,不知道怎么回事。
进入程序界面:可选择操作模式,其中模式一用于指定角度与距离的射击,模式二用于识别红色标靶与距离并进行射击,模式三用于摆动射击。下方为调试信息。
模式一:上方两排分别对应百十个位的加减;Swtch用于切换设置距离或角度;Negti用于设置角度的正负,以炮口正对向前为0度;Start键开始射击;Retrn返回,有时候不太灵,需要重启一下。
模式二:有两个Start,分别对应摄像头测距与超声波测距,点左边那个。红色靶就按题目中给定的做。
模式三:就一个Start,点击过后会开始从-20°到+20°开始搜寻标靶,当搜寻到时会立即射出炮弹。
OpenMV
安装OpenMV IDE以及快速入门:参考链接
#提示包未安装的自己找教程安装一下-_-
import sensor, image, time, math
from pyb import UART
import json
import ustructsensor.reset()
sensor.set_framesize(sensor.QVGA)
sensor.set_pixformat(sensor.RGB565)
sensor.skip_frames(time = 2000)
#sensor.set_auto_gain(False) # must be turned off for color tracking
#sensor.set_auto_whitebal(False) # must be turned off for color tracking
#sensor.set_auto_exposure(False,2000); #控制曝光时间,单位为us
sensor.set_hmirror(True); #控制水平镜像翻转
sensor.set_vflip(True); #控制水平镜像翻转
#sensor.set_windowing((22,7,110,105));clock = time.clock()
uart = UART(3,115200) #定义串口3变量 P4 TX<-->PA10 P5 RX<-->PA9
uart.init(115200, bits=8, parity=None, stop=1) # init with given parameters#binary_threshold = (0, 156)
find_threshold = (51, 77, 34, 67, -33, 50)
K = 12800; #自己选取一个合适的校准值
def find_max(blobs): #定义寻找色块面积最大的函数blobs.sort(key=lambda x:x.pixels(),reverse=True);max_blob={} #默认为空字典length=len(blobs);if length>0:max_blob=blobs[0];return max_blob;def sending_data(cx_max,cy_max):global uart;#frame=[0x2C,18,cx%0xff,int(cx/0xff),cy%0xff,int(cy/0xff),0x5B];#data = bytearray(frame)data = ustruct.pack("<bbhhb", #格式为俩个字符俩个短整型(2字节)0x2C, #帧头10x12, #帧头2int(cx_max), # up sample by 4 #数据1int(cy_max), # up sample by 4 #数据2LCD_ShowStringLCD_ShowString0x5B);uart.write(data); #必须要传入一个字节数组while(True):clock.tick()img = sensor.snapshot()#.lens_corr(1.45);#img.binary([binary_threshold], invert = 1)blobs = img.find_blobs([find_threshold],area_threshold=150)if blobs: #如果找到的话max_blob=find_max(blobs)img.draw_rectangle(max_blob.rect(),color=(0,0,255))img.draw_cross(max_blob.cx(), max_blob.cy(),color=(0,0,255))img.draw_cross(160, 120,color=(0,0,255)) # 在中心点画标记img.draw_line((160,120,max_blob.cx(),max_blob.cy()), color=(0,0,255));phi = (max_blob.w() + max_blob.h())/2;length = K/phi; #获得距标靶距离#print('position:',max_blob.cx(),max_blob.cy())if(max_blob.pixels()>1000): #滤除像素过小的干扰sending_data(max_blob.cx(),length); #发送点位坐标#print(max_blob.pixels())print("Length=",length);else:sending_data(10000,10000);#print(clock.fps())
STM32
开发环境:Keil v5以及相应的F4XX器件包,Keil v4不知道会不会报错。
//这里就简要的说明一下各个文件的内容
//具体工程我放到百度云里了/*
main.c:
包含所有的变量定义,所有的初始化,
主循环while(1)用于模式切换以及数据通信
主要控制逻辑中断函数void TIM3_IRQHandler(void)频率50Hz,用于处理各个模式下的具体执行usart2.c:
用于处理与OpenMV的通信timer.c:
定时器3的初始化pwm.c:
定时器14,11,4的初始化Control.c:
对串口2接收的数据进行处理
对舵机的PID控制函数Interface.c:
各个模式交互界面的编写jidianqi.c:
继电器的初始化US100.c:
超声波的所有相关24cxx.c
ctiic.c
myiic.c
touch.c
ft5206.c
gt9147.c
ott2001.c
*/
相关资料
工程链接:
百度云:https://pan.baidu.com/s/1Z5I1Z38kK1VSa_iRjeXQYQ 提取码:wbrp
CSDN:https://download.csdn.net/download/qq_43243338/11938761
模块资料:
找淘宝店家要…