Difference between revisions of "S22: Road Runner"

From Embedded Systems Learning Academy
Jump to: navigation, search
(Redcat ESC and Servo from the RC Car)
(Redcat ESC and Servo from the RC Car)
Line 623: Line 623:
 
|}
 
|}
  
We used an oscilloscope in the IEEE room to monitor the signals coming from the Redcat receiver which supplied the appropriate PWM values for controlling the Motor ESC and the Servo. The following tables are the PWM min max values for the servo and Motor ESC observed from the oscilloscope respectively.
+
<Motor ESC image> Motor ESC
 +
<Servo Image> Servo
 +
 
 +
Since there were no datasheets provided by Redcat for both the Servo and Motor ESC, we used the connections on the receiver to determine which wire corresponded
 +
to Signal, VCC, and GND connections.
 +
{| class="wikitable" style="text-align: center; width: 400px; height: 150px;"
 +
! Component
 +
! Type
 +
! Pin #
 +
! Color
 +
|-
 +
| Servo || Signal || 1 || Orange
 +
|-
 +
| Servo || VCC || 2 || Brown
 +
|-
 +
| Servo || GND || 3 || Black
 +
|-
 +
| Motor ESC || Signal || 1 || White
 +
|-
 +
| Motor ESC || VCC || 2 || Red
 +
|-
 +
| Motor ESC || GND || 3 || Black
 +
|-
 +
|}
 +
 
 +
 
 +
After we determined the pinouts above, we went to the IEEE lab and used an oscilloscope connected in parallel with receiver to monitor the signals returned from the receiver when applying various inputs through the remote that control the Motor ESC and the Servo. The following tables are the PWM min max values for the servo and Motor ESC observed from the oscilloscope respectively.
 
{| class="wikitable" style="text-align: center; width: 400px; height: 150px;"
 
{| class="wikitable" style="text-align: center; width: 400px; height: 150px;"
 
! Servo Angle (deg)
 
! Servo Angle (deg)

Revision as of 00:51, 28 May 2022

RoadRunner Night.gif

Road Runner

WorkinProgress.jpg




Abstract

  !!! MAKE SURE YOU BUY AN RC CAR THAT HAS RELIABLE PWM ESC AND A BUILT-IN RPM SENSING MECHANISM !!!

Road Runner is a self navigating car built with the ability to drive to a specified geo-location within its battery range, while avoiding any physical obstacles that come on its way and successfully reaching its destination, with no human intervention apart from commanding the destination location . The car’s infrastructure has four important nodes (Driver, Sensor and Bridge, Geo and Motor) communicating within themselves over internal CAN Bus and with the end user over a mobile application. The car always strives to stay on and maintain its path to the destination by periodically sensing and processing all the relevant information from the nodes to arrive at an informed decision. It is built on top of a hobby grade RC car chassis with all the necessary adjustments and components to achieve its intended objective of self navigating and object avoidance, one of the basic requirements and functionality of an autonomous vehicle.

Introduction

The project was divided into 5 modules:

  • Bridge and Sensor Controller
  • Motor Controller
  • Geo Controller
  • Driver / LCD Controller
  • Android app interface

Team Members & Responsibilities

RoadRunner Team.jpg

Road Runner Firmware: GitLab Repo

Team Members:

RoadRunner Dhruv.jpg

  • Dhruv Choksi


RoadRunner Jonathan.jpeg

  • Jonathan Doctolero


RoadRunner Tudor.jpg

  • Tudor Donca


RoadRunner Hisaam.jpg

  • Hisaam Hashim


RoadRunner Charan.jpg

  • Saicharan Kadari [1]


RoadRunner Kyle.jpeg

  • Kyle Kwong


Project Subteams:

  • Bridge and Sensor Controller:
    • Tudor Donca
    • Saicharan Kadari
  • Motor Controller:
    • Dhruv Choksi
    • Hisaam Hashim
  • Geological Controller:
    • Kyle Kwong
    • Jonathan Doctolero
    • Saicharan Kadari
  • Driver and LCD Controller:
    • Hisaam Hashim
    • Kyle Kwong
  • Android Application:
    • Jonathan Doctolero
    • Tudor Donca
  • Integration and System Testing:
    • Tudor Donca
    • Dhruv Choksi
    • Hisaam Hashim
    • Saicharan Kadari
    • Kyle Kwong
    • Jonathan Doctolero




Schedule

Week# Start Date End Date Task Status
1 03/06/2022 03/12/2022
  • Read previous projects, gather information and discuss among the group members.
  • Discuss each team-member's preference and assign controller roles
  • Create parts list for the RC car, discuss, and decide on each item
  • Completed
2 03/13/2022 03/19/2022
  • Design interface for Bridge and Sensor Controller, with unit tests
  • Design interface for Motor Controller, with unit tests
  • Design interface for Driver and LCD Controller, with unit tests
  • Integrate Bridge/Sensor Controller to CAN bus with DBC, handling messages
  • Integrate Motor Controller to CAN bus with DBC, handling messages
  • Integrate Driver Controller to CAN bus with DBC, handling messages
  • Order all parts from list and save tracking/price info
  • Completed
3 03/20/2022 03/26/2022
  • Connect Sensor, Motor, and Driver controller together on CAN bus and verify messages
  • Connect all nodes together on the CAN bus, connect android app, verify messages across all nodes
  • Design interface for Geological Controller, with unit tests
  • Design basic android app connection and communication
  • Completed
4 03/27/2022 04/02/2022
  • Integrate Geological Controller to CAN bus with DBC, handling messages
  • Integrate Android app with Bridge/Sensor controller, sending/receiving messages both ways
  • Integrate Motor and Steering with PWM control, figure out the working ranges
  • Integrate Bluetooth module to Bridge/Sensor controller, with UART logic
  • Connect Android app to Bridge/Sensor controller, send test message
  • All parts received
  • Completed
5 04/03/2022 04/09/2022
  • Connect all nodes together on the CAN bus, connect android app, verify messages across all nodes
  • Mobile app can send coordinates to Bridge controller
  • Start RPM sensor logic implementation and add it to Motor controller
  • MILESTONE - All individual modules considered "Roughly Working" with hardware interfaced
  • Completed
6 04/10/2022 04/16/2022
  • Integrate GPS and Compass peripherals, writing the driver and unit tests
  • Have Driver node process GPS position/heading/bearing messages and incorporate in the driver logic
  • Complete RPM sensor and integrate with Motor controller for reliable PID control
  • Bridge sends to mobile app car status / telemetry data
  • Compile additional parts list and order them
  • MILESTONE - Basic car driving ability with basic obstacle avoidance
  • Completed
7 04/17/2022 04/23/2022
  • Create a on-board battery power supply for all components
  • Complete the Compass I2C integration and read values from the peripheral
  • Have PWM signals reliably controlling the motor speed, start working "backwards driving mode"
  • Interface LED ring on the Driver controller and display heading direction
  • MILESTONE - Integrated, reliably "heading" towards provided destination bearing, basic obstacle avoidance
  • First Demo (Apr. 26)
  • Completed
8 04/24/2022 04/30/2022
  • 3D print sensor housings, mounting for the whole platform
  • Design and solder the prototype board with all SJTwo boards
  • Add data to display on the LCD screen
  • Outdoor testing for longer range trips, and complete necessary enhancements
  • MILESTONE - Integration part 2, perform obstacle avoidance and destination bearing
  • Second Demo (May 3)
  • Completed
9 05/01/2022 05/07/2022
  • Verify that the electrical and mechanical work is complete
  • MILESTONE - Integration and outdoor testing, adding necessary software changes
  • Third Demo (May 10)
  • Completed
10 05/08/2022 05/15/2022
  • enable the Headlights to the car
  • MILESTONE - Integration testing, deal with uneven terrain, reliable waypoints navigation and obstacle avoidance
  • Fourth Demo (May 17)
  • Completed
11 05/05/2022 05/24/2022
  • Full System Testing, any needed Hardware and software fixes and optimizing
  • Completed
11 05/25/2022 05/25/2022
  • Final Project Demo Day
  • Completed


Parts List & Cost

Item# Part Description Vendor Qty Cost
1 RC Car Redcat [2] 1 $153.00
2 CAN Transceivers Various 6 $55
3 Ultrasonic Sensors Max Botix[3] 3 $134.00
4 Adafruit Bluetooth UART Tranceiver Adafruit[4] 1 $33.90
5 GPS Module and Antenna Adafruit[5][6] 1 $59.50
6 Compass Module Adafruit[7] 1 $29.00
7 Wheel Speed RPM Encoder 1
8 Discrete Electronic Components Generic[8] 1 $


Electrical Wiring and Powering the System

Below is the schematic of our RC car. We have tried to keep it simple and self-explanatory so that anyone referring this report can easily trace the connections and understand it.

RoadRunner schematic.png

Power Management

We have used two batteries, one for the RC car which is 7.4V Lipo battery and the other is 5V,2A power bank. The Lipo battery powered the RC card for us and the power bank powered all the SJ2 boards and the peripherals used.

Challenges

The three main challenges we faced with electrical wiring are:

1. Cable Management: Due to large number of wire connections, we faced difficulty in routing and managing all the wires.

2. Component’s placement and orientation: It is very important where you place your components to minimize the wire length and make it more manageable. Except the ultrasonic sensors, almost every other component can be placed anywhere so that you can route the wires easily. We couldn’t figure out the positions at early stage and so we struggled.

3. Power Management: It is one of the most important aspect of electrical wiring. It is most important to supply components with the stable rated power. We took quite some time in finalizing our power management.

Advice

o Read the previous year reports and try to fix the components position before hand and try to make the plug and play kind of prototype board so that you don’t waste your time connecting the components again and again. Doing so will help you focus more on your improvising your software later and make it more reliable and accurate.

o Make a separate power supply for your peripherals and the SJ2 board (preferable avoid using the RC car’s battery).

o Make the pcb/prototype board plug and play so that it’s easy to replace the components if needed.

o We highly recommend you just power all the boards and peripherals at 5V from a simple phone charger bank. Just make sure all your components can handle 5V. This way you can avoid the complications of using a voltage regulator. We had a lot of trouble with voltage regulators breaking and subtly malfunctioning throughout the semester. Keep it simple!



CAN Communication

We used CAN Communication setup between all the nodes of the RoadRunner setup. This CAN Bus is terminated with the use of two 120 ohm resistors at both the ends of the Bus. Message IDs were chosen based upon an agreed priority scheme and Bus arbitration scheme. We assigned high priority IDs to messages that controlled the motor, thereby controlling the actual physical movement of the car, followed by sensor and GEO messages, and lowest priority messages were for debug and status messages from the respective nodes . More technical details to fill in..

Core CAN-draw.png

Hardware Design

RoadRunner CANHW Design compressed.jpg

DBC File

Gitlab link to the RoadRunner DBC file

VERSION ""
 
NS_ :
     BA_
     BA_DEF_
     BA_DEF_DEF_
     BA_DEF_DEF_REL_
     BA_DEF_REL_
     BA_DEF_SGTYPE_
     BA_REL_
     BA_SGTYPE_
     BO_TX_BU_
     BU_BO_REL_
     BU_EV_REL_
     BU_SG_REL_
     CAT_
     CAT_DEF_
     CM_
     ENVVAR_DATA_
     EV_DATA_
     FILTER
     NS_DESC_
     SGTYPE_
     SGTYPE_VAL_
     SG_MUL_VAL_
     SIGTYPE_VALTYPE_
     SIG_GROUP_
     SIG_TYPE_REF_
     SIG_VALTYPE_
     VAL_
     VAL_TABLE_
 
BS_:
 
BU_: DBG DRIVER MOTOR SENSOR_BRIDGE GEOLOGICAL
 
BO_ 100 MOTOR_CMD: 4 DRIVER
 SG_ MOTOR_steer_direction : 0|9@1- (1,-2) [-2|2] "steer direction" MOTOR
 SG_ MOTOR_speed_kph : 9|20@1- (0.1,-10.1) [-10.1|10.1] "kph" MOTOR
 SG_ MOTOR_drive_mode : 29|3@1+ (1,0) [0|0] "mode" MOTOR
 
BO_ 101 MOTOR_STATUS: 4 MOTOR
 SG_ actual_steer_degrees : 0|9@1- (1,-2) [-2|2] "steer direction" DRIVER,DBG
 SG_ actual_speed_kph : 9|20@1- (0.1,-10.1) [-10.1|10.1] "kph" DRIVER,DBG
 SG_ actual_drive_mode : 29|3@1+ (1,0) [0|0] "mode" DRIVER,DBG
 
BO_ 102 MOTOR_TEST_CONTROL_OVERRIDE: 8 DBG
 SG_ override_steer_pwm : 0|24@1+ (0.01,0) [5.01|15.01] "duty" MOTOR
 SG_ override_motor_pwm : 24|24@1+ (0.01,0) [5.01|15.01] "duty" MOTOR
 SG_ override_enable : 48|1@1+ (1,0) [0|1] "flag" MOTOR
 
BO_ 200 SENSOR_SONARS: 8 SENSOR_BRIDGE
 SG_ SENSOR_SONARS_left : 0|10@1+ (1,0) [0|500] "cm" DRIVER
 SG_ SENSOR_SONARS_right : 10|10@1+ (1,0) [0|500] "cm" DRIVER
 SG_ SENSOR_SONARS_middle : 20|10@1+ (1,0) [0|500] "cm" DRIVER
 SG_ SENSOR_SONARS_back : 30|10@1+ (1,0) [0|500] "cm" DRIVER
 
BO_ 201 GPS_DESTINATION: 8 SENSOR_BRIDGE
 SG_ GPS_DESTINATION_latitude : 0|28@1- (0.000001,-90.000000) [-90|90] "Degrees" GEOLOGICAL
 SG_ GPS_DESTINATION_longitude : 28|28@1- (0.000001,-180.000000) [-180|180] "Degrees" GEOLOGICAL
 
BO_ 202 WAYPOINT: 8 SENSOR_BRIDGE
 SG_ WAYPOINT_latitude : 0|28@1- (0.000001,-90.000000) [-90|90] "Degrees" GEOLOGICAL
 SG_ WAYPOINT_longitude : 28|28@1- (0.000001,-180.000000) [-180|180] "Degrees" GEOLOGICAL
 
BO_ 203 BRIDGE_CAR_CONTROL: 8 SENSOR_BRIDGE
 SG_ DRIVER_start_stop : 0|1@1+ (1,0) [0|1] "" DRIVER
 
BO_ 350 GEO_STATUS: 8 GEOLOGICAL
 SG_ GEO_STATUS_COMPASS_HEADING : 0|12@1+ (1,0) [0|359] "Degrees" DRIVER,SENSOR_BRIDGE
 SG_ GEO_STATUS_COMPASS_BEARING : 12|12@1+ (1,0) [0|359] "Degrees" DRIVER,SENSOR_BRIDGE
 SG_ GEO_STATUS_DISTANCE_TO_DESTINATION : 24|24@1+ (1,0) [0|0] "Meters" DRIVER,SENSOR_BRIDGE
 
BO_ 360 GEO_CURRENT_POSITION: 8 GEOLOGICAL
 SG_ GEO_CURRENT_POSITION_latitude : 0|28@1- (0.000001,-90.000000) [-90|90] "Degrees" DRIVER,SENSOR_BRIDGE
 SG_ GEO_CURRENT_POSITION_longitude : 28|28@1- (0.000001,-180.000000) [-180|180] "Degrees" DRIVER,SENSOR_BRIDGE
 
BO_ 500 DEBUG_SENSOR_BRIDGE: 8 SENSOR_BRIDGE
 SG_ sensor_left_raw_value : 0|12@1+ (1,0) [0|0] "" DBG
 SG_ sensor_right_raw_value : 12|12@1+ (1,0) [0|0] "" DBG
 SG_ sensor_middle_raw_value : 24|12@1+ (1,0) [0|0] "" DBG
 SG_ sensor_back_raw_value : 36|12@1+ (1,0) [0|0] "" DBG
 
BO_ 505 DEBUG_MOTOR_CONTROL: 8 MOTOR
 SG_ target_steer_direction : 0|8@1- (1,-2) [-2|2] "steer direction" DBG
 SG_ target_speed_kph : 8|12@1- (0.1,-10.1) [-10.1|10.1] "kph" DBG
 SG_ current_raw_motor_pwm : 20|20@1+ (0.01,0) [5.01|15.01] "duty" DBG
 SG_ current_raw_servo_pwm : 40|20@1+ (0.01,0) [5.01|15.01] "duty" DBG
 SG_ is_override_enabled : 60|1@1+ (1,0) [0|1] "flag" DBG
 
BO_ 506 DEBUG_MOTOR_RPM: 8 MOTOR
 SG_ computed_rpm : 0|12@1+ (1,0) [0|4000] "rpm" DBG
 SG_ computed_kph : 12|12@1- (0.1,-10.1) [-10.1|10.1] "kph" DBG
 SG_ computed_pid_increment : 24|20@1- (0.01,-5.01) [-5.01|5.01] "duty" DBG
 SG_ total_encoder_tick_count : 44|20@1+ (1,0) [0|0] "ticks" DBG
 
BO_ 510 DEBUG_DRIVER: 8 DRIVER
 SG_ driver_intention : 0|8@1+ (1,0) [0|0] "" DBG
 SG_ driver_steer_direction : 8|8@1+ (1,0) [0|0] "" DBG
 SG_ driver_drive_direction : 16|8@1+ (1,0) [0|0] "" DBG
 
CM_ BU_ DRIVER "The driver controller driving the car";
CM_ BU_ MOTOR "The motor controller of the car";
CM_ BU_ SENSOR_BRIDGE "The sensor and bluetooth bridge controller of the car";
CM_ BU_ GEOLOGICAL "The GPS and compass controller of the car";
CM_ BO_ 100 "Sync message used to synchronize the controllers";
 
BA_DEF_ "BusType" STRING ;
BA_DEF_ BO_ "GenMsgCycleTime" INT 0 0;
BA_DEF_ SG_ "FieldType" STRING ;
 
BA_DEF_DEF_ "BusType" "CAN";
BA_DEF_DEF_ "FieldType" "";
BA_DEF_DEF_ "GenMsgCycleTime" 0;
 
BA_ "GenMsgCycleTime" BO_ 100 1000;
BA_ "GenMsgCycleTime" BO_ 200 50;
 
BA_ "FieldType" SG_ 100 DRIVER_HEARTBEAT_cmd "DRIVER_HEARTBEAT_cmd";
VAL_ 100 DRIVER_HEARTBEAT_cmd 2 "DRIVER_HEARTBEAT_cmd_REBOOT" 1 "DRIVER_HEARTBEAT_cmd_SYNC" 0 "DRIVER_HEARTBEAT_cmd_NOOP" ;
 
BA_ "FieldType" SG_ 510 driver_intention "driver_intention";
VAL_ 510 driver_intention 1 "DESTINATION_HEADING" 0 "OBSTACLE_AVOIDANCE";
 
BA_ "FieldType" SG_ 510 driver_steer_direction "driver_steer_direction";
VAL_ 510 driver_steer_direction 2 "TURN_RIGHT" 1 "TURN_LEFT" 0 "STRAIGHT";
 
BA_ "FieldType" SG_ 510 driver_drive_direction "driver_drive_direction";
VAL_ 510 driver_drive_direction 2 "BACKWARDS" 1 "FORWARDS" 0 "STOPPED";
 
BA_ "FieldType" SG_ 100 MOTOR_drive_mode "MOTOR_drive_mode";
VAL_ 100 MOTOR_drive_mode 2 "DRIVE_BACKWARDS" 1 "DRIVE_FORWARDS" 0 "NEUTRAL";
 
BA_ "FieldType" SG_ 100 MOTOR_steer_direction "MOTOR_steer_direction";
VAL_ 100 MOTOR_steer_direction -2 "STEER_MAX_LEFT" -1 "STEER_SLIGHT_LEFT" 0 "STEER_STRAIGHT" 1 "STEER_SLIGHT_RIGHT" 2 "STEER_MAX_RIGHT";




Sensor and Bridge Controller

Sensor Controller Master Branch

Hardware Design

Roadrunner uses three HRLV-MAXSonar-EZ0 MB1003[9] as its eyes and ears on the front. The HRLV-MAXSonar-EZ0 comes in five different models for the ultrasonic sensor. The HRLV-MAXSonar-EZ sensor line is the most cost-effective solution for applications where precision range-finding, low-voltage operation, space-saving, and low-cost are needed. This sensor line features 1mm resolution, target-size and operating-voltage compensation for improved accuracy, superior rejection of outside noise sources, internal speed-of-sound temperature compensation, and optional external speed-of-sound temperature compensation.

These models are the MB1003, MB1013, MB1023, MB1033, and MB1043. The models are based on the beam width of the sensor. It was observed that MB1003 has the optimal beam pattern and was better suited for our objective of object detection. If the beam pattern is too wide given the, there is a greater chance of cross talk between sensors. If the beam pattern is too narrow, there may be blind spots which must be accounted for. Thus, three HRLV-MAXSonar-EZ0 MB1003 sensors were used and the possible unavoidable cross talk is contained by the optimal positioning and software configuration.

Front view of the HRLV-MAXSonar-EZ0 MB1003 000
Rear view of the HRLV-MAXSonar-EZ0 MB1003 000 with pin labels
Beam pattern of the HRLV_MAXSonar-EZ0 MB1003
Sensor Mounts and Placement

It is decided to place the ultrasonic sensors on 3d- printed mounts and a custom setup to reduce the cross talk and also position them to capture the most field of view and possibly avoid any blind spots for the car movement.

Sensor Readings Extraction

To extract range readings from the MB1003, the "AN" pin which represents analog output was utilized. A range reading is commanded by triggering the "RX" pin high for 20us or more. From the details of the sensor datasheet it is observed that the output analog voltage is based on a scaling factor of (Vcc/512) per inch. As in our setup, all three of the sensors were powered from a 5v supply line, thus a 5v VCC was supplied to each sensor. According to the HRLV-MaxSonar-EZ datasheet, with 5v source, the analog output was expected to be roughly 4.88mV per 5mm. However, when initially testing the output range readings, the output was inaccurate. We were thankful for the past students experiences to notice that the ADC channels for the SJTWO are configured at start-up to have an internal pull up resistor. Disabling this pull up resistor will be discussed more in Software Design and this made the range readings more accurate. The readings were tested by having multiple objects placed in front of each sensor and measuring the distance of the object to the sensors physically. Although the range readings accuracy is improved by the pull-up resistor modification, it was found that the range readings did not necessarily follow the expected 4.88 mV per 5 mm. Hence, we decided to calibrate the sensors to work according to the conversion factor. This value was extracted by placing objects around the sensors and checking if the analog output to centimeter conversion matched to the actual measured distance.

SJ2 Board Pin Connections with Ultrasonic sensors

Sensors are interfaced with combination of GPIO, ADC Pins on SJTwo board. Below is the descriptive pin layout:

Sensors pin layout
Sr. No. SJTwo board Pin Maxbotix sensor Pin Function
1 ADC2 - P0.25 AN (Middle sensor) ADC Input (sensor output) from front sensor
2 ADC4 - P1.30 AN (Left Sensor) ADC input from left sensor
3 ADC5 - P1.31 AN (Right Sensor) ADC input from right sensor
4 GPIO - P0.6 RX (Middle Sensor) Trigger Input for left sensor
5 GPIO - P0.7 RX (Left Sensor) Trigger Input for front sensor
6 GPIO - P0.8 RX (Right Sensor) Trigger Input for right sensor


Software Design

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

The software is designed as briefed below:

  • Initialize all the sensors once the car is powered on.
  • Once Initialized, using the callback counter from the 10Hz function, trigger the sensors as two sets, the middle one alone forming set one and the two side sensors, the left and right sensors being set two. (It is also observed and implemented that once we trigger the Maxbotix sensor and we should wait for approximately 90 ms before reading from it to have a good capture of the distance/obstacle).
Timing Diagram of MaxBotix Ultrasonic Sensor in Real Time Triggered Operation
  • The raw ADC values are captured from that group of sensors.
  • The sensors are disabled as groups and the other group is triggered and follows the same set of operations and this pattern cycles.
  • The raw ADC values are captured and converted into physical distance values in cm using a conversion factor based on the supplying voltage and number of ADC bits.
  • The converted values are added to a buffer to calculate the mean from the latest 3 samples (The choice of 3 samples is a compromise we arrived at due to sensor sensitivity, timing and accuracy)and consider it as the current captured value from the sensor.
  • This filtered value is then encoded and transmitted on to the CAN bus, where the driver node considers the distance value to operate in different scenarios.


ultrasonic sensors operational diagram

Technical Challenges

  • The interference between the ultrasonic sensors is an unavoidable challenge for us as we used the same model of the ultrasonic sensor, hence the same frequency of operation for all the three ultrasonic sensors.
- The solution was to group the sensors and trigger them alternatively with the aim to reduce the beam pattern of the left and right sensors from interfering with each other and especially with the middle sensor.
- Part of the solution was also to physically mount them at an angle to avoid the interference to an extent and also not to create any blind spots in the zone of operations.
  • Initially the 3.3v power supply to the Ultrasonic sensor created inconsistent readings from time to time, and later we decided to switch it to 5v operating voltage which made the sensor readings more stable and reliable.
  • The compromise of just using 3 samples in the mean filtering is a limitation for our sensor operations, this is due to the fact that with a higher number of samples, we might miss any samples and output time delayed samples on the CAN BUS which will have unpredictable and unwanted consequences.
  • These sensors had a limited 10Hz reading update, which is too slow to use as a reliable obstacle detector while the car is moving. We highly recommend you find a different sensor that has AT LEAST a 20Hz update rate.
  • The manufacturer recommended reading the sensor data using ADC. This is not reliable, especially if the sensors are connected to the same power supply as the rest of the car. We highly recommend you use some sort of serial communication protocol for the sensors.
  • Please use the sensors that the Testla team used from this same semester. If we had another chance, we would use those instead.
  • Also check out Testla's 3D-printed adjustable sensor mounts. You should do the same thing!


Motor Controller

Motor Controller Master Branch

Hardware Design

Redcat ESC and Servo from the RC Car

The Motor Controller Node Motor and Servo control were heavily dependent on the hardware and communication protocols that came with our RC car. Originally, when we got the RC car, for some reason we expected some support from the vendor on offering some form of datasheet that gave us an idea on the PWM signal range controlled by the ESC and Servo. However, unsurprisingly, that wasn't the case.

The following parts were installed on the Redcat BLACKOUT-SC-RED RC Car:

Type Vendor Model Number Communication Protocol
Motor ESC Redcat WP-1040-BRUSHED PWM
Servo Redcat HX-3CP PWM

<Motor ESC image> Motor ESC <Servo Image> Servo

Since there were no datasheets provided by Redcat for both the Servo and Motor ESC, we used the connections on the receiver to determine which wire corresponded to Signal, VCC, and GND connections.

Component Type Pin # Color
Servo Signal 1 Orange
Servo VCC 2 Brown
Servo GND 3 Black
Motor ESC Signal 1 White
Motor ESC VCC 2 Red
Motor ESC GND 3 Black


After we determined the pinouts above, we went to the IEEE lab and used an oscilloscope connected in parallel with receiver to monitor the signals returned from the receiver when applying various inputs through the remote that control the Motor ESC and the Servo. The following tables are the PWM min max values for the servo and Motor ESC observed from the oscilloscope respectively.

Servo Angle (deg) PWM Duty Cycle % PWM Frequency (Hz)
-45.0 (left) 11.67 60.24
0.0 8.65 60.24
+45.0 (right) 6.024 60.24
Motor Speed (kph) PWM Duty Cycle % PWM Frequency (Hz)
-40.2336 (reverse) 6.205 60.24
0.0 (stopped) 9.21 60.24
+40.2336 (forward) 12.048 60.24

Software Design

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

Technical Challenges

  • The RPM sensor that we used does not work work in direct sunlight. It is an IR sensor, and it becomes useless if exposed to the Sun's IR rays. Please don't use an external IR sensor for your RPM sensing. Buy the Traxxas car that comes with a special RPM sensor that gets installed in the motor housing. This is much more reliable, and it does not get exposed to outdoor lighting.
  • The initial motor code tried to use hard-coded PWM values for controlling the motor. This is not reliable, especially on the cheaper RedCat car, whose ESC is quite terrible. Please buy the Traxxas car to have a more stable ESC (but watch out for the more complicated startup sequence).
  • Use a simple Proportional control (PID) algorithm to control your motor, using the RPM sensor readings as the feedback signal. You might even be able to get away with just P-only control. Watch this video to learn everything you need about implementing this: https://www.youtube.com/watch?v=JEpWlTl95Tw&t=2s




Geographical Controller

Geological Controller Master Branch

Hardware Design

SJTwo board Pin Device Bus-Type Pin-Function
P4.28 GPS UART TX
P4.29 GPS UART RX
P0.10 Compass I2C SDA
P0.11 Compass I2C SCL
P0.0 CAN Tran CAN RD
P0.1 CAN Tran CAN TD

Software Design

GPS

Since we have configured the GPS module to return at 2Hz and only returns GPGGA strings, and we have a coin cell battery for it to hold all of its configuration, we decided that there is no need to re-configure it every time when it starts up. However, the configuration functions gpgga_only and twoHz_only are in the repo for reference.

The main GPS periodic function is gps__run_once, which calls the two sub GPS functions gps__transfer_data_from_uart_driver_to_line_buffer and gps__parse_coordinates_from_line. gps__transfer_data_from_uart_driver_to_line_buffer reads from the UART buffer and adds it to our line buffer data structure. gps__parse_coordinates_from_line removes a line from the line buffer and parses it into latitude and longitude.

The second, third, and fourth periodic function are gps_processor__calculate_distance_to_destination, gps_processor__calculate_distance_to_waypoint, and gps_processor__calculate_bearing_to_destination.

gps_processor__calculate_distance_to_destination calculates the distance between the current position and the destination with the Haversine formula. This function is used for checking if we are close to the destination.

gps_processor__calculate_distance_to_waypoint calculates the distance between the current position and a waypoint position with the same formula. This function is used for calculating the closest waypoint.

After that, we will use gps_processor__calculate_bearing_to_destination to calculate the bearing angle (The angle relative to our car’s position).

The reason why there are two functions for calculating distance is because this allows us to have destination and waypoints to be different types of input.

Distance
 const float earth_radius = 6371000; // 6371km
                                     // Convert current coordinates into radians
 float current_latitude = geo_position.GEO_CURRENT_POSITION_latitude;
 float current_longitutde = geo_position.GEO_CURRENT_POSITION_longitude;
 // Convert destination coords into radians
 float destination_latitude = gps_destination.GPS_DESTINATION_latitude;
 float destination_longitude = gps_destination.GPS_DESTINATION_longitude;
 float theta1 = destination_latitude * PI / 180; // in radian (current_loc or dest_loc as theta1 doesn't matter)
 float theta2 = current_latitude * PI / 180;     // in radian
 float delta_theta = (destination_latitude - current_latitude) * PI / 180;
 float delta_lambda = (destination_longitude - current_longitutde) * PI / 180; // in radian
 float a = powf(sinf(delta_theta / 2), 2) + cosf(theta1) * cosf(theta2) * powf(sinf(delta_lambda / 2), 2);
 float c = 2 * atan2f(sqrtf(a), sqrtf(1 - a));
 float distance = earth_radius * c; // in meters
 return distance;
Bearing
 // Convert current coordinates into radians
 float current_latitude = geo_position.GEO_CURRENT_POSITION_latitude * PI / 180;
 float current_longitude = geo_position.GEO_CURRENT_POSITION_longitude * PI / 180;
 // Convert destination coords into radians
 float destination_latitude = gps_destination.GPS_DESTINATION_latitude * PI / 180;
 float destination_longitude = gps_destination.GPS_DESTINATION_longitude * PI / 180;
 float y = sin(destination_longitude - current_longitude) * cos(destination_latitude);
 float x = cos(current_latitude) * sin(destination_latitude) -
           sin(current_latitude) * cos(destination_latitude) * cos(destination_longitude - current_longitude);
 float theta = atan2(y, x);
 // Convert theta to degrees
 float bearing = fmodf(((theta * 180.0f / PI) + 360.0f), 360);
 return round(bearing);

Compass

The compass we use has an embedded processor that filters and tile compensates the raw data from the magnetometer, so we only need to read the correct slave data registers and shift them to create a 16-bit unsigned integer, then dividing it by 10 since the range is from 0.0 to 359.9. Since the filtering and calibrations are already done by the hardware module, the code doesn’t need further filtering or calibration to work properly.

Waypoint

Instead of having a list of hardcoded waypoint positions, we decided to have our smart phone app send up to ten waypoints. From the list of waypoints, geo_processor calls the waypoint__get_coordinates function to get the closest waypoint coordinates. Within that, the find_closest_waypoint function is called and returns an index between 0-9, and -1 if going straight to the destination is the closest path. If we arrive at the waypoint, it is removed from the list, and we will fetch the next closest waypoint. waypoint__get_coordinates is called continuously so that if the car steers away from the intended path to avoid obstacles, it can find a closer waypoint, instead of going to the original waypoint.

Technical Challenges

  • We first bought a cheaper compass sensor since the good one was out of stock (semiconductor shortage T.T). This led to a lot of trouble trying to do the calibration and motion compensation (the compass will be moving and accelerating with the car as it drives). You NEED to find a self-calibrated/compensated compass like the CMPS-12 or CMPS-14. Order it as soon as you read this. You really need to have as many electrical components as possible that "just work" because otherwise you won't have any time to write quality software or unit tests.
  • At the beginning, we were having issues configuring the GPS to only send GPGGA strings. We knew that the GPS module and the configuration message worked because we tested it in telemetry.
    • We moved the sending of the configuration messages from initialization to a periodic callback that runs only once at the first 20 seconds. This solved our issue. We presume that it is because initialization takes place during RTOS startup, and the UART may not have fully initialized at that point.
  • The GPS peripheral takes time to obtain a lock, and it can get confusing why it is not working all the time. You have to use a GPS that has a coin-cell battery component to maintain the lock data between power cycles. Having a coin cell battery also helped us to not need to re-configure the GPS every time when it loses power.
  • The waypoints algorithm is hard to test if you are still stuck on getting the rest of the car working. We highly recommend you test the GEO node (compass and GPS integration) separately from the car. You can find a table with wheels in the IEEE room or most lecture rooms. Put your peripherals and boards on it, hook up BusMaster, and "drive" the table around as your testbench.






Driver and LCD Module

Driver Master Branch

Hardware Design

Your most important hardware is a moveable table you can put your car on and "drive" around while you verify the motor commands and algorithm decisions on BusMaster! The LCD screen can also be helpful.

RoadRunner table.jpeg

Software Design

High Level Driver Logic:

 if ((gpio__get(EXTERNAL_EMERGENCY_BUTTON))) {
   EMERGENCY_STOP = true;
 } 
 
 
 if (EMERGENCY_STOP) {
   stop();
 } else if (destination_reached()) {
   destination_reached_processor();
 } else if (has_obstacle()) {
   obstacle_avoidance_processor();
   gpio__reset(board_io__get_led3());
 } else {
   heading_processor();
   gpio__toggle(board_io__get_led3());
 }

And then the Obstacle Avoidance Logic:

 if (is_blocked()) {
   do_reverse_sequence();
 } else if (closest_object_on_left()) {
   turn_right();
 } else if (closest_object_on_right()) {
   turn_left();
 } else {
   stay_straight();
 }

Don't do this! Use a state machine instead. This is what happens when you don't buy the Traxxas car, and you end up wasting weeks with the hardware and RPM setup. Please learn from our mistakes.


Technical Challenges

  • The driver controllers is highly dependent on the GEO and SENSOR controllers to obtain the data needed for the control logic. Any delays in those controllers will render your obstacle avoidance or destination logic useless. Minimize the delay in the Sensor node by using sensors with at least 20Hz update rate, and with a filtering algorithm that does not delay outputting the most recent sensor values. Minimize the general communication delay by making sure to send CAN messages on the bus at least 2x the rate of your data generation. Make sure you also process CAN messages on the receiving nodes faster than the sender nodes send them (nodes send CAN messages at 40Hz, and read them at 50Hz).
  • The data coming from the GEO and SENSOR nodes are not perfect, so any noisy data can mess up your driver logic. You have to account for this by not creating a blind, memoryless system which makes decisions only on the current input. You have to build a simple state machine that accounts for past sensor values and compass values. This will allow you to make "smart" obstacle avoidance decisions. We did not have this, and paired with our unreliable sensors, we ended up with very poor obstacle avoidance ability.
  • The Adafruit Ring LED does not work with SJTwo. Do not waste time with this. Find a different ring light setup, or build your with a few discrete LEDs.




Physical Mounting

Hardware Design

RoadRunner physical mounting.jpeg

Technical Challenges

  • It is hard to plan ahead of time where to place everything, so prototype your layout on cardboard first
  • A physical mounting makes it very hard to modify wiring, so get your wiring and power system correct on your cardboard prototype first
  • Plexiglass can be hard to cut into small pieces, so buy small pre-cut pieces from TAP, or buy a proper cutting toolset




Mobile Application

Screenshots

Starting screen List of Devices to Connect/RC Car BLE is highlighted Connected screen Telemtry Table fills data after tapping Read Button Long press on Google Map for placing Destination Marker Tap Screen to add waypoint markers Telemetry table reading from Sensor_Bridge Node Command Buttons collapsed to show only telemetry table

1. The app opens up to show the Google Map with the green marker defaulting to the Engineering Building at SJSU. Once the phone can read the current location of the car, the green marker will update to track the car's location.
2. The RC Car's Bluetooth Device is named "Adafruit Bluefruit LE" and will be highlighted amongst the list of Bluetooth devices
3. Once connected to the RC CAR BLE, the command buttons and telemetry table will appear
4. Pressing the Read button will start reading data from the car and fill the telemetry table with data from the car
5. Furthermore, a long press on the map will add a destination marker, which the coordinates can be sent to the car by pressing Write
6. Then, waypoints can be added by tapping on the map. The waypoints will be sent once the button "Send Waypoints" is pressed. Also, to clear the waypoints, you need to tap on the info window that appears on the destination marker
7. Telemetry window shows data read from the SJ2 board, the data is parsed from CAN messages like current location, destination, sensor values, RPM, compass heading and bearing, and distance to destination
8. The buttons can be hidden away to focus on the telemetry table and the map.


Mobile App Gitlab

Bridge-Sensor Node Firmware Gitlab


Hardware Design

Using the Adafruit BLE Module, we're able to send data from the SJ2 board to the module using UART. Then BLE module then sends the data in packets to the connected mobile phone. By using UART, we can simply print data to the UART to communicate between the Bridge Node and the mobile phone.

Software Design

Code Modules called periodically on the SJ2 board:

- board_processor for reading, parsing and sending data between the SJ2 board and the mobile phone
- can_handler for reading CAN messages on the CAN bus to send telemetry data to the phone and sending CAN messages when reading from the phone

Technical Challenges

- Issue sending more than 20 bytes of data from phone to SJ2 board
 - Resolved by breaking up packets of data into 20 or fewer bytes
- Issue updating telemetry data fast enough
 - Resolved by calling bridge_processor__send_diagnostics_update_periodic() in the 10Hz periodic callback
- Issue handling different data coming into the phone
 - Resolved by hardcoding the fields to fill in the table





Conclusion

At the end of the day, this is by far the most useful course you will take if you are trying to find work in the embedded systems / firmware industry (one of our teammates got their full-time job this semester pretty much due to this course). However, it takes a lot more effort than you think. You will learn what systems engineering is, why it is hard, and what happens when you don't plan ahead of time. You will get an appreciation for just how hard it is to build anything real that actually works. Trying to accomplish all of this in one semester is very hard, so you have to be smart and efficient to get the most value from this project. Invest in high quality components up front, and buy "off-the-shelf" solutions, so that you have more time to learn about the software and unit tests. This is the most important part to learn from this class. Good luck!

RoadRunner final car.jpg

Project Video

Project Video YouTube Link

Project Source Code

Car Firmware: https://gitlab.com/road-runner-ce243/road-runner-firmware


Mobile App Software: https://gitlab.com/road-runner-ce243/road_runner_app

Advise for Future Students

  • Buy the Traxxas car and don't cheap out... trust us T.T
  • Test out a few different ultrasonic sensors... the MaxSonic sensors are very unreliable even though they are more expensive
  • Test outdoors on the garage early to make sure all your sensors and components can handle the sun and other outdoor features
  • Meet weekly, and stay ontop of communication over Slack and WhatsApp
  • Practice doing code reviews early in the semester, and actually do them as you start writing code for the different controllers

Acknowledgement

Thank you to Preet for creating this awesome course, and thank you to all the previous students who wrote their project reports which we used as a helpful reference!