博主联系方式:
QQ:1540984562
QQ交流群:892023501
群里会有往届的smarters和电赛选手,群里也会不时分享一些有用的资料,有问题可以在群里多问问。
规则
激光枪自动射击装置(E题)
【本科组】
一、任务
设计一个能够控制激光枪击发、自动报靶及自动瞄准等功能的电子系统。该系统由激光枪及瞄准机构、胸环靶、弹着点检测电路组成,其结构示意见图1。
二、要求
1.基本要求
(1) 用激光笔改装激光枪,激光枪可受电路控制发射激光束,激光束照射于胸环靶上弹着点的光斑直径<5mm;激光枪与胸环靶间距离为3m。
图1 激光枪自动射击装置示意图
(2) 激光枪固定在一机构上,可通过键盘控制激光枪的弹着点(用键盘设置激光束在靶纸上上下、左右移动一定距离)。
(3) 制作弹着点检测电路,通过摄像头识别激光枪投射在胸环靶上的弹着点光斑,并显示弹着点的环数与方位信息。其中环数包括:10、9、8、7、6、5、脱靶;方位信息是指弹着点与10环区域的相对位置,包括:中心、正上、正下、正左、正右、左上、左下、右上、右下。详见图2-b。
2.发挥部分
(1) 在图形点阵显示器上显示胸环靶的相应图形,并闪烁显示弹着点。
(2) 自动控制激光枪,在15秒内将激光束光斑从胸环靶上的指定位置迅速瞄准并击中靶心(即10环区域)。
(3) 可根据任意设定的环数,控制激光枪瞄准击中胸环靶上相应位置。
(4) 其他
2-a 胸环靶尺寸 2-b胸环靶 环数及方位信息示意
图2 胸环靶示意图
三、说明
1. 激光枪可以由市场上的激光笔改造,由电路控制击发;每次击发使光斑维持3~5s时间,但此期间不得移动光斑。
2. 可采用步进电机、舵机或直流电机等机构对激光枪进行两维控制,以实现瞄准。激光枪及相关机构可由支架支撑。
3. 胸环靶是在不反光的白纸画有一组相距5cm的同心圆(线宽不超过1mm),最内圆环直径10cm,圆环内为10环区域,从最内环至最外环间分别为9、8、7、6、5环区域,最外环外为脱靶。胸环靶上不允许设置摄像头以外的传感器。
4. 当激光枪的弹着点落在胸环靶的环线上时,报靶时采取就高不就低的原则。例如,弹着点在8环与9环之间的环线上时,则认为是9环。
5. 在不影响靶纸上圆环线的前提下,允许在靶纸上做标记。
6. 在完成发挥部分要求时,在正式击发前允许进行1-2次试射;但试射次数越少越好。
7. 不限制摄像头及弹着点检测电路的安装位置,但应方便搬运与快速安装。
8. 测试时自带胸环靶纸,测试评审现场可提供粘贴胸环靶的支架。
四、评分标准
设
计
报
告 项 目 主要内容 分数
系统方案 比较与选择
方案描述 4
理论分析与计算 激光枪自动控制原理分析、计算
弹着点检测原理分析、计算 6
电路与程序设计 电路设计
程序设计 4
测试方案与测试结果 测试方案及测试条件
测试结果及其完整性
测试结果分析 3
设计报告结构及规范性 摘要
设计报告结构、版面
图表的规范性 3
总分 20
基本
要求 实际制作完成情况 50
发挥
部分 完成第(1)项 5
完成第(2)项 20
完成第(3)项 15
完成第(4)项 10
总分 50
代码
使用opencv写的测试代码
#include <opencv2/opencv.hpp>
#include "opencv2/features2d.hpp"
#include <iostream>
#include "windows.h"
#include <stdio.h>
#include <time.h>
#include <math.h>
#include<opencv2/imgproc/types_c.h>
using namespace cv;
using namespace std;
#define PI 3.14159
//获取靶心位置
Point get_target(Mat& rgbMat,Mat& grayMat,Mat& paint)
{
//分离出r通道,BGR
vector<Mat> channels;
split(rgbMat, channels);
Mat R_Mat,G_Mat,B_Mat;
channels.at(2).copyTo(R_Mat);
channels.at(1).copyTo(G_Mat);
channels.at(0).copyTo(B_Mat);
int row_num = R_Mat.rows; //行数
int col_num = R_Mat.cols; //列数
//二值化得到掩膜
Mat mask=Mat::zeros(grayMat.size(), grayMat.type());;
//双重循环,遍历右下角像素值
for (int i = 0;i < row_num;i++) //行循环
{
for (int j = 0 ;j < col_num;j++) //列循环
{
//-------【开始处理每个像素】---------------
if (grayMat.at<uchar>(i, j) >= 90 && grayMat.at<uchar>(i, j) <= 230 && R_Mat.at<uchar>(i, j) >= 240 && G_Mat.at<uchar>(i, j) <= 120 && B_Mat.at<uchar>(i, j) <= 120)
{
mask.at<uchar>(i, j) = 255;
}
//-------【处理结束】---------------
}
}
//连通域计算,限制
Mat lableMat;
Mat statsMat;
Mat centerMat;
int nComp = cv::connectedComponentsWithStats(mask,
lableMat,
statsMat,
centerMat,
8,
CV_32S);
int pixels = 0;
int center_X = 0;
int center_Y = 0;
for (int i = 0; i < nComp; i++)
{
pixels = statsMat.at<int>(i, 4);
//打印信息
if (pixels <= 300 && pixels >= 20)
{
center_X = centerMat.at<double>(i, 0);
center_Y = centerMat.at<double>(i, 1);
cout << "中心X" << center_X << endl;
cout << "中心Y" << center_Y << endl;
cout << "像素个数" << pixels << endl;
//画出中心点
circle(paint, Point(center_X, center_Y), 5, Scalar(0, 255, 0), 2, 8, 0);
break;
}
}
//返回靶心坐标,并且画出来
return Point(center_X, center_Y);
}
//获取激光位置
Point get_gray(Mat& gray,Mat& paint)
{
Mat mask;
//二值化后得到掩膜
threshold(gray, mask, 250, 255, THRESH_BINARY);
Mat lableMat;
Mat statsMat;
Mat centerMat;
int nComp = cv::connectedComponentsWithStats(mask,
lableMat,
statsMat,
centerMat,
8,
CV_32S);
int pixels=0;
int center_X=0;
int center_Y=0;
for (int i = 0; i < nComp; i++)
{
pixels = statsMat.at<int>(i, 4);
//打印信息
if (pixels <= 200 && pixels >= 10)
{
center_X = centerMat.at<double>(i, 0);
center_Y = centerMat.at<double>(i, 1);
cout << "中心X" << center_X << endl;
cout << "中心Y" << center_Y << endl;
cout << "像素个数" << pixels << endl;
//画出中心点
circle(paint, Point(center_X, center_Y), 5, Scalar(0, 0, 255), 2, 8, 0);
break;
}
}
return Point(center_X, center_Y);
}
//获取两个点之间的距离
int get_distance(Point point1, Point point2)
{
int dx = abs(point1.x - point2.x);
int dy = abs(point1.y - point2.y);
int distance = sqrt(dx * dx + dy * dy);
return distance;
}
//获取两个点之间的角度
int get_angle(Point point1, Point point2)
{
float dx = point1.x - point2.x;
float dy = point1.y - point2.y;
int angle = 0;
if (dx == 0 && dy >= 0) angle = 0;
if (dx == 0 && dy < 0) angle = 180;
if (dy == 0 && dx > 0) angle = 90;
if (dy == 0 && dx < 0) angle = 270;
if (dx > 0 && dy > 0) angle = atan(dx / dy) * 180 / PI;
else if (dx < 0 && dy>0) angle = 360 + atan(dx / dy) * 180 / PI;
else if (dx < 0 && dy < 0) angle = 180 + atan(dx / dy) * 180 / PI;
else if (dx > 0 && dy < 0)angle = 180 + atan(dx / dy) * 180 / PI;
return angle;
}
//计算两点方位,返回point1在point2的哪个方位
int get_direction(Point point1, Point point2)
{
int direct = 0;
float dx = point1.x - point2.x;
float dy = point1.y - point2.y;
if (dx == 0 && dy == 0)
{
direct = 1; //重合
}
else if (dx == 0 && dy > 0)
{
direct = 2; //正下方
}
else if (dx == 0 && dy < 0)
{
direct = 3; //正上方
}
else if (dx > 0 && dy == 0)
{
direct = 4; //正右方
}
else if (dx > 0 && dy > 0)
{
direct = 5; //右下方
}
else if (dx > 0 && dy < 0)
{
direct = 6; //右上方
}
else if (dx < 0 && dy == 0)
{
direct = 7; //正左方
}
else if (dx < 0 && dy > 0)
{
direct = 8; //左下方
}
else if (dx < 0 && dy < 0)
{
direct = 9; //左上方
}
else
{
direct = 0; //无方向
}
return direct;
}
void show_direction(int direct)
{
if (direct == 1)
{
cout << "重合" << endl;
}
else if (direct == 2)
{
cout << "正下方" << endl;
}
else if (direct == 3)
{
cout << "正上方" << endl;
}
else if (direct == 4)
{
cout << "正右方" << endl;
}
else if (direct == 5)
{
cout << "右下方" << endl;
}
else if (direct == 6)
{
cout << "右上方" << endl;
}
else if (direct == 7)
{
cout << "正左方" << endl;
}
else if (direct == 8)
{
cout << "左下方" << endl;
}
else if (direct == 9)
{
cout << "左上方" << endl;
}
else
{
cout << "无方向" << endl;
}
}
//环间距获取
void get_ring_space(Mat& paintMat,Mat& canny)
{
Mat mask = Mat::zeros(canny.size(), canny.type());;;
int times=0;
//双重循环,遍历右下角像素值
for (int i = 1;i < canny.rows-1;i++) //行循环
{
for (int j = 1;j < canny.cols-1;j++) //列循环
{
//-------【开始处理每个像素】---------------
if (canny.at<uchar>(i, j) == 255 && paintMat.at<Vec3b>(i, j)[0] == 0 && paintMat.at<Vec3b>(i, j)[1] == 0 && paintMat.at<Vec3b>(i, j)[2] == 0)
{
mask.at<uchar>(i, j) = 255;
times++;
//判断每个格子八领域是否有白色的,如果有,消除掉
if (mask.at<uchar>(i - 1, j - 1) == 255 || mask.at<uchar>(i - 1, j) == 255 || mask.at<uchar>(i - 1, j + 1) == 255
|| mask.at<uchar>(i, j - 1) == 255 || mask.at<uchar>(i, j + 1) == 255
|| mask.at<uchar>(i + 1, j - 1) == 255 || mask.at<uchar>(i + 1, j) == 255 || mask.at<uchar>(i + 1, j + 1) == 255)
{
mask.at<uchar>(i, j) = 0;
times--;
}
}
//-------【处理结束】---------------
}
}
//默认光斑靠环心的边缘与黑线冲突。
cout <<"环数"<< 10-(times) << endl;
}
/****示例**********/
int main()
{
//载入原图
Mat secImage = imread("D:\\opencv_picture_test\\电设\\4.jpg");//加载原图
if (secImage.empty())
{
printf("Could not find the image!\n");
return -1;
}
Mat edge, dstImage,paintMat;
dstImage.create(secImage.size(), secImage.type()); //创建一个同大小类型的矩阵
secImage.copyTo(paintMat);
cvtColor(secImage, dstImage,COLOR_BGR2GRAY);
imshow("【原图的灰度图】", dstImage);
Point target= Point(0, 0), gray=Point(0,0);
//进行均值滤波操作
blur(dstImage, edge, Size(3, 3));
target=get_target(secImage, dstImage,paintMat);
//找到激光的位置并且画出来
gray=get_gray(edge,paintMat);
//计算两者中心距离(减去中心圆的半径)
if (target != Point(0, 0) && gray != Point(0, 0))
{
int distance = get_distance(target, gray);
int angle = get_angle(target, gray);
int direct = get_direction(gray, target);
show_direction(direct);
cout << "两者距离" << distance << endl;
cout << "两者角度" << angle << endl;
int canny_thred = 30;
//运行canny算子
Canny(edge, edge, canny_thred, canny_thred * 2, 3);
line(paintMat, target, gray, (0, 0, 0), 1);
get_ring_space(paintMat, edge);
imshow("canny算子检测【效果图】", edge);
}
else
{
cout << "没有找到靶点或者激光" << endl;
cout << "脱靶" << endl;
}
waitKey(0);
return 0;
}
效果:
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |