Project: System Integration

This is the final project of Udacity’s Self-Driving-Car Nanodegree. The project description and build instructions can be found here, the required simulator here.

The goal of this project is the integration of the perception, planning, and control subsystems of an autonomous vehicle using the Robot Operating System (ROS) to guide a self-driving car on a simulated highway track while taking into account upcoming traffic signals.

System Architecture

The system architecture depicted above shows all the ROS nodes and topics involved in the project. The main focus lies on the implementation and integration of the following three nodes:

Traffic light detection: This node is part of the perception subsystem. It is responsible for the detection and classification of the traffic lights. If a red signal is detected, the node will publish the position of the traffic light’s stop line to the /traffic_waypoint topic.

Waypoint updater: This node is part of the planning subsystem. It is responsible for the generation of a suitable trajectory to be followed by the vehicle. In general, the vehicle will follow the reference trajectory provided by the /base_waypoints topic, and should stop only if a red traffic light was detected. In these cases, the node will update the trajectory in such a way that vehicle comes to a halt at the location specified by the /traffic_wapoint topic. The updated trajectory will be published to the /final_waypoints topic.

Drive by wire controller: This node is part of the control subsystem. It is responsible for the longitudinal and lateral motion control of the vehicle. PID and Stanley controllers are implemented to provide the appropriate throttle, brake, and steering commands that make the vehicle follow the trajectory proposed by planning subsystem above.

In the following, we will take a closer look at the implementation details of each subsystem.

Perception subsystem

Traffic Light Detection

The traffic light detection node is part of the /ros/src/tl_detector/ package and it is implemented in tl_detector.py.

As mentioned above, the node is in charge of detecting the incoming traffic lights and classifying their signal states. In order to do so, it subscribes to the following topics:

  • /base_waypoints - A list of waypoints representing the reference trajectory on the map. Published by the Waypoint Loader node.
  • /image_color - The current RGB image received from the vehicle camera. Published by the simulator.
  • /current_pose - The current position of the vehicle. Published by the simulator.
  • /vehicle/traffic_lights - The positions of all the traffic lights on the map. Published by the simulator.

The node will search for the next upcoming traffic light within a look ahead distance of NUM_LOOKAHEAD waypoints in the front of the vehicle’s current pose. If a traffic light is detected, the node will classify its signal state using the images from the incoming camera stream. If the traffic light signal is red, the node will communicate the position of the corresponding stop line. This is done by searching the reference waypoint that is closest to the stop line in the /base_waypoints list, and publishing its index to the /traffic_waypoint topic. This topic will then be used by the Waypoint Updater node in the planning subsystem to update the trajectory such that the vehicle comes to a halt at the stop line.

Traffic Light Classification

The traffic light classifier is implemented in tl_detector/light_classifier/tl_classifier.py. Up to now, the classification is solved by using color thresholding in the hsv color space. Three masks are defined to isolate the red, yellow and green areas in the image. The summation of the active pixels in each mask represent the intensities of each color. Finally, the color with the highest intensity indicates the color of the current traffic light. In the future, we may come back to this and replace this classification approach with a more robust one that uses Tensorflow’s Object Detection API.

Obstacle Detection

The obstacle detection node is not required in this project.

Planning subsystem

The planning subsystem is responsible for the generation of a suitable trajectory to be followed by the vehicle. Two nodes are implemented to achieve this.

Waypoint Loader

The waypoint loader node is part of the /ros/src/waypoint_loader/ package and it is implemented in waypoint_loader.py. Its only purpose is to load the reference trajectory to be followed by the vehicle and to publish it to the /base_waypoints topic.

The reference trajectory is represented by a list of waypoints, of which each holds the information about a future target Pose (location/orientation) and Twist (linear/angular velocity) of the vehicle. The reference trajecory simply guides the vehicle along a predetermined path on the highway while maintaining a certain speed limit specified in the corresponding launch file. However, the reference waypoints are static and do not yet allow the vehicle to stop at red lights. To achieve this, they need to updated, and this is performed by the waypoint updater node.

Waypoint Updater

The waypoint updater node is part of the /ros/src/waypoint_updater/ package and it is implemented in waypoint_updater.py.

The node is responsible to update the reference waypoints taking into account the traffic lights in the vicinity. To accomplish this, it subscribes to the following topics:

  • /base_waypoints - A list of waypoints representing the reference trajectory on the map. Published by the Waypoint Loader node.
  • /current_pose - The current position of the vehicle. Published by the simulator.
  • /traffic_waypoint - Index to the reference waypoint in the /base_waypoints list that is closest to the stop line of the upcoming red traffic light. Published by the Traffic Light Detection node.
  • /obstacle_waypoints - Not required for this project.

The node will update the next NUM_LOOKAHEAD reference waypoints in front of the vehicle. If a red traffic light is detected (indicated by the /traffic_waypoint topic), the speed of each waypoint between the current position of the vehicle and the stop line will be reduced using a smooth decelerating curve. Otherwise, the original reference waypoints are kept unchanged and the vehicle will continue to follow its designated trajectory. The list of updated waypoints are published to the /final_waypoints topic that will be used by the control subsystem.

Control subsystem

The control subsystem is responsible for the longitudinal and lateral motion control of the vehicle. Two nodes are implemented to achieve this.

Waypoint Follower

The waypoint follower node is part of the /ros/src/waypoint_follower/ package and it is provided for this project using code from Autoware.

The node is responsible to generate the linear and angular target velocities that the vehicle shall adopt. To do this, it uses the updated trajectory that is published to the /final_waypoints topic by the Waypoint Updater node . The determined target velocities are then published themselves to the /twist_cmd topic in form of Twist messages. The target velocities will be used by the Drive by Wire node to generate approriate control commands.

Drive by Wire

The Drive by Wire node is part of the /ros/src/twist_controller/ package and it is implemented in dbw_node.py.

The node is responsible for the generation of appropriate throttle, brake, and steering commands that make the vehicle follow the updated trajectory proposed by planning subsystem. To achieve this, it subscribes to the following topics:

  • /base_waypoints - A list of waypoints representing the reference trajectory on the map. Published by the Waypoint Loader node.
  • /current_pose - The current position of the vehicle. Published by the simulator.
  • /current_velocity - The current linear and angular velocities of the vehicle. Published by the simulator.
  • /twist_cmd - The target linear and angular velocities for the vehicle. Published by the Waypoint Follower node.
  • /vehicle/dbw_enabled - The drive by wire status (true/false). Published by the simulator.

The node uses the Controller class implemented in twist_controller.py to generate the throttle, brake and steering commands and publishes them to the /vehicle/throttle_cmd, /vehicle/brake_cmd, and /vehicle/steering_cmd topics. These are then used as input for the simulator.

Lateral Control

A Stanley controller is implemented in stanley.py to take care of the lateral motion control. The Stanley method is the path tracking approach used by Stanford University’s Darpa Grand Challenge team. The theoretical details of this controller can be found in [1].

The Stanley method uses the front axle as its reference point and simultaneously corrects the heading error ψ and the cross-track error e, while obeying the maximum steering angle bounds δmin and δmax . It requires the two gains k and ks, that typically need to be determined experimentally. After some tuning in the simulator the gains were chosen as follows:

K = 0.1
Ks = 1.0

Longitudinal Control

Two seperate PID controllers are implemented for the longitudinal motion control, one of which generates the throttle commands and the other the brake commands. The speed error between the target and current velocity of the vehicle determines which of both controllers is active. For instance, if the speed error is positive, i.e. the vehicle is too slow, the throttle controller is activated and vice versa. Throttle and brake commands cannot be applied simultaneously. A low pass filter is implemented to remove the high frequency noise from the incoming vehicle velocity.

The simulator requires the throttle commands to be normalized to [0, 1], wheras the brake commands must be provided as a physical torque value. The torque can be computed as T = m * a * r, with the vehicle mass m, the deceleration a, and the wheel radius r, whereby the control value returned by the brake controller is used as deceleration a.

After some tuning in the simulator the gains were chosen as follows:

# PID gains for the throttle controller
Kp = 0.3
Ki = 0.1
Kd = 0.0

# PID gains for the brake controller
Kp = 0.2
Ki = 0.0
Kd = 0.1

The integral gain of the throttle controller ensures a higher accuracy to maintain the target speed while driving. However, no integral gain was defined for the brake controller, since no residual brake demand is required when the speed target changes, e.g. due to a traffic light switching from red to green.

References

[1] Hoffmann, Gabriel M., Claire J. Tomlin, Michael Montemerlo, and Sebastian Thrun. Autonomous Automobile Trajectory Tracking for Off-Road Driving: Controller Design, Experimental Validation and Racing. American Control Conference, 2007, pp. 2296–2301