Implementing Bicycle Model: A Fundamental Concept in Self Driving Vehicles

In the development of self driving vehicles, a cornerstone concept that engineers rely on is the “bicycle model.” This abstract representation simplifies the complex dynamics of a car into a more manageable framework that resembles that of a bicycle. By breaking down the vehicle’s motion into lateral dynamics, the bicycle model offers a structured approach to predicting and controlling car behavior.

In this blog, I will provide Python implementation of this model to demonstrate its significance in understanding the fundamentals of self driving vehicles.

What is a Bicycle Model?

In automobile engineering, “bicycle model” is a model used to understand the lateral movement of the vehicle considering factors like yaw rate, lateral forces, steering angle etc. For the sake of simplification, the wheels on each axle are illustrated as a single wheel and assumed that the slip angle of acting on the wheels of the same axle have same value. Due to this assumption, the bicycle model is sufficient only for low-speed driving maneuvers. The figure below demonstrates an example of a bicycle model:

Important Terminologies

Here are a few terminologies to understand before proceeding:

  • Yaw rate: The angular speed with which the vehicle undergoes a particular turning maneuver.
  • Slip angle: Angle between the longitudinal axis of the vehicle and the direction in which the vehicle is moving (direction of velocity)
  • Steering angle rate: The angular speed with which the steering angle changes during the turning maneuver. When the driver aggressively turns the steering wheel, the steering angle rate also changes drastically. [Note: Steering angle rate is not to be confused with steering wheel angle rate. As an example, steering angle may vary from -25° to 25°, whereas steering wheel angle may vary from -450° to 450°].
  • Multiplying yaw rate and steering angle rate with sample time yields yaw angle and steering angle at that particular timestamp respectively.

Python Code

We will start by importing the necessary modules and creating a Bicycle class in Python.

import numpy as np
import matplotlib.pyplot as plt


class Bicycle:
    def __init__(self):
        self.xc = 0
        self.yc = 0
        self.yaw_angle = 0
        self.sample_time = 0.01
        self.slip_angle = 0
        self.l_r = 1.2 #m
        self.L = 2 #m
        self.max_steering_angle_rate = 1.22 #rad/s
        self.turn_radius = 1 #m
        self.steering_angle = np.arctan(self.L/self.turn_radius)
        
    def reset(self):
        self.xc = 0
        self.yc = 0
        self.yaw_angle = 0
        self.steering_angle = np.arctan(self.L/self.turn_radius)
        self.slip_angle = 0

Here, we have selected the sample time to be 0.01 (10 ms). The term l_r is the distance between the rear axle and the center of gravity of the vehicle, and L is the length of the wheelbase. I’ve limited the maximum steering angle rate to 1.22 rad/s. This is just an arbitrary value that I’ve chosen. Finally, I’ve declared the value of the desired value of steering angle. Looking at the illustration of the Bicycle model above, the formula for steering angle (denoted as \(\delta\)) can be derived using simple geometry. After initializing the values, I created the reset() method to reset the values back to zero.

def step(self, velocity, steering_angle_rate):
        if steering_angle_rate > self.max_steering_angle_rate:
            steering_angle_rate = self.max_steering_angle_rate
        elif steering_angle_rate < -self.max_steering_angle_rate:
            steering_angle_rate = -self.max_steering_angle_rate
        
        self.steering_angle += steering_angle_rate * self.sample_time
        self.slip_angle = np.arctan((self.l_r*np.tan(self.steering_angle))/self.L)
        yaw_rate = (velocity * np.cos(self.slip_angle)*np.tan(self.steering_angle))/self.L
        self.yaw_angle += yaw_rate * self.sample_time
            
        longitudinal_acc = velocity * np.cos(self.yaw_angle + self.slip_angle)
        lateral_acc = velocity * np.sin(self.yaw_angle + self.slip_angle)
        self.xc += longitudinal_acc * self.sample_time
        self.yc += lateral_acc * self.sample_time

One thing to note over here is that the slip angle is assumed to remain constant throughout the drive. Note this point as we will discuss this later in the example.

The step() method takes in the current velocity and steering angle rate as inputs and calculates the vehicle state. These values can be fed by reading data from the vehicle CAN bus. The formulae implemented here are defined as follows:

\(
longitudinal\ acceleration = velocity * \cos{(yaw\ angle + slip\ angle)} \\
lateral\ acceleration = velocity * \sin{(yaw\ angle + slip\ angle)} \\
yaw\ rate = \frac{velocity * \cos{(slip\ angle)} * \tan{(steering\ angle)}}{L} \\
slip\ angle = \tan^{-1}(\frac{l_r * \tan{(steering\ angle)}}{L})\)

Using this class, we can now create bicycle model that follows a path of desired form. As a simple example and to test the above code, I created a scenario where the vehicle moves in a circle.

Moving the Vehicle in Circular Path

In this example, we define a turn radius of 10 m. It is assumed that it will take 20 s for the vehicle to complete one circle. This results in the velocity holding the value of pi (\(speed = distance / time\) where the distance is \(2\pi r\)).

sample_time = 0.01
time_end = 20
model = Bicycle()
model.turn_radius = 10 #m
model.steering_angle = np.arctan(model.L/model.turn_radius)
velocity = 2*np.pi*model.turn_radius/time_end

# set delta directly
t_data = np.arange(0,time_end,sample_time)
x_data = np.zeros_like(t_data)
y_data = np.zeros_like(t_data)
x_solution = np.zeros_like(t_data)
y_solution = np.zeros_like(t_data)

for i in range(t_data.shape[0]):
    x_data[i] = model.xc
    y_data[i] = model.yc
    model.step(velocity, 0)
    
plt.plot(x_data, y_data)
plt.legend()
plt.show()

To keep things simple, I set the value of the steering angle rate to zero, which means that the vehicle moves through the circle with a constant steering angle. The result is the following path:

self driving vehicles

Now let us consider the factor of slip. Above I mentioned that the slip remains constant throughout the drive. If we assume that the slip adds up on every iteration, the path should drift away with every iteration. So let us try that.

Effect of Slip on the Estimated Path

In the Bicycle class, add a + sign to the slip_angle attribute as follows:

self.slip_angle += np.arctan((self.l_r*np.tan(self.steering_angle))/self.L)

To gain a better view of the path, we will reduce the variable time_end to just 2 seconds. Run the code again, and you should get the following result:

self driving vehicles

As expected, the vehicle drifts away from the first circular path due to the effect of slip.

Conclusion

In this blog, we developed a simple bicycle model in Python that is considered to be a fundamental concept in self driving vehicles. This helps us to understand the effect to factors like slip and different steering angle rates on the trajectory of the vehicle. Altering the values of velocity, steering angle rate and steering angle can result in different path forms. Feel free and try out different values of steering angle rate and velocity. Post your results on Instagram and feel free to tag me @machinelearningsite. Looking forward to see you interesting trajectory forms! Happy coding!

Get in touch with me on social media:

If you are interested in such interesting posts on self-driving technology, machine learning and programming, subscribe to my *FREE* newsletter where I remind you monthly about the posts you might have missed. In this constantly changing world, it’s always necessary to keep learning new stuff, isn’t it?

Leave a Reply