User:Kannix/RPI-Cam
Jump to navigation
Jump to search
intro:
This setup shall take photos at different angles on slow moving vehicles (eg. canal-boats) to be shared at mapillary:
- motorised panning camera head
- GPS EXIF tags
parts:
- Raspberry PI EUR 35,-
- Camera Board EUR 25,-
- ULN2003 stepper motor and driver board Arduino EUR 4,50
- Dupont Jumper Cables
- Krick RABOESCH Ruderhebel T-Form Nylon 5mm Durchmesser - rb107-41 EUR 4,-
- GPS-device
- push-button switch
- scrap metal, tiny screws, double sided tape etc ...
some prerequisites:
- image to start with: MINIBIAN
- resize partition: Resize Flash Partitions
apt-get update
apt-get upgrade
apt-get install raspi-config
apt-get install python
apt-get install rpi.gpio
apt-get install python-picamera
apt-get install gpsd gpsd-clients
apt-get install python-dateutil
code:
Note: Pins are numbered according BCM-scheme!
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from time import sleep
import RPi.GPIO as GPIO
import picamera
import gps
import math
from datetime import datetime
from dateutil import parser
# Class Motor is written by Stephen C Phillips.
# It is in the public domain, so you can do what you like with it
# but a link to http://scphillips.com would be nice.
# It works on the Raspberry Pi computer with the standard Debian Wheezy OS and
# the 28BJY-48 stepper motor with ULN2003 control board.
class Motor(object):
def __init__(self, pins, mode=3):
"""Initialise the motor object.
pins -- a list of 4 integers referring to the GPIO pins that the IN1, IN2
IN3 and IN4 pins of the ULN2003 board are wired to
mode -- the stepping mode to use:
1: wave drive (not yet implemented)
2: full step drive
3: half step drive (default)
"""
self.P1 = pins[0]
self.P2 = pins[1]
self.P3 = pins[2]
self.P4 = pins[3]
self.mode = mode
self.deg_per_step = 5.625 / 64 # for half-step drive (mode 3)
self.steps_per_rev = int(360 / self.deg_per_step) # 4096
self.step_angle = 0 # Assume the way it is pointing is zero degrees
for p in pins:
GPIO.setup(p, GPIO.OUT)
GPIO.output(p, 0)
def _set_rpm(self, rpm):
"""Set the turn speed in RPM."""
self._rpm = rpm
# T is the amount of time to stop between signals
self._T = (60.0 / rpm) / self.steps_per_rev
# This means you can set "rpm" as if it is an attribute and
# behind the scenes it sets the _T attribute
rpm = property(lambda self: self._rpm, _set_rpm)
def move_to(self, angle):
"""Take the shortest route to a particular angle (degrees)."""
# Make sure there is a 1:1 mapping between angle and stepper angle
target_step_angle = 8 * (int(angle / self.deg_per_step) / 8)
steps = target_step_angle - self.step_angle
steps = (steps % self.steps_per_rev)
if steps > self.steps_per_rev / 2:
steps -= self.steps_per_rev
print "moving " + `steps` + " steps"
print "moving " + `(steps * self.deg_per_step)` + " degree"
if self.mode == 2:
self._move_acw_2(-steps / 8)
else:
self._move_acw_3(-steps / 8)
else:
print "moving " + `steps` + " steps"
print "moving " + `(steps * self.deg_per_step)` + " degree"
if self.mode == 2:
self._move_cw_2(steps / 8)
else:
self._move_cw_3(steps / 8)
self.step_angle = target_step_angle
def __clear(self):
GPIO.output(self.P1, 0)
GPIO.output(self.P2, 0)
GPIO.output(self.P3, 0)
GPIO.output(self.P4, 0)
def _move_acw_2(self, big_steps):
self.__clear()
for i in range(big_steps):
GPIO.output(self.P3, 0)
GPIO.output(self.P1, 1)
sleep(self._T * 2)
GPIO.output(self.P2, 0)
GPIO.output(self.P4, 1)
sleep(self._T * 2)
GPIO.output(self.P1, 0)
GPIO.output(self.P3, 1)
sleep(self._T * 2)
GPIO.output(self.P4, 0)
GPIO.output(self.P2, 1)
sleep(self._T * 2)
def _move_cw_2(self, big_steps):
self.__clear()
for i in range(big_steps):
GPIO.output(self.P4, 0)
GPIO.output(self.P2, 1)
sleep(self._T * 2)
GPIO.output(self.P1, 0)
GPIO.output(self.P3, 1)
sleep(self._T * 2)
GPIO.output(self.P2, 0)
GPIO.output(self.P4, 1)
sleep(self._T * 2)
GPIO.output(self.P3, 0)
GPIO.output(self.P1, 1)
sleep(self._T * 2)
def _move_acw_3(self, big_steps):
self.__clear()
for i in range(big_steps):
GPIO.output(self.P1, 0)
sleep(self._T)
GPIO.output(self.P3, 1)
sleep(self._T)
GPIO.output(self.P4, 0)
sleep(self._T)
GPIO.output(self.P2, 1)
sleep(self._T)
GPIO.output(self.P3, 0)
sleep(self._T)
GPIO.output(self.P1, 1)
sleep(self._T)
GPIO.output(self.P2, 0)
sleep(self._T)
GPIO.output(self.P4, 1)
sleep(self._T)
def _move_cw_3(self, big_steps):
self.__clear()
for i in range(big_steps):
GPIO.output(self.P3, 0)
sleep(self._T)
GPIO.output(self.P1, 1)
sleep(self._T)
GPIO.output(self.P4, 0)
sleep(self._T)
GPIO.output(self.P2, 1)
sleep(self._T)
GPIO.output(self.P1, 0)
sleep(self._T)
GPIO.output(self.P3, 1)
sleep(self._T)
GPIO.output(self.P2, 0)
sleep(self._T)
GPIO.output(self.P4, 1)
sleep(self._T)
if __name__ == "__main__":
# http://wiki.openstreetmap.org/wiki/User:MHohmann/RPiCam
def gpsexif(redirect):
global camera, gpsd
while True:
report = gpsd.next()
# print report
# Wait for position information.
if report['class'] == 'TPV':
# Set orientation to normal landscape.
camera.exif_tags['IFD0.Orientation'] = '1'
# Set picture date and time to GPS values.
now = parser.parse(report.get('time', datetime.now().isoformat()))
print "date and time: " + `now.strftime('%Y-%m-%d %H:%M:%S')`
camera.exif_tags['EXIF.DateTimeOriginal'] = now.strftime('%Y:%m:%d %H:%M:%S')
# Set altitude to GPS value.
alt = report.get('alt', 0.0)
print "altitude: " + `alt`
camera.exif_tags['GPS.GPSAltitudeRef'] = '0' if alt > 0 else '1'
aalt = math.fabs(alt)
camera.exif_tags['GPS.GPSAltitude'] = '%d/100' % int(100 * aalt)
# Convert speed from m/s to km/h and set tag.
speed = report.get('speed', 0.0)
print "speed (m/s): " + `speed`
camera.exif_tags['GPS.GPSSpeedRef'] = 'K'
camera.exif_tags['GPS.GPSSpeed'] = '%d/1000' % int(3600 * speed)
# Set direction of motion and direction along which the picture is taken (assuming frontal view).
track = report.get('track', 0.0)
print "gpsd track " + `track` + " degrees"
print "redirect " + `redirect` + " degrees"
track = track + redirect
if track > 360:
track = track - 360
elif track < 0:
track = track + 360
print "real track " + `track` + " degrees"
camera.exif_tags['GPS.GPSTrackRef'] = 'T'
camera.exif_tags['GPS.GPSTrack'] = '%d/10' % int(10 * track)
camera.exif_tags['GPS.GPSImgDirectionRef'] = 'T'
camera.exif_tags['GPS.GPSImgDirection'] = '%d/10' % int(10 * track)
# Set GPS latitude.
lat = report.get('lat', 0.0)
print "latitude: " + `lat`
camera.exif_tags['GPS.GPSLatitudeRef'] = 'N' if lat > 0 else 'S'
alat = math.fabs(lat)
dlat = int(alat)
mlat = int(60 * (alat - dlat))
slat = int(6000 * (60 * (alat - dlat) - mlat))
camera.exif_tags['GPS.GPSLatitude'] = '%d/1,%d/1,%d/100' % (dlat, mlat, slat)
# Set GPS longitude.
lon = report.get('lon', 0.0)
print "longitude: " + `lon`
camera.exif_tags['GPS.GPSLongitudeRef'] = 'E' if lon > 0 else 'W'
alon = math.fabs(lon)
dlon = int(alon)
mlon = int(60 * (alon - dlon))
slon = int(6000 * (60 * (alon - dlon) - mlon))
camera.exif_tags['GPS.GPSLongitude'] = '%d/1,%d/1,%d/100' % (dlon, mlon, slon)
# Provide image file name.
filename = now.strftime('%s') + '.jpg'
print "filename: " + `filename`
return filename
def move_and_shoot(directon):
m.move_to(directon)
camera.capture(gpsexif(directon))
sleep(1)
def Start():
global started #use global variable
if not started:
started = True
StartLoop()
def Stop():
global started #use global variable
if started:
started = False
def StartLoop():
global started #use global variable
while started:
move_and_shoot(-45)
move_and_shoot(-90)
move_and_shoot(-135)
move_and_shoot(45)
move_and_shoot(90)
move_and_shoot(135)
move_and_shoot(0)
if GPIO.event_detected(12):
print "Button pressed, stopping ..."
Stop()
# Connect to gpsd.
gpsd = gps.gps(mode=gps.WATCH_ENABLE)
# Initialize camera
camera = picamera.PiCamera()
camera.vflip = True
GPIO.setmode(GPIO.BCM)
# set GPIO pins
m = Motor([24,25,8,7])
m.rpm = 10
print "Motor setup: Pause in seconds: " + `m._T`
GPIO.setup(12, GPIO.IN, pull_up_down=GPIO.PUD_UP)
started = False
try:
GPIO.add_event_detect(12, GPIO.BOTH, bouncetime=200)
while True:
print "waiting for button press ..."
if (GPIO.event_detected(12) and not started):
print "Button pressed, starting ...."
Start()
sleep(2)
except KeyboardInterrupt:
print "CTRL+C entered, exiting ..."
finally:
camera.close()
GPIO.cleanup()