Difference between revisions of "S22: Silver Arrow"
(→Master Module) |
(→Geo Controller Module) |
||
Line 823: | Line 823: | ||
=== Technical Challenges === | === Technical Challenges === | ||
− | |||
− | |||
− | |||
− | |||
− | |||
== Mobile Application == | == Mobile Application == |
Revision as of 22:54, 27 May 2022
Contents
SILVER ARROW
Abstract
Silver Arrow RC is a self-navigating, electric, battery-powered RC car. The goal is to use GPS navigation to get to the location specified in the Android app. To sense its surroundings and avoid obstructions in its route, the car combines input from several sensors and make decisions to navigate itself to the destination location
Introduction
The project was divided into 5 modules:
- Bridge and Sensor Controller
- Motor Controller
- Geographical Controller
- Driver and LCD controller
- Android Application
Team Members & Responsibilities
- Driver Node
- Compass & GPS Calibaration
- Waypoint algorithm
- Mobile Application
- Car Mounting and hardware assembly
- Wiki page manage
- Sensor controller
- Communication Bridge Controller
- Geo controller
- LCD Integration
- Geo controller
- Line Buffer, Geo Logic and Waypoint algorithm
- Git repo manager
- Motor controller
- Hardware Integration and design
- Code Reviewer
- Unit Testing
- Hardware/circuit/PCB designing
Schedule
Week# | Start Date | End Date | Task | Status |
---|---|---|---|---|
1 | 03/16/2022 | 03/22/2022 |
|
|
2 |
03/23/2022
|
03/29/2022
|
|
|
3 |
03/30/2022
|
03/04/2022
|
|
|
4 |
04/05/2022
|
04/11/2022
|
|
|
5 |
04/12/2022
|
04/18/2022
|
|
|
6 |
04/19/2022
|
04/25/2022
|
|
|
7 |
04/26/2022
|
05/2/2022
|
|
|
8 |
05/3/2022
|
05/09/2022
|
|
|
9 |
05/10/2022
|
05/16/2022
|
|
|
10 |
05/17/2022
|
05/23/2022
|
|
|
11 |
05/25/2022
|
05/25/2022
|
|
|
Parts List & Cost
Item# | Part Desciption | Vendor | Qty | Cost |
---|---|---|---|---|
1 | The Traxxas Rustler XL-5® Stadium Truck | Traxxas [1] | 1 | $250.00 |
2 | Waveshare SN65HVD230 CAN Transceivers | Amazon [2] | 6 | $59.34 |
3 | Adafruit Ultimate GPS Breakout | Adafruit [3] | 1 | $29.95 |
4 | LCD 20*4 | RPIGEAR [4] | 1 | $30 |
5 | Adafruit GPS Antenna | Adafruit [5] | 1 | $19.95 |
6 | DC 5v voltage regulator XL6009 | Amazon [6] | 1 | $11.99 |
7 | Traxxas 6520 RPM Sensor | Traxxas [7] | 1 | $13.76 |
8 | Traxxas Trigger Magnet | Traxxas [8] | 1 | $3 |
9 | Maxbotix LV-MaxSonar-EZ1 Ultrasonic Range Finder | Amazon [9] | 4 | $120 ( Provided by Dr. Ozemek) |
10 | Traxxas LiPo Batteries(2) with Dual iD charger(1) | Traxxas [10] | 1 | $269.99 ( Provided by Dr. Ozemek) |
11 | DSD TECH HC-05 Bluetooth Serial Pass-through Module | Amazon[11] | 1 | $9.99 |
12 | HiLetgo CP2102 USB 2.0 to TTL Module Serial Converter | Amazon [12] | 1 | $6.29 |
13 | Connecting wires, LEDs, switches | Anchor electronics [13] | 1 | $97 |
Printed Circuit Board
<Picture and information, including links to your PCB>
CAN Communication
<Talk about your message IDs or communication strategy, such as periodic transmission, MIA management etc.>
CAN protocol was used to establish the communication between the four controllers. The Message IDs for the messages transmitted by the four controllers were selected such that the data that has to be sampled more often than others had the highest priority in arbitration. We also used Debug CAN messages to output any necessary information needed for Debugging, like the GPS fix lock, Destination bearing etc.,
Different controllers are configured to transmit their messages at different periodicities based on the data sampling rate needed for smooth movement of the car.
Hardware Design
<Show your CAN bus hardware design>
We used the CAN in Full CAN mode at 100Kbps baud rate, and enabled hardware filtering to receive only the messages needed. Four CAN transceivers were used to connect the Controllers to the CAN bus. Terminal resistors of 120 Ohms are used at the ends of the CAN bus to reduce signal reflection.
DBC File
https://gitlab.com/naveena.sura/silver-arrow/-/blob/MOTORCONTROLLER/dbc/project.dbc
Sensor ECU
<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>
Motor ECU
Hardware Design
Motor Controller Node includes the operational control of the DC motor, Servo motor, Electronic speed control (ESC), and the wheel encoder(RPM Sensor). The job of the motor controller is to control the steering of front wheels at appropriate angles and to spin the rear wheels at speeds commanded by the driver node in order to traverse the RC car to the destination location.The DC motor, servo motor, and ESC(Traxxas ESC XL-05) were provided with the Traxxas RC car. The wheel encoder and the trigger magnet were purchased separately from Traxxas's website.
SJ2 Board Pin | Description |
---|---|
5V | Input power |
3.3V | CAN transceiver power |
PWM2 P2.1 | DC Motor Speed Control |
PWM5 P2.4 | Servo Motor Angle Control |
CAP0 P2.6 | RPM Sensor\Wheel Encoder |
CAN1 TX | CAN Transceiver Tx |
CAN1 RX | CAN Transceiver Rx |
GND | Grounding |
DC Motor and ESC
The DC motor is controlled by the ESC using PWM signals which were provided by the motor controller board for forward, neutral, and reverse movements.The DC motor and ESC were provided with RC car.The ESC is powered ON using a 7.4 LiPo battery.The ESC converts this 7.4V to 6V and provides input to DC Motor.
ESC wires | Description | Wire Color |
---|---|---|
Vout | Output Power (6V) | RED |
GND | Ground | BLACK |
PWM | PWM input from SJ2-Board (P2.1) | WHITE |
The car can be operated at 100Hz 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)
Servo Motor
The servo motor responds to PWM pulses. It has three pins namely Vcc, PWM Input Signal, and GND. The servo is powered using 6V from the car battery. Based on the PWM signal supplied from the SJTwo board the front wheels are turned.
Servo Wires | Description | Wire Color |
---|---|---|
Vin | Input Voltage (6V) | RED |
GND | Ground | BLACK |
PWM | PWM input from SJ2-Board (P2.4) | WHITE |
The PWM frequency for our Traxxas Servo motor also needed to be 100Hz. An idle (wheel's pointing forward) duty cycle is 15%. The full duty cycle range is [10%, 20%], where [10%, 15%) is the steer left range, and (15%, 20%] is the steer right.
Wheel Encoder
For speed sensing we purchased a Traxxas RPM sensor as it mounted nicely in the gearbox. The RPM sensor works by mounting a magnet to the spur gear and a hall effect sensor fixed to the gearbox. To get the revolutions per second we used Timer2 as an input capture.
RPM Sensor Wires | Description | Wire Color |
---|---|---|
Vin | Input Voltage (6V) | RED |
GND | Ground | BLACK |
Sensor Output | Input Capture to SJ2-Board (P2.6) | WHITE |
Software Design
The software for the motor node was divided into multiple files and made modular to improve readability and understanding of the complex logic involved.The main code modules are for:
- Servo Motor
- DC Motor
- RPM Sensor
- Motor State Machine
- PID
Technical Challenges
- First and foremost, the Traxxas motor ESC and other Traxxas components are Traxxas Hobby Parts, and they are not designed for development. As a result, no technical specification documentation or program/development guidelines are accessible. The motor ESC must be tested by supplying PWM duty cycles in different sequences at different duty cycle percentages. We utilized the remote control to recreate certain circumstances in which the ESC acted strangely.
- If a Lipo battery is used, the ESC setup switches to the Lipo battery state. When the NiMH battery is replaced, the ESC begins flashing the led in a green-red pattern, and the ESC power button stops working. In this case, the handbook contains a calibration procedure that can rescue your boat and restore regular ESC operation.
- Another challenge with the motor node program development is that you cannot rely on unit testing. Whoever works on the motor node, should make sure that the sequence of the duty cycles being fed to the ESC produces the expected results on the motor as well. The forward-reverse transitioning and the speed controlling both can be a bit tricky. Also, the hard brake logic can take a while to get working properly.
Geographical Controller
Hardware Design
The geographical controller handles all compass and GPS data processing. The controller communicates with the Adafruit Ultimate GPS Breakout via UART, which provides accurate GPS data formatted in GPGGA. An antenna has been used to receive the GPS coordinates correctly. The controller uses the I2C protocol to communicate with the Adafruit Magnetometer in order to determine our heading and where the car should point in order to get closer to its destination. The magnetometer provided us with data with a maximum deviation of 5 degrees in any direction. The magnetometer started providing the accurate values only after calibration.
SJTwo Board | GPS/Compass Module | Description |
---|---|---|
P4.28 (TX3) | RX | Adafruit GPS Breakout |
P4.29 (RX3) | TX | Adafruit GPS Breakout |
P0.10 (SDA) | SDA | Adafruit Magnetometer |
P0.10 (SCL) | SCL | Adafruit Magnetometer |
P2.8 | CAN transceiver (Tx) | CAN transmit |
P2.7 | CAN transceiver (Rx) | CAN receive |
Vcc 3.3V | Vcc | Vcc |
GND | GND | Ground |
Software Design
The GEO controller consists of three main modules namely GPS processing, compass Processing and Waypoints Algorithm. These three main module are then used by the GEO logic to determine where the car should be moving. The compass heading and the distance to the destination is sent to the DRIVER node over the CAN bus.
Technical Challenges
< List of problems and their detailed resolutions>
Master Module
The master module or the DRIVER NODE is the central node of our system which controls the navigation of the car. It takes he sensor values from the SENSOR NODE and geographical data from the GEO NODE and takes the navigation decision as per algorithm and sends the motor commands to the MOTOR NODE.
Hardware Design
The driver node is the main node responsible for moving the car to the destination as it receives appropriate messages from the bridge-sensor node and geo node and processes the signals before sending speed and steering values to the motor node. The LCD has also been interfaced to the DRIVER node and communicates with the controller via UART. The LCD displays messages, which helped us in debugging messages on the CAN bus while the car was moving.
SJTwo Board | LCD/CAN | Description |
---|---|---|
P4.28 (TX3) | RX | LCD |
P4.29 (RX3) | TX | LCD |
P0.1 | CAN transceiver (Tx) | CAN transmit |
P0.0 | CAN transceiver (Rx) | CAN receive |
Vcc 3.3V | Vcc | Vcc |
GND | GND | Ground |
Software Design
The Driver begins navigation only when there is some distance to be covered. Once a distance is set, our car has two algorithms to steer and control the speed of the car.
Object Detection Logic
This logic navigate the car according to the objects detected by the ultrasonic sensors mounted over the SENSOR NODE. As the object is detected, driver takes appropriate decisions and navigate the car without hitting the detected object. The algorithm is briefly described in the flow chart below.
static void change_angle_of_car(bool is_obstactle_on_right) { if (is_obstactle_on_right == false) { motor_data.SERVO_STEER_ANGLE_sig = (motor_data.SERVO_STEER_ANGLE_sig >= -40) ? motor_data.SERVO_STEER_ANGLE_sig - offset_to_angle : -max_angle_threshold; } else { motor_data.SERVO_STEER_ANGLE_sig = (motor_data.SERVO_STEER_ANGLE_sig <= 40) ? motor_data.SERVO_STEER_ANGLE_sig + offset_to_angle : max_angle_threshold; } if (received_heading.DISTANCE <= 3) { motor_data.DC_MOTOR_DRIVE_SPEED_sig = 0; gpio__construct_as_output(0, 15); gpio__set(reverse_buzzer); } else { motor_data.DC_MOTOR_DRIVE_SPEED_sig = car_speed_if_obstacle; gpio__reset(reverse_buzzer); } gpio__construct_as_output(2, 0); gpio__construct_as_output(2, 2); gpio__construct_as_output(0, 15); gpio__reset(reverse_buzzer); gpio__set(reverse_light_1); gpio__set(reverse_light_2); } static void reverse_car_and_turn(void) { motor_data.SERVO_STEER_ANGLE_sig = (motor_data.SERVO_STEER_ANGLE_sig <= 35) ? motor_data.SERVO_STEER_ANGLE_sig + 10 : max_angle_threshold; motor_data.DC_MOTOR_DRIVE_SPEED_sig = reverse_speed; gpio__construct_as_output(2, 0); gpio__construct_as_output(2, 2); gpio__construct_as_output(0, 15); gpio__reset(reverse_buzzer); gpio__set(reverse_light_1); gpio__set(reverse_light_2); } dbc_MOTOR_SPEED_AND_ANGLE_MSG_s driver_motor_commands(void) { bool is_object_on_right = false; if (check_for_obstacle()) { if (us_sensor_data.SENSOR_left <= distance_from_obstacle && us_sensor_data.SENSOR__middle <= distance_from_obstacle && us_sensor_data.SENSOR_right <= distance_from_obstacle) { reverse_car_and_turn(); } else if ((us_sensor_data.SENSOR__left <= distance_from_obstacle && us_sensor_data.SENSOR__middle <= distance_from_obstacle) || us_sensor_data.SENSOR__left <= distance_from_obstacle) { is_object_on_right = false; change_angle_of_car(is_object_on_right); } else if ((us_sensor_data.SENSOR__right <= distance_from_obstacle && us_sensor_data.SENSOR__middle <= distance_from_obstacle) || us_sensor_data.SENSOR__right <= distance_from_obstacle) { is_object_on_right = true; change_angle_of_car(is_object_on_right); } else if (us_sensor_data.SENSOR__rear <= distance_from_obstacle_rear) { is_object_on_right = (us_sensor_data.SENSOR__right < us_sensor_data.SENSOR__left) ? true : false; change_angle_of_car(is_object_on_right); gpio__construct_as_output(0, 15); gpio__set(reverse_buzzer); } else if (us_sensor_data.SENSOR__middle <= distance_from_obstacle) { is_object_on_right = (us_sensor_data.SENSOR__right < us_sensor_data.SENSOR__left) ? true : false; change_angle_of_car(is_object_on_right); } else { printf("ERROR condition\n"); } } else { follow_gps_direction(); } set_lcd_motor_status(motor_data); return motor_data; }
Follow Destination Logic
If there is no object detected, then driver takes decision to navigate towards the destination. Master module gets the compass heading, destination heading and the distance between the current location and the destination location, from the GEO NODE. Once the values are received, the DRIVER NODE take decision as per the flow chart below.
static void motor_move_command(direction_to_move_t direction_to_turn, float total_turn_angle, float distance_magnitude) { while (total_turn_angle > 45) total_turn_angle = total_turn_angle / 4; if (direction_to_turn == left) { if (total_turn_angle >= 40) { motor_data.SERVO_STEER_ANGLE_sig = 40; } else { motor_data.SERVO_STEER_ANGLE_sig = ((int)total_turn_angle / 5)*5; } } else { if (total_turn_angle >= 40) { motor_data.SERVO_STEER_ANGLE_sig = -40; } else if (total_turn_angle >= 35) { motor_data.SERVO_STEER_ANGLE_sig = ((int)(total_turn_angle / 5))*(-5); } } if (motor_data.SERVO_STEER_ANGLE_sig != 0) { gpio__construct_as_output(2, 0); gpio__construct_as_output(2, 2); gpio__set(reverse_light_1); gpio__set(reverse_light_2); } else { gpio__construct_as_output(2, 0); gpio__construct_as_output(2, 2); gpio__reset(reverse_light_1); gpio__reset(reverse_light_2); } if (distance_magnitude >= 40) { motor_data.DC_MOTOR_DRIVE_SPEED_sig = MAX_SPEED; gpio__construct_as_output(0, 15); gpio__reset(reverse_buzzer); } else if (distance_magnitude >= 10) { motor_data.DC_MOTOR_DRIVE_SPEED_sig = MED_SPEED; gpio__construct_as_output(0, 15); gpio__reset(reverse_buzzer); } else if (distance_magnitude > 3) { motor_data.DC_MOTOR_DRIVE_SPEED_sig = MIN_SPEED; gpio__construct_as_output(0, 15); gpio__set(reverse_buzzer); } else { motor_data.DC_MOTOR_DRIVE_SPEED_sig = 0; gpio__construct_as_output(0, 15); gpio__set(reverse_buzzer); } }
LCD Module
The purpose to include an LCD display on the RC car was to be able to display signals over the can bus in order to monitor and debug the behavior of the RC car while it is in motion. The messages displayed on the LCD are mentioned below:
- Ultrasonic sensor values (left, right, center and back)
- Compass heading
- Distance to the destination
- Motor speed
- Steering angle
Code to write to the 4 rows of LCD display using UART:
void lcd_row_write(uint8_t row, uint8_t *new_data) { uint8_t x, y; uint8_t data_string[CONST_LCD_WIDTH + 1]; // Check row number if ((row < 1) || (row > CONST_LCD_NUM_ROWS)) { printf("Row %i is out of bounds", row); return 0; } // Check length of string y = strlen(new_data); if (y > CONST_LCD_WIDTH) { printf("Width %i is out of bounds", y); return 0; } // Initialize to empty memset(data_string, CONST_LCD_EMPTY, sizeof(data_string)); // copy the new data memcpy(data_string, new_data, y); // Write to LCD x = 0; for (y = 0; y < CONST_LCD_WIDTH; y++) { x |= lcd_uart_write_reg(CONST_ADDR_LCD_DATA_START + y, data_string[y]); } // Issue update command x |= lcd_uart_write_reg(CONST_ADDR_LCD_ACTION, (row - 1) | CONST_LCD_ACTION_GO_BIT); // Wait for LCD update delay__ms(2); // return x; }
Technical Challenges
Mobile Application
https://gitlab.com/naveena.sura/silver-arrow/-/merge_requests/8
Hardware Design
Google Pixel 6 mobile was used to run the application and HC-05 Bluetooth Module was used to establish communication between the application and Bridge Controller
Software Design
The software's main front-end code was written in Kotlin. We used Bluetooth communication for transferring and receiving data over the application. Before we open the application, we first need to pair the HC-05 Bluetooth module with the mobile. This is done by going to the mobile’s Bluetooth Setting and pairing the module. The application has 3 “activities”. Activity 1: Bluetooth Connection Screen When the app first opens the Bluetooth socket that is created is checked if it is NULL, which it should be with a start-up. If this occurs the Bluetooth connection screen will be brought up, and the user will need to tap a device to attempt a connection. If the connection fails, the screen will again be brought up. Once the connection is successfully established, we move on to the next “Activity 2: Maps Activity”
Activity 2: Maps Activity: The Google Map SDK is so large and has so many capabilities, we created an account on Google Cloud Console, enabled the API, and then utilized the API key in the Android App's Manifest file. In this activity, we have set up 3 “OnClickListners” viz. My Location, North Align, Send Destination Coordinates. If the user clicks on to “My Location” icon and if the Mobile’s location is “On”, then the map will transport itself to the current location of the mobile. The user can zoom in and out like any other GoogleMaps application and click anywhere on the map to drop a pin. If the user again taps onto the pin, then it will show the coordinates of the selected dropped pin. When the user clicks on “Send Destination Coordinates” the coordinates of the dropped pin are sent over Bluetooth to the Bridge Controller, and the app goes to the final activity “Activity 3: CarDataDisplaActivity”. Advice for future students: Enable "Satellite View" in your Maps Activity so that it easier for you to add your destination location.
Activity 3: CarDataDisplayActivity: This is a very simple activity. All this activity does that it shows the various data that are sent over the CAN bus on the car and displays it live on the app. The application receives the data over the HC-05 Bluetooth module in a long comma (,) separated string format and then segregates the data into the respective fields. Few of the data that are displayed on the application include all the sensor values, current car speed, distance remaining towards the destination, etc. Lastly, this activity has two buttons to start and stop the car.
Technical Challenges
One of the major technical challenges was getting acquainted with the Android Studio Environment. One mistake that we made was that instead of implementing with a relatively well-known JavaScript language, we used Kotlin language which was difficult to get the hang of. Luckily, the android developer’s website provides a lot of useful information for newbie application developers for getting started with the basic building blocks.
Secondly, with security and privacy being an essential paradigm for any app development, setting up and granting permissions for the app to use location and Bluetooth was tricky. Googling stuff and diving deep into the developer’s website helps us overcome these issues. When using Google Maps, you’ll need to create an account and use a key to connect to the app. In Android Studio, that key is located in the "googlemapsapi" file and is ignored by default in git.
In order to display different types of car data, such as sensor data, car speed, current, and destination heading, the bridge controller is appending all the necessary data, that it received over the CAN bus, and sends it in a string format and comma (,) separated. This was decoded in the form of an array of range values and then displayed on the mobile app screen. The range for each value and the message format and structure early was pre-decided. For e.g., sensor values will only range from 0 – 99, whereas the distance from the destination will range from 0-999. If you decide to go by our method, we highly suggest that you carefully decide on the message format and structure. Our implementation is as follows:
Decoding the String message of car data to display on the app
val startOfHashIndex: Int = recDataString.indexOf("#") if (startOfHashIndex != -1) { recDataString = recDataString.substring(startOfHashIndex, recDataString.length) val endOfLineIndex: Int = recDataString.indexOf("~") // determine the end-of-line if (endOfLineIndex > 0) { // make sure there data before ~ var dataInPrint: String = recDataString.substring(0, endOfLineIndex) // extract string txtString!!.setText("Data Received = $dataInPrint") val dataLength = dataInPrint.length //get length of data received txtStringLength!!.setText("String Length = $dataLength") if (recDataString[0] === '#') //if it starts with # we know it is what we are looking for { val sensor0: String = recDataString.substring(1,3) //get sensor value from string between indices 1-5 val sensor1: String = recDataString.substring(5, 7) //same again... val sensor2: String = recDataString.substring(9, 11) val sensor3: String = recDataString.substring(13, 15) val distance_remain: String = recDataString.substring(17, 20) val compass_direction: String = recDataString.substring(22, 25) val compass_direction2: String = recDataString.substring(27, 30) car_speed!!.setText("Current car Speed: " + sensor0 + " km/hr") //update the textviews with sensor values sensorView1!!.setText("Right Sensor: " + sensor1 + " inches") sensorView2!!.setText("Left Sensor: " + sensor2 + " inches") sensorView3!!.setText("Middle Sensor: " + sensor3 + " inches") rem_dist!!.setText("Distance from Destination: " + distance_remain + " meters") heading_direction!!.setText("Current Heading: " + compass_direction + " '") heading_direction2!!.setText("Destination Heading: " + compass_direction2 + " '") } }
Conclusion
<Organized summary of the project>
<What did you learn?>
Project Video
Project Source Code
Advise for Future Students
<Bullet points and discussion>