Pan-Tilt-Shoot Webcam

PTZ or Pan-tilt-zoom cameras are off the shelf and reasonably low cost. This project is a home made pan-tilt camera that shoots nerf rockets.

I got the rocket launcher in a bargain bin, but they can be found online starting around $15.

For this project there were 3 main steps:

  • getting the Python code talking to the rocket launcher
  • loading some USB webcam software
  • loading a Python web framework (bottle).

The hardware could be a PC (preferably running Linux) or a Raspberry Pi. On this project I used a Pi clone (an Orange Pi Lite). The faster your hardware the better the streaming video performance that you’ll get.

pts_overview

Getting the Rocket Launcher Working

There are a number of Python libraries that need to loaded:

pip install setuptools
pip install usb

To control the rocket launcher Python can connect to the USB device using the vendor id (0x2123) and product id (0x1010). Rocket launcher commands are issued as USB transfer codes.

A command line test program (rocket1.py) would be:

 import usb  
 import sys  
 import time  
 device = usb.core.find(idVendor=0x2123, idProduct=0x1010)  
 # On Linux we need to detach usb HID first  
 try:  
   device.detach_kernel_driver(0)  
 # except Exception, e:  
 except Exception:  
   pass # already unregistered  
 device.set_configuration()  
 endpoint = device[0][(0,0)][0]  
 down = 1 # down  
 up = 2 # up  
 left = 4 # rotate left  
 right = 8 # rotate right  
 fire = 16 # fire  
 stop = 32 # stop  
 #device.ctrl_transfer(0x21, 0x09, 0x0200, 0, [signal])  
 while True:  
   print('r = right, l = left, u = up, d = down, f = fire ')  
   key = raw_input ('enter key:')  
   if (key == 'l'):  
     device.ctrl_transfer(0x21, 0x09, 0, 0, [0x02, left, 0x00,0x00,0x00,0x00,0x00,0x00])  
   if (key == 'u'):  
     device.ctrl_transfer(0x21, 0x09, 0, 0, [0x02, up, 0x00,0x00,0x00,0x00,0x00,0x00])  
   if (key == 'r'):  
     device.ctrl_transfer(0x21, 0x09, 0, 0, [0x02, right, 0x00,0x00,0x00,0x00,0x00,0x00])  
   if (key == 'd'):  
     device.ctrl_transfer(0x21, 0x09, 0, 0, [0x02, down, 0x00,0x00,0x00,0x00,0x00,0x00])  
   if (key == 'f'):  
     device.ctrl_transfer(0x21, 0x09, 0, 0, [0x02, fire, 0x00,0x00,0x00,0x00,0x00,0x00])  
     time.sleep(4)  
   time.sleep(0.1)  
   device.ctrl_transfer(0x21, 0x09, 0, 0, [0x02, stop, 0x00,0x00,0x00,0x00,0x00,0x00])  

By default the USB port requires superuser rights, so run the program using sudo:

$ sudo python rocket1.py
r = right, l = left, u = up, d = down, f = fire
enter key:r
r = right, l = left, u = up, d = down, f = fire
enter key:

Getting the Web Cam working

A standard USB Web Cam can be connected to a Linux  PC or Raspberry Pi using the  motion package. Motion is super easy to setup and it’s got lots of added features if you want to look at enhancing things later. To install motion:

sudo apt-get install motion

Once you have motion installed you’ll need to tweek some of it’s parameters by:

sudo nano /etc/motion/motion.conf

The /etc/motion/motion.conf file contains a lot of  parameters, some of the more important ones are:

# Image width (pixels). Valid range: Camera dependent, default: 352
width 800

# Image height (pixels). Valid range: Camera dependent, default: 288
height 600

# Maximum number of frames to be captured per second.
framerate 1

# Maximum framerate for stream streams (default: 1)
stream_maxrate 1

# Restrict stream connections to localhost only (default: on)
stream_localhost off

The speed of your hardware and network will determine how many frames per second you can use.

To run the video server enter:

sudo motion &

The motion package has a built in web server that is accessed by: http://your_ip:8081

livevideo

Getting the Bottle Web Server Running

There are a lot of web programming options that are available. In my case I wanted to run the project as a simple standalone app on an Orange Pi (a Raspberry Pi clone). To install bottle:

sudo apt-get install python-bottle

The Pan-Tilt-Shoot Web page (ptscam.html) was designed with 5 buttons and the Web Cam video below. When a button is clicked a javascript function called sendcmd passes a command as a header item in a AJAX request.  The motion web server camweb video is included using the html img tag, with the source being the web link.

<!DOCTYPE html>
<html>
<head> 
<title>Pan-Tilt-Shoot Webcam</title> 
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="data:,"> 
<style>
  html{font-family: Helvetica; display:inline-block; margin: 0px auto; text-align: center;}
  h1{color: #0F3376; padding: 2vh;}p{font-size: 1.5rem;}
  .button{display: inline-block; background-color: #4286f4; border: none; 
  border-radius: 4px; color: white; text-decoration: none; font-size: 30px; width:100%}
  .button2{background-color: green ;width:31%}
  .button3{background-color: red; width:33%}
</style>
</head>
<script>
function sendcmd(thecmd) {
  // send the action as a header item 
  var xhttp = new XMLHttpRequest();
  xhttp.open("GET","/action" , true);
  xhttp.setRequestHeader("myaction", thecmd);
  xhttp.send()
}
</script> 
<body>
<h2>Pan-Tilt-Shoot Webcam</h2> 
<button onclick="sendcmd('up')" class="button">UP</button>
<button onclick="sendcmd('left')" class="button button2">LEFT</button>
<button onclick="sendcmd('fire')" class="button button3">FIRE</button>
<button onclick="sendcmd('right')" class="button button2" >RIGHT</button>
<button onclick="sendcmd('down')" class="button button">DOWN</button>
  
<p><img src='http://192.168.0.117:8081/'></p>
</body>
</html>

The Python web server application sends the web page at startup, and then it processes AJAX requests and passed the requested action (as a header item) to the rocket launcher USB device.

 # Python Bottle   
 #  
 import os, socket  
 from bottle import route, run, static_file, request  
 import usb  
 import sys  
 import time  
    
   
 # Send an action to the rocket launcher  
 def do_action(theaction):  
    
   print("Action : " + theaction)  
   down = 1 # down  
   up = 2 # up  
   left = 4 # rotate left  
   right = 8 # rotate right  
   fire = 16 # fire  
   stop = 32 # stop  
   if (theaction == 'left'):  
     device.ctrl_transfer(0x21, 0x09, 0, 0, [0x02, left, 0x00,0x00,0x00,0x00,0x00,0x00])  
   if (theaction == 'up'):  
     device.ctrl_transfer(0x21, 0x09, 0, 0, [0x02, up, 0x00,0x00,0x00,0x00,0x00,0x00])  
   if (theaction == 'right'):  
     device.ctrl_transfer(0x21, 0x09, 0, 0, [0x02, right, 0x00,0x00,0x00,0x00,0x00,0x00])  
   if (theaction == 'down'):  
     device.ctrl_transfer(0x21, 0x09, 0, 0, [0x02, down, 0x00,0x00,0x00,0x00,0x00,0x00])  
   if (theaction == 'fire'):  
     device.ctrl_transfer(0x21, 0x09, 0, 0, [0x02, fire, 0x00,0x00,0x00,0x00,0x00,0x00])  
     time.sleep(4)  
   time.sleep(0.1)  
   device.ctrl_transfer(0x21, 0x09, 0, 0, [0x02, stop, 0x00,0x00,0x00,0x00,0x00,0x00])  

# Send the starting page   
 @route('/')  
 def server_static():  
   return static_file("ptscam.html", root='')  
# Process an AJAX GET request, pass the action to the rocket launcher code   
 @route('/action')  
 def get_action():  
   print("Requested action: " + request.headers.get('myaction'))  
   do_action(request.headers.get('myaction'))  
       
   
 # On Linux we need to detach usb HID first  
 device = usb.core.find(idVendor=0x2123, idProduct=0x1010)  
 try:  
   device.detach_kernel_driver(0)  
 except Exception:  
   pass # already unregistered  
   
 # Start the bottle web server  
 run(host="192.168.0.117" , port=8000, debug=False)  

Because of the USB connection the Python application need to be run under sudo. When the program is running some diagnostics will show connections and actions.

$ sudo python ptscam.py
Bottle v0.12.13 server starting up (using WSGIRefServer())...
Listening on http://192.168.0.117:8000/
Hit Ctrl-C to quit.

192.168.0.114 - - [23/Nov/2019 19:26:24] "GET / HTTP/1.1" 200 1251
192.168.0.114 - - [23/Nov/2019 19:26:24] "GET /:8081 HTTP/1.1" 404 736
192.168.0.114 - - [23/Nov/2019 19:26:53] "GET / HTTP/1.1" 200 1271
Requested action: up
Action : up

The web page is accessed by : http://your_ip:8000

pts_screen

Final Comments

The next step will be to mount the rocket launcher with the USB web cam on a little rover.

2 thoughts on “Pan-Tilt-Shoot Webcam

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s