【E1221】2017年全国大学生电子设计竞赛-板球(滚球)控制系统源代码

2021-09-17 11:57:48      索炜达电子      767     

项目编号:E1221

文件大小:5M

源码说明:带中文注释

开发环境:C编译器

简要概述:

一、选材

     1、主控。经过本人血泪实践,证明战舰加7670做图像处理还是很吃力,这次毕设直接买了阿波罗,主频216M,性能杠杠滴。用了的人都说好。如果改其他摄像头战舰也是完全可以胜任的,摄像头见下面!

     2、摄像头。采用7670,读取图像费时,处理也头疼。比赛要求不准使用学习板(毕设的话没那么严格就用了阿波罗学习板)。另外摄像头用杜邦线引出来通信极其容易异常,就算采用了原子哥的摄像头延长线干扰也是十分严重。综合来看原子的摄像头不太适合做板球(个人意见,如果不恰当还请大佬指点)。直到比赛最后一天,我同学发现了一款摄像头openmv,据某宝卖家所说2017电赛专用,输出帧率每秒可以达到85帧。本人顿时两眼放光,但是又到了最后一天了,那种心情就像你看着希望在你面前哼着小曲慢慢悠悠的走了,你抓都抓不到~

    言归正传,opemmv3采用STM32F7作为处理核心,时钟频率可以达到216M。搭载7725,输出帧率可以高达85帧。此外外部提供python接口,只要几行代码即可完成一个项目,最后提供多种例程,如找小球,找色块,循迹,光流等等都不在话下。这次毕设也是直接花四百多大洋入手一款,确实非常好用,具体链接各位某宝搜索啦!如果各位想用电阻屏检测小球位置倒也是可以的,题目要求检测小球运动方式不限,不过那样感觉就不是正宗的板球了。

     3、电机。在比赛之前网上广为流传的一个预测帖子说今年很有可能出板球控制系统,这点倒是预测的很准,但是,帖子推荐了一款电机-直流推杆电机-确是坑了一大批人,单价贵不说还没有反馈。PS:我也是被坑的一位(关键是组委会推荐元器件清单也是直线电机,欲哭无泪啊!)。赛前买的老板卖断货,下了的单都能接到老板打电话过来道歉说没货了,请退掉订单……在尝试之后,最终决定采用舵机,舵机一个周期为20ms,控制简单,响应快。这次毕设我采用的是MG946R金属齿轮舵机,不要用塑料的,金属的扭力大,稳定。

     4、球,球是很关键的一个,乒乓球太轻而且重心不在球心。比赛的时候找不到合适的球只能采用类似樟脑丸的一种用来驱虫的一种木球(对,没错就是这么心塞,还是管我们班女生要的……。现在毕设易老师买了钢球,相比之下钢球确实是最合适的选择,几乎不受风的影响。但是钢球难以上色,采用摄像头的色块捕捉就不行了,只能采用灰度捕捉。灰度捕捉这样发挥部分第四问就不知道怎么发挥了,假如采用色块捕捉的话,发挥部分第四问可以采用激光引导,指哪去哪。我采用普通钢珠,发挥部分想让它画个圆,但是效果很差,所以就没做。某宝上只有一个卖彩色的钢球的,但是不零售,那我只能呵呵了。

     5、平板,建议大家采用亚克力板,轻薄平整,玻璃的话太厚。亚克力板有无色跟白色可选,看个人喜好了,我是拿透明的亚克力板,一面撕去纸,一面将纸张用墨水染黑,这样调整一下灰度的阈值就能追踪小球了。

二、机械结构


      机械机构很重要!机械结构很重要!机械结构很重要!多少队伍都是在机械结构上止步国奖。首先机械结构一定要稳,其次也要灵活。下面附上一张我们当时采用的机械机构。注意下面的结构是错误的!(图片来自网络)

【E1221】2017年全国大学生电子设计竞赛-板球(滚球)控制系统源代码

       这种结构中间采用万向节是正确的,比赛的时候我是这样用,毕设现在也是这样用。我要强调的是两边舵机上面跟板连接的结构,这样连接板在这个方向上只能有一个活动方向,两个舵机只要运动板一定变形。如果换成三个万向节链接三个就没问题了。但是万向节跟亚克力之间不好连接,在易老师的指点帮助下,两边我采用了下面图的连接。

【E1221】2017年全国大学生电子设计竞赛-板球(滚球)控制系统源代码

       活动部分钻一个空插上铁丝或者加一个螺丝就行,固定部分切割亚克力板用亚克力胶水粘就行,亚克力胶水的粘度是可以的,很给力。这样板在上下运动的时候板子就不回变形了。我的板子面积是不符合题目要求的,表面也没有按照题目要求画圆圈,这点主要是考虑到体积小容易挪动,大板没什么意思的,除了占地方!


三、软件部分


       软件部分就是两个PID参数校正,这里是随动系统,基本上用不到微分量。PD也是非常容易找。恐怕这是这几年电赛最简单的控制题了吧。只要大家能把球定在中间,基础部分全部可以完成,只要改一下设定位置就行,发挥部分1、3也是没什么问题。就发挥部分第二问有点意思,下面详细说一下发挥部分第二问。第二问就是一个自动选路算法。我的思路是在题目要求的九个区域之外再加上四个缓冲区域就可以完成任务。

【E1221】2017年全国大学生电子设计竞赛-板球(滚球)控制系统源代码

       如上图,四个缓冲区域如上图B1、B2、B3、B4,当进入到这个区域的时候,主函数扫描红外信号,收到数字几就将区域几设置为最终区域。然后中断里面判断球是不是到了设定区域周围10个像素范围内,如果是将计次加一,否则i清零。这样我们在主函数中可以不断查询这个全局变量,我们设定当累积次数达到50次后认为球已经到达了目标区域。

下面是判断是否到达设定位置部分代码

[mw_shl_code=c,true]                //以下语句用来统计当前球的位置是不是连续都在目标区域内部,如果有一次不进入

                //那么计次清零,当球在目标区域正负5个像素点的时候,全局变量计次增加。外部程序可以

                //通过读取这个全局变量判断是不是达到一定值来判断是不是进入到了目标区域。

                if((abs(rol_curpos - rol_setpos) <= 10)&&(abs(pit_curpos - pit_setpos) <= 10))

                {

                        PASSBY_TIME++;

                }

                else

                {

                        PASSBY_TIME = 0;

                }        

                [/mw_shl_code]

      接下来判断最终位置跟目前所处的位置是不是在一定像素点以内,如果是那就直接滚过去,不会跑到其他区域内部(这里是像素单位,图示见上面坐标点。大家可以根据自己实际情况自己决定)。如果不是的话,判断当前是不是处在缓冲区域,如果是,进入下一个缓冲区域再去判断最终位置与当前位置的位置范围。如果不是,那么遍历所有缓冲区域,进入最近的哪一个。视频中我也是只是演示了一下基础部分1、2,然后就是发挥部分2了。我的算法对当前处在所有区域都是通用的,就算从最远区域,比如说区域1到区域9也不会超过10秒;题目要求40秒内由A到D,也就是选三次路,单次选路不超过10秒,三次绝对不会超过四十秒。可以说是完全可以满足要求,详细见下面流程图。最后备注一点,判断当前区域判断的是设定区域跟目标区域的差值,不建议用当前位置跟目标位置比较,因为在测试的时候,我用当前位置去比较,得到效果很差,经常卡在缓冲区域出不来。(算法见下面模式六代码,流程图如下)

【E1221】2017年全国大学生电子设计竞赛-板球(滚球)控制系统源代码

下面是模式六的代码

[mw_shl_code=c,true]//模式六

void mode_6(void)

{

        u8 i;

        u8 rol_set_pos_temp = 0,pit_set_pos_temp = 0;        //存放最近的缓冲区域的坐标。

        int distance,min_distance = 120;        

        if(PASSBY_TIME >= 50)                                                                                                                //稳定到了一个新位置

        {

                if((abs(rol_setpos - FINAL_ROL_POS)<=30)&&(abs(pit_setpos - FINAL_PIT_POS)<=30))

                {

                        //最终位置与当前位置之间R与P方向上像素差值都小于30个像素值,那么球可以直接滚动过去。

                        rol_setpos = FINAL_ROL_POS;

                        pit_setpos = FINAL_PIT_POS;

                        PASSBY_TIME = 0;                                                                                                                //更新目标位置,清零经过次数

                        return;                                                                                                                                                        //距离目标位置近,可以直接滚过去,返回。

                }

                else if((rol_setpos == 40)&&(pit_setpos == 40))//当前小球处在一号缓冲区域里

                {

                        rol_setpos = ROL_BUFF[1];

                        pit_setpos = PIT_BUFF[1];

                        PASSBY_TIME = 0;                                                                                                                //控制小球进入2号缓冲区域

                }

                else if((rol_setpos == 70)&&(pit_setpos == 40))

                {

                        rol_setpos = ROL_BUFF[2];

                        pit_setpos = PIT_BUFF[2];

                        PASSBY_TIME = 0;                                                                                                                //控制小球进入3号缓冲区域

                }

                else if((rol_setpos == 70)&&(pit_setpos == 70))

                {

                        rol_setpos = ROL_BUFF[3];

                        pit_setpos = PIT_BUFF[3];

                        PASSBY_TIME = 0;                                                                                                                //控制小球进入4号缓冲区域

                }

                else if((rol_setpos == 40)&&(pit_setpos == 70))

                {

                        rol_setpos = ROL_BUFF[0];

                        pit_setpos = PIT_BUFF[0];

                        PASSBY_TIME = 0;                                                                                                                //控制小球进入1号缓冲区域

                }

                else                                                                                                                                                                        //小球不在任何缓冲区域

                {

                        for(i = 0;i <= 3;i++)                                                                                                //遍历所有的缓冲区域,寻找到最近的缓冲区域。

                        {

                                distance = abs((int)(rol_setpos - ROL_BUFF))

                                                                 + abs((int)(pit_setpos - PIT_BUFF));

                                if(distance < min_distance)

                                {

                                        min_distance = distance;

                                        rol_set_pos_temp = ROL_BUFF;

                                        pit_set_pos_temp = PIT_BUFF;

                                        k = i;

                                }

                        }

                        rol_setpos = rol_set_pos_temp;

                        pit_setpos = pit_set_pos_temp;

                        PASSBY_TIME = 0;

                }

                }        }  

[/mw_shl_code]

     至于发挥部分第四问,我才用的是灰度范围追踪,这样激光引导就没法做了。能做的也就是球画圆圈了,这点倒和风力摆有点像。我想让球画圆,但是机械结构还是粗糙了点,基本上画不了所以就没再继续改进了,大家参考我的风力摆帖子吧!


四、其他


     其他一些就是jasson方面的了,在串口读取摄像头输出的时候,用串口助手观察收到的数据,如果摄像头找到了多个目标那么这些坐标的位置都会传输过去,外部用大括号与小括号。每帧数据长度不一致,所以我在原代码的基础上比较了一下找到的灰度块,只发送面积最大的一个。此外jasson数据串如果找打的色块中心坐标是一位数或者三位数的话,那只会发一个字节的数字,这样串口看到收到数据长度就变了。jasson 解码我没搞懂怎么用,只是把它的keil编解码库下载了过来,具体在附件里。变通一下我设置了感兴趣区域就是10-99,这样保证输出的数据长度都是一致的。各位大佬懂得jasson编解码怎么用还希望分享一下经验哈!


结语


       经过小编上面的分析想必这道题对大家已经没有难度了吧?希望准备电赛的小伙伴都能取得理想的成绩。如果有小伙伴想拿这道题练习整定PID参数,个人不建议这样做。这个题目的PID参数非常容易整定,各位只要稍微懂的各个参数的含义就能整定出来。想要磨练自己整定PID参数的能力建议做一下风力摆(15年国赛题)、倒立摆(13年国赛题)、平板摆(11年国赛题)。这几个题目都出的很不错!17年题目要求板子的面积对于板球系统来说还是太大了,哪有这么大的板球系统,一般30×30就差不多了,这么大的板子摄像头也要架的老高老高了。但是出题者为了降低难度故意要求的,毕竟海阔任鱼跃,天高任鸟飞!

      最后衷心感谢易家傅老师这么长时间以来对我的指点与帮助,感谢原子哥开发的学习平台及详细的例程代码,同时也感谢在这么长时间来给予我帮助的老师、同学们,谢谢你们这么久以来的帮助跟支持。

      本人水平有限,如有错误还欢迎各位路过的大佬指正!

目录│文件列表:

 └ 板球(滚球)控制系统源代码

    │ Keil.Jansson.1.0.0.pack

    │ main.py

    │ 板球系统图示.jpg

    └ 毕设板球控制系统

       │ keilkill.bat

       │ readme.txt

       ├ HARDWARE

       │  ├ 24CXX

       │  │  │ 24cxx.c

       │  │  │ 24cxx.h

       │  │  │ myiic.c

       │  │  └ myiic.h

       │  ├ ADC

       │  │  │ adc.c

       │  │  └ adc.h

       │  ├ AP3216C

       │  │  │ ap3216c.c

       │  │  └ ap3216c.h

       │  ├ CAN

       │  │  │ can.c

       │  │  └ can.h

       │  ├ DAC

       │  │  │ dac.c

       │  │  └ dac.h

       │  ├ DMA

       │  │  │ dma.c

       │  │  └ dma.h

       │  ├ EXTI

       │  │  │ exti.c

       │  │  └ exti.h

       │  ├ KEY

       │  │  │ key.c

       │  │  └ key.h

       │  ├ LCD

       │  │  │ font.h

       │  │  │ lcd.c

       │  │  │ lcd.h

       │  │  │ ltdc.c

       │  │  └ ltdc.h

       │  ├ LED

       │  │  │ led.c

       │  │  └ led.h

       │  ├ MOTOR

       │  │  │ motor.c

       │  │  └ motor.h

       │  ├ MPU

       │  │  │ mpu.c

       │  │  └ mpu.h

       │  ├ OLED

       │  │  │ oled.c

       │  │  │ oled.h

       │  │  └ oledfont.h

       │  ├ PCF8574

       │  │  │ pcf8574.c

       │  │  └ pcf8574.h

       │  ├ PID

       │  │  │ pid.c

       │  │  └ pid.h

       │  ├ QSPI

       │  │  │ qspi.c

       │  │  └ qspi.h

       │  ├ REMOTE

TAG板球
  • 3 次
  • 5 分