【电设控制与图像训练题】【激光打靶】【opencv测试代码以及效果】

本文介绍了激光枪自动射击装置的设计任务,包括激光枪控制、弹着点检测和瞄准功能。利用OpenCV进行弹着点检测,通过摄像头识别光斑位置。同时,文章提供了用OpenCV编写的测试代码及其效果展示。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

博主联系方式:
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;
}

效果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

拾牙慧者

欢迎请作者喝奶茶

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值