S20: Bucephalus

From Embedded Systems Learning Academy
Revision as of 04:08, 22 May 2020 by Proj user2 (talk | contribs) (Geographical Controller)

Jump to: navigation, search
Bucephalus Logo

ABSTRACT

Bucephalus is a Self Driving RC car using CAN communication based on FreeRTOS(Hard RTOS). The RC car takes real time inputs and covert it into the data that can be processed to monitor and control to meet the desired requirements. In this project, we aim to design and develop a self-driving car that autonomously navigates from the current location to the destination (using Waypoint Algorithm )which is selected through an Android application and at the same time avoiding all the obstacles in the path using Obstacle avoidance algorithm . It also Increases or Decreases speed on Uphill and downhill (using PID Algorithm)as well as applies breaks at required places. The car comprises of 4 control units communicating with each other over the CAN Bus using CAN communication protocol, each having a specific functionality that helps the car to navigate to its destination successfully.

INTRODUCTION

Objectives of the RC Car:-

1) Driver Controller:- Detection and avoidance of the obstacles coming in the path of the RC car by following Obstacle detection avoidance.
2) Geographical Controller:- Getting the GPS coordinates from the Android Application and traveling to that point using Waypoint Algorithm
3) System hardware communication using PCB Design.
4) Bridge and Sensor Controller:- Communication between the Driver Board and Android Mobile Application using wireless bluetooth commmunication.
5) Motor Controller:- Control the Servo Motor for Direction and DC motor for speed. Implementation of PID Algorithm on normal road uphill and down hill to maintain speed

The project is divided into six main modules:


CORE MODULES OF RC CAR

  • Android Mobile Application
  • Bridge and Sensor Controller
  • Geographical Controller
  • Driver and LCD Controller
  • Motor Controller
  • Hardware Integration and PCB Designing
Project Overview

Team Members & Responsibilities

<Team Picture>

Bucephalous GitLab - [1]

  • Mohit Ingale GitLab LinkedIn
    • Driver and LCD Controller
    • Hardware Integration and PCB Designing
    • Testing Team / Code Reviewers
  • Shreya Patankar GitLab LinkedIn
    • Geographical Controller
    • Hardware Integration and PCB Designing
    • Testing Team / Code Reviewers
    • Wiki Page
  • Nicholas Kaiser GitLab LinkedIn
    • Bridge and Sensor Controller
    • Wiki Page
    • Hardware Integration and PCB Designing
  • Hari Haran Kura GitLab LinkedIn
    • Motor Controller
    • Testing Team / Code Reviewers
    • Hardware Integration and PCB Designing
  • Basangouda Patil GitLab LinkedIn
    • Android Mobile Application
    • Testing Team / Code Reviewers
  • Abhinandan Burli GitLab LinkedIn
    • Driver and LCD Controller
    • Testing Team / Code Reviewers
    • Hardware Integration and PCB Designing


Schedule

Week# Start Date End Date Task Status
1 02/16/2020 02/22/2020
  • Setup a team Google Docs folder
  • Brainstorm RC car design options
  • Research past semester RC car projects for ideas and parts needed
  • Put together a rough draft parts list
  • Setup a team GitLab repository
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
2 02/23/2020 02/29/2020
  • Decide on and order chassis
  • Discuss possible GPS modules
  • Discuss schedule for meeting dates and work days (Tuesdays are for code review and syncing, Saturdays are work days)
  • Discuss bluetooth communication approach (1 phone on car, 1 phone in controller's hands)
  • Discuss vehicle's driving checkpoints (checkpoints calculated after point B is specified)
  • Discuss wiring on RC car (1 battery to power motors and 1 power bank for everything else)
  • Discuss GitLab workflow (mirror our repo with Preet's, 3 approvals to merge to "working master" branch, resolve conflicts on "working master" branch, then can merge to master branch)
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
3 03/01/2020 03/07/2020
  • Decide on sensors (4 ultrasonic sensors: 3 in front, 1 in back)
  • Decide on a GPS module (Adafruit ADA746)
  • Research GPS antennas
  • Decide on CAN transceivers (SN65HVD230 IC's)
  • Request 15 CAN transceiver samples from ti.com
  • Discuss tasks of all 4 board nodes (geographical, driver, motors, bridge controller/sensors)
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
4 03/08/2020 03/14/2020
  • Assemble car chassis and plan general layout
  • Delegate tasks for each 2 person teams
  • Create branches for all nodes and add motor and sensor messages to DBC file
  • Discuss and research possible GPS antennas
  • Design block diagrams for motor node, bridge controller/sensor node, and full car
  • Solve GitLab branches vs folders issue (1 branch per node, or 1 folder per node)
  • Order 4 + 1 extra ultrasonic sensors (MaxBotix MB1003-000 HRLV-MaxSonar-EZ0)
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
5 03/15/2020 03/21/2020
  • Decide what to include on PCB board
  • Read previous student's reports to decide on a compass module (CMPS14)
  • Order GPS antenna
  • Add GPS node messages (longitude, latitude, heading) and bridge sensor node messages (destination latitude and longitude) to DBC file
  • Start learning Android app development
  • Begin researching filtering algorithms for ultrasonic sensors
  • Ultrasonic sensor values are converted to centimeters and transmit to driver node
  • Research ultrasonic sensor mounts
  • Transmit CAN messages from sensor to driver node, and from driver to motor node
  • Driver node is able to respond correctly based on sensor obstacle detection scenarios (correct LED's light up)
  • Decide movement and steering directions based on all possible sensor obstacle detection scenarios
  • Begin research on PID implementation to control speed of RC car
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
6 03/22/2020 03/28/2020
  • Draw block diagrams with pin information for each board and begin PCB design based on these diagrams
  • Order a new compass module (CMPS14)
  • Finalize parts list and place orders for remaining unordered items
  • Decide on tap plastic acrylic sheet dimensions and PCB dimensions
  • Start implementing a basic Android app without Google maps API and create a separate GitLab repo for app
  • Bridge sensor node is able to transmit a destination latitude and longitude coordinates message to geological node
  • Decide on ultrasonic sensor mounts and order extra if needed
  • Geological node is able to transmit a heading message to the driver node
  • Integrate driver board diagnostic testing with LEDs and ultrasonic sensors (car goes left, left LEDs light up, etc.)
  • Continue research on PID controller design and begin basic implementation
  • Finish designing team logo and upload to Wiki page
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
7 03/29/2020 04/04/2020
  • Complete rough draft of DBC file messages and signals
  • Purchase tap plastic acrylic sheet
  • Learn how to integrate Google maps API into Android app
  • Finish a basic implementation of filtering ultrasonic sensor's ADC data
  • Design a block diagram for optimal ultrasonic sensor placement
  • Bluetooth Module driver is finished, can connect to Android phone, and can receive "Hello World" data from phone
  • Geological node is able to parse the GPS NMEA string to extract latitude and longitude coordinates
  • Geological node is able to receive a current heading (0-360 degrees) from the compass module
  • Geological node is able to receive an NMEA string from the GPS
  • Geological node is able to compute the destination heading (0-360 degrees) and send to driver node
  • Add PWM functionality to motor board code and test on DC and servo motors
  • Complete a basic implementation of encoder code on motor board
  • Complete rough draft of schedule and upload to Wiki page
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
8 04/05/2020 04/11/2020
  • Google maps API is fully integrated into Android App
  • Finish ultrasonic filtering algorithm for ultrasonic sensor's ADC data
  • Design ultrasonic sensor shields to minimize sensor interference with each other
  • Bluetooth Module is able to receive data from Android app
  • Test obstacle avoidance algorithm (indoor)
  • Complete motor board code controlling RC car's DC motor and servo motor
  • Complete "push button" motor test (servo turns wheels left and right, and DC motor spins wheel forwards and backwards)
  • Begin car chassis wiring on a breadboard
  • Finalize and review PCB schematic
  • Complete a rough draft car chassis block diagram for the placement of all boards and modules
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
9 04/12/2020 04/18/2020
  • Discuss checkpoint algorithm
  • Display sensor and motor data on Android app
  • Bluetooth module is able to receive "dummy" destination latitude and longitude coordinates from Android app
  • Finish GPS module integration with geographical controller
  • Test obstacle avoidance algorithm (outdoor)
  • Test existing motor board code on RC car's motors
  • Begin wheel encoder implementation and unit testing
  • Complete car chassis wiring on a breadboard
  • Establish and test CAN communication between all boards
  • Design and solder a prototype PCB board in case PCB isn't delivered in time
  • Mount sensors, motors, LCD, and all four sjtwo boards onto car chassis
  • Finish routing PCB and review to verify the circuitry
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
10 04/19/2020 04/25/2020
  • Android app is able to send start and stop commands to the car
  • Android app is able to display car data (speed, sensor values, destination coordinates, source coordinates)
  • Bluetooth module is able to receive actual destination latitude and longitude coordinates from Android app
  • Test drive outdoors to check obstacle avoidance algorithm
  • Complete motor control code with optimal speed and PWM values without PID control
  • Begin unit testing the PID control algorithm
  • Finish wheel encoder implementation and unit testing
  • Make final changes to PCB and place order
  • Test drive the soldered PCB board to ensure everything is working properly
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
11 04/26/2020 05/02/2020
  • Test drive from start to destination (outdoor)
  • GEO controller can compute the heading from Android app's actual destination coordinates, and send to driver board
  • Finalize obstacle avoidance algorithm
  • LCD display is able to display car's speed, PWM values, destination coordinates, and sensor values
  • Complete a basic PID algorithm and begin uphill and downhill testing
  • Fully integrate wheel encoder onto car chassis
  • Finish basic implementation of PID control and test on car
  • Solder and integrate PCB onto car and test drive to make sure everything is working properly
  • Mount GPS and compass modules onto car chassis
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
12 05/03/2020 05/09/2020
  • Test drive from start to destination with obstacles and making U-turns (outdoor)
  • Finalize DBC file
  • Complete Android app's basic features (start, stop, connect, google maps, displaying car data)
  • Finalize sensor mounting heights and positions
  • Complete basic implementation and unit testing of checkpoint algorithm
  • Test checkpoint algorithm on car (outdoor)
  • Finalize PID control implementation and test on car (outdoor)
  • Integrate GPS antenna onto car chassis
  • Drill acrylic sheet and mount circuity with screws instead of glue
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
13 05/10/2020 05/16/2020
  • Test drive from start to destination at SJSU and determine checkpoints
  • Begin implementing checkpoint selection capability on Android app
  • Design and integrate adjustable sensor shields
  • Finalize checkpoint algorithm based on feedback from test drives
  • Add headlights, tail lights, and side lights code to car and test
  • Add battery percentage computation code to car and test
  • Tweak PID control implementation based on feedback from test drives
  • Integrate headlights, tail lights, side lights, and battery percentage computation circuitry onto car chassis
  • Upload rough draft of report to Wiki page
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
  • Completed
14 05/17/2020 05/23/2020
  • Test drive from start to destination at SJSU and finalize checkpoints
  • Film demo video
  • Demo
  • Push final code to GitLab
  • Submit individual contributions feedback for all team members
  • Make final updates to Wiki report
  • Finalize Android app's checkpoint selection capability, and 6 point precision of destination coordinates
  • Add headlights "ON" and "OFF" button to Android app
  • Completed


Parts List & Cost

Item# Part Desciption Vendor Qty Cost
1 RC Car Chassis Traxxas 1 $250.00
2 Lithium-Ion Battery 1
3 Battery Charger 1
4 Tap Plastics Acrylic Sheet 1
5 Ultrasonic Sensors Amazon [2] 4
6 GPS Module 1
7 GPS Antenna 1
8 Compass Module 1
9 UART LCD Display 1
10 Bluetooth Module 1
11 CAN Transceivers SN65HVD230DR 15 Free Samples
12 Sjtwo Board Preet 4 $50.00
13 12" Pipe 1
14 Android Mobile Phone 1
15 Sensor Mounts 4


Hardware Integration:- Printed Circuit Board


We Initially started with a very basic design of mounting all the hardware on a cardboard sheet for our first round of Integrated hardware testing.

First Prototype Board


Challenges:- The wires were an entire mess and the car could not navigate properly due to the wiring issues as all the wires were entangling and few had connectivity issues. Hence we decided to go for a basic dot matrix Design before finalizing our final PCB Design as a Prototype board for testing if anything goes haywire.

The Prototype Board just before the actual PCB board was created on a dot matrix PCB along with all the hardware components for the Intermediate Integrated testing phase is as follows:

Prototype board 1
Prototype board 2
Prototype board 3


1) To avoid all the above challenges We designed the custom PCB using EasyEDA in which we implemented connections for all the controller modules(SJTwo Board LPC4078) all communicating/sending data via CAN bus. The data is sent by individual sensors to the respective controllers. GPS and Compass are connected to Geographical Controller. RPM sensor, DC and Servo Motors are connected to Motor Controller.
2) Ultrasonic are connected to Bridge and Sensor Controller. LCD is connected to Driver Controller. Bluetooth is connected to Bridge and Sensor Controller. CAN Bus is implemented using CAN Transceivers SN65HVD230 terminated by 120 Ohms; with PCAN for monitoring CAN Debug Messages and Data. Some Components need 5V while some sensors worked on 3.3V power supply. Also it was difficult to use separate USB's to power up all boards.Hence we used CorpCo breadboard power supply 3.3V/5V.
3) PCB was sent to fabrication to JLCPCB China which provided PCB with MOQ of 5 with the lead time of 1 week. We implemented 2 layers of PCB with most of the parts in top layer GPS sensor and Compass sensor. We implemented rectangular header connector for SJTwo boards, RPM sensor, DC & Servo Motor on the bottom layer. There were 2 iterations of this board.
4) Challenges :-We also need to change the header for LCD since it was having different pitch.


DESIGNING:-

Top Layer development
Bottom Layer development

AFTER DELIVERY:-

Top Layer development
Bottom Layer development






CAN Communication

<Talk about your message IDs or communication strategy, such as periodic transmission, MIA management etc.>

Hardware Design

<Show your CAN bus hardware design>

DBC File

DBC File




Bridge and Sensor Controller: Bluetooth Module

Hardware Design

Software Design

<List the code modules that are being called periodically.>

Technical Challenges

< List of problems and their detailed resolutions>

Bridge and Sensor Controller: Sensors

A total of four Maxbotix MaxSonar HRLV-EZ0 ultrasonic sensors are connected to the bridge and sensor controller; three in front and one in back. We decided to use four ultrasonic sensors due to their relatively easy setup process, ranging capabilities, and reliability. We considered using lidar, but these sensors rely on additional factors such as lighting conditions.

We specifically chose Maxbotix’s more expensive MaxSonar line because of their greater accuracy over their standard counterparts. The MaxSonar sensors have internal filtering to greatly reduce the amount of external noise affecting your sensor readings, making your readings far more reliable. The MaxSonar sensors also include an on-board temperature sensor to automatically adjust the speed of sound value based on the temperature of the environment the sensors are being used in. This means the sensor readings will stay consistent regardless of the weather and time of day (night time is much cooler than during the day).

Maxbotix offers ultrasonic sensors with different beam widths, and we chose their EZ0 sensors because they have the widest beam. Initially we thought this was a good idea because wider beams means an increased detection zone and less blind spots (width-wise). Please refer to the Technical Challenges section for details as to why we don’t recommend getting the widest possible beam for all four sensors.

<Picture (sensors only)>

Hardware Design

The ultrasonic sensors are powered using +5V, and connected to the SJtwo board’s ADC pins. There are only 3 available exclusively ADC pins on the SJtwo board, and so we changed the DAC pin to ADC mode in our ADC driver since we needed a total of 4 ADC pins. We left the RX pin of each sensor unconnected, and so each sensor ranges continuously and independently of one another. We don’t recommend doing this because the sensor beams interfere with each other when firing at the same time, but due to implementation issues, we weren’t able to use the RX pin approach. Please refer to the Technical Challenges section for details as to what problems we encountered when using the RX pin, and how we resolved the interference problems as a result of not using the RX pin.

<Insert easyEDA schematic (sensors and sjtwo board only)>

Input Voltage to Sensors

<...>

Sensor Mounts and Shields

<...>

Placement on Chassis

<...>

Software Design

The software design of the sensor modules consisted of the following ordered steps. Initialize all sensors once the car is powered on Left sensor Read 10 values from the left sensor’s ADC channel Convert these 10 ADC values to centimeters Filter these 10 centimeter values into one filtered value Right sensor Read 10 values from the right sensor’s ADC channel Convert these 10 ADC values to centimeters Filter these 10 centimeter values into one filtered value Front sensor Read 10 values from the front sensor’s ADC channel Convert these 10 ADC values to centimeters Filter these 10 centimeter values into one filtered value Back sensor Read 10 values from the back sensor’s ADC channel Convert these 10 ADC values to centimeters Filter these 10 centimeter values into one filtered value Transmit one CAN message with four filtered values (one for each sensor)

The flowchart below is for a single sensor between the initialization step, and the send CAN message step. The three in-between steps are done a total of four times (for each sensor). To keep things clean and easy to understand, the flowchart only shows the steps for a single sensor.

Single Sensor Flowchart

Initializing the Sensors

Some initialization and ADC driver modifications were required in order to get our sensors up and running. First, we wanted to use the analog out pin to receive data from all four sensors due to the simplicity of this interface. Since the Sjtwo board only has 3 available header pins with ADC functionality, we read the user manual for the LPC4058 [insert reference to user manual?] in order to see if any other available header pins had ADC capabilities. Shown below is Table 86 in the user manual, which lists all pins that have ADC functionality. Comparing this table with our SJtwo board, we found that ADC channel 3 located on P0[26] (labeled as DAC on SJtwo board) could be used as our fourth ADC pin.

Table 86 in LPC4058 User Manual

We modified the existing ADC driver in order to add support for P0[26] as an ADC pin. An enumeration for ADC channel 3 was added to adc.h, and ADC channel 3 was added to the “channel check” if statement in adc.c’s adc__get_adc_value(...) function. These modifications are shown below.

adc.h Modification
adc.c Modification in adc__get_adc_value(...)

In order to begin reading values from the sensors, all ADC pins must be set to “Analog Mode”. Looking at Table 85 in the LPC4058 user manual, we can see that upon reset, all ADC pins are in “Digital Mode”.

Part of Table 85 in LPC4058 User Manual

The SJtwo board’s ADC pins will not work if they are not set to “Analog Mode”, and so this code had to be added to the existing ADC driver since it was missing. We did the following IOCON register bit 7 clearing for all ADC pins inside the adc__initialize(...) function.


adc.c Modification in adc__initialize(...) <insert pic>

File:Adc.c Modification in adc initialize(...)
adc.c Modification in adc__initialize(...)

Once the modifications to the existing ADC driver had been made, we called adc__initialize(...) in our periodic_callbacks_initialize(...) function in order to perform the initialization of our ADC interface.

Converting to Centimeters

The raw ADC values read from each sensor aren’t inherently meaningful, and so they must be converted to centimeters in order to gauge the distance of obstacles. In order to perform the conversion, we sampled and recorded a sensor’s raw ADC values with obstacles placed at distances ranging from 0cm to 120cm in increments of 10cm. We decided to stop at 120cm because the process of collecting the sensor’s data was very time consuming and our maximum threshold value was 110cm, so there was no need to go farther. Shown below are the measurements gathered for our particular sensor model. It should be noted that the sensors produce a range of values when detecting an obstacle at a given distance, and so we averaged the values to obtain the most accurate results.


Ultrasonic Sensor Measurements <insert pic>

The values were plotted in a distance (Y-axis) versus ADC value (X-axis) graph, and a curve fit was performed in order to generate distance as a function of an ADC value. We used an application called Logger Pro because of its ability to automatically generate a curve fit for your data set.


Ultrasonic Sensor Distance vs. ADC Graph <insert pic>

We implemented the curve fit function generated by Logger Pro into code in order to convert our sensor’s ADC values to centimeters right after they were read from their respective ADC channel.

Filtering

Occasionally, the sensors will report incorrect values due to internal or external noise. Getting sensors that are more expensive usually decreases the number of times this happens, but even one incorrect reading can be a problem. For example, a sensor reading may incorrectly dip below the threshold and detect an obstacle even when there is none. Or worse, an obstacle may be within the threshold range but the sensor reading doesn’t reflect an obstacle being detected. Both of these scenarios can be avoided by filtering the sensor values before sending them to the driver controller over CAN.

The problem we were facing before filtering was the following; when setting up an obstacle in front of a sensor a certain distance away, 80cm for example, we monitored the sensor values to make sure all the readings were within a couple centimeters of 80cm. We noticed that occasionally, the output sensor readings would look something like “...80, 81, 80, 79, 82, 80, 45, 80, 78, 83, 81, 80, 79, 80…”. The sudden drop to 45 is clearly an incorrect reading because the obstacle remained at a constant 80cm away the entire time.

We considered several different filtering algorithms such as Mean, Median, Mode, and Kalman Filter. The Kalman Filter is very complex and so we didn’t feel the need to use it since other simpler options would work just as well for our application. The Mean Filter was completely out of the question because it averages the values. We want to remove the incorrect value, not incorporate it into the filtered value. The Median Filter requires the input values to be sorted in ascending order, which increases the run time and may cause us to run past our time slot in the 50Hz periodic callback function. Ultimately, we decided to use the Mode Filter because of its simplicity, short run time, and doesn’t require input values to be sorted. Choosing a filtering algorithm that has a short run time was one of the most important considerations because the sensors are running in a fast periodic callback function.

We implemented our own Mode Filter algorithm that took a number of input sensor values that have been converted to centimeters, finds the mode (most frequently occurring value) of the values, and then returns the mode as the filtered value. Through testing, we found that using an input size of 10 values to the mode filter was perfectly sufficient. A list of possible input values and the mode filter output can be seen below to better illustrate how our mode filter implementation works.


Mode Filter Output <insert pic>

A corner-case that we needed to address was the possibility of multiple modes. This case can be seen in lines 2, 3, and 5 in the table above. We chose to return the mode that was most recent in order to reflect the most recent changes in sensor values. Take line 5 for example; all values occur exactly twice, which means there are five modes. The mode returned is 69 because it is the most recent mode.

As for the actual implementation of the Mode Filter, we based it off of the Counting Sort algorithm’s “bucket storing method” with some modifications in order to make the runtime O(n), with n being the input data size (10 in our case). There is no need to sort the values for our Mode Filter, and so the sorting part of the Counting Sort algorithm is completely omitted. The idea of storing the occurence of each value in an array element number equal to the current sensor value (in centimeters) is the only idea we used from the Counting Sort algorithm. An example of how our Mode Filter works can be seen below.


Mode Filter Iterations (1) <insert pic>


Mode Filter Iterations (2) <insert pic>

The size of the array inside the Mode Filter algorithm is specifically set to 600 elements. The array needs to have element numbers for every possible sensor value (in centimeters). Our sensor values range is from 0 to 500 centimeters and so we added an extra 100 just for safety, which gives us a total of 600 array elements. This is the reason we chose to convert our sensor values to centimeters before passing them as input into the Mode Filter. If we were to pass the raw sensor values into the filter, we would need an array with around 4096 elements (SJtwo board’s ADC is 12-bit, so 2^12 = 4096).

Technical Challenges

ADC

Three main problems occurred relating to our ADC interface. First, we were unable to get the sensors to output anything besides its maximum value. We checked the voltage on the ADC pin for multiple sensors and found that instead of increasing as an object got farther away, the voltage stayed constant. We concluded that since this was happening for all the sensors we tested, it must be a problem with our initialization code. After reading the ADC section in the user manual for our SJtwo board’s processor, we realized that our ADC driver was missing one thing. For each GPIO pin being used as an ADC pin, Pin 7 of the IOCON register has to be set to “ADC Mode”. After making this change, our sensors worked like a charm.

Next, we observed that our maximum sensor values weren’t the advertised 500cm. We experimented by putting obstacles in front of a sensor at varying distances and found that 300cm was the farthest the sensor could range. After reading the user manual for our SJtwo board’s processor, we realized that its ADC module operates in reference to 3.3V. We then switched to powering our sensors with 3.3V instead of 5V, hoping to get the 500cm range as advertised. This created the problem discussed below.

Our third problem we encountered was after making the switch from powering our sensors with 5V to 3.3V. 3.3V did not supply our sensors with enough power, and so we experienced large blind spots in our detection zone, and very inconsistent sensor readings due to the sensors generating weak sound waves from a lack of sufficient power. We decided to go back to powering our sensors with 5V since 300cm was more than enough ranging capability for this project.

Inconsistent Sensor Readings

When we first started testing the sensors, we used a single sensor to make sure our connections and code initialization was correct. We placed a flat object in front of the sensor at the same distance for a very long time to see how consistent our sensor readings were. We noticed that sometimes one reading would be very low or very high compared to the other readings. After doing some research, we found that this is a very common occurrence among sensors and can be resolved by filtering. We chose to implement our own filtering algorithm, which takes in a certain number of raw sensor values, finds the mode, and returns this as the filtered sensor value.

Slow Response to Obstacles

When we first began testing obstacle detection, we used one LED for each sensor for diagnostic testing purposes. If a sensor detected an obstacle, then its corresponding LED would turn on. When the obstacle was no longer present, the LED would turn off. We noticed that the response of the LED was very slow, and the sensor values in Busmaster would not change immediately when an obstacle was present. We solved this by moving our “get sensor values” code from 10Hz to 50Hz in order to get a faster obstacle detection response.

Additionally, when we started testing our PID acceleration, we noticed that the car would not stop in time before hitting an obstacle when moving at faster speeds. We monitored the sensor values on the android app to try and troubleshoot the issue. We found that the sensor values were not changing quick enough, and so the car was crashing because it didn’t have enough time to stop. We solved this problem by removing our “light up SJtwo board’s LEDs” function from our sensor’s CAN transmit function, which was slowing down the transmission of the sensor’s CAN messages.

Detecting Obstacles when None are Present

During our initial drive testing, our car would randomly take a sharp turn or stop when there were no obstacles in range. We hooked up Busmaster to the car’s on-board PCAN dongle and walked alongside the car while it was moving in order to diagnose the issue. We saw that the sensor values were dropping below the threshold when there were no obstacles present, and even when the car was not moving. This meant that the sensors were detecting the road or even parts of the car. We resolved this by tilting the sensor heads upwards until the sensor values were at their maximum (300cm for us).

Sensor's TX/RX Pins Not Working

Our first approach to solving sensor interference issues was to fire the sensor beams one at a time, and only after the previous sensor had finished. This functionality is built into MaxBotix sensors through the TX and RX pins. If these pins are connected by following the diagrams in the sensor user manual, a sensor will fire, and then trigger the next sensor to fire once it is finished. This process will repeat continuously or only once depending upon which wiring diagram you choose in the user manual.

We followed all coding guidelines and circuitry schematics in the user manual, but our sensors would fire once, and then go into a non-responsive idle state. After almost 2 weeks of trying different configurations, wiring, and coding implementations, we decided we couldn’t waste any more time on the TX/RX pin approach and opted to solve our interference problems in a different way.

Sensor Beam Interference

The beams of our three front sensors interfered with each other, resulting in completely inaccurate sensor readings. To solve this issue, we mounted the front sensor on a platform so that it stood about 4 inches higher than the left and right sensors. We also turned the left and right sensors 45 degrees outward and added shields in order to minimize the interference from the front sensor’s beam. We found that the optimal shield placement for our setup was turning them outwards at about 22.5 degrees or less from the sensor’s head.



Motor Controller

The Motor Controller SJ2-Board is mainly responsible for RC-Car’s steering and speed to move the car towards the destination. The RC-Car we are using is 2 Wheel Drive which means the front wheels are used for steering which is controlled by servo motor and the rear wheels are used for car’s forward and reverse movements which are controlled by DC Motor through ESC.

Hardware Design

Motor Module Schematic.PNG


ESC & DC Motor

The ESC(Electronic Speed Control, Traxxas ESC XL-05) and DC Motor we used were provided with the RC car. The DC motor is controlled by ESC using PWM signals provided by the motor controller for forward and reverse movements. We used the 7.4v LiPo battery to power up the ESC. The DC motor is powered by the ESC which has a dc-to-dc converter which converts 7.4v to 6v. ESC can provide high current to the power-hungry DC motor running at faster speeds. ESC has an LED and a button which is used for calibration and setting different modes for the car.

Wires on ESC Description Wire Color
PWM(P2.1) Takes PWM input from SJ2-Board WHITE
VDD(6V) Power Output RED
GND Ground BLACK


The car can be operated in the following 3 modes:
Sport mode(100% Forward, 100% Brakes, 100% Reverse)
Racing mode(100% Forward, 100% Brakes, No Reverse)
Training mode(50% Forward, 100% Brakes, 50% Reverse)

As we needed more than 50% speed for steep ramps, we used Sport mode. The frequency of the PWM signal fed to the servo motor is 100Hz. Based on the duty cycle set by the user, the car will go forward, reverse, or neutral. 10% - 20% duty cycle for reverse. 15% duty cycle for neutral. 15%-20% duty cycle for the forward motion.


Traxxas ESC
Traxxas Brushless DC Motor

SERVO

The servo motor we used is Traxxas 2075 which was provided with the car and it is responsible for steering the car. It takes the 6V power directly from ESC. The servo motor is controlled directly from the SJ2 micro-controller board. The PWM signal fed to the servo motor is of frequency 100Hz. Based on the duty cycle of the signal sent to the servo, it rotates in the left / right direction. 10% - 20% duty cycle for left. 15% duty cycle for straight. 15%-20% duty cycle for right.

Wires on ESC Description Wire Color
PWM(P2.0) Takes PWM input from SJ2-Board WHITE
VDD(6V) Power Input RED
GND Ground BLACK


Traxxas Servo

RPM SENSOR

There are two parts to the RPM sensor - one is the trigger magnet and the other is the sensor. The RPM sensor is used as an input to maintain a constant speed of the vehicle. The sensor mounts on the inside of the gear cover, the trigger magnet mounts on the DC motor shaft. The gear cover and motor shaft need to be removed using the toolkit provided along with the RC car. The procedure is similar to this video, but note that the car used in the video is a 4WD RC car by Traxxas. We made use of the trigger magnet attached to the spur gear to trigger a pulse on the sensor for every rotation of the spur gear. These pulses are then read by the SJ2 board to calculate rotations in a second and later convert it to RPM and MPH. The RPM sensor has 3 wires, the white wire is the output wire that provides the pulses to the SJ2 Board, and the other wires are Supply(6V) and GND. We also used a Pull Down 1K Resistor between Supply and RPM output wires.

Wires on ESC Description Wire Color
GPIO(P0.6) Provides pulses to Motor SJ2-Board WHITE
VDD(6V) Power Input RED
GND Ground BLACK


Traxxas RPM Sensor
Trigger Magnet

Software Design

Periodic Callbacks:

Periodic Init: CAN, PWM(Servo and Motor), and Interrupts(RPM Sensor and Test button) were initialized in this function.

10Hz function: CAN messages were transmitted in this function. Also, cleared wheel interrupt rotations every 500ms.

100Hz function: Used this function to run at 20Hz. When the test button is pressed, Motor tests are performed else the car will follow driver commands.



Periodic Callbacks Flowchart

Servo Motor:

The PWM signal fed to the servo motor is of frequency 100Hz. The motor controller receives steer controls from the driver controller and it sends corresponding PWM value to the servo motor.


Servo Flowchart

DC Motor:

The PWM signal fed to the DC motor is of frequency 100Hz. The motor controller receives speed controls from the driver controller and it sends corresponding PWM value to the ESC which controls the DC motor.


DC Motor Flowchart

RPM Sensor:

RPM Sensor gives one pulse for each wheel rotation and the software calculates speed in mph using the no. of rotations. We are re-calculating the speed, every 500ms to maintain the speed of the motor while the car is going uphill or downhill.


Speed Control Flowchart

Technical Challenges

Car Reverse Movement

Problem: The RC car sometimes wouldn’t go reverse and this used to happen randomly.

Solution: We checked the code and were giving the correct PWM values for the car to go reverse but the car wouldn’t do it sometimes. Then, we read the ESC manual and figured out that the wheels need to stop before giving the reverse command. For us, using these Commands (FULL REVERSE and STOP) before giving reverse PWM values for the reverse movement worked well. Also, we had to use current speed in combination with these FULL REVERSE commands to make sure that these reverse commands are given only when the car is moving forward.

Car downhill movement

Problem: The car would move too fast while downhill even though the feedback loop for the speed control is reducing the PWM.

Solution: The feedback loop to maintain the speed of the car was working fine while the car was going uphill, but while downhill the car would go too fast. To resolve this, we applied brakes(FULL REVERSE commands) to the car whenever the speed crosses a certain limit.

Mounting the RPM Sensor

Problem: We had a hard time figuring out where to mount the rpm sensor, as the documentation provided by Traxxas was for 4WD and ours was a 2WD RC car.

Solution: We figured out the place to mount the sensor by chance, using common sense. It was mounted on the inside of the gear cover, where a tiny hidden place was provided.




Geographical Controller

GEO Board Block Diagram

Hardware Design

The geographical controller is responsible for providing the required destination heading angle(destination direction) and the destination distance using compass, GPS sensors for the car to reach destination. directions for the car to reach its destination. This is achieved using the Adafruit MTK3339 Ultimate GPS module and the CMPS14 9DOF compass module to obtain the heading, bearing, and distance to checkpoint. Geographical controller receives checkpoint from bridge module, with which the distance and deflection angle are calculated. In our design, the pathfinding and the continuous sending of different checkpoints as car reaches each of them is checked by the Bridge module. The geographic controller is only responsible for obtaining the car’s current coordinates, computing distance and angle to the next checkpoint that is provided by the Bridge module over CAN and send these results over the CAN bus.

The car's starting latitude and longitude coordinates, were calculated by the GPS and then transmitted over CAN to the bridge controller, which transmitted them to the Android phone, using bluetooth. Upon executing the RUN-D.B.C Android mobile application, the user was able to select a destination location, by placing a marker on the Google Map fragment. The app parsed the destination's latitude and longitude coordinates and then sent them to the bridge controller. The pathfinding algorithm (Dikstra's algorithm) is used to calculate a path to the destination based on the starting location and a series of pre-defined checkpoints comprised of longitude and latitude data.

Software Design

<List the code modules that are being called periodically.>

Technical Challenges

< List of problems and their detailed resolutions>



Driver Module

The Driver module is the brain of our autonomous car. It takes input from the Sensor and Geological controller to compute the direction of travel and sends the driving signal to the motor controller. It communicates with other modules via the CAN bus using CAN transceiver.

Master Block Diagram

Hardware Design

Driver Schematic Block Diagram
Driver Schematics

Software Design

  • The Driver control module is the brain of the Self-driving car as it takes all the critical driving decisions based on messages from the other control modules like Sensor, Geo and sends the message to the Motor control module for driving the car.
  • The Driver Board receives CAN message from Geological board which includes the current heading angle of the car, destination heading angle of the car, and distance to the destination.
  • The Driver Board also receives CAN message from the sensor board which includes the sensor values indicating if an obstacle is present or not. If obstacle is present the distance to the obstacle is part of the message. The values for front, left, right and back sensor is included in the message.
  • The driver control board makes use of the message from the geological board and sensor board to compute the motor command for driving the car.
  • The driver board has two algorithms, one which computes the motor commands based on geo controller information and other which computes the direction based on obstacle avoidance algorithm. The Obstacle avoidance algorithm has higher priority and is used if there is an obstacle detected by any of the sonar sensors. Otherwise, it makes use of the geo algorithm to compute the motor commands.
  • The geo algorithm computes the deflection angle based on current and destination heading and decides on the directions of the car. The distance to the destination decides the forward movement of the car.
static float driving_algo__compute_deflection(const dbc_GEO_COMPASS_s *heading_angle) {
  float deflection = heading_angle->GEO_COMPASS_desitination_heading - heading_angle->GEO_COMPASS_current_heading;
  if (deflection > 180) {
    deflection -= 360;
  } else if (deflection < -180) {
    deflection += 360;
  }
  return deflection;
}

static void driving_algo__get_gps_heading_direction(dbc_DRIVER_STEER_SPEED_s *driving_direction) {
  float deflection = driving_algo__compute_deflection(&current_and_destination_heading_angle);
  driving_direction->DRIVER_STEER_move_speed = DRIVER_STEER_move_FORWARD_at_SPEED;
  if (deflection > TOLERANCE_DIRECTION_POSITIVE) {
    driving_direction->DRIVER_STEER_direction =
        (deflection < 45) ? DRIVER_STEER_direction_SOFT_RIGHT : DRIVER_STEER_direction_HARD_RIGHT;
  } else if (deflection < TOLERANCE_DIRECTION_NEGATIVE) {
    driving_direction->DRIVER_STEER_direction =
        (deflection > -45) ? DRIVER_STEER_direction_SOFT_LEFT : DRIVER_STEER_direction_HARD_LEFT;
  } else {
    driving_direction->DRIVER_STEER_direction = DRIVER_STEER_direction_STRAIGHT;
  }
}
  • The obstacle avoidance algorithm first fills the data structure which maintains information if the obstacle is far, near, or very near. The algorithm then uses this information to compute direction to drive the car away from the obstacle.
typedef union {
  struct obs {
    uint8_t front : 2;
    uint8_t back : 2;
    uint8_t right : 2;
    uint8_t left : 2;
  } obs;
  uint8_t obstacle_var;
} obstacle_s;

static void obstacle_avoidance__is_fill_sensor_data() {
  ultrasonic_data.obstacle_var = 0;
  is_obstacle = false;
  if (sensor_data.SENSOR_USONARS_front <= FRONT_BACK_THRESHOLD_VERY_NEAR) {
    ultrasonic_data.obs.front = 3;
    is_obstacle = true;
  } else if (sensor_data.SENSOR_USONARS_front <= FRONT_BACK_THRESHOLD_NEAR) {
    ultrasonic_data.obs.front = 1;
    is_obstacle = true;
  }
  if (sensor_data.SENSOR_USONARS_back <= FRONT_BACK_THRESHOLD_VERY_NEAR) {
    ultrasonic_data.obs.back = 3;
    is_obstacle = true;
  } else if (sensor_data.SENSOR_USONARS_back <= FRONT_BACK_THRESHOLD_NEAR) {
    ultrasonic_data.obs.back = 1;
    is_obstacle = true;
  }
  if (sensor_data.SENSOR_USONARS_right <= THRESHOLD_VERY_NEAR) {
    ultrasonic_data.obs.right = 3;
    is_obstacle = true;
  } else if (sensor_data.SENSOR_USONARS_right <= THRESHOLD_NEAR) {
    ultrasonic_data.obs.right = 1;
    is_obstacle = true;
  }
  if (sensor_data.SENSOR_USONARS_left <= THRESHOLD_VERY_NEAR) {
    ultrasonic_data.obs.left = 3;
    is_obstacle = true;
  } else if (sensor_data.SENSOR_USONARS_left <= THRESHOLD_NEAR) {
    ultrasonic_data.obs.left = 1;
    is_obstacle = true;
  }
}

static void obstacle_avoidance__get_motor_direction(dbc_DRIVER_STEER_SPEED_s *motor_info) {
  switch (ultrasonic_data.obstacle_var & 0x0F) {
  case FRONT_VERY_NEAR_OBSTACLE:
  case FRONT_VERY_NEAR_BACK_NEAR_OBSTACLE:
    motor_info->DRIVER_STEER_move_speed = DRIVER_STEER_move_REVERSE_at_SPEED;
    break;
  case FRONT_NEAR_OBSTACLE:
  case FRONT_BACK_NEAR_OBSTACLE:
  case FRONT_NEAR_BACK_VERY_NEAR_OBSTACLE:
    motor_info->DRIVER_STEER_move_speed = DRIVER_STEER_move_FORWARD_at_LOW_SPEED;
    break;
  case FRONT_BACK_VERY_NEAR_OBSTACLE:
    motor_info->DRIVER_STEER_move_speed = DRIVER_STEER_move_STOP;
    break;
  case BACK_VERY_NEAR_OBSTACLE:
  case BACK_NEAR_OBSTACLE:
  case FRONT_BACK_NO_OBSTACLE:
  default:
    motor_info->DRIVER_STEER_move_speed = DRIVER_STEER_move_FORWARD_at_SPEED;
    break;
  }
}

static void obstacle_avoidance__get_steer_direction(dbc_DRIVER_STEER_SPEED_s *motor_info) {
  switch (ultrasonic_data.obstacle_var & 0xF0) {
  case RIGHT_VERY_NEAR_OBSTACLE:
    (motor_info->DRIVER_STEER_move_speed == DRIVER_STEER_move_FORWARD_at_SPEED)
        ? (motor_info->DRIVER_STEER_direction = DRIVER_STEER_direction_HARD_LEFT)
        : (motor_info->DRIVER_STEER_direction = DRIVER_STEER_direction_HARD_RIGHT);
    break;
  case RIGHT_NEAR_OBSTACLE:
    (motor_info->DRIVER_STEER_move_speed == DRIVER_STEER_move_FORWARD_at_SPEED)
        ? (motor_info->DRIVER_STEER_direction = DRIVER_STEER_direction_SOFT_LEFT)
        : (motor_info->DRIVER_STEER_direction = DRIVER_STEER_direction_SOFT_RIGHT);
    break;
  case LEFT_VERY_NEAR_OBSTACLE:
    (motor_info->DRIVER_STEER_move_speed == DRIVER_STEER_move_FORWARD_at_SPEED)
        ? (motor_info->DRIVER_STEER_direction = DRIVER_STEER_direction_HARD_RIGHT)
        : (motor_info->DRIVER_STEER_direction = DRIVER_STEER_direction_HARD_LEFT);
    break;
  case LEFT_NEAR_OBSTACLE:
    (motor_info->DRIVER_STEER_move_speed == DRIVER_STEER_move_FORWARD_at_SPEED)
        ? (motor_info->DRIVER_STEER_direction = DRIVER_STEER_direction_SOFT_RIGHT)
        : (motor_info->DRIVER_STEER_direction = DRIVER_STEER_direction_SOFT_LEFT);
    break;
  case RIGHT_LEFT_NO_OBSTACLE:
    (motor_info->DRIVER_STEER_move_speed == DRIVER_STEER_move_REVERSE_at_SPEED)
        ? (motor_info->DRIVER_STEER_direction = DRIVER_STEER_direction_HARD_LEFT)
        : (motor_info->DRIVER_STEER_direction = DRIVER_STEER_direction_STRAIGHT);
    break;
  case RIGHT_VERY_NEAR_LEFT_NEAR_OBSTACLE:
    (motor_info->DRIVER_STEER_move_speed == DRIVER_STEER_move_REVERSE_at_SPEED)
        ? (motor_info->DRIVER_STEER_direction = DRIVER_STEER_direction_SOFT_RIGHT)
        : (motor_info->DRIVER_STEER_direction = DRIVER_STEER_direction_STRAIGHT);
    break;
  case RIGHT_NEAR_LEFT_VERY_NEAR_OBSTACLE:
  case RIGHT_LEFT_NEAR_OBSTACLE:
    (motor_info->DRIVER_STEER_move_speed == DRIVER_STEER_move_REVERSE_at_SPEED)
        ? (motor_info->DRIVER_STEER_direction = DRIVER_STEER_direction_SOFT_LEFT)
        : (motor_info->DRIVER_STEER_direction = DRIVER_STEER_direction_STRAIGHT);
    break;
  case RIGHT_LEFT_VERY_NEAR_OBSTACLE:
  default:
    motor_info->DRIVER_STEER_direction = DRIVER_STEER_direction_STRAIGHT;
    break;
  }
}
  • The Navigation and Obstacle avoidance algorithms run @20Hz, CAN messages are processed @20Hz, and debug information is sent out @1Hz.

The detailed flowcharts are shown below.

Driving Algorithm

Technical Challenges

< List of problems and their detailed resolutions>



Mobile Application

<Picture and link to Gitlab>

Hardware Design

Software Design

<List the code modules that are being called periodically.>

Technical Challenges

< List of problems and their detailed resolutions>






Conclusion

This project taught us how to work with so many people with same goal but different working patterns to come together and execute their ideas together in tandem to accomplish the team goal. We developed an autonomously navigating RC car. Each team member brought unique skills, passions and personalities to the project. Some team people were skilled with designing and placing components on the PCB Board, while others were skilled in firmware development, project management, catching bugs, organizing meetings etc. Some people had the ability to lighten the mood on disappointing demo days, while others had the management skill to keep the team focused and making the car its best version than the day before, while avoiding burning the team out at each other. When these different minds and their skills came together, it resulted in the final product that we all are extremely proud of, as well as one that performed well on the final demo day. The shelter in place brought more challenges in coordination as we couldn't meet much for in person meetings and everyone needed the hardware for testing their code. It was difficult for a single person who had all the hardware to always test with few members or alone and to explain others where exactly their code was failing.

It wasn't always fun and lively though. We experienced soaring highs, during few of our progress demos, where we hit almost all of our specs. We also experienced devastating lows, when we were not able to demo properly as there were coordination issues offline at the start. While we look back fondly on our project, the reality is that it was the result of 7 graduate students spending almost every day of the semester developing and fine tuning the final product. We failed and succeeded together as a team. The goal was too important. Blame fell on the team as a whole and so did success and praise. We succeeded because we failed together. We succeeded because we didn't let failure break us and instead worked harder to fine tune and improve our test cases so that no case in the testing is left unexplored and the car worked exceptionally well.

There was so much to learn. Always something new, it felt like. We developed a clean and best working PCB, to integrate all of our subsystems together with least number of wires needed and visible on the car, even though none of us had experience developing a board that complex before. We also approached new challenges such as developing an Android Mobile Application, even though none of us knew JAVA or knew Android Studio beforehand. We never stopped learning. We learned the value of investing in quality components. We honed our unit testing skills, trying to write firmware that was as high quality as our hardware. We learned about CANbus on an intimate level and applied all of the knowledge. Testing at residential area gave us a tough time especially with the GPS module and the ultrasonic sensors as the google maps did not give us the exact co-ordinates of internal society roads. while the ultrasonic sensors faced difficulty in detecting low height sidewalks.

Working on this project was a difficult yet ultimately rewarding experience. It taught us how to make use of unit tests so that we don't need hardware at all and this helped alot in online coordination with team members. We can't stress enough how important is to start the project as early as possible in the semester and to keep pushing until the final demo. Each of contributed roughly 20 hours a week to work on the project. As a result, we were exhausted by the end of it, but we also never had to pull nights sometimes to meet the deadlines. We had excellent leadership from our team lead, Mohit Ingale, as well as our mentors, Preet and ISA members Aakash, Vignesh. We also had excellent peers in CMPE 243 and are proud of what they achieved as well. Overall, we were happy to be part of this class and we hope that our project will inspire future students to develop electric vehicles.


Project Video

Project Source Code

Advise for Future Students

  • FORM YOUR TEAM AS EARLY AS POSSIBLE AND START WORKING ON THE PROJECT AS EARLY AS POSSIBLE Dont't go by the team size. There is lot of work which keeps on stalking up and then can haunt us in the end of semester.
  • If you are having trouble with your GPS or compass, ultrasonic(due to more obstacles at residential areas) try testing on top of one of the SJSU parking garages on a weekend. There is much less signal interference.
  • Order spare parts and all parts before hand so that you don't keep running around at last moment. You will encounter defective parts and shipping wastes time and increases stress (especially nearing demo day)
  • Make sure to get a power supply doesn't bun you board's fuse and supplies current less than 1A. When all the car's subsystems are running, the current draw may be higher than expected
  • Don't go for cheap modules and car model from Ebay, those will probably waste more of your time and probably cost you more at the end as you might end up buying hardware again. Get quality modules and they will pay for themselves down the road.
  • Make use of all the holidays specially Spring break as there are assignments from other classes too and this would be the best time when probably everyone can be free. This will save you on the final DEMO day.
  • Getting access to a 3D printer can be very useful for this project, with which you can customize sensor mounts, car hood.
  • Take advantage of the code reviews that Preet offers! They will fix a lot of bugs in your code and will make it a lot easier to manage each module.