Difference between revisions of "Self-driving Car"
(Created page with "This project is about a team of 20 getting a car to self-drive to a selected destination. This involves working with an RTOS running on a low power processor and various diff...") |
(→Communication) |
||
Line 48: | Line 48: | ||
Each controller shall pick a controller number. The controller with the highest priority shall pick the lowest ID. Using this protocol, each controller can specifically send a message to any other controller, and likewise, upon a received message, we can tell who it came from. | Each controller shall pick a controller number. The controller with the highest priority shall pick the lowest ID. Using this protocol, each controller can specifically send a message to any other controller, and likewise, upon a received message, we can tell who it came from. | ||
+ | === Recommended CAN Message ID format === | ||
{| class="wikitable" | {| class="wikitable" | ||
− | |+ CAN Communication Protocol | + | |+ CAN Communication Protocol (29-bit CAN ID) |
|- | |- | ||
| Destination Controller (8-bit) B28:B21 | | Destination Controller (8-bit) B28:B21 | ||
Line 63: | Line 64: | ||
* Message ID 0x400 - 0x4FF for "subscribed" messages | * Message ID 0x400 - 0x4FF for "subscribed" messages | ||
+ | === Example Communication table for the Geographical Controller === | ||
{| class="wikitable" | {| class="wikitable" | ||
|+ Geographical Controller Communication Table | |+ Geographical Controller Communication Table | ||
Line 80: | Line 82: | ||
|- | |- | ||
| 0x301 | | 0x301 | ||
− | | Subscribe to | + | | Subscribe to GPS data |
| byte 0: Subscription rate, 1=1Hz, 5=5Hz, 10=10Hz etc. | | byte 0: Subscription rate, 1=1Hz, 5=5Hz, 10=10Hz etc. | ||
| See message number 0x401 for response data | | See message number 0x401 for response data | ||
Line 86: | Line 88: | ||
| 0x401 | | 0x401 | ||
| Subscribed data of 0x301 | | Subscribed data of 0x301 | ||
− | |colspan="2"| 4 bytes(float): Longitude, 4 bytes(float): Latitude | + | |colspan="2"| 4 bytes(float): Longitude, 4 bytes(float): Latitude |
|} | |} | ||
+ | |||
+ | === Sample Code === | ||
+ | <syntaxhighlight lang="C"> | ||
+ | |||
+ | /** | ||
+ | * Create a "union" whose struct overlaps with the uint32_t | ||
+ | */ | ||
+ | typedef union { | ||
+ | struct { | ||
+ | uint32_t id : 14; ///< Message ID | ||
+ | uint32_t src : 8; ///< Source ID | ||
+ | uint32_t dst : 8; ///< Destination ID | ||
+ | }; | ||
+ | /// This "raw" overlaps with <DST> <SRC> <ID> | ||
+ | uint32_t raw; | ||
+ | |||
+ | } __attribute__((packed)) controller_id_t; | ||
+ | |||
+ | /** | ||
+ | * Creates a message ID based on the message ID protocol | ||
+ | * @param [in] src The source controller ID | ||
+ | * @param [in] dst The destination controller ID | ||
+ | * @param [in] msg_id The message number to send to the dst controller | ||
+ | * | ||
+ | * @returns The 32-bit message ID created by the input parameters | ||
+ | */ | ||
+ | uint32_t make_id(uint8_t src, uint8_t dst, uint16_t msg_id) | ||
+ | { | ||
+ | controller_id_t cid; | ||
+ | cid.id = msg_id; | ||
+ | cid.dst = dst; | ||
+ | cid.src = src; | ||
+ | return cid.raw; | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * Have an enumeration of controller IDs | ||
+ | */ | ||
+ | typedef enum { | ||
+ | cid_geographical_controller = 5, | ||
+ | cid_master_controller = 6, | ||
+ | } cid_t; | ||
+ | |||
+ | /** | ||
+ | * Each controller can then set its own ID | ||
+ | */ | ||
+ | const cid_t our_controller_id = cid_master_controller; | ||
+ | |||
+ | int main(void) | ||
+ | { | ||
+ | /** | ||
+ | * We use magic numbers here, but each message ID (such as 0x301) should be | ||
+ | * part of an enumeration that is shared between different controllers. | ||
+ | */ | ||
+ | can_msg_t msg = { 0 }; | ||
+ | msg.msg_id = make_id(our_controller_id, cid_geographical_controller, 0x301); | ||
+ | |||
+ | /** | ||
+ | * Send the message to the geographical controller to subscribe to | ||
+ | * GPS data to be sent at 5Hz: | ||
+ | */ | ||
+ | msg.frame_fields.data_len = 1; | ||
+ | msg.frame_fields.is_29bit = 1; | ||
+ | msg.data.bytes[0] = 5; | ||
+ | CAN_tx(can1, &msg, portMAX_DELAY); | ||
+ | |||
+ | /** | ||
+ | * Now, we should be able to retrieve our data from the geographical | ||
+ | * controller at 5Hz. Since we may have subscribed to many messages, | ||
+ | * you will need to pCid->src of where the message came from, and | ||
+ | * then also check the pCid->id to detect what message it is, and then | ||
+ | * parse the data bytes into our variable types. | ||
+ | */ | ||
+ | if (CAN_rx(can1, &msg, 200)) { | ||
+ | controller_id_t *pCid = &(msg.msg_id); | ||
+ | |||
+ | /** | ||
+ | * Check if we got the response from the GPS controller | ||
+ | */ | ||
+ | if (pCid->src == cid_geographical_controller) | ||
+ | { | ||
+ | if (pCid->id == 0x401) /* 0x401 is a magic num, should be an enum */ | ||
+ | { | ||
+ | float longitude = * (float*) &(msg.data.bytes[0]); | ||
+ | float latitude = * (float*) &(msg.data.bytes[4]); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | return 0; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
== Features == | == Features == |
Revision as of 21:18, 22 May 2014
This project is about a team of 20 getting a car to self-drive to a selected destination. This involves working with an RTOS running on a low power processor and various different processor boards working together over a CAN bus.
Contents
Schedule
<TODO>
Controllers
Given below are the controllers, their duties, and the number of people involved.
- Sensor Controller (3 members)
- Interfaced to front and rear vision.
- Consider Sonar, and/or IR sensors with long distance vision
- Sensors must be "filtered" and must provide reliable "vision"
- What else can you incorporate as additional features?
- Acceleration sensor to provide tilt? (for hill hold assist)
- Light sensor to turn on automatic headlights?
- Interfaced to front and rear vision.
- Motor Controller (3 members)
- Interfaced to motor control system of the car
- Provide a means to steer, and drive the car
- Provide feedback of the speed using a wheel encoder or speed sensor
- Interfaced to motor control system of the car
- I/O Unit (3 members)
- Provide an LCD screen to report car status
- Errors and communication status
- Sensor values
- Buttons to start and stop the car
- Button hard-coded to set a specific destination
- Provide means to turn on/off the headlights
- Provide an LCD screen to report car status
- Communication Bridge + Android (4 members)
- Provide means to communicate and display status on an Android/iPhone device
- Allow a user to see sensor values, car speed (etc)
- Allow a user to select a destination from Google Earth
- Geographical Controller (3 members)
- Interface to a 5Hz or faster GPS
- Interface to a backup GPS in case primary one fail (use a different manufacturer)
- Interface to a compass
- Allow a GPS coordinate to be "set"
- Based on the set coordinate, calculate, and provide CAN data regarding
- the current heading, and the desired heading to reach the destination
- This unit needs to compute the "heading degree" to reach the destination
- Master Controller (4 members)
- This is the primary unit that communicates with every controller to drive the car
- This unit shall also turn on headlights (etc), and be the "brain" of the car
- Upon a detection of a "Start" condition, work with different controllers to drive the car
- Motor Controller and Geographical Controller
- Command the Motor Controller based on Sensor Controller and Geographical Controller data
Communication
Each controller shall provide a means to communicate with the other controllers. Before you read any further, it requires that you have deep knowledge of the CAN bus. The most important thing to realize is that CAN bus is 1:1 communication rather than 1:many. Although most of the time, all the controllers should only communicate with the Master Controller, sometimes there may be a necessity for any one controller to communicate with a specific controller, therefore we need to provide a means to facilitate this. Given below is an example communication interface for one controller.
Each controller shall pick a controller number. The controller with the highest priority shall pick the lowest ID. Using this protocol, each controller can specifically send a message to any other controller, and likewise, upon a received message, we can tell who it came from.
Recommended CAN Message ID format
Destination Controller (8-bit) B28:B21 | Source Controller (8-bit) B20:B14 | Message number (13-bit) B13:B00 |
I recommend a standard similar to the following:
- Message ID 0x000 for critical message
- Message ID 0x100 - 0x1ff for "common" message types
- Message ID 0x200 - 0x2FF for "command" messages
- Message ID 0x300 - 0x3FF for "subscription" messages
- Message ID 0x400 - 0x4FF for "subscribed" messages
Example Communication table for the Geographical Controller
Message ID | Purpose | Data layout | Notes |
0x100 | Report status | 1 byte: Error code, 1 byte: CPU usage % ... (etc) | |
0x200 | Set GPS destination | 4 bytes(float): Longitude, 4 bytes(float): Latitude | |
0x301 | Subscribe to GPS data | byte 0: Subscription rate, 1=1Hz, 5=5Hz, 10=10Hz etc. | See message number 0x401 for response data |
0x401 | Subscribed data of 0x301 | 4 bytes(float): Longitude, 4 bytes(float): Latitude |
Sample Code
/**
* Create a "union" whose struct overlaps with the uint32_t
*/
typedef union {
struct {
uint32_t id : 14; ///< Message ID
uint32_t src : 8; ///< Source ID
uint32_t dst : 8; ///< Destination ID
};
/// This "raw" overlaps with <DST> <SRC> <ID>
uint32_t raw;
} __attribute__((packed)) controller_id_t;
/**
* Creates a message ID based on the message ID protocol
* @param [in] src The source controller ID
* @param [in] dst The destination controller ID
* @param [in] msg_id The message number to send to the dst controller
*
* @returns The 32-bit message ID created by the input parameters
*/
uint32_t make_id(uint8_t src, uint8_t dst, uint16_t msg_id)
{
controller_id_t cid;
cid.id = msg_id;
cid.dst = dst;
cid.src = src;
return cid.raw;
}
/**
* Have an enumeration of controller IDs
*/
typedef enum {
cid_geographical_controller = 5,
cid_master_controller = 6,
} cid_t;
/**
* Each controller can then set its own ID
*/
const cid_t our_controller_id = cid_master_controller;
int main(void)
{
/**
* We use magic numbers here, but each message ID (such as 0x301) should be
* part of an enumeration that is shared between different controllers.
*/
can_msg_t msg = { 0 };
msg.msg_id = make_id(our_controller_id, cid_geographical_controller, 0x301);
/**
* Send the message to the geographical controller to subscribe to
* GPS data to be sent at 5Hz:
*/
msg.frame_fields.data_len = 1;
msg.frame_fields.is_29bit = 1;
msg.data.bytes[0] = 5;
CAN_tx(can1, &msg, portMAX_DELAY);
/**
* Now, we should be able to retrieve our data from the geographical
* controller at 5Hz. Since we may have subscribed to many messages,
* you will need to pCid->src of where the message came from, and
* then also check the pCid->id to detect what message it is, and then
* parse the data bytes into our variable types.
*/
if (CAN_rx(can1, &msg, 200)) {
controller_id_t *pCid = &(msg.msg_id);
/**
* Check if we got the response from the GPS controller
*/
if (pCid->src == cid_geographical_controller)
{
if (pCid->id == 0x401) /* 0x401 is a magic num, should be an enum */
{
float longitude = * (float*) &(msg.data.bytes[0]);
float latitude = * (float*) &(msg.data.bytes[4]);
}
}
}
return 0;
}
Features
The first feature to develop is the self-drive capability and everything else comes later. While some people in the team may be focusing on delivering this primary feature, other members can focus on other things such as automatic headlights, hill-hold capability etc.
Grade
Your grade is relative. The best team earns the best grade. Remember than three out of three features working 100% is far better than nine out of ten features working. Focus on less features, with highest quality.