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


01 import RPIO, time
03 sonic_pin=17
04 stop=time.time()
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)
13 # Capture pulse
14 RPIO.setup(sonic_pin, RPIO.IN)
16 while RPIO.input(sonic_pin)==0:
17         start = time.time()
19 while RPIO.input(sonic_pin)==1:
20         stop = time.time()
22 # Calculate pulse length
23 elapsed = stop-start
25 # Calculate distance in cms.
26 distance = elapsed * 17000
28 print distance
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


001 #!/usr/bin/env python
003 import RPIO.PWM as PWM
004 import RPIO, time, random, os, shutil
005 import pygame.camera
006 from PIL import Image
008 import pygame.image
009 pygame.camera.init()
010 cam = pygame.camera.Camera(pygame.camera.list_cameras()[0])
012 def setup(start, end, resolution):
014   distance={}
016   d1=scan(start, end+1, resolution)
017   d2=scan(end, start-1, -resolution)
018   d3=scan(start, end+1, resolution)
020   for i in d1.keys():
021     distance[i]=int((d1[i]+d2[i]+d3[i])/3)
023   return distance
025 def scan(start, end, step):
026   servo = PWM.Servo()
027   servo_pin=2
028   distances={}
030   for pos in range (start, end, step):
031     servo.set_servo(servo_pin, pos)
032     distances[pos]=getDistance()
034   servo.stop_servo(2)
036   return distances
038 def moveTo(pos):
039   servo = PWM.Servo()
041   servo_pin=2
042   servo.set_servo(servo_pin, pos)
043   return int(getDistance())
045   servo.stop_servo(2)
047 def getDistance():
048   RPIO.cleanup()
050   sonic_pin=17
051   stop=time.time()
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)
060   # Capture pulse
061   RPIO.setup(sonic_pin, RPIO.IN)
063   while RPIO.input(sonic_pin)==0:
064     start = time.time()
066   while RPIO.input(sonic_pin)==1:
067     stop = time.time()
069   # Calculate pulse length
070   elapsed = stop-start
072   # Get the distance in cms.
073   distance = elapsed * 17000
076   # Reset GPIO settings
077   RPIO.cleanup()
079   return distance
081 def startCam ():
082   global cam
083   cam.start()
085 def stopCam ():
086   pygame.camera.quit()
088 def takePic():
089   global cam
090   img = cam.get_image()
091   pygame.image.save(img, "/tmp/photo.bmp")
093   im=Image.open("/tmp/photo.bmp")
094   newImName=time.strftime("%y_%m_%d_%H_%M_%S") + ".png"
095   im.save("/tmp/" + newImName)
097   shutil.copy("/tmp/" + newImName, "/home/pi/.secretdirectory/" + newImName)
099 if __name__ == "__main__":
101   # Spin around and get
102   distance=setup(550, 2450, 100)
104   # Start watching
105   startCam()
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
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

Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content