Difference between revisions of "Self-driving Car"

From Embedded Systems Learning Academy
Jump to: navigation, search
(Recommended CAN Message ID format)
Line 129: Line 129:
  
 
=== Recommended CAN Message ID format ===
 
=== Recommended CAN Message ID format ===
We split the 29-bit CAN message ID into 3 portions to support peer-to-peer communication. '''You should configure your CAN hardware filter based on your controller ID.''' When a CAN message arrives, we will have its 29-bit ID, and out of this, we can determine who sent it, and what message number they sent.
+
We split the 29-bit CAN message ID into 3 portions to support peer-to-peer communication.  When a CAN message arrives, we will have its 29-bit ID, and out of this, we can determine who sent it, and what message number they sent.
 +
 
 +
'''You should configure your CAN hardware filter based on your controller ID.'''  So if your controller is ID 0x50, you should accept all messages in this range: '''</code>0x50.00.000 - 0x50.FF.FFF</code>'''.  In particular, your CAN hardware filter needs a single EXTENDED GROUP filter.
  
 
{| class="wikitable"
 
{| class="wikitable"
 
|+ CAN Communication Protocol (29-bit CAN ID)
 
|+ CAN Communication Protocol (29-bit CAN ID)
 
|-
 
|-
 +
| Reserved bit
 
| Destination Controller
 
| Destination Controller
 
| Source Controller
 
| Source Controller
 
| Message number
 
| Message number
 
|-
 
|-
| <code>8-bit : B28:B21</code>
+
| <code>1-bit : B28</code>
| <code>8-bit : B20:B14</code>
+
| <code>8-bit : B27:B20</code>
| <code>13-bit: B13:B00</code>
+
| <code>8-bit : B19:B13</code>
 +
| <code>12-bit: B12:B00</code>
 
|}
 
|}
  

Revision as of 18:22, 29 May 2014

This project is about a large team 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.

   
Self-Drive Car Block Diagram

Schedule

Proposed Schedule
Week Milestone
October (Week1)
  • Create Wikipedia Project
  • Team Collaborations
  • Order large RC car (1:5 or 1:4)
October (Week2)
  • Assemble the RC car
  • Finish CAN Bus Wiring
October (Week3)
  • Verify CAN communication
  • Implement Startup Tests
October (Week4)
  • Deliver basic functionality (tested)
  • Log critical/debug data
  • Basic Android interface
October (Week5)
  • Project Integration
  • Implement release control (with change-log)
November (Week6)
  • Prototype project demonstration
  • Code Review
November (Week7)
  • Implement code review feedback
November (Week8)
  • Finalize project features
November (Week9)
  • Testing
December(Week10)
  • More testing and trial runs
  • Optimize & Tweak
December(Week11) Project Demonstration

Controllers

Given below are the controllers, their duties, and the number of people involved. It is quite possible that one team gets done with their part, but that doesn't mean your job is done. If you are done, help others. If you are done, and the primary objective is met (the car can self-drive), then add more features. There are many things you can do, and the 16-week semester definitely won't provide an opportunity to sit and relax. Get up and learn!.

Controllers
Sensor Controller (2 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"
  • Provide additional sensor inputs:
    Provide battery voltage, and % charge remaining
    Light sensor reading
    Tilt (angle of the car)
Motor Controller (2 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
I/O Unit (2 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 (etc).
Communication Bridge + Android (3 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(3 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 to drive the car to destination
    Avoid obstacles on the way to the destination
    Add features once you finish the primary goal

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

We split the 29-bit CAN message ID into 3 portions to support peer-to-peer communication. When a CAN message arrives, we will have its 29-bit ID, and out of this, we can determine who sent it, and what message number they sent.

You should configure your CAN hardware filter based on your controller ID.  So if your controller is ID 0x50, you should accept all messages in this range: </code>0x50.00.000 - 0x50.FF.FFF</code>.  In particular, your CAN hardware filter needs a single EXTENDED GROUP filter.
CAN Communication Protocol (29-bit CAN ID)
Reserved bit Destination Controller Source Controller Message number
1-bit : B28 8-bit : B27:B20 8-bit : B19:B13 12-bit: B12:B00
Message Numbers
Reserved 0x000 - 0x0FF
Common responses 0x100 - 0x1FF
Common commands 0x200 - 0x2FF
Controller specific commands 0x300 - 0x3FF
Subscription messages 0x400 - 0x4FF
Subscribed messages 0x500 - 0x5FF

Example Controller Communication Table

Common Communication Table
Message Number Purpose / Data layout
0x201 Get version and boot info (0x101 will be sent)
0x202 Get general info (0x102 will be sent)
0x203 Synchronize (set) time:
byte [0-3] : System time
0x101
byte [0-3] : Version Info
byte [4-7] : boot timestamp
0x102
byte [0-3] : Current time
byte [4]   : CPU usage %
Subscription Rate Info
Byte 0 Effect
0 Off
1 1Hz
5 5Hz
10 10Hz
20 20Hz
255 "On data change", capped to 20Hz
Geographical Controller Communication Table
Message Number Purpose Data layout
0x301 Set GPS destination
byte [0-3] : (float) Longitude
byte [4-7] : (float) Latitude
0x401 Subscribe to GPS data See Subscription Rate above
0x402 Subscribe to compass data See Subscription Rate above
0x501 Subscribed data of 0x401
byte [0-3] : (float) Longitude
byte [4-7] : (float) Latitude
0x502 Subscribed data of 0x402
byte [0-1] : (uint16) Current compass degree
byte [2-3] : (uint16) Destination compass degree
Sensor Controller Communication Table
Message Number Purpose Data layout
0x401 Subscribe to distance sensor data See Subscription Rate above
0x501 Subscribed data of 0x401
byte [0] : Front sensor value in inches
byte [1] : Left sensor value in inches
byte [2] : Right sensor value in inches
etc.

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, variable speed settings through Android interface etc.

Quick and Easy Features

These features are mandatory, just to help you debug faster.

  • Each controller shall display its version information at startup, for example:
    "Version 1.2"
    "Fixed rear sensor reporting zero value"
    "Version 1.1"
    "Added rear sensor value"
  • Each controller shall use the 2-digit LED display to display meaningful info
    Maybe Geo Controller can display # of feet to destination
    Master controller can display number of CAN messages received per second.
  • Each controller shall use the 4-LED lights for some indication
    LED0 should be lit if an error happens (common to everyone)
    Each LED should be labeled about what it means(maybe with a label machine?)

Robustness

Your project is one project as a whole. So if it doesn't work, do not blame it on "hey, their controller crashed". If a controller crashes it will restart, and the subscribed messages will vanish. If subscribed messages vanish, other controllers should re-subscribe. So your code should be robust, and self-recover from any crashed event or any brief power disruptions.

Likewise, if you send a message, and it fails (in case the other controller is down), your CAN bus may go to abnormal state and turn off. In this condition, all of your messages will fail, and the entire communication needs to be re-done including all the subscriptions. I recommend the following:

  • Attach a BUS off callback function that gives "can_bus_crashed" semaphore.
  • If the semaphore is ever given, reset your CAN bus, and re-subscribe to the desired messages.

Startup Tests

Since we rely on multiple controllers, it is critical to be able to test each controller quickly, reliably, and easily. So here is a startup test the master controller must process upon each boot:

  • Master controller sends a message requesting boot information from each controller
  • Master controller checks responses after about 100 ms:
    Each controller must have responded
    Each controller's boot code (normal, abnormal) should be validated.

Considerations

You should consider and design your software for all of these events:

  • Where is the kill switch?
  • Can you remotely shut down the car in 3 seconds?
  • Where is the kill switch?
  • If controller A sends subscription message twice, are you sure you won't double subscribe?
  • If your controller goes down, will it fully recover to "last known configuration"?
  • If you stop receiving subscribed messages, what will you do?
  • If critical sensor data stops coming, how will you stop the car?
  • How can you quickly discover one or more controllers reaching an error state?
  • Log the data on the SD card as much as possible.
    If something wrong happens, you need to know what happened.
    Each controller must log its "startup" time, to debug when a controller crashes and restarts

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.

Sample Code


Subscribe and handle subscribed data

/**
 * Have an enumeration of controller IDs
 */
typedef enum {
    cid_geographical_controller = 50,
    cid_master_controller = 60,
} cid_t;

/**
 * Each controller shall then set its own ID
 */
const cid_t our_controller_id = cid_master_controller;

/**
 * Create a "union" whose struct overlaps with the uint32_t
 */
typedef union {
    struct {
        uint32_t msg_num : 12; ///< Message number
        uint32_t src : 8;      ///< Source ID
        uint32_t dst : 8;      ///< Destination ID
        uint32_t : 1;          ///< Unused (reserved 29th bit)
    };
    /// 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] dst  The destination controller ID
 * @param [in] msg_num  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 dst, uint16_t msg_num)
{
    controller_id_t cid;
    cid.raw = 0;
    cid.msg_num  = msg_num;
    cid.src = our_controller_id;
    cid.dst = dst;
    return cid.raw;
}



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(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 see pCid->src to find out
     * 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, portMAX_DELAY)) {
        controller_id_t *pCid = &(msg.msg_id);

        /**
         * Check if we got the response from the GPS controller
         * If you subscribe to a lot of messages, you might want to put this 
         * in a large switch statement that calls functions.
         */
        if (pCid->src == cid_geographical_controller)
        {
            if (pCid->msg_num== 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;
}


Recommended main loop or main task

/* Either a "plain vanilla" FreeRTOS task, or run() method of scheduler_task */
void main_task(void *p)
{
    can_msg_t msg;
    while(1) {
        if (CAN_rx(can1, &msg, portMAX_DELAY)) {
            /* This is psuedocode, so add your real logic here */
            if (!dst_is_not_us) {
                log_error("HW CAN Filter must be incorrect!");
            }
            else if (command_message) {  /* 0x200 - 0x2FF */
                send_cmd_response(msg);  /* 0x100 - 0x1FF */
            }
            else if (our_command) {      /* 0x300 - 0x3FF */
                handle_cmd_message(msg);
            }
            else if (new_subscribe) {    /* 0x400 - 0x4FF */
                add_subscription(msg);
            }
            else {                       /* 0x500 - 0x5FF OR 0x100 - 0x1FF */
                /* Either a subscribed message, or a command response message */
                handle_message_destined_for_us(msg);
            }
        }
    }
}
void subscription_task(void *p)
{
    /* Subscription message loop at 20Hz
     * This should send all messages ranging from 1-20Hz
     */
    while (1) {
        send_subscription_messages();
        vTaskDelay(OS_MS(50));
    }
}