A home intrusion detection setup (sort of)

And Yet It Sees

The second thing I had to deal with was vision. I could have used vision literally in my project by hooking up a camera and using the Motion [3] program to check differences between one image and the next, because I had a camera I could have taken apart. (I did eventually use it, but not for that purpose, as you will see later in Figure 7.) The problem is that I had an ultrasonic sensor, and I wanted to play with it.

The sensor is an old SeeedStudio Ultrasonic Sensor V1.0, as shown in Figure 6. Originally, I thought I would have to break out my trusty Digispark [4], assuming the sensor would deliver the distance as an analogical number back over the wire with the distance in centimeters, microns, or whatever.

Figure 6: The rather old-fashioned SeeedStudio Ultrasonic Sensor V1.0 only has three pins.

Not so. An ultrasonic sensor is much more basic than that, and your software has to do the heavy lifting. It calculates the distance in the same way you would a thunderstorm: You send a pulse over the pin to the sensor that makes the speaker emit a very short ultrasonic blast. Then, you start counting.

When the microphone picks up the echo, you can calculate the distance to the nearest object, taking into account the speed of sound. Sound travels at about 34,000cm/sec. If the sensor captures the echo, say, 0.001 seconds after it was sent, the pulse has traveled 34 centimeters. But, the ultrasonic pulse travels to the object and then bounces back, so you have to divide that figure by two to get the real distance to the object: 17cm.

Many newer ultrasonic sensors have four pins: one for power, one for ground, and then an output pin that receives the pulse from one of the Pi's GPIO pins and an input pin to capture the echo and send a HIGH signal to the Pi when it arrives. This allows you to set up the two GPIOs – one as output and another as input – at the beginning of your program, so you don't have to faff about with that later when timing is of the essence.

But, not my V1.0! The V1.0 only has three pins: The sensor pin is used for input and output, so you have to set your GPIO to output, send the pulse out, switch the pin to input, start counting, and then wait for the echo. If you're not careful about the order in which you do all of the above, you'll find that the pulse has come and gone before you start listening, and your program will loop forever waiting for a sonic pulse that's already history.

After a lot of trial and error, I managed to get things to work with a program like you can see in Listing 3.

Listing 3

sonar.py

01 import RPIO, time
02
03 sonic_pin=17
04 stop=time.time()
05
06 # Shoot pulse
07 RPIO.setup(sonic_pin, RPIO.OUT, initial=RPIO.LOW)
08 time.sleep(0.5)
09 RPIO.output(sonic_pin, True)
10 time.sleep(0.00001)
11 RPIO.output(sonic_pin, False)
12
13 # Capture pulse
14 RPIO.setup(sonic_pin, RPIO.IN)
15
16 while RPIO.input(sonic_pin)==0:
17         start = time.time()
18
19 while RPIO.input(sonic_pin)==1:
20         stop = time.time()
21
22 # Calculate pulse length
23 elapsed = stop-start
24
25 # Calculate distance in cms.
26 distance = elapsed * 17000
27
28 print distance
29
30 # Reset GPIO settings
31 RPIO.cleanup()

If you run the script with

sudo python sonar.py

it will print out the distance to the nearest object in centimeters, which is much cooler than using a tape measure.

Bear in mind that this program works for a sensor that has its input and output going through the same pin. If you have a more modern sensor with four pins, you will be able to separate the input and output onto two separate pins.

Mounting the ultrasonic sensor proved a challenge. I ended up carving a piece of cardboard from a box, screwing it to the plaque, and then sticking the sensor to the cardboard with Blu-Tack. You can see the result of my handiwork in Figure 7.

Figure 7: The ultrasonic sensor is stuck to the turret using Blu-Tack. The camera is screwed to the plaque using the screw that held its original casing together.

All Together Now!

The next step is making the servo and ultrasonic sensor work together. I decided that my turret would have two modes: scanning and surveillance. First, it would scan the environment, calculating the distance to objects in its 180º arch. That's what's going on in lines 25 to 36 in Listing 4. In the setup() function block of code (lines 12-23), you call the scan() function three times (I'll explain why later) and rotate the turret first counterclockwise, then clockwise, and finally counterclockwise again, measuring the distance in each position as you go.

Listing 4

surveillance.py

001 #!/usr/bin/env python
002
003 import RPIO.PWM as PWM
004 import RPIO, time, random, os, shutil
005 import pygame.camera
006 from PIL import Image
007
008 import pygame.image
009 pygame.camera.init()
010 cam = pygame.camera.Camera(pygame.camera.list_cameras()[0])
011
012 def setup(start, end, resolution):
013
014   distance={}
015
016   d1=scan(start, end+1, resolution)
017   d2=scan(end, start-1, -resolution)
018   d3=scan(start, end+1, resolution)
019
020   for i in d1.keys():
021     distance[i]=int((d1[i]+d2[i]+d3[i])/3)
022
023   return distance
024
025 def scan(start, end, step):
026   servo = PWM.Servo()
027   servo_pin=2
028   distances={}
029
030   for pos in range (start, end, step):
031     servo.set_servo(servo_pin, pos)
032     distances[pos]=getDistance()
033
034   servo.stop_servo(2)
035
036   return distances
037
038 def moveTo(pos):
039   servo = PWM.Servo()
040
041   servo_pin=2
042   servo.set_servo(servo_pin, pos)
043   return int(getDistance())
044
045   servo.stop_servo(2)
046
047 def getDistance():
048   RPIO.cleanup()
049
050   sonic_pin=17
051   stop=time.time()
052
053   # Shoot pulse
054   RPIO.setup(sonic_pin, RPIO.OUT, initial=RPIO.LOW)
055   time.sleep(0.5)
056   RPIO.output(sonic_pin, True)
057   time.sleep(0.00001)
058   RPIO.output(sonic_pin, False)
059
060   # Capture pulse
061   RPIO.setup(sonic_pin, RPIO.IN)
062
063   while RPIO.input(sonic_pin)==0:
064     start = time.time()
065
066   while RPIO.input(sonic_pin)==1:
067     stop = time.time()
068
069   # Calculate pulse length
070   elapsed = stop-start
071
072   # Get the distance in cms.
073   distance = elapsed * 17000
074
075
076   # Reset GPIO settings
077   RPIO.cleanup()
078
079   return distance
080
081 def startCam ():
082   global cam
083   cam.start()
084
085 def stopCam ():
086   pygame.camera.quit()
087
088 def takePic():
089   global cam
090   img = cam.get_image()
091   pygame.image.save(img, "/tmp/photo.bmp")
092
093   im=Image.open("/tmp/photo.bmp")
094   newImName=time.strftime("%y_%m_%d_%H_%M_%S") + ".png"
095   im.save("/tmp/" + newImName)
096
097   shutil.copy("/tmp/" + newImName, "/home/pi/.secretdirectory/" + newImName)
098
099 if __name__ == "__main__":
100
101   # Spin around and get
102   distance=setup(550, 2450, 100)
103
104   # Start watching
105   startCam()
106
107   while True:
108     pos = random.choice(distance.keys())
109     watched = moveTo(pos)
110     if  (watched < distance[pos] - 10) or (watched > distance[pos] + 10):
111       print "Something changed at %s. Original: %s, New: %s " % (pos, distance[pos], watched)
112       # Take photo
113       takePic()
114       distance[pos] = watched
115
116   stopCam()

Real-world data is notably imprecise. If the servo is not exactly in the right place when taking the measurement, the measurement will be wrong. The solution is to measure distances for each position three times, just to make sure (lines 16-18) and then average them out (line 21). The final list of measurements, along with the position at which they were taken indexed as keys, are stored in the Python dictionary construct distance.

When you're done scanning, the turret goes into surveillance mode (lines 107-114). In surveillance mode, it rotates to random positions (line 108) and checks to see whether anything has moved from its original position (line 110). If a sizable difference exists in the measurement (more than 10cm), the program takes a photo with the webcam, replaces the original value in the dictionary, and continues watching.

Buy this article as PDF

Express-Checkout as PDF

Pages: 8

Price $2.95
(incl. VAT)

Buy Raspberry Pi Geek

SINGLE ISSUES
 
SUBSCRIPTIONS
 
TABLET & SMARTPHONE APPS
Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content