Josh Newman
Published © GPL3+

Arduino Omnidirectional Car

Created an omnidirectional car that is powered by Arduino and controlled by a PS2 controller.

IntermediateShowcase (no instructions)1,310
Arduino Omnidirectional Car

Things used in this project

Hardware components

Arduino UNO
Arduino UNO
×1
Adafruit Motor Shield V2
×1
EMOZNY Mecanum Wheel Robot Kit
×1
Screw Terminal Breakout Shield
×1
Ultrasonic Sensor - HC-SR04 (Generic)
Ultrasonic Sensor - HC-SR04 (Generic)
×2
PS2 wireless remote and reciever
×1
Chassis expansion plate
×1
Adafruit 6 X AA battery holder with 5.5mm/2.1mm plug
×1
Adafruit In-line power switch for 2.1mm barrel jack
×1
Piezo buzzer
×1
LED (generic)
LED (generic)
×4
Resistor 220 ohm
Resistor 220 ohm
×4

Software apps and online services

Arduino IDE
Arduino IDE

Hand tools and fabrication machines

Premium Female/Male Extension Jumper Wires, 40 x 6" (150mm)
Premium Female/Male Extension Jumper Wires, 40 x 6" (150mm)
10 Pc. Jumper Wire Kit, 5 cm Long
10 Pc. Jumper Wire Kit, 5 cm Long
Soldering iron (generic)
Soldering iron (generic)
Solder Wire, Lead Free
Solder Wire, Lead Free

Story

Read more

Schematics

Circuit Schematic

This is the circuit schematic, admittedly its quite busy, I will label all pin assignments in the project write up.

Circuit Schematic PNG

PNG file of Circuit Schematic

Code

Omni-Car code

Arduino
This is the code for the car, made in the arduino IDE for an Arduino UNO
#include <Wire.h>
#include <Adafruit_MotorShield.h>
#include <PS2X_lib.h> //library by Bill Porter, http://www.billporter.info/2010/06/05/playstation-2-controller-arduino-library-v1-0/

// Create the motor shield object with the default I2C address
Adafruit_MotorShield AFMS = Adafruit_MotorShield();

// Creat motor objects
Adafruit_DCMotor *BackLeftMotor = AFMS.getMotor(1);
Adafruit_DCMotor *BackRightMotor = AFMS.getMotor(2);
Adafruit_DCMotor *FrontRightMotor = AFMS.getMotor(3);
Adafruit_DCMotor *FrontLeftMotor = AFMS.getMotor(4);

// create PS2 Controller Class
PS2X ps2x;

//variables used by ps2 controller
int error = 0;
byte type = 0;
byte vibrate = 0;

// variables to hold the Motor Commands
int motorSpeed = 50;                     //Speed command is stored here

//Variables used from blinking LED
int ledState = LOW;
unsigned long previousMillis = 0;
const long interval = 500;

//ultrasonic sensor variables
const int trigPinFr = 3;    //Trigger Pin for the front HC-SR04
const int echoPinFr = 2;    //Echo Pin for the front HC-SR04
const int trigPinBk = 9;    //Trigger Pin for the back HC-SR04
const int echoPinBk = 4;    //Echo Pin for the back HC-SR04

float durationFr, distanceFr;
float durationBk, distanceBk;

bool objectDetecFr = false;   //When object avoidance is on, this variable will be true if the front HC-SR04 detects an object within 15cm
bool objectDetecBk = false;   //When object avoidance is on, this variable will be true if the back HC-SR04 detects an object within 15cm
bool objectAvoidOn = false;   

bool vibrateMotors = false;   //When true the large motor in the ps2 controller will vibrate at "vibrateSpeed"
byte vibrateSpeed = 0;

void setup() {
  // put your setup code here, to run once:

  pinMode(1, OUTPUT);   //blue LED (used to indicate object avoidance mode)
  pinMode(5, OUTPUT);   //green LED
  pinMode(6, OUTPUT);   //yellow LED
  pinMode(7, OUTPUT);   //red LED

  //Front HC-SRO4 Pins
  pinMode(trigPinFr, OUTPUT);   
  pinMode(echoPinFr, INPUT);

  //Back HC-SR04 Pins
  pinMode(trigPinBk, OUTPUT);
  pinMode(echoPinBk, INPUT);

  //Initialize motor shield and PS2 controller
  AFMS.begin();
  ps2Setup();

}

void loop() {
  ps2x.read_gamepad(false,vibrateSpeed); //ps2x.read_gamepad(small motor on?, Large motor vibrate speed), set large motor vibrate speed to zero if vibration unwanted
  moveCar();  //Call this function to move the car according to joystick input
  buttons();  //Checks for button state on PS2 controller, currently just checks up and down on D-pad,circle, and X
  objectDetec();
  LEDs();
}

//Function for commanding the car to move based on the position of the joysticks. Also checks for object detection. 
void moveCar() {

  BackLeftMotor->setSpeed(motorSpeed);
  BackRightMotor->setSpeed(motorSpeed);
  FrontLeftMotor->setSpeed(motorSpeed);
  FrontRightMotor->setSpeed(motorSpeed);

  if ((ps2x.Analog(PSS_LY) < 100) && (ps2x.Analog(PSS_LX) > 100) && (ps2x.Analog(PSS_LX) < 200) && (objectDetecFr == false)) {
    //Execute forward motor commands

    BackLeftMotor->run(FORWARD);
    BackRightMotor->run(FORWARD);
    FrontLeftMotor->run(FORWARD);
    FrontRightMotor->run(FORWARD);
    vibrateSpeed = 0;

  } else if ((ps2x.Analog(PSS_LY) > 200) && (ps2x.Analog(PSS_LX) > 100) && (ps2x.Analog(PSS_LX) < 200) && (objectDetecBk == false)) {
    //Execute revese motor commands

    BackLeftMotor->run(BACKWARD);
    BackRightMotor->run(BACKWARD);
    FrontLeftMotor->run(BACKWARD);
    FrontRightMotor->run(BACKWARD);
    vibrateSpeed = 0;

  } else if ((ps2x.Analog(PSS_LX) < 100) && (ps2x.Analog(PSS_LY) > 100) && (ps2x.Analog(PSS_LY) < 200)) {
    //Execute Left motor commands

    BackLeftMotor->run(FORWARD);
    BackRightMotor->run(BACKWARD);
    FrontLeftMotor->run(BACKWARD);
    FrontRightMotor->run(FORWARD);
    vibrateSpeed = 0;

  } else if ((ps2x.Analog(PSS_LX) > 200) && (ps2x.Analog(PSS_LY) > 100) && (ps2x.Analog(PSS_LY) < 200)) {
    //Execute Right motor commands

    BackLeftMotor->run(BACKWARD);
    BackRightMotor->run(FORWARD);
    FrontLeftMotor->run(FORWARD);
    FrontRightMotor->run(BACKWARD);
    vibrateSpeed = 0;

  } else if ((ps2x.Analog(PSS_LX) > 200) && (ps2x.Analog(PSS_LY) < 100) && (objectDetecFr == false)) {
    //Execute Upper Right Diagonal motor commands

    BackLeftMotor->run(RELEASE);
    BackRightMotor->run(FORWARD);
    FrontLeftMotor->run(FORWARD);
    FrontRightMotor->run(RELEASE);
    vibrateSpeed = 0;

  }  else if ((ps2x.Analog(PSS_LX) < 100) && (ps2x.Analog(PSS_LY) < 100) && (objectDetecFr == false)) {
    //Execute Upper Left Diagonal motor commands

    BackLeftMotor->run(FORWARD);
    BackRightMotor->run(RELEASE);
    FrontLeftMotor->run(RELEASE);
    FrontRightMotor->run(FORWARD);
    vibrateSpeed = 0;

  } else if ((ps2x.Analog(PSS_LX) > 200) && (ps2x.Analog(PSS_LY) > 200) && (objectDetecBk == false)) {
    //Execute Lower Right Diagonal motor commands

    BackLeftMotor->run(BACKWARD);
    BackRightMotor->run(RELEASE);
    FrontLeftMotor->run(RELEASE);
    FrontRightMotor->run(BACKWARD);
    vibrateSpeed = 0;

  } else if ((ps2x.Analog(PSS_LX) < 100) && (ps2x.Analog(PSS_LY) > 200) && (objectDetecBk == false)) {
    //Execute Lower Left Diagonal motor commands

    BackLeftMotor->run(RELEASE);
    BackRightMotor->run(BACKWARD);
    FrontLeftMotor->run(BACKWARD);
    FrontRightMotor->run(RELEASE);
    vibrateSpeed = 0;

  } else if ((ps2x.Analog(PSS_RX) > 200)) {
    //Execute Clockwise rotate motor commands

    BackLeftMotor->run(FORWARD);
    BackRightMotor->run(BACKWARD);
    FrontLeftMotor->run(FORWARD);
    FrontRightMotor->run(BACKWARD);
    vibrateSpeed = 0;

  } else if ((ps2x.Analog(PSS_RX) < 100)) {
    //Execute anit-Clockwise rotate motor commands

    BackLeftMotor->run(BACKWARD);
    BackRightMotor->run(FORWARD);
    FrontLeftMotor->run(BACKWARD);
    FrontRightMotor->run(FORWARD);
    vibrateSpeed = 0;

  } else if ((ps2x.Analog(PSS_LY) < 100) && (ps2x.Analog(PSS_LX) > 100) && (ps2x.Analog(PSS_LX) < 200) && (objectDetecFr == true)) {
    //If car is commanded forward, object detection is on and there is an object detected in front. Vibrate controller motor and turn off car motors
    vibrateSpeed = 100;
    BackLeftMotor->run(RELEASE);
    BackRightMotor->run(RELEASE);
    FrontLeftMotor->run(RELEASE);
    FrontRightMotor->run(RELEASE);
  } else if ((ps2x.Analog(PSS_LY) > 200) && (ps2x.Analog(PSS_LX) > 100) && (ps2x.Analog(PSS_LX) < 200) && (objectDetecBk == true)) {
    //If car is commanded backwards, object detection is on and there is an object detected in back. Vibrate controller motor and turn off car motors
    vibrateSpeed = 100;
    BackLeftMotor->run(RELEASE);
    BackRightMotor->run(RELEASE);
    FrontLeftMotor->run(RELEASE);
    FrontRightMotor->run(RELEASE);    
  } else if ((ps2x.Analog(PSS_LX) > 200) && (ps2x.Analog(PSS_LY) < 100) && (objectDetecFr == true)) {
    //If car is commanded upper-right diagonal, object detection is on and there is an object detected in front. Vibrate controller motor and turn off car motors
    vibrateSpeed = 100;
    BackLeftMotor->run(RELEASE);
    BackRightMotor->run(RELEASE);
    FrontLeftMotor->run(RELEASE);
    FrontRightMotor->run(RELEASE);    
  } else if ((ps2x.Analog(PSS_LX) < 100) && (ps2x.Analog(PSS_LY) < 100) && (objectDetecFr == true)) {
    //If car is commanded upper-left diagonal, object detection is on and there is an object detected in front. Vibrate controller motor and turn off car motors
    vibrateSpeed = 100;
    BackLeftMotor->run(RELEASE);
    BackRightMotor->run(RELEASE);
    FrontLeftMotor->run(RELEASE);
    FrontRightMotor->run(RELEASE);    
  } else if ((ps2x.Analog(PSS_LX) > 200) && (ps2x.Analog(PSS_LY) > 200) && (objectDetecBk == true)) {
    //If car is commanded lower-right diagonal, object detection is on and there is an object detected in back. Vibrate controller motor and turn off car motors
    vibrateSpeed = 100;
    BackLeftMotor->run(RELEASE);
    BackRightMotor->run(RELEASE);
    FrontLeftMotor->run(RELEASE);
    FrontRightMotor->run(RELEASE);    
  } else if ((ps2x.Analog(PSS_LX) < 100) && (ps2x.Analog(PSS_LY) > 200) && (objectDetecBk == true)) {
    //If car is commanded lower-left diagonal, object detection is on and there is an object detected in back. Vibrate controller motor and turn off car motors
    vibrateSpeed = 100;
    BackLeftMotor->run(RELEASE);
    BackRightMotor->run(RELEASE);
    FrontLeftMotor->run(RELEASE);
    FrontRightMotor->run(RELEASE);    
  }
  else {
    //Stop motors and shut off vibration motor if joysticks are in default position
    BackLeftMotor->run(RELEASE);
    BackRightMotor->run(RELEASE);
    FrontLeftMotor->run(RELEASE);
    FrontRightMotor->run(RELEASE);
    vibrateSpeed = 0;

  }
}

//initialize ps2 controller, code from bill porter, http://www.billporter.info/2010/06/05/playstation-2-controller-arduino-library-v1-0/
void ps2Setup() {
  error = ps2x.config_gamepad(13, 11, 10, 12, true, true); //setup pins and settings:  GamePad(clock, command, attention, data, Pressures?, Rumble?) check for error

  if (error == 0) {
    //Serial.println("Found Controller, configured successful");
  }

  else if (error == 1) {
    //Serial.println("No controller found, check wiring, see readme.txt to enable debug. visit www.billporter.info for troubleshooting tips");
  }
  else if (error == 2) {
    //Serial.println("Controller found but not accepting commands. see readme.txt to enable debug. Visit www.billporter.info for troubleshooting tips");
  }
  else if (error == 3) {
    //Serial.println("Controller refusing to enter Pressures mode, may not support it. ");
  }
  //Serial.print(ps2x.Analog(1), HEX);

  type = ps2x.readType();
  switch (type) {
    case 0:
      //Serial.println("Unknown Controller type");
      break;
    case 1:
      //Serial.println("DualShock Controller Found");
      break;
    case 2:
      //Serial.println("GuitarHero Controller Found");
      break;
  }
}

void objectDetec() {
  //only run code of object avoidance mode is on
  if (objectAvoidOn) {
    //Read front sensor
    digitalWrite(trigPinFr, LOW);
    delayMicroseconds(2);
    digitalWrite(trigPinFr, HIGH);
    delayMicroseconds(10);
    digitalWrite(trigPinFr, LOW);

    durationFr = pulseIn(echoPinFr, HIGH);
    distanceFr = (durationFr * .0343) / 2;

    //check if sensors read object within 15 cm
    if (distanceFr < 15) {
      objectDetecFr = true;
    } else {
      objectDetecFr = false;
    }

    //read back sensor
    digitalWrite(trigPinBk, LOW);
    delayMicroseconds(2);
    digitalWrite(trigPinBk, HIGH);
    delayMicroseconds(10);
    digitalWrite(trigPinBk, LOW);

    durationBk = pulseIn(echoPinBk, HIGH);
    distanceBk = (durationBk * .0343) / 2;

    //check if sensor reads object within 15cm
    if (distanceBk < 15) {
      objectDetecBk = true;
    } else {
      objectDetecBk = false;
    }
  } else {
    objectDetecFr = false;
    objectDetecBk = false;
  }
}

//function used to detect button presses from user and set states based on presses
void buttons() {

  //initialize horn, horn will play until circle button (red) is released
  if (ps2x.ButtonPressed(PSB_RED)) {
    tone(8, 330); //tone(PIN,Frequency)
  }
  //horn stops when circle (red) is released
  if (ps2x.ButtonReleased(PSB_RED)) {
    noTone(8);
  }
  //toggle object avoidance when x (blue) is pressed
  if (ps2x.ButtonPressed(PSB_BLUE)) {
    objectAvoidOn = !objectAvoidOn;
  }
  //increase motor speed by 10 when up pad is pressed, up to 240
  if (ps2x.ButtonPressed(PSB_PAD_UP)) {
    if (motorSpeed < 240) {
      motorSpeed = motorSpeed + 10;
    }
  }
  //decrease motor speed by 10 when down is pressed, down to 50
  if (ps2x.ButtonPressed(PSB_PAD_DOWN)) {
    if (motorSpeed > 50) {
      motorSpeed = motorSpeed - 10;
    }
  }
}

//contolled the LEDS
void LEDs() {
  //if motor speed is less than 115, illuminate the red led, if between 119 and 181, illumiate red and yellow, if over 181 illuminate red,yellow and blue
  if (motorSpeed < 115) {
    digitalWrite(7, HIGH);
    digitalWrite(6, LOW);
    digitalWrite(5, LOW);
  } else if ((motorSpeed > 119) && (motorSpeed < 181)) {
    digitalWrite(7, HIGH);
    digitalWrite(6, HIGH);
    digitalWrite(5, LOW);
  } else if (motorSpeed > 181) {
    digitalWrite(7, HIGH);
    digitalWrite(6, HIGH);
    digitalWrite(5, HIGH);
  }

  unsigned long currentMillis = millis();

//if object avoidance is on (blue button pressed) illuminate blue LED, if object is detected, flash blue LED
  if (objectAvoidOn && !objectDetecFr && !objectDetecBk) {
    digitalWrite(1, HIGH);
  } else if (objectAvoidOn && (objectDetecFr || objectDetecBk)) {
    if (currentMillis - previousMillis >= interval) {
      // save the last time you blinked the LED
      previousMillis = currentMillis;

      // if the LED is off turn it on and vice-versa:
      if (ledState == LOW) {
        ledState = HIGH;
      } else {
        ledState = LOW;
      }

      // set the LED with the ledState of the variable:
      digitalWrite(1, ledState);
    }
  } else {
    digitalWrite(1, LOW);
  }
}

Credits

Josh Newman

Josh Newman

0 projects • 1 follower

Comments