< async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"">

Saturday, September 20, 2014

A simple contact less human computer interface with ultrasonic transsducers and arduino uno or atmega328 [part 1]

ADVERTISEMENT
Arduino ultrasonic computer interface- as there are
 only two sensors the active area is small
In this simple project, i used a pair of ultrasonic sensors (HC-SR04 modules) with arduino uno to check weather they can be used to track the x and y cordinates in a two dimensional plane. This is a beginner stage of a project where the number of transducers can be extended to produce a 3d mouse based on sonic sensors. This can be used to control simple things without really touching the computer so that it can be used in kitchen , labs etc when hands are dirty. Similar things can be achieved with infrared sensor and better results with a pair of video camera (but needs a better hardware than atmega328) The setup is very simple and is shown in the figure below. Two HC-SR04 sensors are hooked up in to arduino uno. Connection is straight forward (all pins are marked on the sensor silk screen). Connect the grounds to the ground pin of the uno/atmega328 and the positive (vcc) pins to 5v pins on the arduino. It uses the NewPing library which can be downloaded here. A simple explanation on connecting and using the library can be read at arduino playground

The whole set up in action can be seen in the video below. Basically the limitation in the active pointer area is small due to the limited number of sensors used. But this can be used as a base and adding more sensors can improve the results. Currently i used it to plot on a gui based on python (matplotlib) and can be extended as a real mouse by adding a few more lines of code and the V-USB on arduino uno or using a leonardo/ newer version of micro-controllers which can act as a usb HID.




The arduino sketch is very simple and it basically sends x and y cordinates via serial to the computer and on the PC , just plot the x,y values on a 2d plane

Code

sonic_mouse.ino

// a simple sonic mouse, code adapted from New ping example
// Connect PIN 13 to Trigger of first sensor and 12 to the Echo pin
// Connect PIN 11 to Trigger of second sensor and 10 to the Echo pin
// If you use multiple sensor for x / y axis, use the max value from the read array along the axis
// Read more about it at http://blog.riyas.org

#include <NewPing.h>

#define SONAR_NUM     2 // Number or sensors.
#define MAX_DISTANCE 50 // Maximum distance (in cm) to ping.
#define PING_INTERVAL 33 // Milliseconds between sensor pings (29ms is about the min to avoid cross-sensor echo).

unsigned long pingTimer[SONAR_NUM]; // Holds the times when the next ping should happen for each sensor.
unsigned int cm[SONAR_NUM];         // Where the ping distances are stored.
uint8_t currentSensor = 0;          // Keeps track of which sensor is active.

NewPing sonar[SONAR_NUM] = {     // Sensor object array.
  NewPing(13, 12, MAX_DISTANCE), // Each sensor's trigger pin, echo pin, and max distance to ping.
  NewPing(11, 10, MAX_DISTANCE),
};

void setup() {
  Serial.begin(115200);
  pingTimer[0] = millis() + 75;           // First ping starts at 75ms, gives time for the Arduino to chill before starting.
  for (uint8_t i = 1; i < SONAR_NUM; i++) // Set the starting time for each sensor.
    pingTimer[i] = pingTimer[i - 1] + PING_INTERVAL;
}

void loop() {
  for (uint8_t i = 0; i < SONAR_NUM; i++) { 
    if (millis() >= pingTimer[i]) {         
      pingTimer[i] += PING_INTERVAL * SONAR_NUM;  
      if (i == 0 && currentSensor == SONAR_NUM - 1) SensorReadLoop(); 
      sonar[currentSensor].timer_stop();         
      currentSensor = i;                          
      cm[currentSensor] = 0;                      
      sonar[currentSensor].ping_timer(echoCheck); 
    }
  }
  // eXTRA  CONTROL CAN BE DONE HERE
}

void echoCheck() { 
  if (sonar[currentSensor].check_timer())
    cm[currentSensor] = sonar[currentSensor].ping_result;
}

void SensorReadLoop() { // ADDITIONALLY HERE you CAN ADD THE v-usb
  for (uint8_t i = 0; i < SONAR_NUM; i++) {
    Serial.print(cm[i]);
    Serial.print(" ");
  }
  Serial.print("\n");
}


Plotting the pointer data from arduino on the computer using python and matplotlib

Copy the following code in to a new file plot_mouse.py and execute it by typing python plot_mouse.py in a terminal. On windows, you need to modify it (first make sure to get the python serial and matplotlib working )

################################################################################
# plot_mouse.py
# Plot the data from sonic_mouse.ino on a pc
# It uses matplotlib
# install matplotlib, pyserial and numpy to get it working
# code adapted from https://gist.github.com/electronut/5641938
# more details on the project at http://blog.riyas.org
################################################################################

import sys, serial
import numpy as np
from time import sleep
from collections import deque
from matplotlib import pyplot as plt

# store the pointer data
class PointerData:
  def __init__(self, maxLen):
    self.ax = deque([0.0]*maxLen)
    self.ay = deque([0.0]*maxLen)
    self.maxLen = maxLen

  def addToBuf(self, buf, val):
    if len(buf) < self.maxLen:
      buf.append(val)
    else:
      buf.pop()
      buf.appendleft(val)

  def add(self, data):
    assert(len(data) == 2)
    self.addToBuf(self.ax, data[0])
    self.addToBuf(self.ay, data[1])  

class PlotData:
  def __init__(self, PointerData):
    plt.ion() 
    self.axline, = plt.plot(PointerData.ax,PointerData.ay,'r',linewidth=10)
    mng = plt.get_current_fig_manager()
    mng.resize(*mng.window.maxsize())    
    plt.ylim([0, 2000])
    plt.xlim([0, 2000])
  def update(self, PointerData):
    self.axline.set_xdata(PointerData.ax)
    self.axline.set_ydata(PointerData.ay)
    plt.draw()

# main() function
def main():
  if(len(sys.argv) != 2):
    print 'Example usage: python plot_mouse.py "/dev/ttyACM0"'
    exit(1)

  strPort = sys.argv[1];
  PointerData = PointerData(4)
  PlotData = PlotData(PointerData)
  # open serial port
  ser = serial.Serial(strPort, 115200)
  while True:
    try:
      line = ser.readline()
      #print line
      data = [float(val) for val in line.split()]
      if(len(data) == 2):
        PointerData.add(data)
        PlotData.update(PointerData)
    except KeyboardInterrupt:
      print 'exiting'
      break
  ser.flush()
  ser.close()
# call main
if __name__ == '__main__':
  main()

end

No comments:

Post a Comment