Introduction: Realtime Gun Aiming
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:
- Plastic beads pixel art
- Aruino UNO
- Arduino motor driver board
- Two servo motors
- A web camera
- Arduino programming knowledge
- Servo programming knowledge
- openCV machie vision library knowledge
Steps:
- Build the gun.
- Build the mount for gun.
- Arduino programming for gun motion control.
- OpenCV programming for motion detection.
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>

Participated in the
Microcontroller Contest 2017
3 Comments
5 years ago
The code is not readable (HTML tags all over)
5 years ago
This would make a fun Internet of Things project.
Reply 5 years ago
I still have to learn it... lets see.