So following on from my previous posts about visualising sensor data in Processing, I’m now looking at drawing 3D representations of the data recorded from the Sharp IR sensor – although can be any kind of range finder.
I started by rigging 2 servos, one to pan left to right, the other up to down. The latter is limited to only 60 degrees of movement. I’m going to represent each servo position in a grid to start with, so a grid 180 wide (x) by 60 tall (y). The distance measured from the sensor will form the z index position so how far/ close a point will appear.
The setup is pretty much the same, Arduino records and sends the sensor data to the serial port buffer along with the X and Y position of the servos. Processing then picks this up and populates an array. Now this time I’m using multi-dimensional arrays, that is one array that stores other arrays. I could make just one array of 10800 items (180 * 60) but its far more efficient to make an array of 180 items, each item being another array of 60 items to store the distance measured. So for every 1 degree I move left to right, I record the 60 positions of the up/ down servo and the reading taken.
To visualise this I started drawing squares using the QUAD_STRIP shape to produce a grid layout (image at the top of the post). Of course because I’m actually using polar co-ordinates (degrees) instead of cartesian (x, y) then what the sensor is reading can’t really be translated to a flat grid. Because when it measures something vertical the measurements will all be different and when translated to my flat grid, it would make anything vertical look slanted. Trouble is now we’re trying to view something in 3D space so it doesn’t look all that great – so I’ve added in some animation to rotate the object slightly, looks fine when you zoom in close enough to one section but otherwise it’s just a random shape.
There are 2 ways around this, firstly we can convert our co-ordinates to cartesian. Or we can alter the grid used to display our data, the shape needed is basically a 60 degree arc thats lathed 180 degrees – so it kind of looks like a tyre wall. The radius used to draw our 60 degree arc is calculated by the distance taken from the sensor.
Shopping List
SRF05 Ultrasonic range finder or Sharp GP2Y0A02 IR sensor (basically any kind of distance sensor)
Arduino Deumilanove w/ ATMEGA328
Breadboard / Prototyping board
Jumper/ Connector wires
2x Servos (has to need no more than 5v supply)
C shaped Servo brackets (If anyone can make these cheap enough let me know!)
The Circuit
Using the below posts you should be able to figure out how to wire the sensor and how to use the servos. I use digital pins 9 and 10 for the servos and analog pin 1 for the sensor reading.
Stuff with SRF-05
Stuff with Servos
How to use the Sharp IR range finder
Arduino Sketch
We use the servo libary and a couple of FOR loops to control 2 servos, for every degree panned we tilt the other servo through 60 degrees taking a series of sensor readings for each position and averaging them. We output these values with the X and Y position in degrees to the serial port buffer. At the end of each loop we reset the servo position. If the sensor reading is outside the range of operation of the servo we also handle this.
/* luckylarry.co.uk 3D Scan Visualisation for Sharp GP2Y0A02 IR range finder Sends sensor readings for every degree moved by the servo values sent to serial port to be picked up by Processing */ #include <Servo.h> // include the standard servo library Servo leftRightServo; // set a variable to map the servo int leftRightPos = 0; // set a variable to store the servo position const int numReadings = 10; // set a variable for the number of readings to take int index = 0; // the index of the current reading float total = 0; // the total of all readings must be a float to allow totaling of float values int average = 0; // the average int IRpin = 1; // analog pin for reading the IR sensor /* setup the pins, servo and serial port */ void setup() { leftRightServo.attach(9); // initialize the serial port: Serial.begin(9600); } /* begin rotating the servo and getting sensor values */ void loop() { for(leftRightPos = 0; leftRightPos < 180; leftRightPos++) { // going left to right. leftRightServo.write(leftRightPos); for (index = 0; index<=numReadings;index++) { // take x number of readings from the sensor and average them float volts = analogRead(IRpin)*0.0048828125; // value from sensor * (5/1024) - if running 3.3.volts then change 5 to 3.3 float distance = 65*pow(volts, -1.10); // worked out from graph 65 = theretical distance / (1/Volts)S - luckylarry.co.uk total = total + distance; // update total delay(20); } average = (int) total/numReadings; // create average reading CAST TO INT!! remove the decimal places if (index >= numReadings) { // reset the counts when at the last item of the array index = 0; total = 0; } Serial.print("X"); // print leading X to mark the following value as degrees Serial.print(leftRightPos); // current servo position Serial.print("V"); // preceeding character to separate values Serial.println(average); // average of sensor readings } /* start going right to left after we got to 180 degrees same code as above */ for(leftRightPos = 180; leftRightPos > 0; leftRightPos--) { // going right to left leftRightServo.write(leftRightPos); for (index = 0; index<=numReadings;index++) { float volts = analogRead(IRpin)*0.0048828125; // value from sensor * (5/1024) - if running 3.3.volts then change 5 to 3.3 float distance = 65*pow(volts, -1.10); // worked out from graph 65 = theretical distance / (1/Volts)S - luckylarry.co.uk total = total + distance; delay(20); } average = (int) total/numReadings; if (index >= numReadings) { index = 0; total = 0; } Serial.print("X"); Serial.print(leftRightPos); Serial.print("V"); Serial.println(average); } }
Processing Sketch
We now listen to the serial port and each time there is an event we take the values and populate an array based on the X and Y values sent from the Arduino code. We add this value on to the existing value and then for each complete mapping we work out the average value – so the longer it runs for the more normalised the results should appear.
Using some basic trigonometry we have a class that makes an arc. Our draw() method calls this arc and translates/ rotates it for each of the 180 degrees. The arc itself is calculated from using the distance reading from the sensor as its radius.
If using a grid instead of this shape then we just say at point X,Y set the Z value instead of worrying about radius’s etc…
/* luckylarry.co.uk 3D Scan Visualisation for Sharp GP2Y0A02 Maps out an area of what the GP2Y0A02 sees into a 3D view */ import processing.serial.*; // import serial library Serial myPort; // declare a serial port int x, y, z; // variable to store x and y co-ordinates for vertices int value = 0; // value from sensor int[][][] data = new int[181][61][1]; // create an array to store each new sensor value for each servo position int count; int average = 1; PFont myFont; // setup fonts in Processing int numberOfSegments = 179; createArc Arc; // create an instance of the Arc class float radius = 150; float angle = 0; void setup(){ size(640, 360, P3D); Arc = new createArc(); // assign the Arc classes to an object myFont = createFont("verdana", 12); textFont(myFont); // setup the serial port and buffer myPort = new Serial(this, Serial.list()[1], 9600); myPort.bufferUntil('\n'); } void draw(){ background(255); float tempX = 0, tempY = 1, tempZ = 0; fill(200); lights(); translate(width/2, 200, 110); angle = 180.0 / numberOfSegments; rotateY(count * PI/10800); for (int j = 0; j < numberOfSegments; j++){ tempZ = cos(radians(angle))*radius; tempX = sin(radians(angle))*radius; pushMatrix(); translate(tempX, tempY, tempZ); // move each instance we create of the arc object to a slightly new position and rotation rotateY(radians(angle)); Arc.create(j); popMatrix(); angle += 180.0/numberOfSegments; } } // creates an arc object using QUAD_STRIP class createArc { // pass values to create method to know which array to load 1 to 180... void create(int degree){ pushMatrix(); rotateY(radians(135)); beginShape(QUAD_STRIP); for(int a=60; a < 120;a++) { float x1 = cos(radians(((90+a))))*(data[degree][a-60][0]/average); float y1 = sin(radians(((90+a))))*(data[degree][a-60][0]/average); float x2 = cos(radians(((90+a))))*(data[degree+1][a-60][0]/average); float y2 = sin(radians(((90+a))))*(data[degree+1][a-60][0]/average); vertex(x1,y1,100); vertex(x2,y2,105); } endShape(); popMatrix(); } } /* get values from serial port */ void serialEvent (Serial myPort) { String xString = myPort.readStringUntil('\n'); // read the serial port until a new line if (xString != null) { // if theres data in between the new lines xString = trim(xString); // get rid of any whitespace just in case String getY = xString.substring(1, xString.indexOf("X")); // get the value of the servo position String getX = xString.substring(xString.indexOf("X")+1, xString.indexOf("Z")); // get the value of the sensor reading String getZ = xString.substring(xString.indexOf("Z")+1, xString.length()); // get the value of the sensor reading x = Integer.parseInt(getX); // set the values to variables y = Integer.parseInt(getY); z = Integer.parseInt(getZ); data[x][y][0] += z; //println(z); // for debugging count++; if (count > 10800) { count = 0; average++; } } }
The Results
So here’s a quick screen grab of the 3D object that represents what the sensor sees, looks pretty confusing but if you zoom in and take a small section of it then it’s a bit better. Other than that its just a random shape!
This content is published under the Attribution-Noncommercial-Share Alike 3.0 Unported license.
- Arduino + Processing: Make a Radar Screen – Part 3: Visualising the Data from Sharp Infrared Range Finder
- Arduino + Processing: Make a Radar Screen to Visualise Sensor Data from SRF-05 – Part 2: Visualising the Data
- Arduino + Processing: Make a Radar Screen to Visualise Sensor Data from SRF-05 – Part 1: Setting up the Circuit and Outputting Values
- Arduino: Using a Sharp IR Sensor for Distance Calculation
- Arduino: Controlling the Robot Arm
- Arduino: Basic Theremin meets Processing!
- Arduino + Processing: Getting values from SRF05 ultrasound sensor & serial port
- Arduino: IR remote/ intervalometer for Nikon D80 DSLR (that means timelapse photography yarrr!)
- Using Processing to Send Values to Arduino
- 3 LED Crossfade with PWM and Arduino











Hey Larry,
This is very cool stuff! I had made something similar to your 2d sensor, but hadn’t thought to try 3d. What is the run time on one complete sweep? As I was trying to do this from a robot platform and was having issues getting enough quality samples quick enough to be able to react to the data before it was stale… ran in to wall before discovering it was there. Slow down you might say? Where is the fun in that!
Thanks for the article!
Nate
Hi Nate,
It takes a while for a full 180 sweep – it all depends on how accurate you want the data, mine takes about 3 minutes, which is a 10 20ms scans with the sensor and then averaged, even then its still very erratic. But it is interesting to see what the sensor is reading, helps me understand just how poor one sensor is for object detection.
The IR range finder is much better placed to detect smaller objects than the Ultrasound one but even then its still not great.
What I would like to do is use a laser/ barcode scanner to see if I can get better accuracy but it’d require more processing power than I can stick on a robot. Or alternatively build an array of sensors to get the time down as I wouldnt need to take multiple readings, the idea being lots of sensor look at the same area to build a better picture.
However, I want also to spend a bit of time investigating sending data wirelessly which may help and then get on to getting robots to build maps of their area.
Hi Larry, see you are taking it further. How about letting go of the radarbeam plot and instead only plot a rectangle in the location you measure ’something’, ie. the ‘end’ of the radar beam?
And then center the image as if looking from where your scanner is. This would give you some roman-theatre-seat-rows image looking as if photographed from its stage. You will need to z-buffer the data and plot the farthest rectangles first, overlaying them with rectangles positioned closer to the center of scanning. Maybe you can also brighten the color if you are closer to the scanner, so you get a color-depth cue too.
What this code does is plot the distance measured as a rectangle and draws this as a 3d object but in an arc- but it doesnt center on where the sensor is looking at which would be cool.
So instead of an arc and extruding faces from the arc towards the center, just plot arbitary boxes with where an object is detected?
Like the idea of the colour changing as well
cheers for the ideas.
Look here: http://blogs.hebali.com/itp/?p=211 I did not see this when I posted my previous comments. Scout’s honour.
oh cool… doubt the IR sensor will do things that well
I’d have to make a sensor array out of many of them to get a decent resolution.
But I do have a laser – I’ll need to get some sort of scanning done with that and a camera I guess… I think I could just find the laser line then try converting that to 3d spaces.
The word “coordinates” does not have a hyphen.
Hi Alex,
As one pedant to another thanks for pointing that out but why are you using double quotes around the word coordinates, what are you quoting? Thats surely just as bad for grammar and punctuation as is my spelling?
Larry