Optical mouse odometer for robots using Raspberry Pi

One of my university projects involved developing a search and rescue autonomous robot using a Raspberry Pi. The logic deployed for the application required the precise position of the robot. The usual methods for determining the position of the robot is to use a stepper motor (not cheap) or an encoder (didn’t want to interpret the IO signals on a Raspberry Pi). Using an optical mouse odometer would be a cheap solution for the problem as the surface of the game area for the robot is flat.

John Graham’s post about using a optical mouse odometer served as an inspiration. However, he suggests using the signals directly from the sensors, again interpreting the signals on a Raspberry Pi and it requires a mouse with a particular optical sensor.

Mouse data

The file /dev/input/mice provides data in a PS/2 format for the USB mouse. The python code demonstrates the methods of accessing the position of the mouse. The file access is “blocking” and returns a relative position value whenever there is a mouse event.

import struct, os

file = open( "/dev/input/mice", "rb" );

point_x = 0;
point_y = 0;

class Point:
	x = 0.0
	y = 0.0

def getMouseEvent():
  	buf = file.read(3); 
  	x,y = struct.unpack( "bb", buf[1:] );
  	dis = Point();
  	dis.x = x;
  	dis.y = y;
  	return dis;

while( 1 ):
  	dis = getMouseEvent();
	point_x = point_x + dis.x;
	point_y = point_y + dis.y;
	print ("%d  %d" % (point_x, point_y));
file.close();

This method will work for any standard USB Mouse connected to a Raspberry Pi.

Running as a service

Accessing the mouse /dev/input/mice from multiple locations could result in a conflict in the file access and missing of a mouse event. So I created a background process that acquires the relative position data, converts into an absolute value with a scaling and puts it onto one or more FIFOs (Named pipes) for access by other applications.

The python code writes the calculated position onto a FIFO. Refer to this post for better understanding of FIFOs.

import struct, math, os, errno

file = open( "/dev/input/mice", "rb" );
output = "mouse_FIFO";

point_x = 0;
point_y = 0;
scaling = 0.046875; #determine the scaling based on trial and calibration

class Point:
	x = 0.0
	y = 0.0

def getMouseEvent():
  	buf = file.read(3);
  	x,y = struct.unpack( "bb", buf[1:] );
  	dis = Point();
  	dis.x = x;
  	dis.y = y;
  	return dis;

while( 1 ):
  	dis = getMouseEvent();
	point_x = point_x + (scaling * dis.x);
	point_y = point_y + (scaling * dis.y);
	
	try:
		pipe = os.open(output, os.O_WRONLY | os.O_NONBLOCK);
		os.write(pipe, "%d %d" % (point_x,point_y));
		os.close(pipe);
	except OSError as err:
		if err.errno == 6:
			pass;
		else:
			raise err;
file.close();

The following C++ function demonstrates accessing the FIFO for determining the absolute position of the robot. However note the data can be accessed from any other programming language.

Point robot_position(){
    FILE* fp;
    char readbuf[10];

    fp = fopen("mouse_FIFO", "r");
    fgets(readbuf, 10, fp);
    fclose(fp);

    char *pch;

    pch = strtok(readbuf," ");
    int x = atoi(pch);
    pch = strtok (NULL, " ");
    int y = atoi(pch);
    
    return Point(x, y);
}

Hope this examples provided you with a cheap solution for determining the position of a robot. Mind that the cheapness comes with its restrictions, an optical mouse requires a proper surface to achieve proper movement data.

Leave a Reply

Your email address will not be published.