Difference between revisions of "Self-driving Car"

From Embedded Systems Learning Academy
Jump to: navigation, search
Line 163: Line 163:
 
=== DBC Format ===
 
=== DBC Format ===
 
DBC format is a well known format to describe the format of a CAN message.
 
DBC format is a well known format to describe the format of a CAN message.
[[File:Screenshot 2015-11-22 16.02.04.png|800px|left|dbc File]]
+
 
 +
[[File:Screenshot 2015-11-22 16.02.04.png|center|800px|DBC File]]
 +
<BR/>
  
 
<syntaxhighlight>
 
<syntaxhighlight>
Line 263: Line 265:
  
 
*  Each controller shall display its version information at startup, for example:
 
*  Each controller shall display its version information at startup, for example:
*:  "Version 1.2"
+
*:  printf("Vesion: %s %s\n", __DATE__, __TIME__);
*:  "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
 
*  Each controller shall use the 2-digit LED display to display meaningful info
 
*:  Maybe Geo Controller can display # of feet to destination
 
*:  Maybe Geo Controller can display # of feet to destination
Line 278: Line 277:
  
 
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 you will have to handle this.  I recommend the following:
 
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 you will have to handle this.  I recommend the following:
Attach a BUS off callback function that gives "can_bus_crashed" semaphore.
+
In 1Hz periodic callback, check if the Bus if OFF, and reset it
*  If the semaphore is ever given, reset your CAN bus after a 3 second timeout.
 
  
 
=== Startup Tests ===
 
=== Startup Tests ===
Line 292: Line 290:
 
*  Where is the kill switch?
 
*  Where is the kill switch?
 
*  Can you remotely shut down the car in 3 seconds?
 
*  Can you remotely shut down the car in 3 seconds?
*  Where is the kill switch?
 
 
*  If your controller goes down, will it fully recover to "last known configuration"?
 
*  If your controller goes down, will it fully recover to "last known configuration"?
 
*  If critical sensor data stops coming, how will you stop the car?
 
*  If critical sensor data stops coming, how will you stop the car?

Revision as of 18:34, 23 November 2015

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

Gitlab

Create a "master" Gitlab project that contains sub-folders of each project. Please provide me the access (username: preet) to your master project so I can peek at all of your source code when needed. The Gitlab will track your commit history so I would also know how much work each person or team is contributing.

The folder structure should be:

  • TeamX_CmpE_Fall2015
    Sensor
    IO
    <other controller projects>

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
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

Parts

Controllers

Given below are the controllers, their duties, and the number of people involved. It is 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 cum I/O 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
  • 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

(2 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

(2 members)

  • Interface to a 5Hz or faster GPS
  • 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
Central Controller

(2 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. CAN is a BROADCAST communication bus, but in our software we can add addressing such that we can have 1:1 communication (rather than 1:many). 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: 0x50.00.000 - 0x50.FF.FFF. In particular, your CAN hardware filter needs a single EXTENDED GROUP filter.

You should use 0xFF as a "BROADCAST" address. So if a node sends a message with destination address 0xFF and message number is 0x101, then ALL controllers should respond to this command. So this means you need another acceptance filter with this range: 0xFF.00.000 - 0xFF.FF.FFF

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

DBC Format

DBC format is a well known format to describe the format of a CAN message.

DBC File


VERSION ""


NS_ : 
	NS_DESC_
	CM_
	BA_DEF_
	BA_
	VAL_
	CAT_DEF_
	CAT_
	FILTER
	BA_DEF_DEF_
	EV_DATA_
	ENVVAR_DATA_
	SGTYPE_
	SGTYPE_VAL_
	BA_DEF_SGTYPE_
	BA_SGTYPE_
	SIG_TYPE_REF_
	VAL_TABLE_
	SIG_GROUP_
	SIG_VALTYPE_
	SIGTYPE_VALTYPE_
	BO_TX_BU_
	BA_DEF_REL_
	BA_REL_
	BA_DEF_DEF_REL_
	BU_SG_REL_
	BU_EV_REL_
	BU_BO_REL_
	SG_MUL_VAL_

BS_:

BU_: NOONE SENSOR DRIVER MOTOR IO

BO_ 100 HEARTBEAT: 1 DRIVER
 SG_ DRIVER_HEARTBEAT_cmd : 0|8@1+ (1,0) [0|0] "" SENSOR,MOTOR

BO_ 200 SONARS: 6 SENSOR
 SG_ SENSOR_SONARS_mux M : 0|4@1+ (1,0) [0|0] "" DRIVER,IO
 SG_ SENSOR_SONARS_s1_fault : 4|1@1+ (1,0) [0|0] "" DRIVER,IO
 SG_ SENSOR_SONARS_s2_fault : 5|1@1+ (1,0) [0|0] "" DRIVER,IO
 SG_ SENSOR_SONARS_s3_fault : 6|1@1+ (1,0) [0|0] "" DRIVER,IO
 SG_ SENSOR_SONARS_s4_fault : 7|1@1+ (1,0) [0|0] "" DRIVER,IO
 SG_ SENSOR_SONARS_left m0 : 8|12@1+ (0.1,0) [0|400] "" DRIVER,IO
 SG_ SENSOR_SONARS_middle m0 : 20|12@1+ (0.1,0) [0|0] "" DRIVER,IO
 SG_ SENSOR_SONARS_right m0 : 32|12@1+ (0.1,0) [0|0] "" DRIVER,IO
 SG_ SENSOR_SONARS_rear m0 : 44|12@1+ (0.1,0) [0|0] "" DRIVER,IO
 SG_ SENSOR_SONARS_no_filt_left m1 : 8|12@1+ (0.1,0) [0|400] "" NOONE
 SG_ SENSOR_SONARS_no_filt_middle m1 : 20|12@1+ (0.1,0) [0|400] "" NOONE
 SG_ SENSOR_SONARS_no_filt_right m1 : 32|12@1+ (0.1,0) [0|400] "" NOONE
 SG_ SENSOR_SONARS_no_filt_rear m1 : 44|12@1+ (0.1,0) [0|400] "" NOONE

BO_ 300 MOTOR_CMD: 1 DRIVER
 SG_ MOTOR_CMD_steer : 0|4@1- (1,-5) [-5|5] "" MOTOR
 SG_ MOTOR_CMD_drive : 4|4@1+ (1,0) [0|9] "" MOTOR

BO_ 400 MOTOR_STATUS: 3 MOTOR
 SG_ MOTOR_STATUS_wheel_error : 0|1@1+ (1,0) [0|0] "" DRIVER
 SG_ MOTOR_STATUS_speed : 8|16@1+ (0.001,0) [0|0] "kph" DRIVER

CM_ BU_ NOONE "No node, used to indicate if it's a debug message going to no one";
CM_ BU_ DRIVER "The driver controller driving the car";
CM_ BU_ SENSOR "The sensor controller of the car";
CM_ BU_ MOTOR "The motor controller of the car";
CM_ BO_ 100 "Sync message used to synchronize the controllers";

BA_DEF_  "BusType" STRING ;
BA_DEF_ SG_ "FieldType" STRING ;
BA_DEF_ BO_ "GenMsgCycleTime" INT 0 0;

BA_DEF_DEF_ "BusType" "CAN";
BA_DEF_DEF_ "FieldType" "";

BA_ "GenMsgCycleTime" BO_ 100 1000;
BA_ "GenMsgCycleTime" BO_ 200 100;
BA_ "GenMsgCycleTime" BO_ 300 100;
BA_ "FieldType" SG_ 100 DRIVER_HEARTBEAT_cmd "DRIVER_HEARTBEAT_cmd";

VAL_ 100 DRIVER_HEARTBEAT_cmd 0 "DRIVER_HEARTBEAT_cmd_NOOP" 1 "DRIVER_HEARTBEAT_cmd_SYNC" 2 "DRIVER_HEARTBEAT_cmd_REBOOT";

How will communication work?

After startup, begin to send your data messages at the desired periodic rates using the periodic API as listed below in the example. Whichever controller wants to listen to your periodic message shall intercept your message and use it for its needs.

You can have global variables for the CAN data messages, and tasks should update the data within these messages. Assuming that you've added periodic message to be sent to broadcast address (0xFF), the periodic tasks will always grab the latest CAN message and send it out for you.

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. If your product team fails, then just like it would happen in the industry, you will get laid off, and I will see you again in the course ;(

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:
    printf("Vesion: %s %s\n", __DATE__, __TIME__);
  • Each controller shall use the 2-digit LED display to display meaningful info
    Maybe Geo Controller can display # of feet to destination
    Central 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 you will have live with missing data messages. 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 you will have to handle this. I recommend the following:

  • In 1Hz periodic callback, check if the Bus if OFF, and reset it

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 Central Controller must process upon each boot:

  • Central controller sends a message requesting boot information from each controller
  • Central 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?
  • If your controller goes down, will it fully recover to "last known configuration"?
  • 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


Part 1: Basic structure and CAN initialization

/************** can_msg_id.h *************/
#ifndef CAN_MSG_ID_H_
#define CAN_MSG_ID_H_


/**
 * Have an enumeration of controller IDs
 */
typedef enum :uint8_t {
    cid_geographical_controller = 1,
    cid_central_controller = 2,
    cid_broadcast = 0xff
} cid_t;

/// TODO Each controller shall set its own ID
#define OUR_CONTROLLER_ID       (cid_central_controller)

/**
 * Create a "union" whose struct overlaps with the uint32_t of CAN message id
 */
typedef union {
    /// This "raw" overlaps with <DST> <SRC> <ID>
    uint32_t raw;

    /// Struct members overlap with "raw"
    struct {
        uint32_t msg_num : 12; ///< Message number
        uint32_t src :  8;     ///< Source ID
        uint32_t dst :  8;     ///< Destination ID
    };
} __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
 */
static inline uint32_t make_id(uint8_t dst, uint16_t msg_num)
{
    controller_id_t cid = { 0 };
    cid.msg_num  = msg_num;
    cid.src = OUR_CONTROLLER_ID;
    cid.dst = dst;
    return cid.raw;
}

#endif /* CAN_MSG_ID_H_ */


/************** periodic_callbacks.cpp ************/
#include "can.h"

bool period_init(void)
{
    /* Initialize CAN Bus and set the acceptance filter(s) */
    CAN_init(can1, 100, 10, 10, NULL, NULL);

    /* TODO Initialize acceptance filters */
    CAN_reset_bus(can1);
    
    return true;
}


Part 2: Periodic Parsing Task

Only one FreeRTOS periodic function should be responsible to receive CAN messages (while any task can send a CAN message). This receiving task should "route" the incoming messages to the appropriate "consumers" in your code.

void period_100Hz(void)
{
    can_msg_t msg;
    const controller_id_t *msgid = (controller_id_t *)(&(msg.msg_id));
    
    {
        while (CAN_rx(can1, &msg, 0)) {
            /* TODO This is psuedocode, so add your real logic here */
            if (msgid->dst != cid_broadcast && msgid->dst != OUR_CONTROLLER_ID) {
                LOG_ERROR("CAN acceptance filter must be incorrect (0x%08X)!", msg.msg_id);
            }

            /* TODO: Call auto generated code from DBC parser to parse the message */
        }
    }
}