Skip to content


Visualizing Sensor Data with Arduino and Processing

sensor_on_bot Visualizing Sensor Data With the Arduino using Processing and an Infrared Distance Sensor. This article was submitted by Cory Barton as part of the uCHobby giveaway program. Cory shows us how to combine the Arduino, sensors, and the Processing environment to visualize real world sensor measurements.

In this first picture we see an IR sensor Mounted to a servo on small robot

 

Introduction

I recently acquired a few Sharp GP2Y0A21YK0F IR distance sensors. This is an inexpensive proximity sensor which can detect objects from 10-80cm. A nice tutorial  on this sensor can be found at robotroom.com. These sensors only detect objects within a narrow beam, so I decided to mount mine on a servo, so that I could pan the sensor approximately 180 degrees, and take multiple readings to build up an idea of what obstacles are in front of my robot. I like to visualize things, so I decided to write a small program in processing to visualize the sensor data for debugging and to help me better understand what the sensor is seeing.

For this project I used an Arduino Diecimila, A standard hobby servo, and a Sharp IR sensor. I used Version 0013 of the Arduino IDE, and version 1.0.1 of the Processing IDE. This article assumes that you have some basic bread boarding experience, can program your Arduino, and have some basic programming experience.

Testing the IR Sensor 
The very first step in this process is testing the IR sensor. I attached the sensor output to analog pin 0, power and ground and loaded the following bit of code into the Arduino.

/***********************************************
* ir_sensor_test
* reads analog output of Sharp IR sensor
* and sends to value to serial port
* Max analog output is about 3V at 5cm distance
* ATD has 10 bit resolution, so max 5v = 1024
* 3v will give about 630
************************************************/

#define BAUDRATE 9600
#define IRPIN 0 //analog pin for reading the IR sensor
#define WAIT 300 //milliseconds to delay

int val = 0;       // variable to store the value coming from the sensor

void setup() {
  Serial.begin(BAUDRATE);
}

void loop() {
  val = analogRead(IRPIN);    // read the value from the sensor
  Serial.println(val);        // print the raw analog value to serial port
  delay(WAIT); // stop the program for some time
}

Assuming that you have properly wired your test circuit, this code should give you analog values between 0 and 600ish, with the higher numbers indicating a closer object.

For the next part of this project, you will have to mount your IR sensor on a servo. I have a circular servo attachment, and I mounted the sensor to it using some double sided tape that I had handy.

Multiple Servos

The servo library that comes with the Arduino IDE has support for only two servos, so if you need to run more than two servos, you will need to find another alternative. My sensor is mounted on a robot base with continuous rotation drive servos, so I need support for more than two servos. I am using the ServoTimer2 library which has support for up to eight servos. With a little modification, the code in this article could be adapted to any of the available servo control libraries. I like this library since it uses interrupts, and it allows direct control of the pulse width which is sent to the servos. If the previous sentence did not make any sense to you, Seattle Robotics Society has an excellent article explaining how a servos work, and how they are controlled. Servos will generally go to a minimum position when given a 1.0 millisecond pulse, go to center with a 1.5 millisecond pulse, and a maximum position when given a 2.0 millisecond pulse. However, you will find that individual servos vary. The servo I am using does not reach its minimum position until it gets a 0.7 millisecond pulse, and only reaches its maximum when given a 2.3 millisecond pulse. Experiment with pulse widths to find your servo’s limits. You may also find that your servo does not use all of its mechanical travel. I have a mini servo which would be a much more appropriate servo for a light load application like panning a sensor, but it does not have a full 180 degrees of travel when commanded electrically, even though it is mechanically capable of 180 degree rotation.

Sensor panning back and forth(Note the motion blur) Sensor Panning Code

The following Arduino program pans the servo and reports the pulse width sent to the servo, distance reading, and step in the panning process. The program can be configured by changing the define statements. Set the MIN_PULSE, and MAX_PULSE defines to use all of your servos travel. You can change the number of readings taken by the sensor by adjusting NUM_STEPS, but be aware that NUM_STEPS must be set to 17 in order to work properly with the accompanying processing program. READWAIT specifies how long to wait for the servo to finish moving before taking a reading from the distance sensor. With my setup 150 milliseconds is shortest delay I can use and get consistent readings from the sensor.

This program uses millis() in order to control timing. I use millis() instead of the delay() function because all processing halts when using delay(). By using millis() other things can be done by the processor while waiting for the servo to finish moving. From here, the comments should be enough to guide you. Once you load this code onto your Arduino, observer your sensor to see if it pans properly, and use the serial console to verify that the program is working properly. The serial string should be “PULSE,DISTANCE,STEP”. You should see something like this:serial_console

/*****************************************************************************
*ir_sensor_pan_test - this pans an IR distance sensor and sends the sensor readings
*via the serial port. configure the program by changing the constants: MIN_PULSE,
*MAX_PULSE, NUM_STEPS, and READWAIT.
*
* Inputs: Sharp GP2Y0A21YK0F IR distance sensor (front mount ANALOG PIN 0)
*Outputs: IR pan servo (DIGITAL PIN 6) PAN servo has about 176 degrees of rotation
********************************************************************************/
/**********INCLUDES***************************/
#include <ServoTimer2.h>

/**********DEFINES****************************/
//input pins
#define FRONT_IR_PIN 0

//output pins
#define FR_PAN_SERVO_PIN 6

//serial
#define BAUDRATE 38400

//servo defines
#define MIN_PULSE 700  //min command pulse width
#define MAX_PULSE 2300 //max command pulse width

//panning control defines
#define NUM_STEPS 17   //number of steps to pan servo
#define READWAIT 150   //number of milliseconds to wait before reading IR sensor on panning servo

//do not change the following defines
#define PAN_STEP ((MAX_PULSE - MIN_PULSE) / (NUM_STEPS - 1) ) //when panning, change pulse by this many uSeconds
#define MAX_SEND_PULSE (MIN_PULSE + PAN_STEP * (NUM_STEPS - 1))

#define UP   1 //count directions for panning
#define DOWN 0

/**********GLOBAL VARIABLES******************/
ServoTimer2 servoFrontIRPan; //servos
//servo command vars
int frontPan = MIN_PULSE; //set pan servo to min
int step = 1; //set current step
char countDir = UP; //initialize to count up
int frontIRVal; //ir sensor value
unsigned long time; //store time here for timing from time to time

void setup() {
  Serial.begin(BAUDRATE);

  //attach the servos
  servoFrontIRPan.attach(FR_PAN_SERVO_PIN);
  servoFrontIRPan.write(frontPan); //initialize pan servo postion
  delay(200); //short delay to allow servo to reach position

  time = millis(); //set time = now
}

void loop() {
  if (millis() >= time + READWAIT) { //Have at least READWAIT miliseconds passed?
    frontIRVal = analogRead(FRONT_IR_PIN); //read IR sensor
    Serial.print(frontPan); //send info via serial
    Serial.print(",");
    Serial.print(frontIRVal);
    Serial.print(",");
    Serial.println(step);
    pan(); //move the sensor

    time = millis(); //record current time
  }
}

void pan() { //we move the servo with this function
  //calculate front pan servo pulse width and step
  if (frontPan == MIN_PULSE) countDir = UP; //change count direction at boundaries
  if (frontPan == MAX_SEND_PULSE) countDir = DOWN;
  if (countDir == UP) {
    frontPan += PAN_STEP;
    step += 1;
  }
  else {
    frontPan -= PAN_STEP;
    step -= 1;
  }

  //move servo
  servoFrontIRPan.write(frontPan);  }

Processing Code

sample-screenThe program in Processing reads the serial data from the Arduino, and creates a pie chart, where the location of a particular wedge in the chart corresponds to the sensor angle, and the radius of the wedge corresponds to the perceived distance. I have not made any effort to calculate the actual distance, I just simply subtract the actual sensor reading from the observed maximum reading(found by testing the IR sensor) to determine the radius in pixels. So if the sensor does not detect anything, the voltage reading will be zero volts and the radius of the associated pie wedge will be 630-0. If an object is about 5cm away from the sensor, the sensor should output about three volts, and the reading returned will be close to 630, so the radius of the associated pie wedge will be smaller. This program is hard coded for 17 pan steps, and 180 degrees of rotation. I have not taken the time to make it as robust as the Arduino program above. It does however work nicely for displaying the sensor data. If you want to use fewer pan steps or a smaller rotation for your sensor, you will have to modify this program, so that it works properly. In order to use this program, make sure that your Arduino is panning your servo and is connected to your computer. Make sure that you are not using the serial console in the Arduino IDE, and then start the processing sketch. If all is working correctly, after a full sweep of the servo, you should see something like the image here.

//I learned how to do the serial communication from the example code
//by Tom Igoe at: http://www.tigoe.net/pcomp/code/category/Processing/50

//serial variables
import processing.serial.*;
Serial myPort;    // The serial port:
int baudRate = 38400; //make sure this matches the baud rate in the arduino program.

int lf = 10;  // ASCII linefeed

//variables for displaying text
PFont myFont; // The display font:
int fontSize = 24;

//variables for drawing arcs
int diameter  = width - 20;
int numAngles = 17;

//angle values for drawing the arcs. Ideally, these would be calculated programmatically.
float[] startAngs = {180, 190.59, 201.18, 211.76, 222.35, 232.94, 243.53, 254.12, 264.71, 275.29, 285.88, 296.47, 307.06, 317.65, 328.24, 338.82, 349.41};

float[] stopAngs = {190.59, 201.18, 211.76, 222.35, 232.94, 243.53, 254.12, 264.71, 275.29, 285.88, 296.47, 307.06, 317.65, 328.24, 338.82, 349.41, 360};

float[] distances = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; //distance readings for each angle are stored here

//variables for data to graph
int servoPos, distance, step;

int maxDistRead = 630; //maximum reading returned by sensor at minumim distance
                       //this was determined by testing the IR sensor

void setup() {
  size(800, 400);
  smooth();
  noStroke();

  // List all the available serial ports:
  println(Serial.list());

  // I know that the first port in the serial list on my machine
  // is always my Arduino, so I open Serial.list()[0].

  // Open whatever port is the one you're using.
  myPort = new Serial(this, Serial.list()[0], baudRate);

  myPort.bufferUntil(lf);

  //set up the display text which will display the data recieved via serial
  myFont = createFont(PFont.list()[2], fontSize);
  textFont(myFont);

  noLoop(); //don't redraw unless serial data so draw() will not loop.

}

void draw(){
  background(200); //nice light gray here
  fill(100); //darker grey

  text("Dist: " + distance + " | Pos: " + servoPos + " | Step: " + step, 10,25); //this is the information recieved from the serial port.

  for (int i = 0; i < numAngles; i++){ //draw arcs representing sensor angle and percieved distance
    arc(width/2, height, distances[i], distances[i], radians(startAngs[i]), radians(stopAngs[i]));
  }
}

void serialEvent(Serial p) {
  int delimIndex = -1;
  String inString;
  String posString;
  String newString;
  String disString;
  String stepString;

  try { //just in case something goes wrong with the serial we wrap all of
        //this with a try statement, so the whole program will not die.

    inString = (myPort.readString());
    delimIndex = inString.indexOf(','); //find first comma
    if (delimIndex != -1) { //if we found first comma
      posString = inString.substring(0, delimIndex); //parse servo pulse
      newString = inString.substring(delimIndex+1, inString.length()-2); //length()-2 <- strips off the linefeed
      delimIndex = newString.indexOf(','); //find second comma

      if (delimIndex != -1) { //if we found second comma
        disString = newString.substring(0, delimIndex); //parse distance reading
        stepString = newString.substring(delimIndex+1, newString.length()); //parse
        servoPos = int(posString); //convert from nasty stringses to nice intses.
        distance = int(disString);

        step = int(stepString);

        distances[step-1] = maxDistRead - distance; //use the step to index the array of distances

        //subtracting 1 to account for zero indexed array of course.
        redraw(); //update display; calls draw()
      }
    }
  }
  catch(Exception e) { //DOH! something broke
    println(e); //print description of badness to console.
  }
}

Results

I have photographed two different test setups and combined them with the screen shot from the processing program. Note that the screen shot does not line up exactly with the test setup. This is partly due the fact that the screen shots have not been adjusted for scale and perspective when they were overlayed onto the image, and that the processing program assumes a 180 degree sensor rotation whereas actual rotation is actually about 175 degrees. merge1

[merge2

Posted in Arduino, Development Tools, Discovering, Ideas, Microcontroller, Projects.


10 Responses

Stay in touch with the conversation, subscribe to the RSS feed for comments on this post.

  1. Daniel Andrade says

    Amazing post! I will try this someday for sure :)

  2. Joseph Yumul says

    I wish i can do this on my RBBA :D

  3. Win Heagy says

    Very nice! This looks like an easy, cheap “vision” solution for inexpensive robots. Thanks.

  4. Cory Barton says

    @Joseph,

    Why can’t you? (use the RBBB)

  5. Tius says

    Nice post! You can also use sonar and build a radar :)

  6. Alan Parekh says

    Great idea to mount it on a servo. Nicely documented!

  7. Joseph Yumul says

    @ Cory Barton

    because until now it’s still a Bare Board LOL

Continuing the Discussion

  1. Infrared distance sensing with Arduino and Processing | SquareCows linked to this post on March 10, 2009

    […] Visualizing Sensor With Arduino and Processing […]

  2. How-To: Visualize sensor data with Arduino & Processing | thekevinpipe.com linked to this post on March 10, 2009

    […] prove quite useful for remote ‘exploratory missions’. Instructions and source code available at uC Hobby. In the Maker Shed: Make: Arduino Read more | Permalink | Comments | Read more articles in […]

  3. How-To: Visualize sensor data with Arduino & Processing » Developages - Development and Technology Blog linked to this post on March 10, 2009

    […] m&#105ss&#105ons’. Instruct&#105ons &#97nd source code &#97v&#97&#105&#108&#97b&#108e &#97t uC &#72&#111bb&#121. &#73&#110 the Make&#114 &#83hed: Make: […]