The very first passenger to ride in one of our self-driving taxis was a blind woman named Bev. When asked about the drive, Bev said she felt safe—that the ride felt much smoother than some vehicles driven by sighted people. At Voyage, we want every passenger to feel that same level of safety and comfort.
As a small startup competing against many larger organizations working on autonomous driving technology, we want to iterate as quickly as possible. One of our team’s goals is to minimize the time between exploring ideas on the whiteboard and getting those ideas onto the road. To achieve that goal, we focused our efforts, scoping our first taxi service to operations in small communities (Figure 1), and refined our design through multiple iterations. We used Docker containers to manage system dependencies and the Robot Operating System (ROS) as the middleware for perception, motion planning, and controls. Instead of manually coding the model predictive control (MPC) algorithms for the longitudinal control system, we used Model-Based Design with MATLAB® and Simulink®.
Our team of three engineers completed the initial braking and acceleration control system in just two months.
Bounding the Complexity of the Self-Driving Car
Self-driving cars incorporate multiple complex systems to sense the surrounding environment, plan a path to a destination, and control steering and speed (Figure 2). Compounding the challenge of designing and implementing these systems are all the objects and hazards in the environment, which include intersections, crosswalks, roundabouts, construction activity, pedestrians, U-turns, one-way streets, animals, and speed limits, not to mention the unpredictable driving patterns of other vehicles.
To simplify the control design challenge, we decided to deploy our first self-driving taxis in strategic partner retirement communities (Figure 3). Not only are these communities well-mapped and clearly defined, they also have set speed limits, typically 25 mph (40 kph).
Jumpstarting Development with an Adaptive Cruise Control System Example
First, our team researched ways to safely implement longitudinal control as rapidly as possible. We decided to begin with the MATLAB adaptive cruise control (ACC) system example. This example includes a Simulink model that uses MPC to implement an ACC system capable of maintaining a set speed or a set distance from a lead vehicle (Figure 4).
After downloading this model and running some preliminary simulations in Simulink, I generated C++ code from the model for a standalone ROS node with Robotics System Toolbox™ and Simulink Coder™. All the software for our self-driving taxi is modular, and each subsystem—perception, path planning, and longitudinal control, among others—runs as a ROS node. Within three days we were running the generated code for the ACC in our vehicle.
Creating Our Own Model Predictive Controller from the Ground Up
While the ACC Simulink model had potential, it could not meet all our requirements. For example, the vehicle was too jerky when starting and stopping, and we found that riders are especially sensitive to this type of motion. (A passenger in our taxi will not necessarily feel how well the detection and perception algorithms are working, but they will immediately feel how well the longitudinal control works.)
We went back to the drawing board and designed a system from the ground up, quite literally going to a whiteboard and creating a kinematic model that describes the motion of the taxi based on first principles. We implemented this kinematic model in Simulink, using it as a foundation for the controller design. We then modified the parameters of the MPC model to meet our requirements and incorporated additional logic to handle edge cases and scenarios that the original MPC model handled suboptimally, such as stop-and-go driving.
In these early stages of development, we imported gigabytes of data from rosbag log files into the MATLAB environment with Robotics System Toolbox, and filtered out all ROS topics not relevant to the longitudinal controller. Once the data was imported, we could access it like any other MATLAB variable, which made it easy to analyze and work with.
We simulated the control model in Simulink to make sure that its output, accelerator pedal position, and braking pedal position looked reasonable and that the model behaved as we expected for our target sets of inputs.
Conducting In-Vehicle Tests
The simulations gave us enough confidence in our control design to try it out in the car, with our team as the first passengers. We generated C++ code from the redesigned control model for a ROS node and deployed the node to the vehicle within a Docker container. Docker enabled us to create an image of our production environment with all the necessary dependencies and then maintain and replicate that image consistently throughout development and testing.
During the initial in-vehicle tests, it was immediately apparent that our controller was too aggressive with acceleration and braking. Although the graphs we plotted during simulations showed what looked like smooth changes in velocity, the actual riding experience was anything but smooth. This realization highlighted for us the importance of quickly going from concept to onroad tests with Model-Based Design. We simply could not judge the quality of our design well enough in the lab; we had to experience it as our passengers would, in the car.
We completed several design iterations, tuning parameters and constraints, including limits on acceleration and jerk, as well as time constants and the rate at which outputs from the MPC were updated. We set up ROS parameters in the Simulink model to make it easier for our colleagues to calibrate parameters directly via ROS. They could quickly update parameter values even if they had no prior experience with Simulink.
Creating Virtual Vehicles to Test Braking Scenarios
Because it would be unsafe to test scenarios in which another vehicle swerves into our vehicle’s lane, we created a new type of ROS node to simulate a ghost barrier—essentially, a virtual vehicle that we could position at various distances from the taxi. We created this virtual vehicle in Simulink and parameterized it so that we could, for example, have it start at zero velocity and gradually increase speed. We generated code for the ROS node with Simulink Coder and then used the node to test and tune the controller's braking performance. With this node, which took only a few hours to develop, we could generate virtual obstacles in front of our taxi to see how it would respond, and then adjust its performance until it stopped safely and smoothly.
On the Road
The longitudinal controller we developed using Model-Based Design is in operation in self-driving taxis in the retirement communities that Voyage serves. We are seeing increased demand, with usage growing by 10% each week. Our engineering team is learning from data gathered during these rides, and we continue to refine the controller by incorporating what we learn.