Realtime Gun Aiming

1,476

35

3

About: I am a robotics hobbyist and like circuit designing. I live in Stockholm and am keen to join groups / individuals who like to have fun with hardware.

This was a fun project I wanted to build since I was a child.

A toy gun that aims itself to moving object in front of it.

Ingredients:

  1. Plastic beads pixel art
  2. Aruino UNO
  3. Arduino motor driver board
  4. Two servo motors
  5. A web camera
  6. Arduino programming knowledge
  7. Servo programming knowledge
  8. openCV machie vision library knowledge

Steps:

  1. Build the gun.
  2. Build the mount for gun.
  3. Arduino programming for gun motion control.
  4. OpenCV programming for motion detection.

Teacher Notes

Teachers! Did you use this instructable in your classroom?
Add a Teacher Note to share how you incorporated it into your lesson.

Step 1: Build the Gun

Buy a set of pixel beads and use your creativity to make a toy gun.

I have used combination of gray, white and black pixel beads to make my toy gun.

Once assembled on the board they should be ironed with a baking paper on top ( so to avoid sticking )

Once individual pieces are made they can be glued together with glue gun.

Any toy gun would work if you want to be quick with this step.

Step 2: Build the Mount for Gun

The mount is simple

I used mechanix game to make the mount for first servo.

Second servo was pasted to first with a glue gun.

The gun was mounted to the second servo with glue gun again.

Step 3: Arduino Programming for Gun Motion Control

Arduino code used:

It connects to serial port.

Uses arduino uno motor driver shield. Servo motors are connected to pin 9 and 10.

Command format it accepts: 50:70\n (x and y rotation angle in degrees)

<p>#include <br>#include </p><p>/* Serial control
 by aroramayank2002@gmail.com
 14-Apr-2017
 Gets angle of rotation from serial port in format 50:70\n
 Parses it and orients the servo x,y to that angle</p><p>  Gun pointing real hardware 
  xMin = top end
  xMax = bottom end
  yMin = right end
  yMax = left end
*/</p><p>#include 
#include </p><p>Servo myservo_10; 
Servo myservo_09;  </p><p>// variable to store the servo position
int posy = 90;    
int posx = 90;</p><p>int posxMax = 140;
int posxMin = 45;</p><p>int posyMax = 140;
int posyMin = 40;</p><p>void setup() {
  // Works parallely
  myservo_10.attach(10);  
  myservo_09.attach(9);</p><p>  Serial.begin(115200);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }
  servoTest();
  //establishContact();
}</p><p>void printHelp(){
  String xMsg = "x(min,max):(";
  String yMsg = "y(min,max):(";
  Serial.println("----------------------------------------");
  Serial.println("Eg. 12:45   horizontal(x):vertical(y) angle of orientation ");
  Serial.println(xMsg + posxMin + "," + posxMax + ")");
  Serial.println(yMsg + posyMin + "," + posyMax + ")");
  Serial.println("----------------------------------------");
}</p><p>void servoTest(){
  myservo_10.write(posy);              
  myservo_09.write(posx);
  delay(1000);
  myservo_10.detach();  
  myservo_09.detach();</p><p>  Serial.println("Ready!");
}</p><p>// Parse the serial command and execute
void parseCommand(String cmd){</p><p>   char separator = ':';
   int separatorAt = -1;
    int strIndex[] = { 0, -1 };
    int maxIndex = cmd.length() - 1;</p><p>    for (int i = 0; i <= maxIndex; i++) {
        if (cmd.charAt(i) == separator) {
            separatorAt = i;
        }
    }
   if(-1==separatorAt){
     Serial.println("Invalid option, check command '"+cmd+"'");
     printHelp();
     return;
   }
   String x = cmd.substring(0, separatorAt);
   String y = cmd.substring( (separatorAt+1), maxIndex+1);
   String msg1 = "x:" + x;
   String msg2 = "y:" + y;
   Serial.println(msg1); 
   Serial.println(msg2); </p><p>   posy = y.toInt();
   posx = x.toInt();
   
   if(posy<=posyMax && posy>=posyMin && posx<=posxMax && posx>=posxMin){</p><p>      myservo_10.attach(10);  
      myservo_09.attach(9);
  
      myservo_10.write(posy);  
      myservo_09.write(posx);            
  
      delay(50);
  
      myservo_10.detach();  
      myservo_09.detach();
    
    }else{
      Serial.println("Invalid option, check command '"+cmd+"'");
      printHelp();
    }
}</p><p>void establishContact() {
  while (Serial.available() <= 0) {
    Serial.print('.');   // send a .
    delay(300);
  }
}</p><p>String command;
char inByte;
void loop() {</p><p>   if (Serial.available() > 0) {
    inByte = Serial.read();
    if(inByte == '\n' ) {
      parseCommand(command);
      command="";
    }
    else{
      command+=inByte;
    }
  } 
}</p>

Step 4: OpenCV Programming for Motion Detection

OpenCV is leading library for machine vision applications.

Some basic understanding of c / c++ languages are required to understand the program.

Also you would need to use serial communication library to communicate between openCV to the Arduino board.

Refer code for more information:

<p>// MotionDetectionOpenCVVisualStudio.cpp : Defines the entry point for the console application.<br>// Author: aroramayank2002@gmail.com
// Description: Traking moving object on the web camera and sending signal to hardware board to orient the gun.</p><p>// Use VideoCapture capture(videoFilename); to VideoCapture capture(0); if web cam is connected
#include "stdafx.h" // This should be the first line
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/videoio.hpp"
#include 
#include </p><p>//C
#include 
//C++
#include 
#include </p><p>////// Serial related
#include 
#include 
#include 
#include </p><p>// OS Specific sleep
#ifdef _WIN32
#include 
#else
#include 
#endif</p><p>#include "serial/serial.h"</p><p>using namespace cv;
using namespace std;
using namespace serial;</p><p>Mat frame3; //current frame3
Mat fgMaskMOG3; //fg mask fg mask generated by MOG2 method
Ptr pMOG3; //MOG2 Background subtractor
int keyboard3; //input from keyboard3
double frameNumber = 0;
bool debug3 = false;</p><p>// Serial port parameters, the board should be connected before this program is launched.
serial::Serial my_serial("COM8", 115200, serial::Timeout::simpleTimeout(1000));</p><p>void processVideo3();</p><p>int testMovingObjects3(int argc, char* argv[]) {
	cout << "Close this window to exit..." << endl;
	namedWindow("Frame");
	pMOG3 = createBackgroundSubtractorMOG2(2000, 16.0, false); //MOG2 approach
	processVideo3();
	//destroy GUI windows
	destroyAllWindows();
	return EXIT_SUCCESS;
}</p><p>// Draws a line on the video frame
void MyLine3(Mat img, Point start, Point end) {
	int thickness = 5;  // 1
	int lineType = LINE_8;
	line(img,
		start,
		end,
		Scalar(255, 255, 255),  //Scalar(0, 0, 255),
		thickness,
		lineType);
}</p><p>Mat crossHair3(Point center, Mat image) {
	MyLine3(image, Point(center.x, center.y - 10), Point(center.x, center.y + 10));
	MyLine3(image, Point(center.x - 10, center.y), Point(center.x + 10, center.y));
	return image;
}</p><p>Point matchingMethod3(int, void*, Mat img)
{
	Mat templ(100, 100, CV_8UC1, Scalar(155)); //b,g,r white image, 1 channel
	Mat result;
	Mat img_display;
	img.copyTo(img_display);
	int result_cols = img.cols - templ.cols + 1;
	int result_rows = img.rows - templ.rows + 1;
	result.create(result_rows, result_cols, CV_32FC1);
	//bool method_accepts_mask = (CV_TM_SQDIFF == match_method || match_method == CV_TM_CCORR_NORMED);
	int match_method = CV_TM_SQDIFF;
	matchTemplate(img, templ, result, match_method);</p><p>	normalize(result, result, 0, 1, NORM_MINMAX, -1, Mat());
	double minVal; double maxVal; Point minLoc; Point maxLoc;
	Point matchLoc;
	minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc, Mat());
	if (match_method == TM_SQDIFF || match_method == TM_SQDIFF_NORMED)
	{
		matchLoc = minLoc;
	}
	else
	{
		matchLoc = maxLoc;
	}
	Point p(matchLoc.x + templ.cols / 2, matchLoc.y + templ.rows / 2);
	rectangle(result, matchLoc, p, Scalar::all(0), 2, 8, 0);
	if (debug3) {
		const char* image_window = "Orginal";
		const char* result_window = "Matched";
		namedWindow(image_window);
		namedWindow(result_window);
		imshow(image_window, img_display);
		imshow(result_window, result);
	}</p><p>	return p;
}</p><p>Mat updateCoordinatesOnFrame(Mat frame, Point p) {
	//get the frame3 number and write it on the current frame3
	stringstream ss;
	rectangle(frame, cv::Point(10, 2), cv::Point(100, 20),
		cv::Scalar(255, 255, 255), -1);
	ss << p.x << ", " << p.y;
	string frameNumberString = ss.str();
	putText(frame, frameNumberString.c_str(), cv::Point(15, 15),
		FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 0));
	return frame;
}</p><p>int framesForAvg3 = 10;
int currentFrame3 = 0;
Point pArray3[10];
Point stablePoint3 = Point(100, 100);
Point getStablePoint(Point p) {
	int xSum = 0;
	int ySum = 0;
	if (currentFrame3 < framesForAvg3) {
		pArray3[currentFrame3++] = p;
	}
	else {
		currentFrame3 = 0;</p><p>		for (int i = 0; i < framesForAvg3; i++) {
			xSum += pArray3[i].x;
			ySum += pArray3[i].y;
			stablePoint3 = Point(int(xSum / framesForAvg3), int(ySum / framesForAvg3));
		}</p><p>	}</p><p>	return stablePoint3;
}</p><p>//Returns translates frame to degree.
int minMaxHorizontalAngle3[2] = { 45, 90 }; //vertical in hardware code
int minMaxVerticalAngle3[2] = { 65, 95 }; // horizontal in hardware code</p><p>Point getServoTranslatedCoordinates3(Point p, Mat frame) {
	int rows = frame.rows;
	int cols = frame.cols;</p><p>	double xPercent = (double)p.x / cols;
	double yPercent = (double)p.y / rows;</p><p>	int newX = minMaxHorizontalAngle3[0] + (int)((minMaxHorizontalAngle3[1] - minMaxHorizontalAngle3[0]) * xPercent);
	int newY = minMaxVerticalAngle3[0] + (int)((minMaxVerticalAngle3[1] - minMaxVerticalAngle3[0]) * yPercent);
	Point degrees = Point(newX, newY);
	return degrees;
}</p><p>int frequency3 = 3;
int frequencyCounter3 = 0;
void writeToSerial(Point p) {
	if (frequencyCounter3++ < frequency3) {
		//Do nothing		
	}
	else {
		frequencyCounter3 = 0;</p><p>		stringstream ss;
		ss << p.y;
		ss << ":";
		ss << (minMaxHorizontalAngle3[0] + minMaxHorizontalAngle3[1] - p.x);
		//ss << "65";
		cout << ss.str() << "\n";
		size_t bytes_wrote = my_serial.write(ss.str() + "\n");
	}
}</p><p>void findMovement3(Mat fgMaskMOG3, Mat frame) {
	Point p = matchingMethod3(0, 0, fgMaskMOG3);
	frame = crossHair3(p, frame);
	Point p3 = getServoTranslatedCoordinates3(p, frame);
	writeToSerial(p3);
	imshow("Frame", frame);
}</p><p>void processVideo3() {
	VideoCapture capture(1);
	if (!capture.isOpened()) {
		//error in opening the video input
		cerr << "Unable to open camera file: " << endl;
		exit(EXIT_FAILURE);
	}
	//read input data. ESC or 'q' for quitting
	while ((char)keyboard3 != 'q' && (char)keyboard3 != 27) {
		//read the current frame
		if (!capture.read(frame3)) {
			cerr << "Unable to read next frame3." << endl;
			cerr << "Exiting..." << endl;
			exit(EXIT_FAILURE);
		}
		//update the background model
		pMOG3->apply(frame3, fgMaskMOG3);</p><p>		findMovement3(fgMaskMOG3, frame3);</p><p>		keyboard3 = waitKey(30);
		if (255 != keyboard3) {
			cout << keyboard3 << endl;
		}
	}
	//delete capture object
	capture.release();
}</p><p>// To be used when testing serial program.
void testSerial() {
	int c;
	do {
	cout << "Enter char: ";
	c = _getch();
	size_t bytes_wrote = my_serial.write("50:80\n");
	cout << bytes_wrote << endl;
	//bytes_wrote = my_serial.write(string(1, '\n'));
	//cout << bytes_wrote << endl;
	} while (c != '.');	
}</p><p>int main(int argc, char* argv[])
{	
	return testMovingObjects3(argc, argv);
}</p>
Microcontroller Contest 2017

Participated in the
Microcontroller Contest 2017

Be the First to Share

    Recommendations

    • CNC Contest

      CNC Contest
    • Make it Move

      Make it Move
    • Teacher Contest

      Teacher Contest

    3 Discussions

    0
    None
    EyalSch

    2 years ago

    The code is not readable (HTML tags all over)