Aim

Design and build a set of calipers to measure the length of items, you will need to:

  • Move to a fixed position - fully open.
  • Slowly close the jaws, using an optical encoder to measure the distance travelled.
  • Since the LEGO optical sensor is reflective, you will have to determine its focal length - the optimal distance to place the encoder - by experimentation (unless you find it documented somewhere).
  • Stop when the object to be measured is engaged
  • Display the calculated length of the item on the RCX's LCD panel.

Design Stages

The code development for this assignment was in two parts. First some skeleton code was produced that monitored the rotating disc and incremented a global counter on each segment change. Once this was shown to work and the physical robot had been built, code was built round this to control the directional movement of the jaws and to use the counter to perform measurments.

Disk Monitoring

Using a task based approach, we started with a simple counter task. This continually monitored the light sensor and incremented a global counter on every transition between segments.

Once the physical enclosure for the encoder disk and the light sensor had been built, we could see what kind of values appear were read by the light sensor for dark and light regions. It was noted that the light was around 57 and dark around 33. The central point between these two is 45, and this reading was decided to be used as a 'THRESHOLD' to mark between light and dark.

With the threshold ascertained, it was then a simple matter of keeping track of wether we are moving from light to dar, or from dark to light in a while loop and incrementing counters and changing direction as necessary, ala:

span class="co1">//Dir is true - we are going "up"
     //Dir is false - we are going "down""
 

We were using the encoder disk with 16 segments, and to check the accuracy of the counter loop created a simple main function that beeped once per rotation. In experimentation we found it was acurate and didn't drift over time.

 

Object Collision Detection

Using the slip gear we made it so that the encoder disk stopped rotating when the object to be measured (or the end of the track) was reached. We could then detect this collision had happened by the stopping of the encoder disk transitions over a period of time. This meant we could implement our measuring system without needing the use of a switch to detect collisions.

This detection was done using one of the in-built system timers. On every transition we reset the timer, and if we detect the timer ever gets above a threshold ( 2 seconds we decided upon ) we change a variable to signal that the object has been encountered.

Searching and Resetting

Once the entire robot had been built, we put code around the counter task to control the motor and make the behaviours we wanted. We decided to have a 'Resetting' behaviour that would reset the jaws to the fully open position, and a 'Searching' behaviour that would search for the object from the fully open (reset) position.

It was decided that a push button would allow the user to reset the robot, and a second press would make it search. This was handled with a simple task that detects if the button has been pressed while we are not doing an action (button presses while a task is being performed are ignored to prevent accidental re-triggering).

span class="co1">//if button pressed and we arn't doing something already
 

Transitioning into the searching task also forces a reset of the count variable, so we can measure from 0 how far along the track we manage to go before encountering an object. We calibrated that the length of the track was 254mm, and that it took (on average) 123.67 counts to get from the start to the end (across a set of 6 readings). We used this to create a scaling factor of 2.1, so each click was equivalent to 2.1mm. Our theoretical-best accuracy was therefore ~0.2cm, which is 0.83% of the largest thing we could measure (which isn't too bad). We added code to perform the count-to-mm's conversion in the counter routine, and to display the result:

 
    /* Recalculate value and display it */
    value = LENGTH - (count * FACTOR);
    SetUserDisplay(value,1);

Full Code Listing

/*
 * Define useful inputs and outputs
 */
#define CMD_BUTTON SENSOR_1
#define LIGHT SENSOR_3
#define MOTOR OUT_C
 
/*
 * Define the different states we
 * can be in - searching or resetting
 */
#define STATE_RESET 0
#define STATE_SEARCH 1
 
/*
 * Define the actions we can be preforming,
 * No action, a resetting action, or a searching
 * action.
 */
#define ACTION_NONE 0
#define ACTION_RESETTING 1
#define ACTION_SEARCHING 2
 
/*
 * Define the threshold value for the light
 */
#define THRESHOLD 45
 
/*
 * Define some contents from experimental work.
 */
#define LENGTH 2542
#define NUM_CLICKS 123.67
 
#define FACTOR 21
 
//the current state
//the current action
//the current "measurement" (which is displayed)
//the count of the number of pulses
/*
 * The main task:
 * this initialises the system and then starts all the helper tasks off
 */
task main() {
 
     initialise();
 
     start motors;
     start counter;
     start motors;
     start button_monitor;
     
}
 
 
/* Motors Task
 * Make the motors do stuff as necessary
 *//* If we have changed action, reset the timer
	       used by the counter task *//* if ( action == ACTION_NONE ) */ {
                Off(MOTOR);
            }
 
     }
 
}
 
/* Button Monitor Task
 * Watch the button, and respond when it is pressed
 *///if button pressed and we arn't doing something already
/*
 * Counter Task
 * This monitors the light sensor and increments
 * the global variable count on every transition
 * from light->dark->light
 *///Dir is true - we are going "up"
     //Dir is false - we are going "down""
/* If it has been a while with no
		    transitions, then do the no-action
		    action *//* Recalculate value and display it */
                 value = LENGTH - (count * FACTOR);
                 SetUserDisplay(value,1);
     }
 
}
 
/*
 * Initialise method:
 * Sets up the sensors and the motors
 * Initialise the global variables to default values
 */