# Measuring distance using a webcam and a laser

After seeing mini radar project by Aron Horan last year (http://robohoran.wordpress.com/2013/11/27/mini-radar/), I decided to give it a go using a webcam and a laser. The reason for doing this is because using an ultrasonic sensor does not provide precise readings of gaps due to its large viewing angle, the Sharp IR sensor is another option but it produces a noisy output. This can be filtered out in code, but it still does not offer the precision and range needed for doing something like making a 2d map of a room. Part two of this project is spinning the webcam and laser and mapping a room, maybe eventually mounting it on a robot. There is a fair amount of information on webcam rangefinders on available online, most of it is fairly similar due to plagerisation and all that craic. The source I found most useful was a blog by Todd Danko https://sites.google.com/site/todddanko/home/webcam_laser_ranger.

Step 1: Get a webcam, get a laser , fix the webcam in place but leave the laser alone for now

Webcam and laser hot glued to a piece of wood

Calculating distance using this method works off basic trigonometry:

Webcam and laser trigonometry

Source of image and equations : https://sites.google.com/site/todddanko/home/webcam_laser_ranger

Step 2: grabbing images from the webcam and doing stuff with them

From the top equation we know that the distance to the object equals the distance between the webcam and laser , divided by the tan of theta. The next step is to work out how theta relates to the pixels contained in the webcam image. To work out theta we need to find the radians per pixel pitch of the webcam and the radian offset. These are constants for a particular webcam, the pixels from focal plane will change depending on how far away the image is. ( pixels from focal plane just means how many pixels the laser is from the centre of the image ). The OpenCV library for python was used to get images from the webcam and process them. Here is the code:

All the lines of code from line 1-31 are just setting up stuff, like which capture device is being used and defining variables. Note that if you run this program, you might have to edit line 15, 0 is the first device which in this case is my laptops webcam and then 1 was the webcam plugged in via usb. Line 16 is where things start to happen. vc.read() grabs a frame from the webcam and stores the pixel data for the image in a numpy array. In this case it stores it twice, in rval and frame but rval is just there for error checking. Frame is what we’re interested in. So all of the pixel data is stored in a numpy array called frame. By default opencv grabs a 640 x 480 image ( thats 640 on the x-axis and 480 on the y ). The numpy array has three dimensions, (x)(y)(BGR), so the first two dimensions have just one number in each specifying the x or y co-ordinate of the pixel. The third dimension contains three numbers, the RGB content of that pixel. Although for some reason the values are arranged BGR. If you wanted, you could manually scan though each pixel using nested for loops. I tried doing it that way but it tookseveral seconds to process one frame! Using numpy arrays to scan through the image takes a fraction of a second. For more information on how to use numpy arrays, check out http://docs.scipy.org/doc/numpy/reference/index.html. On line 36, that one line of code scans though the whole array only looking at the third element of the third dimension( the R of RGB). If any of those pixels contain a high red number ( the highest is 255, I just picked 237 by trial and error ), num will be true (1), if the pixel does not have a high red content num will be false (0). After the whole image has been checked we are left with an array of ones and zeros called num. The x-y co-ordinates of the ones are what we’re interested in, line 37 finds the x-y co-ords of all non zero elements of num. The laser dot is definitely going to take up more than one pixel, it’s likely to be tens of pixels. We want the centre of this circle of pixels, the next two lines take care of that. Okay so now that we have an x-y co-ordinate of the laser dot, working out how far away this point is from the center of the image is fairly simple. For a 640×480 image, the centre point will be (320, 240). Since the laser was mounted horizontally to the laser. The laser will move across the image horizantally, for this reason I only calculated the distance from the center of the x-axis. It’s probably better to comment out line 43 and just use line 42 instead it might be more accurate that way. Now that we have the laser dots distance from the center being printed to the screen, calibration can begin.

Step 3 : Calibrating the rangefinder

The laser has to be in the center of the image at its max distance. In my case it was the wall of my room, 2.35 meters away. So I lined the laser up to the center of the image and then glued it in place.
The way I calibrated it was by getting a measuring tape and holding the notepad at a known distance from the webcam. I then write down the distance in centimeters and the distance from center pixel readout on the screen.

Here are my values :

Remember, we are still looking for the variables that relate the laser dots distance from the center, to theta. Here’s the equation again:

We have two of those variables available to us, pfc was measured and theta can be worked out using inverse tan of h/d. If these two sets of numbers are plotted on a graph, the two constants rpc and ro can be obtained from the equation of the line.

Here are the two sets of numbers:

Now plot them on a graph, put a trendline throught the points and display the equation of the trendline:

Plot of pix_dist vs theta

By comparing equations, rpc = 0.001145 and ro = 0.0154.
Now we can work out theta for a given pfc ( laser dots distance from center ). From here just fill into the equation D = h / tan(theta), job done.

Here is a video of it in action:

Feel free to ask any questions!

This entry was posted in 2d mapping, Hardware, Python and tagged , , , , , . Bookmark the permalink.

### 7 Responses to Measuring distance using a webcam and a laser

1. EngrStudent says:

You can do rowsum (or colsum) to get close to region of max pixel. That can be fast.

If you find the region of maxness of the ifft2(fft2(image),fft2(dot,zeropad)) then you can fit a quadratic surface to it, find the analytic root, and go sub-pixel for your measurement.

• batchloaf says:

Hi EngrStudent,

This is an interesting idea, but can you explain a little bit more about how it would work please?

It kind of looks like you convolving the image with an impulse (the zeropadded dot) by IFFTing the product of their FFTs, but presumably I’m missing something because I don’t think convolving the original image with an impulse would actually change it at all. Am I reading it wrong?

Ted

2. vu2aeo says:

great project! i would like to ask you about accuracy vs distance. the lateral displacement of the laser dot is inversely proportional to the distance to the reflective surface. this is apparent when you demonstrate your calibration run. accuracy would therefore also keep reducing with distance. would that be correct? have you found a way around this?

• shaneormonde says:

Thats correct yeah. It’s caused by the fact that the camera lens is curved. You can account for this by finding two characteristics : the radians per pixel pitch and the radian offset of your webcam. I’ve outlined how to do that in this post, or you could check out Todd Danko’s post here: https://sites.google.com/site/todddanko/home/webcam_laser_ranger. This allows for linear measurements even though as you said, the lateral displacement of the dot is inversely proportional to the distance of the surface.

3. Hello, great projet!
Could you explain me why you multiply by 2 then divide by 2 in line 42?anks
Tanks

• wattnotions says:

Hi Jean Louis, it is the formula to calculate the distance between two points. I’m on my phone right now so i can’t easily type or out but if you Google “distance between two points formula” and it should come up. Also in that line of code it’s not dividing by two it’s raising it to the power of 0.5 which is the same a taking the square root. In python ** is exponential.

Shane