Autnomous Rover Instructions

Arduino on top plate
Follow ALL the instructions below to setup an Autonomous Rover.
When finished, click on the Remote Instructions link to the left to optionally control the vehicle using a PS4 Dual-Shock controller.
**IMPORTANT: Sections have been marked where to skip if you're only planning on building a Remote-Controlled-Vehicle.
    Use these layout diagrams to make your connections:
  • GPIO PIN LAYOUT

    Gpio pin layout
  • L293D CHIP LAYOUT
    L293d chip layout

Prerequisites

Supplies
Autonomous-Rover-Kit
Mvp top
*This tutorial assumes you have already installed the Raspian OS onto your SD card. If not, follow the instructions here to install manually. If you don't think you can install the OS manually, there are SD cards which come pre-installed with NOOBS.
*Make sure you have setup SSH from your computer to the Raspberry-Pi before starting these steps. Follow instructions here if you need guidance.

README

Project Breakdown:

We will be using the Arduino UNO R3 to take readings from the HC-SR04 ultrasonic-distance sensors, and then sending information through USB serial communication to the Raspberry-Pi for it to make a decision on how to maneuver.

You might be wondering why we didn't just use the Raspberry-Pi to take the sensor readings from the HC-SR04's directly.

That's because the Raspberry-Pi is not a micro-controller; therefore, it is unable to read the echos in "real-time", hence having to use a while-loop to listen for the echo, creating the possibility of completely missing it in some cases.

Using a micro-controller like an Arduino UNO R3 to take the readings in "real-time" with functions like pulseIn(echo_pin_number, HIGH) and then sending those readings via USB serial communication to the Raspberry-Pi is the more efficient method we will be exploring.

Read more in detail about it here and my original plan to use the RasPi to capture readings on my Raspberry-Pi StackExchange account.

Step 1:

Solder the wires to the DC motors and assemble the chassis

• Solder the wires onto the motor prongs before attaching the motors to the chassis.

• Solder one end of the male to male jumper wires to each end of the metalic prongs connected on the DC motors, the other end of the wire will be plugged into a breadboard later. If you don't know how to solder, I recommend watching a couple videos like this one on YouTube first.

• When attaching the motors to the chassis, make sure that the motors are screwed on with the wires facing inward and with the axles attached! Then go ahead and screw on the top piece.

• Now add your tires.

Pull the wires through the holes

• Carefully pull the left wires upward through the top chassis, then repeating again for the right wires, making sure to keep the wires on the left side and right side separated.

Motor wires
Motor wires closeup

Step 2:

Add the breadboard to the chassis and add the L293D chips

• Place the breadboard on the chassis in a location that will allow you to connect the DC motor wires on easily.

• Add the L293D chips to the breadboard as seen below. The yellow lines represent the top DC wires, and the green lines represent the wires underneath the top wires. *The top 2 motors in the diagram represent the front motors of the vehicle, and the bottom motors represent the back motors of the vehicle.

• You can read more about the L293D chip and what it does here in the Texas Instruments documentation.


"The L293 and L293D are quadruple high-current half-H drivers. These devices are designed to drive a wide arrayof inductive loads such as relays, solenoids, DC and bipolar stepping motors, as well as other high-current and high-voltage loads. All inputs are TTL compatible and tolerant up to 7 V.

Each output is a complete totem-pole drive circuit, with a Darlington transistor sink and a pseudo-Darlington source. Drivers are enabled in pairs, with drivers 1 and 2 enabled by 1,2EN and drivers 3 and 4 enabled by 3,4EN. When an enable input is high, the associated drivers are enabled, and their outputs are active and in phase with their inputs. When the enable input is low, those drivers are disabled, and their outputs are off and in the high-impedance state. With the proper data inputs, each pair of drivers forms a full-H (or bridge) reversible drive suitable for solenoid or motor applications.

On the L293, external high-speed output clamp diodes should be used for inductive transient suppression. On the L293D, these diodes are integrated to reduce system complexity and overall system size. A VCC1 terminal, separate from VCC2, is provided for the logic inputs to minimize device power dissipation. The L293 and L293D" - Texas Instruments

L293d out connection
Motor breadboard
Motor breadboard closeup

Step 3:

**SKIP THIS SECTION FOR REMOTE-CONTROLLED VEHICLE ONLY**
Attaching the brackets

• Screw on the brackets to the front of the rover according to the picture(s) below. (Dont pay attention to the other stuff behind it, we'll get to that in a moment).

• (OPTIONAL) For the LEFT and RIGHT brackets, I like to leave off the screws that are closest to the center bracket, allowing the bracket to swivel to different angles.

Rav bracket mount
Rav bracket closeup

Step 4:

Connect the RasPi input pins to the L293D chip

You can use the L293D chip and RasPi GPIO pin layout diagram(s) at the top of this page to see where to plug in your wires. There is also a visual layout below to help guide you.

• Place your Raspberry-Pi in the location according to the picture below.

Left Front Motor

• Top DC wire -> OUT4

• Bottom DC wire -> OUT3

• GPIO-14 -> EN2

• GPIO-15 -> IN4

• GPIO-18 -> IN3

Left Back Motor

• Top DC wire -> OUT1

• Bottom DC wire -> OUT2

• GPIO-23 -> EN1

• GPIO-24 -> IN1

• GPIO-25 -> IN2

Right Front Motor

• Top DC wire -> OUT1

• Bottom DC wire -> OUT2

• GPIO-08 -> EN1

• GPIO-07 -> IN1

• GPIO-25 -> IN2

Right Back Motor

• Top DC wire -> OUT4

• Bottom DC wire -> OUT3

• GPIO-16 -> EN2

• GPIO-20 -> IN4

• GPIO-21 -> IN3

L293d in connection
Gpio complete view

Step 5:

Connect ground connections

• Follow the diagram to connect all ground units to GND pin on the Rasperry-Pi.

L293d ground connection

Step 6:

Connect power sources

• Connect the battery clip-connector's positive and negative wires into the breadboard according to the diagram below. *DO NOT PLUG THE BATTERY INTO THE CLIP-CONNECTOR YET!

• Connect the 5v Raspberry-Pi pin to the breadboard according to the diagram below.

L293d power connection
Gpio side view

Step 7:

**SKIP THIS SECTION FOR REMOTE-CONTROLLED VEHICLE ONLY**
Insert the HC-SR04 sensors and attach the connections

• Insert the HC-SR04 sensor into the bracket as seen in the picture(s) below.

• Attach the female to male jumper wires to the pins on the sensor as seen below.

• Repeat for last 2 sensors.

• Red wire - VCC

• Green wire - Trig

• Blue wire - Echo

• Black wire - GND

Rav hcsr04 insert
Rav hcsr04 insert sideview

Step 8:

**SKIP THIS SECTION FOR REMOTE-CONTROLLED VEHICLE ONLY**
Attach the Top Metal Plate to the back of the Rover

• Screw on the Top Metal Plate to the back of the rover so that it sits just above the Raspberry-Pi like in the picture below.

• Place the power bank under the breadboard and as shown in the picture below.

• Place the Arduino on top of the Top Metal Plate. (Picture in Step 9).

Top plate

Step 9:

**SKIP THIS SECTION FOR REMOTE-CONTROLLED VEHICLE ONLY**
Connect the HC-SR04 sensors to the Arduino

• Follow the diagram bellow to make the connections. Add another mini-breadboard in front of the breadboard sitting on top of the power-bank as shown in the picture(s) above and below to allow the sensors to receive power and communicate with the Arduino.

• The HC-SR04 sensors send out an ultrasonic pulse, then a distance is calculated based on the time the pulse's echo takes to return to the sensor. Read more about the HC-SR04 sensor here

Pin Connections:

Left Trigger Pin - 13

Left Echo Pin - 12


Middle Trigger Pin - 9

Middle Echo Pin - 11


Right Trigger Pin - 8

Right Echo Pin - 10

Hc sr04 arduino
Arduino on top plate

Step 10:

Write the code for Raspberry-Pi

• SSH into your Raspberry-Pi.

• Type cd ~ and press enter.

• Type mkdir RaspberryPi/Rovers and press enter.

• Type cd RaspberryPi/Rovers and press enter.

• From your Mac or PC, create a similar directory, but add a file that directory: ~/RaspberryPi/Rovers/autonomous_rover_001.py

• Open the ~/RaspberryPi/Rovers/autonomous_rover_001.py file and copy the code below to place inside.

• Navigate to the Rovers directory in your terminal. If on a Mac, type the following command to copy the autonomous_rover_001.py file into your RaspberryPi's directory: scp autonomous_rover_001.py pi@ip_address:/home/pi/Rovers

• After entering the password, you should see something like this: autonomous_rover_001.py 100% 6892 877.3KB/s 00:00

• Check your RaspberryPi's directory to make sure the file transferred (You should still be in your /Rovers directory): sudo nano autonomous_rover_001.py

• Make sure the code is there and correct, then press control + X to exit.

The code below is Python code. To learn more about Python programming, visit here

(If you do not see a white-background below, please refresh your page)

              
                ### Code to copy:

                import RPi.GPIO as GPIO
                import time
                from time import sleep
                import pygame
                import serial

                GPIO.setmode(GPIO.BCM)
                GPIO.setwarnings(False)

                # Autonomous or Controller:
                a = input("Autonomous Mode? T/F: ")
                if a == "T":
                    AUTONOMOUS_MODE = True
                else:
                    AUTONOMOUS_MODE = False

                ### MOTORS
                FREQUENCY = 20
                DUTY_CYCLE = 90
                PWM_STOP = 0

                ## FRONT RIGHT
                Motor_FrontR_1 = 7
                Motor_FrontR_2 = 12
                Motor_FrontR_Enable = 8

                GPIO.setup(Motor_FrontR_1, GPIO.OUT)
                GPIO.setup(Motor_FrontR_2, GPIO.OUT)
                GPIO.setup(Motor_FrontR_Enable, GPIO.OUT)

                ## BACK RIGHT
                Motor_BackR_3 = 21
                Motor_BackR_4 = 20
                Motor_BackR_Enable = 16

                GPIO.setup(Motor_BackR_3, GPIO.OUT)
                GPIO.setup(Motor_BackR_4, GPIO.OUT)
                GPIO.setup(Motor_BackR_Enable, GPIO.OUT)

                ## FRONT LEFT
                Motor_FrontR_1 = 7
                Motor_FrontR_2 = 12
                Motor_FrontR_Enable = 8

                GPIO.setup(Motor_FrontR_1, GPIO.OUT)
                GPIO.setup(Motor_FrontR_2, GPIO.OUT)
                GPIO.setup(Motor_FrontR_Enable, GPIO.OUT)

                ## BACK RIGHT
                Motor_BackR_3 = 21
                Motor_BackR_4 = 20
                Motor_BackR_Enable = 16

                GPIO.setup(Motor_BackR_3, GPIO.OUT)
                GPIO.setup(Motor_BackR_4, GPIO.OUT)
                GPIO.setup(Motor_BackR_Enable, GPIO.OUT)

                ## FRONT LEFT
                Motor_FrontL_3 = 18
                Motor_FrontL_4 = 15
                Motor_FrontL_Enable = 14

                GPIO.setup(Motor_FrontL_3, GPIO.OUT)
                GPIO.setup(Motor_FrontL_4, GPIO.OUT)
                GPIO.setup(Motor_FrontL_Enable, GPIO.OUT)

                ## BACK LEFT
                Motor_BackL_1 = 24
                Motor_BackL_2 = 25
                Motor_BackL_Enable = 23

                GPIO.setup(Motor_BackL_1, GPIO.OUT)
                GPIO.setup(Motor_BackL_2, GPIO.OUT)
                GPIO.setup(Motor_BackL_Enable, GPIO.OUT)

                # Set up PWM pins:
                pwmMotor_FrontR_1 = GPIO.PWM(Motor_FrontR_1, FREQUENCY)
                pwmMotor_FrontR_2 = GPIO.PWM(Motor_FrontR_2, FREQUENCY)
                pwmMotor_BackR_3 = GPIO.PWM(Motor_BackR_3, FREQUENCY)
                pwmMotor_BackR_4 = GPIO.PWM(Motor_BackR_4, FREQUENCY)
                pwmMotor_FrontL_3 = GPIO.PWM(Motor_FrontL_3, FREQUENCY)
                pwmMotor_FrontL_4 = GPIO.PWM(Motor_FrontL_4, FREQUENCY)
                pwmMotor_BackL_1 = GPIO.PWM(Motor_BackL_1, FREQUENCY)
                pwmMotor_BackL_2 = GPIO.PWM(Motor_BackL_2, FREQUENCY)

                # Initial PWM Start:
                pwmMotor_FrontR_1.start(PWM_STOP)
                pwmMotor_FrontR_2.start(PWM_STOP)
                pwmMotor_BackR_3.start(PWM_STOP)
                pwmMotor_BackR_4.start(PWM_STOP)
                pwmMotor_FrontL_3.start(PWM_STOP)
                pwmMotor_FrontL_4.start(PWM_STOP)
                pwmMotor_BackL_1.start(PWM_STOP)
                pwmMotor_BackL_2.start(PWM_STOP)

                # PWM Control:
                def pwm_stop():
                    pwmMotor_FrontR_1.ChangeDutyCycle(PWM_STOP)
                    pwmMotor_FrontR_2.ChangeDutyCycle(PWM_STOP)
                    pwmMotor_BackR_3.ChangeDutyCycle(PWM_STOP)
                    pwmMotor_BackR_4.ChangeDutyCycle(PWM_STOP)
                    pwmMotor_FrontL_3.ChangeDutyCycle(PWM_STOP)
                    pwmMotor_FrontL_4.ChangeDutyCycle(PWM_STOP)
                    pwmMotor_BackL_1.ChangeDutyCycle(PWM_STOP)
                    pwmMotor_BackL_2.ChangeDutyCycle(PWM_STOP)

                def pwm_forward():
                    # Front Right Motors
                    pwmMotor_FrontR_1.ChangeDutyCycle(PWM_STOP)
                    pwmMotor_FrontR_2.ChangeDutyCycle(DUTY_CYCLE)
                    GPIO.output(Motor_FrontR_Enable, GPIO.HIGH)

                    # Back Right Motors
                    pwmMotor_BackR_3.ChangeDutyCycle(PWM_STOP)
                    pwmMotor_BackR_4.ChangeDutyCycle(DUTY_CYCLE)
                    GPIO.output(Motor_BackR_Enable, GPIO.HIGH)

                    # Front Left Motors
                    pwmMotor_FrontL_3.ChangeDutyCycle(DUTY_CYCLE)
                    pwmMotor_FrontL_4.ChangeDutyCycle(PWM_STOP)
                    GPIO.output(Motor_FrontL_Enable, GPIO.HIGH)

                    # Back Left Motors
                    pwmMotor_BackL_1.ChangeDutyCycle(DUTY_CYCLE)
                    pwmMotor_BackL_2.ChangeDutyCycle(PWM_STOP)
                    GPIO.output(Motor_BackL_Enable, GPIO.HIGH)

                def pwm_backward():
                    # Front Right Motors
                    pwmMotor_FrontR_1.ChangeDutyCycle(DUTY_CYCLE)
                    pwmMotor_FrontR_2.ChangeDutyCycle(PWM_STOP)
                    GPIO.output(Motor_FrontR_Enable, GPIO.HIGH)

                    # Back Right Motors
                    pwmMotor_BackR_3.ChangeDutyCycle(DUTY_CYCLE)
                    pwmMotor_BackR_4.ChangeDutyCycle(PWM_STOP)
                    GPIO.output(Motor_BackR_Enable, GPIO.HIGH)

                    # Front Left Motors
                    pwmMotor_FrontL_3.ChangeDutyCycle(PWM_STOP)
                    pwmMotor_FrontL_4.ChangeDutyCycle(DUTY_CYCLE)
                    GPIO.output(Motor_FrontL_Enable, GPIO.HIGH)

                    # Back Left Motors
                    pwmMotor_BackL_1.ChangeDutyCycle(PWM_STOP)
                    pwmMotor_BackL_2.ChangeDutyCycle(DUTY_CYCLE)
                    GPIO.output(Motor_BackL_Enable, GPIO.HIGH)

                def pwm_right():
                    # Front Left Motors
                    pwmMotor_FrontL_3.ChangeDutyCycle(DUTY_CYCLE)
                    pwmMotor_FrontL_4.ChangeDutyCycle(PWM_STOP)
                    GPIO.output(Motor_FrontL_Enable, GPIO.HIGH)

                    # Back Left Motors
                    pwmMotor_BackL_1.ChangeDutyCycle(DUTY_CYCLE)
                    pwmMotor_BackL_2.ChangeDutyCycle(PWM_STOP)
                    GPIO.output(Motor_BackL_Enable, GPIO.HIGH)

                def pwm_left():
                    # Front Right Motors
                    pwmMotor_FrontR_1.ChangeDutyCycle(PWM_STOP)
                    pwmMotor_FrontR_2.ChangeDutyCycle(DUTY_CYCLE)
                    GPIO.output(Motor_FrontR_Enable, GPIO.HIGH)

                    # Back Right Motors
                    pwmMotor_BackR_3.ChangeDutyCycle(PWM_STOP)
                    pwmMotor_BackR_4.ChangeDutyCycle(DUTY_CYCLE)
                    GPIO.output(Motor_BackR_Enable, GPIO.HIGH)

                def drive_autonomously():
                    ser = serial.Serial('/dev/ttyACM0', 115200)
                    ser = ser.readline().decode("utf-8", "ignore")
                    for s in ser:
                        if '1' == s:
                            print("FORWARD!")
                            pwm_forward()
                        elif '2' == s:
                            pwm_stop()
                            time.sleep(2)
                            pwm_right()
                            time.sleep(1.25)
                            print("RIGHT!")
                            pwm_stop()
                            time.sleep(2)
                        elif '3' == s:
                            pwm_stop()
                            time.sleep(2)
                            pwm_backward()
                            time.sleep(1)
                            print("BACK!")
                            pwm_stop()
                            time.sleep(2)
                        elif '4' == s:
                            pwm_stop()
                            time.sleep(2)
                            pwm_left()
                            time.sleep(1.25)
                            print("LEFT!")
                            pwm_stop()
                            time.sleep(2)

                if __name__ == '__main__':
                    try:
                        while True:
                            if AUTONOMOUS_MODE == True:
                                drive_autonomously()
                            else:
                                pygame.init()
                                pygame.joystick.init()
                                j = pygame.joystick.Joystick(0)
                                j.init()
                                #print("DRIVER_MODE")
                                events = pygame.event.get()
                                for event in events:
                                    if event.type == pygame.JOYBUTTONDOWN:
                                        if j.get_button(3):
                                            print("Moving Forward")
                                            pwm_forward()
                                        elif j.get_button(1):
                                            print("Moving Backward")
                                            pwm_backward()
                                        elif j.get_button(2):
                                            print("Moving Right")
                                            pwm_right()
                                        elif j.get_button(0):
                                            print("Moving Left")
                                            pwm_left()
                                    elif event.type == pygame.JOYBUTTONUP:
                                        pwm_stop()
                                        print("Listening...")
                    except KeyboardInterrupt:
                        print("Shutting Down and Cleaning Up")
                        pwm_stop()
                        GPIO.cleanup()
              
            
Write the code for Arduino
**SKIP THIS SECTION FOR REMOTE-CONTROLLED VEHICLE ONLY**

• If you haven't already, download the Arduino IDE for either Windows or Mac OS X.

• Open a new file in the IDE and copy the code below and save it as autonomous_rover_001_hcsr04.

• Use the USB A male to B male cable to connect your computer to your Arduino.

• Compile the code by clicking on the "check mark" in the top nav-bar of the Arduino IDE to verify.

• Upload the code by clicking on the "right arrow" in the top nav-bar of the Arduino IDE to upload your code to your Arduino board.

• If you want to test the sensors right now, click up top on "tools", and then click on "Serial Monitor". Move your hands around the sensors to gauge its sensitivity.

• If no errors occur, click the "arrow pointing to the right" in the top nav-bar of the Arduino IDE to upload the code to the Arduino board.

The code below is C/C++ which is used by Arduino. Read more about learning Arduino code here

(If you do not see a white-background below, please refresh your page)

              
                const int trigPinLeft = 13;
                const int echoPinLeft = 12;
                const int trigPinMiddle = 9;
                const int echoPinMiddle = 11;
                const int trigPinRight = 8;
                const int echoPinRight = 10;

                long duration;
                int distanceLeft;
                int distanceMiddle;
                int distanceRight;

                void setup() {
                  pinMode(trigPinLeft, OUTPUT);
                  pinMode(echoPinLeft, INPUT);
                  pinMode(trigPinMiddle, OUTPUT);
                  pinMode(echoPinMiddle, INPUT);
                  pinMode(trigPinRight, OUTPUT);
                  pinMode(echoPinRight, INPUT);
                  Serial.begin(115200);
                }

                void loop() {
                  digitalWrite(trigPinLeft, LOW);
                  delayMicroseconds(2);

                  digitalWrite(trigPinLeft, HIGH);
                  delayMicroseconds(10);
                  digitalWrite(trigPinLeft, LOW);

                  duration = pulseIn(echoPinLeft, HIGH);
                  distanceLeft = duration * 0.034 / 2;

                  digitalWrite(trigPinMiddle, LOW);
                  delayMicroseconds(2);

                  digitalWrite(trigPinMiddle, HIGH);
                  delayMicroseconds(10);
                  digitalWrite(trigPinMiddle, LOW);

                  duration = pulseIn(echoPinMiddle, HIGH);
                  distanceMiddle = duration * 0.034 / 2;


                  digitalWrite(trigPinRight, LOW);
                  delayMicroseconds(2);

                  digitalWrite(trigPinRight, HIGH);
                  delayMicroseconds(10);
                  digitalWrite(trigPinRight, LOW);

                  duration = pulseIn(echoPinRight, HIGH);
                  distanceRight = duration * 0.034 / 2;

                  if(distanceMiddle < 100){
                    if(distanceRight < 30 && distanceLeft < 30){
                     // Move Backwards
                      Serial.println("3");
                    }else if(distanceRight > distanceLeft){
                     // Move Right
                      Serial.println("2");
                    }else{
                     // Move Left
                      Serial.println("4");
                    }
                  }else if(distanceRight < 30){
                    // Move Left
                    Serial.println("4");
                  }else if(distanceLeft < 30){
                    // Move Right
                    Serial.println("2");
                  }else if(distanceRight < 30 && distanceLeft < 30){
                    // Move Backwards
                    Serial.println("3");
                  }else{
                    // Move Forward
                    Serial.println("1");
                  }

                }
              
            

Step 11:

**SKIP THIS SECTION FOR REMOTE-CONTROLLED VEHICLE ONLY**
Testing the autonomous vehicle

• If you can, try to get a couple boxes together to create an enclosure to allow your vehicle to maneuver around in. Maybe even throw in some obstacles in the middle of the enclosure as well to make things more interesting.

• Plug the Arduino into the Raspberry-Pi using the USB A to B cable. The rectangular end will go into the USB port of the Rasberry-Pi, and the box/square shaped end will plug into the Arduino.

• Place your vehicle in the enclosure and from your Raspberry-Pi's /Rovers directory, type sudo python3 autonomous_rover_001.py. When the program asks if you would like to engage Autonomous-Mode, type T (make sure it's capital) and press enter.

• The Raspberry-Pi will begin taking serial data from the Arduino to make decisions on how to maneuver based on those readings.

• Feel free to tweak the distances, baud rate, etc... of the vehicle in the code to your preference.

• If your vehicle is moving too slow you can change the DUTY_CYCLE to a higher number in the Raspberry-Pi code, not exceeding 100 though.

• To stop the program, simply type control + C.

• When you're ready to turn off your Raspberry-Pi, type sudo shutdown -h now and then press enter. Wait at least 30 seconds before removing the power USB cable from the power bank.

• Have fun and enjoy your autonomous vehicle. Remember, you can always add more sensors to the sides and back of the vehicle as well as a camera!