Micromouse Lab 2

Micromouse
Encoders

Tracking Wheel Movement

When we're trying to navigate a maze, we'd like to answer a few motor-related questions:

  • How quickly is our robot driving?
  • Is it driving straight?
  • How far has it driven?

To answer these questions, we'll use the quadrature encoder sensors mounted on the back of your motors. These are comprised of a spinning magnet and two Hall effect sensors. In the case below, the Hall sensors output:

  • HIGH when they're closer to the north pole of a magnet
  • LOW when they're closer to the north pole of a magnet
Hall Sensor Pic

The two waveforms next to the picture plot the signal (y-axis) from our Hall sensors with respect to time (x-axis).

Let's try reading from an encoder! With your left motor plugged in, try viewing the output of following code in the Serial Plotter:

#define PIN_ENCODER_LEFT_A 7
#define PIN_ENCODER_LEFT_B 2

void setup() {
  pinMode(PIN_ENCODER_LEFT_A, INPUT);
  pinMode(PIN_ENCODER_LEFT_B, INPUT);

  Serial.begin(9600);
}

void loop() {
  Serial.println(digitalRead(PIN_ENCODER_LEFT_A));
}

Spin the motor by hand, at various speeds. Note that by reading just one input, it’s impossible to distinguish between clockwise and counterclockwise rotations.

Checkoff #1

  1. What do you see in the Serial monitor when the motor is stationary?
  2. What happens to the output when you spin the motor faster? Why?
  3. Why do we need two hall effect sensors per motor? What would be wrong if we only had one?
  4. Refer to the two waveforms in the graphic above. Which direction is the motor spinning? How do you know?

Polling

To calculate how far our micromouse has travelled, we can count the number of “rising edges” we see from our encoder output. One way to accomplish this is by “polling”, or repeatedly reading a digital input that the encoder is attached to and actively counting these edges.


Interrupts

The Arduino’s microcontroller has specialized hardware that can listen for signals on a digital input pin. The moment it sees an edge, it can tell the program to stop whatever it’s doing to count the tick, and then resume where it left off. These are called interrupts. Hall Sensor Pic

Try tracking the position of your left wheel. Here’s some code to get you started!

#define PIN_ENCODER_LEFT_A 7
#define PIN_ENCODER_LEFT_B 2

int left_position = 0;

bool prevLeftEncoderVal = 0;

void setup() {
    pinMode(PIN_ENCODER_LEFT_A, INPUT);
    pinMode(PIN_ENCODER_LEFT_B, INPUT);
 
    Serial.begin(9600);
}

int count = 0;
void loop() {
  	bool leftEncoderVal = digitalRead(PIN_ENCODER_LEFT_B);

  	// Detect a rising edge of the left encoder’s B channel
  	if (leftEncoderVal == HIGH and prevLeftEncoderVal == LOW) {
		leftEncoderRisingEdge();
  	}

  	prevLeftEncoderVal = leftEncoderVal;

  	// Print the position every 1000 loops
  	if (count % 1000 == 0) {
		Serial.println(left_position);
  	}
  	count++;
}

void leftEncoderRisingEdge()
{
  	// TODO: increment (or decrement) left_position      
}

Once you’ve filled out the leftEncoderRisingEdge function, your Arduino should be able to start accurately tracking the position of your left motor!

Checkoff #2

Use the Tools > Serial Plotter utility to view the position of your motor as you turn it.

  1. Roughly how many ticks correspond to a revolution? How do you think this is computed?
  2. What issues might the “polling” method of counting encoder ticks have?
  3. Now that we’ve successfully measured the wheel’s position, one logical next step might be to measure its velocity.
    • How might we do this?


Introduction to Polling and Interrupts

We’ve just learned about motor control and encoders. We counted the number of encoder ticks using a method called polling, which involves repeatedly reading an input to see if it changes. Polling is inefficient and can miss encoder edges if the wheel is spinning quickly. One alternative is to use interrupts. Interrupts are a way to run some code every time an input changes, without having to check it continuously. We will also learn in Lab 3 how to use distance sensors, which tell us how far away our mouse is from a wall. The distance sensors we use (VL53L0X) use a protocol called I2C. I2C is a way to “talk” to multiple sensors using only two wires.


Interrupts

The Arduino’s microcontroller has specialized hardware that can listen for signals on a digital input pin. The moment it sees an edge, it can tell the program to stop whatever it’s doing to count the tick, and then resume where it left off. These are called interrupts. In Arduino, an interrupt can be made by running the attachInterrupt function in your setup() function:

// Call function() when pin goes from LOW to HIGH
attachInterrupt(digitalPinToInterrupt(pin), function, RISING);

Besides counting encoder ticks to find out how far a motor has spun, we might also want to know how fast a motor is spinning. Using interrupts, we can get an accurate measurement of the time between encoder ticks. We can take the reciprocal to get the number of encoder ticks per second, or rotational speed.

Hint: You can get the number of microseconds since your Arduino code started running using the micros() function. If you are curious about this function: https://www.arduino.cc/reference/en/language/functions/time/micros/.

Try re-implementing your encoder tick counter from previous section using interrupts, and add velocity measurements! Some skeleton code is provided below. Make sure you are paying attention to the type you are using (float, int, etc.), ask your mentor!

#define PIN_LED1 A2

#define PIN_ENCODER_LEFT_A 7
#define PIN_ENCODER_LEFT_B 2

int left_position = 0;
float left_velocity = 0;

bool prevLeftEncoderVal = 0;

void setup() {
	pinMode(PIN_ENCODER_LEFT_A, INPUT);
	pinMode(PIN_ENCODER_LEFT_B, INPUT);
	pinMode(PIN_LED1, OUTPUT);
	Serial.begin(9600);

  	// TODO: set up your interrupt here
  	// make sure you use pin B, not pin A -- interrupts are only supported on B
}

void loop() {
	// You might want to remove this
  	digitalWrite(PIN_LED1, HIGH);
  	delay(200);
  	digitalWrite(PIN_LED1, LOW);
  	delay(200);
  	Serial.println(left_position);
}

void leftEncoderRisingEdge()
{
  	// TODO: increment or decrement left_position,
  	//       based on the direction of rotation 
  	// TODO: find the number of seconds since the last rising edge,
  	//       then store its reciprocal to left_velocity
}

Checkoff #3

  1. Re-implement your encoder tick counter using interrupts.
    • Show the serial plotter output while spinning the motor.
    • Why might we use interrupts instead of polling?
  2. Implement velocity measurement using interrupts, and print the velocity in revolutions per second (recall that 1 revolution ~ 210 ticks).
    • Show the serial plotter output while spinning the motor.
    • If you stop the motor, the velocity will never actually hit zero. Why is this? How can we fix it?