STEM-X

Currently, the only inhabitants on Mars are robots. Every time we explore a new part of our solar system, whether it be to Europa to search for life, or to an asteroid to mine it for its resources, we will be sending a robot first. It's extremely important to know every intricate detail, especially when you're 33,926,867.096 miles from home.

Don't Get Left Behind
mars_manned_mission


Autonomous/Remote-Controlled Rover

Parts:

  • Raspberry-Pi
  • Arduino Uno R3
  • 3 HC-SR04 ultrasonic distance sensors with brackets
  • Jumper wires
  • 2 L293D Dual H-Bridge Motor Drivers
  • Chassis with wheels
  • Micro-SD card
  • 6 inch USB A male to USB B male cable
  • 4 DC Motors
  • 2 mini breadboards
  • T-Type Clip
  • Top Metal Plate
  • 9v Battery
  • Anchor Mini Battery Pack
triangle_lights_rover
mvp_front.png
mvp_top

Instructions:



Follow ALL the instructions below to setup an Autonomous Rover.
When finished, click on the link at the bottom 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.


*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.


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

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

l293d_pinout.jpg
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 Mac or Linux, 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 written using Python3. To learn more about Python3 programming, visit here

        
          ### 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!

Remote Controlled Instructions