Final project progress: Mini-Tesla car

Description:

For my project, I am building a mini-Tesla car that will feature both autopilot mode and manual mode. When the autopilot mode is enabled, the car will be able to move around and avoid all obstacles. Whereas for the manual mode, the user will be able to control the car through processing using the arrows/buttons.

Progress:
Autopilot mode:

Instead of the L298N motor driver shield, I am using the Adafruit motor shield (due to its unavailability) to control the wheels and add movement to the car. To use it correctly, I had to download a new library called “Adafruit Motor Shield V2 Library”, and a couple of other sub-libraries that come with it. 

#include <Adafruit_MotorShield.h>

Adafruit_MotorShield AFMS(0x61);

Adafruit_DCMotor *rb = AFMS.getMotor(3);
Adafruit_DCMotor *lb = AFMS.getMotor(2);
Adafruit_DCMotor *rf = AFMS.getMotor(1);
Adafruit_DCMotor *lf = AFMS.getMotor(4);
Calculate the distance:

For that, I am using pulseIn() which starts the timing when the pin goes to HIGH then stops it when it goes to LOW. It works on pulses higher than 10 microseconds so I had to use a delay there.

// calculate the distance ahead
digitalWrite(4, HIGH);
delayMicroseconds(10);
digitalWrite(4, LOW);
dist = pulseIn(5, HIGH, 10000) * 340 / 10000 / 2;
Movement:

If all directions are blocked, do a U-turn:

To do that, I am alternating BACKWARD and FORWARD between the 4 wheels.

Right back wheel: Backward

Left back wheel: Forward

Right front wheel: Backward

Left front wheel: Forward

I have also added speed for the turn, then decreased it back at the end.

The delay is quite high 700 microseconds, to leave them time to make the whole U-turn.

if (rdist<= min_dist && ldist<= min_dist) {
      rb->setSpeed(80);
      lb->setSpeed(80);
      rf->setSpeed(80);
      lf->setSpeed(80);
      rb->run(BACKWARD);
      lb->run(FORWARD);
      rf->run(BACKWARD);
      lf->run(FORWARD);
      delay(700);
      rb->setSpeed(40);
      lb->setSpeed(40);
      rf->setSpeed(40);
      lf->setSpeed(40);
      rb->run(RELEASE);
      lb->run(RELEASE);
      rf->run(RELEASE);
      lf->run(RELEASE);
    }

If both directions are free, choose right:

The delay is half the one used for a U-turn.

// if both directions are free, choose right
    else if (rdist>=220 && ldist>=220){
      rb->setSpeed(80);
      lb->setSpeed(80);
      rf->setSpeed(80);
      lf->setSpeed(80);
      rb->run(BACKWARD);
      lb->run(FORWARD);
      rf->run(BACKWARD);
      lf->run(FORWARD);
      delay(400);
      rb->setSpeed(40);
      lb->setSpeed(40);
      rf->setSpeed(40);
      lf->setSpeed(40);
      rb->run(RELEASE);
      lb->run(RELEASE);
      rf->run(RELEASE);
      lf->run(RELEASE);
    }

If the right way is free, choose right:

// if right way is free, choose right
    else if (rdist>=ldist){
      rb->setSpeed(80);
      lb->setSpeed(80);
      rf->setSpeed(80);
      lf->setSpeed(80);
      rb->run(BACKWARD);
      lb->run(FORWARD);
      rf->run(BACKWARD);
      lf->run(FORWARD);
      delay(400);
      rb->setSpeed(40);
      lb->setSpeed(40);
      rf->setSpeed(40);
      lf->setSpeed(40);
      rb->run(RELEASE);
      lb->run(RELEASE);
      rf->run(RELEASE);
      lf->run(RELEASE);
    }

If the left way is free, choose left:

// if left way is free, choose left
    else if (rdist>=ldist){
      rb->setSpeed(80);
      lb->setSpeed(80);
      rf->setSpeed(80);
      lf->setSpeed(80);
      rb->run(FORWARD);
      lb->run(BACKWARD);
      rf->run(FORWARD);
      lf->run(BACKWARD);
      delay(400);
      rb->setSpeed(40);
      lb->setSpeed(40);
      rf->setSpeed(40);
      lf->setSpeed(40);
      rb->run(RELEASE);
      lb->run(RELEASE);
      rf->run(RELEASE);
      lf->run(RELEASE);
    }
Manual mode:

When enabled, the user can control the car through Processing using either the arrows or the screen buttons. When an action is specified, Processing sends the data to Arduino.

else {
    // forward
    if (direc == 0) {
      rb->run(FORWARD);
      lb->run(FORWARD);
      rf->run(FORWARD);
      lf->run(FORWARD);
    }
    // right
    if (direc == 1) {
      rb->setSpeed(80);
      lb->setSpeed(80);
      rf->setSpeed(80);
      lf->setSpeed(80);
      rb->run(BACKWARD);
      lb->run(FORWARD);
      rf->run(BACKWARD);
      lf->run(FORWARD);
      delay(400);
      rb->setSpeed(40);
      lb->setSpeed(40);
      rf->setSpeed(40);
      lf->setSpeed(40);
      rb->run(RELEASE);
      lb->run(RELEASE);
      rf->run(RELEASE);
      lf->run(RELEASE);
    }
    // backward
    if (direc == 2) {
      rb->run(BACKWARD);
      lb->run(BACKWARD);
      rf->run(BACKWARD);
      lf->run(BACKWARD);
    }
    // left
    if (direc == 3) {
      rb->setSpeed(80);
      lb->setSpeed(80);
      rf->setSpeed(80);
      lf->setSpeed(80);
      rb->run(FORWARD);
      lb->run(BACKWARD);
      rf->run(FORWARD);
      lf->run(BACKWARD);
      delay(400);
      rb->setSpeed(40);
      lb->setSpeed(40);
      rf->setSpeed(40);
      lf->setSpeed(40);
      rb->run(RELEASE);
      lb->run(RELEASE);
      rf->run(RELEASE);
      lf->run(RELEASE);
    }
    // stop
    if (direc == 4) {
      rb->run(RELEASE);
      lb->run(RELEASE);
      rf->run(RELEASE);
      lf->run(RELEASE);
    }
}
Processing:
import processing.serial.*;
Serial myPort;  
PImage img;

void setup() { 
  size(960,720);
  printArray(Serial.list());
  String portname=Serial.list()[1];
  println(portname);
  myPort = new Serial(this,portname,9600);
  myPort.clear();
  myPort.bufferUntil('\n');
  img = loadImage("bg.png");
}

void draw() {
  background(#e9eaea);
  image(img, width*1/2-135, height/2-125,270,250);
  fill(#555555);
  textSize(30);
  text("Use the arrows to control the car.", width*1/2-200, height/2+ 145);
  text("Click on S to switch to stop the car.", width*1/2-210, height/2+ 195);
  text("Click on A to switch to autopilot mode.", width*1/2-225, height/2+ 245);
  if(keyPressed) {                                                                                            //if a key is pressed, do the following
    if (keyCode == UP){     
      myPort.write(0+","+0+"\n"); 
    }
    else if (keyCode == DOWN){     
      myPort.write(0+","+2+"\n");  
    }
    else if (keyCode == RIGHT){     
      myPort.write(0+","+1+"\n");  
    }
    else if (keyCode == LEFT){     
      myPort.write(0+","+3+"\n");  
    }
    else if (key == 's'){     
      myPort.write(0+","+4+"\n");
    }
    else if (key == 'a'){     
      myPort.write(1+","+0+"\n");
    }
    else {
      myPort.write(4+","+9+"\n");
    }
  }
  else {
    myPort.write(4+","+9+"\n");
  }
}

 

View post on imgur.com

View post on imgur.com

Hardest parts:

One of the challenges I have faced was using the motor shield, as some of the parts were soldered (between the two right pads), which affected the address of the shield. So I had to either fix it through code or remove the soldering. One more issue was related to the VIN jumper because for some time there was no power in the shield, it might be because the 5v pad was also soldered.

User testing:

View post on imgur.com

View post on imgur.com

Comments from user:
  • Add more speed (Answer: I think it would be a bad idea because it will lead to the car hitting obstacles)
  • Add sound ( Answer: I will look into adding a buzzer if it is not going to affect the circuit because the motors require too much voltage)
  • Add lights that automatically turn on when it’s dark ( Answer: I will implement that using LDRs)

Leave a Reply