TI杯2019年全国电子设计大赛总结

文章目录

  • 准备
  • 选题
  • 准备材料
  • 原理设计及仿真
  • 代码部分
    • 摄像头模块
    • Arduino模块
  • 最后

准备

在比赛开始之前,我们团队对往年的国赛题目进行了分析比较,由于博主是计算机系(之前是电子信息)的小菜鸟,所以会尽量选择控制类题目。于是在此之前我们尝试做出一个往年的国赛题目,我们做的是‘平衡车跷跷板’,这里简单提一下:

TI杯2019年全国电子设计大赛总结_第1张图片

所以在题目下来之前,我们还买了大量有关于控制类的元器件,比如说:各种传感器、小车底座等等(后来发现很多用不上,肉疼!)……

选题

2019年8月7日,题目出来
题目链接

TI杯2019年全国电子设计大赛总结_第2张图片
我的内心是崩溃的~

很显然,对于计算机的我这个小菜鸟来说这很不友好啊——不对!B题的巡线机器人貌似还不错,而且还是控制类,满怀欣喜的打开发现……

TI杯2019年全国电子设计大赛总结_第3张图片

纳尼?要我做飞控,emmmm……我一点经验都没有啊~
但是,此时有一个队友似乎很喜欢玩这个,然后,我们就抱着“搏一搏,单车变摩托”的良好心态毅然选择了B题,然后我们就开始各种找资料,最后发现貌似没有想象中那么难,然鹅,一个玩飞控的师兄过来实验室说“这个没有几年时间是啃不下来的……”
一阵讨论……
最后小组一致决定——换题!选H题:模拟电磁炮

TI杯2019年全国电子设计大赛总结_第4张图片

准备材料

题目选好了,现在开始准备各种缺少的耗材(博主死死捂住钱包)
我们参考了B站和YouTube等大神的电磁炮作品后发现需要:炮管,漆包线,云台(舵机),摄像头,强磁铁(炮弹),等等……

TI杯2019年全国电子设计大赛总结_第5张图片

原理设计及仿真

终于,在高中学的斜抛运动公式,自由落体运动要在这里用上了,因为笔记本没带回来,所以这一块就先欠着哈~~
TI杯2019年全国电子设计大赛总结_第6张图片
TI杯2019年全国电子设计大赛总结_第7张图片

代码部分

以下代码都不是完整版,不可直接使用

摄像头模块

我们使用的摄像头是OpenMV,主要是它可以用python写机器视觉,其丰富的内置库以及优质的算法可以让你事半功倍!

OpenMV简介:

——上代码——

# By Cooper_David_H

# 导入内置库
import sensor, image, time
from pid import PID
from pyb import Servo, UART, LED

# 定义灯光,作用:显示摄像头工作状态,
# 这里是openmv封装好的写法,红灯-1,绿灯-2,蓝灯-3
red_led = LED(1)
green_led = LED(2)
blue_led = LED(3)

# 定义摄像头舵机
up_down = Servo(1)
L_R = Servo(2)

# 设置颜色识别的阈值,双阈值增强容错性,同时准确率率可能会降低
target_color  = [(45, 66, 24, 73, 0, 56), (50, 12, 12, 93, -21, 98)]

# 设置PID,P,I值经过多次检验后得出
up_pid = PID(p=0.15, i=0.035 , imax=200)
LR_pid = PID(p=0.15, i=0.035 , imax=200)

# 定于测距常数K值
K=24700

# 重置传感器
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QQVGA)
sensor.skip_frames(10)
sensor.set_auto_whitebal(False)
clock = time.clock()

#让蓝灯亮起1秒
blue_led.on()
time.sleep(1000)
blue_led.off()

# 绿灯亮起:表示摄像头开始巡视四周环境
def look():
    print("looking")
    for i in range(-40,40):
        L_R.angle(i)
        green_led.on()
        time.sleep(20)
        green_led.off()
        
look()

#所有舵机复位
L_R.angle(0)
up_down.angle(0)
time.sleep(1000)

# 寻找最大色块
def find_max(blobs):
    max_size=0
    for blob in blobs:
        if blob[2]*blob[3] > max_size:
            max_b=blob
            max_size = blob[2]*blob[3]
    return max_b

# 敲黑板,重点来了~
while(True):
	try:
	    clock.tick()
	    img = sensor.snapshot()
	    blobs = img.find_blobs(target_color)
	    
	    # 当找到最大色块,用矩形框画出
	    if blobs:
	        max_b = find_max(blobs)
	        LR_error = max_b.cx()-img.width()/2
	        up_error = max_b.cy()-img.height()/2
	        img.draw_cross(max_b.cx(), max_b.cy())
	        
	        # 将画面切分为四个区域
	        img.draw_line(img.width()//2,0,img.width()//2,img.height(), color = (0, 0, 255))
	        img.draw_line(0, img.height()//2, img.width(), img.height()//2, color = (0,0,255))
	        
	        LR_output = LR_pid.get_pid(LR_error,1)/2
	        up_output = up_pid.get_pid(up_error,1)/2
	        
	        L_R.angle(L_R.angle() - LR_output)
	        up_down.angle(up_down.angle() - up_output)
	        
	        x = max_b.cx()
	        y = max_b.cy()
	
			# 判断色块是否位于画面正中间,如果是红灯亮1秒
	        if (77 <= x <= 81):
	            red_led.on()
	            time.sleep(5)
	            red_led.off()
	            
	            b = blobs[0]
	            Lm = (b[2]+b[3])/2
	            length = int(K/Lm)
	            print(length)
	            
	            # 高速测距,并进入点火状态
	            while (3000 >= length >= 2800):
	                up_down.angle(35)
	                from fire import fire
	                break
	            while (2800 > length >= 2500):
	                up_down.angle(30)
	                from fire import fire
	                break
	            while (2500 > length >= 2000):
	                up_down.angle(25)
	                from fire import fire
	                break
	         # 当找到最大色块,但是色块不位于画面正中间,绿灯亮
	        else:
	            green_led.on()
	            time.sleep(5)
	            green_led.off()


	except IOError:
   		 print "Error: some wrongs happended! "
		

Arduino模块

用arduino进行手动发射炮弹,以及调节炮弹射程,输入角度和位置坐标自动定到那个角度然后发射打到那个位置
by the way :这里也是有删改的代码,需要用来学习参考的请留言

// By Cooper_Havid_H

#include 
#include 
#include "MPU6050.h"
//实例化舵机
Servo myservo1;     //控制旋转角
Servo myservo2;     //控制仰角
MPU6050 accelgyro;

int pin1 = 10;    //舵机
int pin2 = 11;    //舵机
int pin3 = 12;    //充电
int pin4 = 13;    //放电

const byte ROWS = 4; //four rows
const byte COLS = 3; //three columns
char keys[ROWS][COLS] = {
  {'1', '2', '3'},
  {'4', '5', '6'},
  {'7', '8', '9'},
  {'#', '0', '*'}
};
byte rowPins[ROWS] = {5, 4, 3, 2}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {8, 7, 6}; //connect to the column pinouts of the keypad

Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );
/*************************************************************************************/
unsigned long now, lastTime = 0;
float dt;                                   //微分时间

int16_t ax, ay, az, gx, gy, gz;             //加速度计陀螺仪原始数据
float aax = 0, aay = 0, aaz = 0, agx = 0, agy = 0, agz = 0; //角度变量
long axo = 0, ayo = 0, azo = 0;             //加速度计偏移量
long gxo = 0, gyo = 0, gzo = 0;             //陀螺仪偏移量

float pi = 3.1415926;
float AcceRatio = 16384.0;                  //加速度计比例系数
float GyroRatio = 131.0;                    //陀螺仪比例系数

uint8_t n_sample = 8;                       //加速度计滤波算法采样个数
float aaxs[8] = {0}, aays[8] = {0}, aazs[8] = {0};         //x,y轴采样队列
long aax_sum, aay_sum, aaz_sum;                     //x,y轴采样和

float a_x[10] = {0}, a_y[10] = {0}, a_z[10] = {0} , g_x[10] = {0} , g_y[10] = {0}, g_z[10] = {0}; //加速度计协方差计算队列
float Px = 1, Rx, Kx, Sx, Vx, Qx;           //x轴卡尔曼变量
float Py = 1, Ry, Ky, Sy, Vy, Qy;           //y轴卡尔曼变量
float Pz = 1, Rz, Kz, Sz, Vz, Qz;           //z轴卡尔曼变量

/*********************************************************************************************************************************/

void setup() {
  Serial.begin(9600);
  myservo1.attach(10);
  myservo2.attach(11);

  //开机归中指令
  myservo1.write(95);
  myservo2.write(95);

  pinMode(pin3, OUTPUT);
  pinMode(pin4, OUTPUT);

  /*************************/

  accelgyro.initialize();                 //初始化
  unsigned short times = 200;             //采样次数
  for (int i = 0; i < times; i++)
  {
    accelgyro.getMotion6(&ax, &ay, &az, &gx, &gy, &gz); //读取六轴原始数值
    axo += ax; ayo += ay; azo += az;      //采样和
    gxo += gx; gyo += gy; gzo += gz;

  }

  axo /= times; ayo /= times; azo /= times; //计算加速度计偏移
  gxo /= times; gyo /= times; gzo /= times; //计算陀螺仪偏移
}

/***************************************************************************/
void loop() {

  unsigned long now = millis();             //当前时间(ms)
  dt = (now - lastTime) / 1000.0;           //微分时间(s)
  lastTime = now;                           //上一次采样时间(ms)

  accelgyro.getMotion6(&ax, &ay, &az, &gx, &gy, &gz); //读取六轴原始数值

  float accx = ax / AcceRatio;              //x轴加速度
  float accy = ay / AcceRatio;              //y轴加速度
  float accz = az / AcceRatio;              //z轴加速度

  aax = atan(accy / accz) * (-180) / pi;    //y轴对于z轴的夹角
  aay = atan(accx / accz) * 180 / pi;       //x轴对于z轴的夹角
  aaz = atan(accz / accy) * 180 / pi;       //z轴对于y轴的夹角

  aax_sum = 0;                              // 对于加速度计原始数据的滑动加权滤波算法
  aay_sum = 0;
  aaz_sum = 0;

  for (int i = 1; i < n_sample; i++)
  {
    aaxs[i - 1] = aaxs[i];
    aax_sum += aaxs[i] * i;
    aays[i - 1] = aays[i];
    aay_sum += aays[i] * i;
    aazs[i - 1] = aazs[i];
    aaz_sum += aazs[i] * i;

  }

  aaxs[n_sample - 1] = aax;
  aax_sum += aax * n_sample;
  aax = (aax_sum / (11 * n_sample / 2.0)) * 9 / 7.0; //角度调幅至0-90°
  aays[n_sample - 1] = aay;                      //此处应用实验法取得合适的系数
  aay_sum += aay * n_sample;                     //本例系数为9/7
  aay = (aay_sum / (11 * n_sample / 2.0)) * 9 / 7.0;
  aazs[n_sample - 1] = aaz;
  aaz_sum += aaz * n_sample;
  aaz = (aaz_sum / (11 * n_sample / 2.0)) * 9 / 7.0;

  float gyrox = - (gx - gxo) / GyroRatio * dt; //x轴角速度
  float gyroy = - (gy - gyo) / GyroRatio * dt; //y轴角速度
  float gyroz = - (gz - gzo) / GyroRatio * dt; //z轴角速度
  agx += gyrox;                             //x轴角速度积分
  agy += gyroy;                             //x轴角速度积分
  agz += gyroz;

  /* kalman start */
  Sx = 0; Rx = 0;
  Sy = 0; Ry = 0;
  Sz = 0; Rz = 0;

  for (int i = 1; i < 10; i++)
  { //测量值平均值运算
    a_x[i - 1] = a_x[i];                    //即加速度平均值
    Sx += a_x[i];
    a_y[i - 1] = a_y[i];
    Sy += a_y[i];
    a_z[i - 1] = a_z[i];
    Sz += a_z[i];

  }

  a_x[9] = aax;
  Sx += aax;
  Sx /= 10;                                 //x轴加速度平均值
  a_y[9] = aay;
  Sy += aay;
  Sy /= 10;                                 //y轴加速度平均值
  a_z[9] = aaz;
  Sz += aaz;
  Sz /= 10;

  for (int i = 0; i < 10; i++)
  {
    Rx += sq(a_x[i] - Sx);
    Ry += sq(a_y[i] - Sy);
    Rz += sq(a_z[i] - Sz);

  }

  Rx = Rx / 9;                              //得到方差
  Ry = Ry / 9;
  Rz = Rz / 9;

  Px = Px + 0.0025;                         // 0.0025在下面有说明...
  Kx = Px / (Px + Rx);                      //计算卡尔曼增益
  agx = agx + Kx * (aax - agx);             //陀螺仪角度与加速度计速度叠加
  Px = (1 - Kx) * Px;                       //更新p值

  Py = Py + 0.0025;
  Ky = Py / (Py + Ry);
  agy = agy + Ky * (aay - agy);
  Py = (1 - Ky) * Py;

  Pz = Pz + 0.0025;
  Kz = Pz / (Pz + Rz);
  agz = agz + Kz * (aaz - agz);
  Pz = (1 - Kz) * Pz;

  Serial.println(agy);
  delay(200);
  /********************************************************/
  if (key == '6') {
    Serial.println(key);
    //调整仰角32°
    myservo2.write(122);
  }
  if (key == '7') {
    Serial.println(key);
    //控制左右旋转舵机1旋转0°
    myservo1.write(95);
    delay(100);
  }
  if (key == '8') {
    Serial.println(key);
    //控制左右旋转舵机1旋转25°
    myservo1.write(120);
    delay(100);
  }
  if (key == '9') {
    Serial.println(key);
    //控制舵机1旋转-25°
    myservo1.write(70);
    delay(100);
  }
}//loop()的括号

最后

先放一张成品图吧!

TI杯2019年全国电子设计大赛总结_第8张图片

谨以此文,纪念2019年8月不眠不休的四天三夜

感谢每一位队友的付出:

郑璇、俊贤

You are the best !

你可能感兴趣的:(全国电子设计大赛,openmv,模拟电磁炮,个人总结)