Laser Shootin' Robot

0 4388 Medium

Use a DFRobot MiniQ chassis with Raspberry Pi Zero W to create a robot that can shoot a laser at anything you choose! (Not at eyes, please.)

 

https://hackster.imgix.net/uploads/attachments/347609/hackster_thumbnail_jZQrzEsDcJ.gif?auto=format%2Ccompress&gifq=35&w=900&h=675&fit=min&fm=mp4

 

Things used in this project

HARDWARE LIST
1 DFRobot MiniQ Chassis
1 Raspberry Pi Zero Wireless
1 Arduino Nano R3
1 Texas Instruments Dual H-Bridge motor drivers L293D
1 NPN Transistor
1 5V Regulator 1.5A output
1 3 Terminal, 2 Position SPDT Switch
1 HC-05 Bluetooth Module
1 Joystick
1 1800 mAh LiPo Battery 11.7V

Software apps and online services

-  Arduino IDE

 

Hand tools and fabrication machines

- Soldering iron (generic)

Story

 

Introduction

Ever wanted to make a robot? How about one that is controlled via Bluetooth? Still not exciting enough for you? This robot can fire lasers, basically becoming a mobile artillery piece that shoots light! Now you can tease your cat, make a presentation more exciting, and even shine it at people (just not at their eyes please)! This project will document how I went about creating a robot capable of such epic fun!

Parts Needed

DFRobot was generous to me by sending out a couple of their 2WD MiniQ Robot Chassis. These things are great! Each chassis came with 2 50:1 geared motors, nice sturdy tires, and plenty of mounting holes. Next, I got a Raspberry Pi Zero; it is small, yet very capable, due to its on board Wifi and Bluetooth. I also got an Arduino Nano and an HC-05 for the controller. See the BoM for the full list of parts needed.

 

 

Assembly

I began by assembling the 2WD MiniQ chassis kit from DFRobot. I slid the wheels onto the motor shafts, then inserted them into brackets and attached them to the chassis. Finally, I added the metal supports.

 

 

Now it was time to build the main board. The L293d motor driver got soldered in place, along with wires running to the Raspberry Pi's GPIO pins. Next, I soldered a connector for the battery, as that will provide the main power. After the power source was added, I installed a 5V regulator along with an NPN transistor. The regulator provides the right voltage for the Pi and laser diode, and the transistor allows the Pi to safely control the laser without blowing up.

 

Motor Control

This was actually one of the trickiest parts of this project. I had to decide the way I should transmit information to drive the wheels. I was faced with two options: tank or arcade drive. Tank drive operates like a.. tank, of course. Left makes right wheel go forwards and the left wheel goes backwards, turning the robot left. Here is a table for movements:

CODE
Joystick Position | Left Wheel | Right Wheel
Left | -1 | 1 
Right | 1 | -1
Up | 1 | 1
Down | -1 | -1
Neutral | 0 | 0

But that gives you very limited control. Plus, when working with a large voltage, the robot tends to get zippy. A better option was to use PWM to variably control the motor speeds. Now, it isn't as simple as using an Arduino, there isn't any analogWrite(motor_pin, value) abstraction, which can get frustrating. Luckily, the RPi GPIO library has a PWM class. To use it, I first made each motor pin into an output with GPIO.setup(motor_pin, GPIO.OUT) . Then I made an object for each motor pin with motor_pwm = GPIO.PWM(motor_pin, 100) . Make sure to change the motor_pwm variable name for each object. Now that I had variable motor speeds, could focus on getting the values from the joystick translated into motor values.

The Controller

 

 

The basic analog joystick is made of two potentiometers that give a 10-bit value ranging from 0-1023 on the Arduino. I set up the Arduino Nano to take a reading from each analog pin that the joystick was connected to (in my case it was A1 and A2). After that, the code maps x and y to values between -50 and 50, like this:

CODE
int x = map(A1_reading, 0, 1023, -50, 50); 
int y = map(A2_reading, 0, 1023, -50, 50); 

Then, I added a constrain call to capture any outliers:

CODE
x = constrain(x, -100, 100); 
y = constrain(y, -100, 100); 

Sparkfun recently did a tutorial on how to use an RC controller for DC motors, which was perfect for what I needed. You can find that blog post here. The way to determine the way each motor goes is pretty simple. Just do:

CODE
int motor1 = x+y; 
int motor2 = y-x; 

And then:

CODE
motor1 = constrain(motor1, -100, 100); 
motor2 = constrain(motor2, -100, 100); 

...to ensure it stays within -100 and 100. And you may be asking, "How do I make it come to a stop?" Well, that is where a deadzone comes into play. By adding this as a global variable:

CODE
int deadzone = 10;

...and then:

CODE
if(abs(motor1) <= deadzone){ 
   motor1 = 0; 
 } 
 if(abs(motor2) <= deadzone){ 
   motor2 = 0; 
 } 

...you can effectively stop a motor whenever you aren't pushing the joystick. Lastly, I was wondering how to efficiently send this motor data to the Raspberry Pi. It needed to be easily readable and have good decoding support. Then this idea popped into my head: JSON! I could send a string that conveys the motor data to the Pi, then the Pi can decode it into variables, and lastly control the motors accordingly. So I added these lines:

CODE
String json_string = "{\"motor\":[" + String(motor1)+","+String(motor2)+"]}";
  Serial.println(json_string); 

which send the motor data as a JSON string, where "motor" is the key, and an array containing the motors values is its value. There is also an interrupt that will send "Fire" to the Pi if the button is pushed. Now for the last part: having fun with it!

Using the Robot

Before I began to use this new machine of light beams, I had to somehow get the HC-05 module talking to the Pi. Here is the condensed version. First, power on the HC-05 and Raspberry Pi, then click on the Bluetooth icon on the Raspberry Pi and pair your HC-05. The password should be: "1234" without the quotes.

 

On the Raspberry Pi Zero W, enter these commands:

CODE
hcitool scan

This will scan for bluetooth devices and return their MAC addresses. Now enter:

CODE
sudo rfcomm /dev/rfcomm0 mac_address

...where mac_address is the address of the HC-05. Download the code for this project and transfer the Python files to a directory. REMEMBER THAT DIRECTORY; you will need it soon. Now open up the rc_car_main.py python script with a text editor, and where you see BT_addr , enter in "/dev/rfcomm0". Also, enter in the motor pins for the motors in:

CODE
car.config_motors(pin1, pin2, pin3, pin4, invert=False)

Run the script and see if the robot moves the proper way. If it goes left when it should be going right, set invert=False to invert=True .

 

Have fun with your new laser firing bot!

 

Schematics

Robot Schematic

 

 

Controller Schematic

 

Controller Code

CODE
int JS_VALS[2] = {};

const int button_pin = 2;
const int deadzone = 8;

void setup() {
  // put your setup code here, to run once:
Serial.begin(9600);
pinMode(button_pin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(button_pin), fire_laser, FALLING);
}

void loop() {
  // put your main code here, to run repeatedly:
  JS_VALS[0] = analogRead(A1);
  JS_VALS[1] = analogRead(A2);
  int x = map(JS_VALS[0], 0, 1023, -50, 50);
  x = constrain(x, -100, 100);
  int y = map(JS_VALS[1], 0, 1023, -50, 50);
  y = constrain(y, -100, 100);
  int motor1 = x+y;
  int motor2 = y-x;
  motor1 = constrain(motor1, -100, 100);
  motor2 = constrain(motor2, -100, 100);
  if(abs(motor1) <= deadzone){
    motor1 = 0;
  }
  if(abs(motor2) <= deadzone){
    motor2 = 0;
  }
  //Serial.println("X: "+String(JS_VALS[0])+", Y: "+String(JS_VALS[1]));
  String json_string = "{\"motor\":[" + String(motor1)+","+String(motor2)+"]}";
  Serial.println(json_string);
  delay(30);
}

void fire_laser(){
  String json_string = "{\"shoot\":\"1\"}";
  Serial.println(json_string);
}

Python Robot Class

CODE
import RPi.GPIO as GPIO
from time import sleep
from serial import Serial
import json

class Car(object):

	def __init__(self, BT_Port, laser_pin):
		GPIO.setmode(GPIO.BCM)
		self.ser = Serial(BT_Port, baudrate=9600)
                self.laser_pin = laser_pin
                GPIO.setup(self.laser_pin, GPIO.OUT)
		
	def configMotors(self, mL1, mL2, mR1, mR2, invertLR=False):
	    self.motor_array = [mL1, mL2, mR1, mR2]
	    self.invert = invertLR
	    for pin in self.motor_array:
		GPIO.setup(pin, GPIO.OUT)
		#self.motor_pwm.append(GPIO.PWM(pin, 490))
	    self.motorL1_pwm = GPIO.PWM(mL1, 100)
	    self.motorL2_pwm = GPIO.PWM(mL2, 100)
	    self.motorR1_pwm = GPIO.PWM(mR1, 100)
	    self.motorR2_pwm = GPIO.PWM(mR2, 100)
	    self.motorL1_pwm.start(0)
	    self.motorL2_pwm.start(0)
	    self.motorR1_pwm.start(0)
	    self.motorR2_pwm.start(0)
	    
		
	def receive_command(self):
            self.cmd = ""
            if self.ser.inWaiting():
                self.cmd = self.ser.readline()
                if self.cmd != None:
                    try:
                        data = self.cmd.decode('utf-8')
                        data = json.loads(data.strip())
                        print data
                        if data != "":
                            return data
                        else:
                            return ""
                    except ValueError:
                        pass
			
	def turn_wheel(self, motor1, motor2):
            if self.invert:
                temp = motor1
                motor1 = motor2
                motor2 = temp
            motor1 = (motor1 / 2)
            motor2 = (motor2 / 2)
            if motor1<0:
		self.motorL2_pwm.ChangeDutyCycle(motor1*-1)
		self.motorL1_pwm.ChangeDutyCycle(0)#make positive if negative
            else:
		self.motorL1_pwm.ChangeDutyCycle(motor1)
		self.motorL2_pwm.ChangeDutyCycle(0)
		
            if motor2<0:
		self.motorR2_pwm.ChangeDutyCycle(motor2*-1)
		self.motorR1_pwm.ChangeDutyCycle(0)#make positive if negative
            else:
		self.motorR1_pwm.ChangeDutyCycle(motor2)
		self.motorR2_pwm.ChangeDutyCycle(0)
			
        def fire_laser(self):
            GPIO.output(self.laser_pin, GPIO.HIGH)
            sleep(2)
            GPIO.output(self.laser_pin, GPIO.LOW)

	def update(self): #Run to make the robot work
            self.json_obj = self.receive_command()
	    print self.json_obj
	    try:
                if "motor" in self.cmd:
                    if self.json_obj["motor"] != None:
                   	 val1 = self.json_obj["motor"][0]
			# if val1 > 0:
      			 #    val1 += 10
                   	 val2 = self.json_obj["motor"][1]
			# if val2 <0:
			#	val2 += 4
                   	 self.turn_wheel(val1, val2)
		elif "shoot" in self.cmd:
		    self.fire_laser()
            except TypeError:
                pass

Python Robot Main Script (run this one)

CODE
import car

bt_port = "/dev/rfcomm0"
laser_pin = pin5
rc_car = car.Car(bt_port, laser_pin)

rc_car.configMotors(pin1,pin2,pin3,pin4, invertLR=False)

while 1:
    rc_car.update()
        

The article was first published in hackster, September 12,  2017

cr: https://www.hackster.io/gatoninja236/laser-shootin-robot-defad0

author: Arduino “having11” Guy

License
All Rights
Reserved
licensBg
0