SPECIAL OFFER - Buy (1) Apeiros Robot & Get (1) 8x2 Character LCD Free!

Lesson 1

Lesson 1: Controlling Motors

In this lesson you will learn how to control your robot's left and right gear motors in order to generate motion. Learning objectives for this lesson are listed below.

  • Learn how to create an instance of the ApeirosMotors class library.
  • Learn how to control the left & right gear motors by setting their speed & direction.
  • Learn how to create an instance of the ApeirosIoExpander class library.
  • Learn how to read the current state of the front left & front right infrared (IR) sensors.
  • Learn how to create your own obstacle avoidance behavior sketch.

Prerequisites

Before starting this lesson be sure to follow the Initial Setup & Configuration. Also, we strongly recommend that you take some time to read "Getting Started with Arduino and Genuino products". You will need to purchase and install an additional infrared sensor in the front center location as shown in Lesson 8 if you want to complete the Custom Obstacle Avoidance activity.

Introduction

In this lesson we will learn how to control Apeiros motion by commanding the left and right gear motors. Apeiros makes use of what is called a differential drive and this simply means that two powered wheels provide motion while a rear caster wheel provides stability. Apeiros is best-suited to drive on flat, smooth surfaces but can be operated on low-nap carpet. Refer to the image below and take a look at the bottom-side of Apeiros.

Apeiros Class Library Functions

The ApeirosMotors class contains several functions that provide motor control. Each motor is capable of spinning in a clockwise or a counter-clockwise direction and at varying speeds. We can simultaneously set the speed and direction of rotation for both the left and right motor by using the setSpeeds() function.

static void setSpeeds(int16_t leftPWM, int16_t rightPWM);

When using the setSpeeds() function we must specify both the left and right PWM value as a signed 16-bit integer. We can specify a minimum of -180 for full reverse and up to a maximum of 180 for full speed forward. Please note that PWM stands for Pulse Width Modulation. Whenever you set the left and right PWM parameter the class library is sending a PWM signal along with a direction signal to the motor controller chip.

leftPWM - A number from -180 to 180 representing the speed and direction of the left motor. Values of -180 or less result in full speed reverse, and values of 180 or more result in full speed forward.

rightPWM - A number from -180 to 180 representing the speed and direction of the left motor. Values of -180 or less result in full speed reverse, and values of 180 or more result in full speed forward.

Before attempting to use the setSpeeds() function in a sketch, we first must declare an instance of the ApeirosMotors class as shown below.

ApeirosMotors motors;

In the above line of code we have declared an instance of the ApeirosMotors class and appropriately named it motors. Now we can easily access the setSpeeds() function and set the left and right motor PWM to a positive value of 75 as shown below. Setting both motor PWM parameters to the same positive value will drive Apeiros forward in a straight line.

motors.setSpeeds(75,75);

Suppose that we want to stop the left and right motors. We would set both the left and right motor PWM parameter to a value of zero as shown below.

motors.setSpeeds(0,0);

How would we command Apeiros to spin clockwise in a circle? That's right, set the left motor PWM to a positive value and the right motor PWM to a negative value.

motors.setSpeeds(75,-75);

Let's take a closer look at one of the Apeiros_Obstacle_Avoidance Example sketch that is provided with the Apeiros Class Libraries. Go ahead and launch the Arduino IDE and then select File -> Examples -> Apeiros -> Apeiros_Obstacle_Avoidance as shown below.

 

 

TIP:

You can always refer to the Apeiros Example sketches and learn about how to use the Apeiros class libraries. Also, be sure to check out the Apeiros Robot compiled help file, ApeirosRobot.chm. The compiled help file provides an in-depth understanding of the Apeiros class libraries along with a brief summary.

 

The first thing we notice is that the Apeiros_Obstacle_Avoidance sketch includes several files. Every Apeiros sketch must list these (3) include files at the beginning of the sketch.

#include <Wire.h>
#include <Servo.h>
#include "Apeiros.h"

Next, we see that this sketch declares an instance of the ApeirosMotors, ApeirosIoExpander and ApeirosBuzzer class libraries.

// Declare Apeiros class instances.//
/***********************************/
ApeirosMotors motors;
ApeirosIoExpander io;
ApeirosBuzzer buzzer;
/***********************************/

You can access all related library functions once an instance of a class has been declared. In this example we can access motor functions with motors, sensor and LCD functions with io and sound functions with buzzer.

Every sketch should contain a setup() function and this is used to initialize libraries, various pieces of hardware along with any user defined variables.

void setup() {
  // put your setup code here, to run once:
  
  // Initialize ApeirosIoExpander.
  io.begin();
  
  io.lcdClear();
  io.print("Obstacle");
  io.lcdGotoXY(0, 1);
  io.print("Avoid!");

  // Set motor PWM offsets. Default is 55.
  // Use motor PWM offsets to drive straight.
  motors.setPwmOffsets(55,55);

}

In the above setup() function we see that the ApeirosIoExpander class is initialized with "io.begin();". Next the LCD screen is cleared and the word "Obstacle" is printed to the first line. Then the LCD cursor is instructed to move to the second line so that "Avoid!" can be printed out. Finally, the PWM offsets for the left and right motor are set to a value of 55. You may need to adjust the PWM motor offsets if your robot is not driving straight. A valid PWM offset value can be any integer from 0 to 75.

EXPERIMENT



Play around with the PWM offsets and observe how your robot responds. Were you able to get Apeiros to drive straight? HINT: If you reduce the PWM offset too low, then the motors will require a higher motor speed before they will begin spinning. Likewise, increasing the PWM offset means that the motors will begin spinning when commanded with a lower motor speed.


 

Finally, we get to the main loop for the Apeiros_Obstacle_Avoidance sketch. Every sketch must have a main loop and this is where you place custom code that will be executed repeatedly since it is a loop. First, we can see that several boolean variables have been defined: LFIR, RFIR and CFIR. A boolean variable can be assigned a 1 (TRUE) or 0 (FALSE), and so it can be one of two different states. Think of a light switch in your house where it can be switched on or it can be in the off state. We have defined LFIR to hold the state of the left front IR sensor. Likewise, RFIR will hold the state of the right front IR and the state of the center front IR will be stored in CFIR. Next, we see that an integer variable, motorSpeed, has been declared and it is initialized to a value of 75. You have guessed correct if you said that motorSpeed will be used to control the speed of the left and right motors!

void loop() {
  // put your main code here, to run repeatedly:
  
  bool LFIR, RFIR, CFIR;
  int motorSpeed = 75;
  
  // Read front right IR sensor state.
  // Sensor clear = 0 and blocked = 1.
  RFIR = io.getFrontRightIR();
  
  // Read front left IR sensor state.
  LFIR = io.getFrontLeftIR();
  
  // Read front center IR sensor state.
  // Does not get used, but to demonstrate
  // how to read center IR sensor.
  CFIR = io.getFrontCenterIR();
  
  if (!LFIR && RFIR) // If left front IR is clear and right front IR is blocked.
  {
    // Play a tone at 250 Hz for 250ms.
    buzzer.playToneForDuration(250, 250);
    motors.setSpeeds(-motorSpeed, motorSpeed); // Spin robot counterclockwise.
  }
  else if (LFIR && !RFIR) // If left front IR is blocked and right front IR is clear.
  {
    // Play a tone at 300 Hz for 250ms.
    buzzer.playToneForDuration(300, 250);
    motors.setSpeeds(motorSpeed, -motorSpeed); // Spin robot clockwise.
  }
  else if (LFIR && RFIR) // If left front IR is blocked and right front IR is blocked.
  {
    // Play a tone at 500 Hz for 250ms.
    buzzer.playToneForDuration(500, 250);
    motors.setSpeeds(-motorSpeed, -motorSpeed); // Run robot backwards.
  }
  else
  {
    motors.setSpeeds(motorSpeed, motorSpeed); // Else drive robot forward.
  }

  // Toggle state of all LED lights.
  io.toggleAllLEDsState();
  
  delay(150);  // Delay for a specified amount of time in milliseconds
}

Next, we need to perform a read of each of the (3) front IR sensors. We achieve this using (3) different functions from the ApeirosIoExpander class. More specifically, we use the getFrontRightIR(), getFrontLeftIR() and getFrontCenterIR() functions and store the returned value in the boolean variables RFIR, LFIR & FCIR.

Once we have the current state of each of the front IR sensors, then we need to determine which sensors are blocked by an obstacle and which ones are clear so that we can specify the correct motor speeds. In order to accomplish this in code we need to use an if statement along with two else if and one else statement. You can learn more about using an if statement on the Arduino website here. Learn more about the else if and else statement here. These statements allow us to control the flow of our sketch and execute specific sections only when a prescribed condition is found to be TRUE.

For the Apeiros_Obstacle_Avoidance sketch we want Apeiros to turn counter-clockwise whenever the left front IR, LFIR, is blocked (1) and the right front IR, RFIR, is clear (0). Can you spot which section of the sketch achieves this behavior? See below for the answer.

  if (!LFIR && RFIR) // If left front IR is clear and right front IR is blocked.
  {
    // Play a tone at 250 Hz for 250ms.
    buzzer.playToneForDuration(250, 250);
    motors.setSpeeds(-motorSpeed, motorSpeed); // Spin robot counterclockwise.
  }

Next we want the sketch to spin Apeiros clockwise whenever the left front IR, LFIR, is clear (0) and the right front IR, RFIR, is blocked (1). Can you spot which section of the sketch achieves this behavior? See below for the answer.

  else if (LFIR && !RFIR) // If left front IR is blocked and right front IR is clear.
  {
    // Play a tone at 300 Hz for 250ms.
    buzzer.playToneForDuration(300, 250);
    motors.setSpeeds(motorSpeed, -motorSpeed); // Spin robot clockwise.
  }

For the first two cases only one of the sensors was blocked. What about when both the front left and front right IR sensors are blocked? If both sensors are blocked, then we want Apeiros to drive backwards.

  else if (LFIR && RFIR) // If left front IR is blocked and right front IR is blocked.
  {
    // Play a tone at 500 Hz for 250ms.
    buzzer.playToneForDuration(500, 250);
    motors.setSpeeds(-motorSpeed, -motorSpeed); // Run robot backwards.
  }

Finally, we need to consider the case when both front IR sensors are clear. Whenever both front sensors are clear we want Apeiros to drive forward in a straight line. For this case we are using the else statement.

  else
  {
    motors.setSpeeds(motorSpeed, motorSpeed); // Else drive robot forward.
  }

At this time, we have not discussed using the buzzer or flashing the LEDs and will save it for a future lesson. However, there is one final part of the sketch that is critical to the function of our obstacle avoidance behavior. Our sketch is looping through the code over and over again at a relatively high rate of execution. We need to implement a brief delay at the end of the loop by using the delay() function. For this example sketch we have specified a 150 millisecond delay value. This means that the code will be paused for 150 milliseconds before starting the loop all over again.

  delay(150);  // Delay for a specified amount of time in milliseconds
}

EXPERIMENT



Try increasing the delay time and be sure to observe how Apeiros behaves. Does Apeiros perform better when avoiding obstacles? What happens when you decrease the delay time?


 

Create Custom Obstacle Avoidance Behavior

You are now ready to create your own custom obstacle avoidance behavior! The example Apeiros_Obstacle_Avoidance sketch uses the state of the left and right front IR sensors in order to avoid obstacles. Your assignment is to create a custom obstacle avoidance behavior that uses the left, center and right front IR sensors. HINT: You can modify the example Apeiros_Obstacle_Avoidance sketch by first opening it and then clicking File -> Save from within the Arduino IDE. The example sketch already reads the state of the front center IR and stores it in FCIR. You will need to modify the if, else if and else conditional statements to include the FCIR variable.

Think about how you want Apeiros to avoid obstacles when different IR sensors are blocked. You may want to create a table that lists out the state of each IR sensor along with the desired left and right motor PWM values.

 TABLE OF SENSOR INPUTS AND MOTOR OUTPUTS

LFIR FCIR RFIR  Left PWM Right PWM
1 0 0 75 -75
0 1 0 0 0
0 0 1 -75 75
1 1 1 -75 -75
0 0 0 75 75

Sample code is listed below in order to help you kickstart your custom obstacle avoidance behavior.

  if (LFIR && !FCIR && !RFIR) // If left front IR is blocked and center & right front IRs are clear.
  {
    motors.setSpeeds(motorSpeed, -motorSpeed); // Spin robot clockwise.
  }
  else if (!LFIR && FCIR && !RFIR) // If left & right front IRs are clear and center front IR is blocked.
  {
    motors.setSpeeds(0, 0); // Stop robot.
  }
  else if (!LFIR && !FCIR && RFIR) // If left & center front IRs are clear and right front IR is blocked.
  {
    motors.setSpeeds(-motorSpeed, motorSpeed); // Spin robot counter-clockwise.
  }
  else if (LFIR && FCIR && RFIR) // If left, center & right front IRs are blocked.
  {
    motors.setSpeeds(-motorSpeed, -motorSpeed); // Run robot backwards.
  }
  else
  {
    motors.setSpeeds(motorSpeed, motorSpeed); // Else drive robot forward.
  }

Please note that we have not covered all of the possible sensor states. Since we have a total of (3) sensors and each one can be either a (1) or a (0) there can be a total of 2^3 = 8 different sensor combinations when we AND each of the (3) sensor states together! Can you think of what the missing sensor combinations might be? See below for the answer!

TABLE OF ALL SENSOR INPUT COMBINATIONS

FLIR  FCIR FRIR
0 0 0
1 0 0
0 1 0
1 1 0
0 0 1
1 0 1
0 1 1
1 1 1