Vehicle Dynamics Simulator (Barc Gym)
Graduate Student Instructor Project (Jan 2025 — May 2025)
- Developed a lightweight vehicle dynamics simulator based on the dynamic bicycle model, providing standard OpenAI Gymnasium APIs for reinforcement learning and control tasks.
- Implemented a connector to interface with the CARLA simulator, allowing seamless transition between simple dynamics models and high-fidelity physics simulations.
- Created automated shell scripts for quick installation and setup.
- Deployed the simulator on Google Colab to provide easy access for students in the vehicle dynamics class.
Vehicle_Dynamics_Simulator.ipynb
Python · Kernel: Python 3 · nbformat 4 · 40 cells
Vehicle Dynamics Simulator
Contributors: Shengfan Cao, Edward Zhu, Thomas Fork.
%load_ext autoreload
%autoreload 2Install Dependencies
if "dependencies_installed" not in globals():
!git clone https://github.com/MPC-Berkeley/barc_gym.git --depth 1
!pip install -r barc_gym/requirements.txt
!pip install -e barc_gym/gym-carla
!pip install -e barc_gym/mpclab_common
!pip install -e barc_gym/mpclab_controllers
!pip install -e barc_gym/mpclab_simulation
dependencies_installed = TrueCloning into 'barc_gym'...
remote: Enumerating objects: 73, done.[K
remote: Counting objects: 100% (73/73), done.[K
remote: Compressing objects: 100% (62/62), done.[K
remote: Total 73 (delta 7), reused 47 (delta 4), pack-reused 0 (from 0)[K
Receiving objects: 100% (73/73), 94.97 KiB | 2.97 MiB/s, done.
Resolving deltas: 100% (7/7), done.
Requirement already satisfied: numpy in /usr/local/lib/python3.11/dist-packages (from -r barc_gym/requirements.txt (line 2)) (2.0.2)
Requirement already satisfied: pyomo in /usr/local/lib/python3.11/dist-packages (from -r barc_gym/requirements.txt (line 3)) (6.8.2)
Collecting casadi (from -r barc_gym/requirements.txt (line 4))
Downloading casadi-3.7.0-cp311-none-manylinux2014_x86_64.whl.metadata (2.2 kB)
Requirement already satisfied: matplotlib in /usr/local/lib/python3.11/dist-packages (from -r barc_gym/requirements.txt (line 5)) (3.10.0)
Requirement already satisfied: pyyaml in /usr/local/lib/python3.11/dist-packages (from -r barc_gym/requirements.txt (line 6)) (6.0.2)
Requirement already satisfied: tqdm in /usr/local/lib/python3.11/dist-packages (from -r barc_gym/requirements.txt (line 7)) (4.67.1)
Requirement already satisfied: pillow in /usr/local/lib/python3.11/dist-packages (from -r barc_gym/requirements.txt (line 8)) (11.1.0)
Collecting loguru (from -r barc_gym/requirements.txt (line 9))
Downloading loguru-0.7.3-py3-none-any.whl.metadata (22 kB)
Requirement already satisfied: requests in /usr/local/lib/python3.11/dist-packages (from -r barc_gym/requirements.txt (line 10)) (2.32.3)
Requirement already satisfied: gymnasium in /usr/local/lib/python3.11/dist-packages (from -r barc_gym/requirements.txt (line 12)) (1.1.1)
Requirement already satisfied: pygame in /usr/local/lib/python3.11/dist-packages (from -r barc_gym/requirements.txt (line 13)) (2.6.1)
Requirement already satisfied: scikit-image in /usr/local/lib/python3.11/dist-packages (from -r barc_gym/requirements.txt (line 14)) (0.25.2)
Requirement already satisfied: ply in /usr/local/lib/python3.11/dist-packages (from pyomo->-r barc_gym/requirements.txt (line 3)) (3.11)
Requirement already satisfied: contourpy>=1.0.1 in /usr/local/lib/python3.11/dist-packages (from matplotlib->-r barc_gym/requirements.txt (line 5)) (1.3.2)
Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.11/dist-packages (from matplotlib->-r barc_gym/requirements.txt (line 5)) (0.12.1)
Requirement already satisfied: fonttools>=4.22.0 in /usr/local/lib/python3.11/dist-packages (from matplotlib->-r barc_gym/requirements.txt (line 5)) (4.57.0)
Requirement already satisfied: kiwisolver>=1.3.1 in /usr/local/lib/python3.11/dist-packages (from matplotlib->-r barc_gym/requirements.txt (line 5)) (1.4.8)
Requirement already satisfied: packaging>=20.0 in /usr/local/lib/python3.11/dist-packages (from matplotlib->-r barc_gym/requirements.txt (line 5)) (24.2)
Requirement already satisfied: pyparsing>=2.3.1 in /usr/local/lib/python3.11/dist-packages (from matplotlib->-r barc_gym/requirements.txt (line 5)) (3.2.3)
Requirement already satisfied: python-dateutil>=2.7 in /usr/local/lib/python3.11/dist-packages (from matplotlib->-r barc_gym/requirements.txt (line 5)) (2.8.2)
Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.11/dist-packages (from requests->-r barc_gym/requirements.txt (line 10)) (3.4.1)
Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.11/dist-packages (from requests->-r barc_gym/requirements.txt (line 10)) (3.10)
Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.11/dist-packages (from requests->-r barc_gym/requirements.txt (line 10)) (2.3.0)
Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.11/dist-packages (from requests->-r barc_gym/requirements.txt (line 10)) (2025.1.31)
Requirement already satisfied: cloudpickle>=1.2.0 in /usr/local/lib/python3.11/dist-packages (from gymnasium->-r barc_gym/requirements.txt (line 12)) (3.1.1)
Requirement already satisfied: typing-extensions>=4.3.0 in /usr/local/lib/python3.11/dist-packages (from gymnasium->-r barc_gym/requirements.txt (line 12)) (4.13.2)
Requirement already satisfied: farama-notifications>=0.0.1 in /usr/local/lib/python3.11/dist-packages (from gymnasium->-r barc_gym/requirements.txt (line 12)) (0.0.4)
Requirement already satisfied: scipy>=1.11.4 in /usr/local/lib/python3.11/dist-packages (from scikit-image->-r barc_gym/requirements.txt (line 14)) (1.14.1)
Requirement already satisfied: networkx>=3.0 in /usr/local/lib/python3.11/dist-packages (from scikit-image->-r barc_gym/requirements.txt (line 14)) (3.4.2)
Requirement already satisfied: imageio!=2.35.0,>=2.33 in /usr/local/lib/python3.11/dist-packages (from scikit-image->-r barc_gym/requirements.txt (line 14)) (2.37.0)
Requirement already satisfied: tifffile>=2022.8.12 in /usr/local/lib/python3.11/dist-packages (from scikit-image->-r barc_gym/requirements.txt (line 14)) (2025.3.30)
Requirement already satisfied: lazy-loader>=0.4 in /usr/local/lib/python3.11/dist-packages (from scikit-image->-r barc_gym/requirements.txt (line 14)) (0.4)
Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.11/dist-packages (from python-dateutil>=2.7->matplotlib->-r barc_gym/requirements.txt (line 5)) (1.17.0)
Downloading casadi-3.7.0-cp311-none-manylinux2014_x86_64.whl (77.3 MB)
[2K [90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━[0m [32m77.3/77.3 MB[0m [31m9.8 MB/s[0m eta [36m0:00:00[0m
[?25hDownloading loguru-0.7.3-py3-none-any.whl (61 kB)
[2K [90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━[0m [32m61.6/61.6 kB[0m [31m3.5 MB/s[0m eta [36m0:00:00[0m
[?25hInstalling collected packages: loguru, casadi
Successfully installed casadi-3.7.0 loguru-0.7.3
Obtaining file:///content/barc_gym/gym-carla
Preparing metadata (setup.py) ... [?25l[?25hdone
Requirement already satisfied: gymnasium in /usr/local/lib/python3.11/dist-packages (from gym_carla==0.0.1) (1.1.1)
Requirement already satisfied: pygame in /usr/local/lib/python3.11/dist-packages (from gym_carla==0.0.1) (2.6.1)
Requirement already satisfied: numpy>=1.21.0 in /usr/local/lib/python3.11/dist-packages (from gymnasium->gym_carla==0.0.1) (2.0.2)
Requirement already satisfied: cloudpickle>=1.2.0 in /usr/local/lib/python3.11/dist-packages (from gymnasium->gym_carla==0.0.1) (3.1.1)
Requirement already satisfied: typing-extensions>=4.3.0 in /usr/local/lib/python3.11/dist-packages (from gymnasium->gym_carla==0.0.1) (4.13.2)
Requirement already satisfied: farama-notifications>=0.0.1 in /usr/local/lib/python3.11/dist-packages (from gymnasium->gym_carla==0.0.1) (0.0.4)
Installing collected packages: gym_carla
Running setup.py develop for gym_carla
Successfully installed gym_carla-0.0.1
Obtaining file:///content/barc_gym/mpclab_common
Preparing metadata (setup.py) ... [?25l[?25hdone
Installing collected packages: mpclab_common
Running setup.py develop for mpclab_common
Successfully installed mpclab_common-0.1
Obtaining file:///content/barc_gym/mpclab_controllers
Preparing metadata (setup.py) ... [?25l[?25hdone
Installing collected packages: mpclab_controllers
Running setup.py develop for mpclab_controllers
Successfully installed mpclab_controllers-0.1
Obtaining file:///content/barc_gym/mpclab_simulation
Preparing metadata (setup.py) ... [?25l[?25hdone
Installing collected packages: mpclab_simulation
Running setup.py develop for mpclab_simulation
Successfully installed mpclab_simulation-0.1
import site
import warnings
site.main()
warnings.filterwarnings("ignore", category=UserWarning)
warnings.filterwarnings("ignore", category=RuntimeWarning)import numpy as np
import gymnasium as gym
import gym_carla
from matplotlib import pyplot as plt
from loguru import loggerPart 0: Defining the base PID controller
**TODO: Reference Slides (slide 32) or the book (chapter 5.1) and lay out the formula. **
Preliminary: Vehicle State definition
We follow the same vehicle state definition as the ROS messages we use in the hardware experiments. The following snippet shows the definition of fields that are the most relevant to your task.
@dataclass
class Position(PythonMsg):
x: float = field(default=0)
y: float = field(default=0)
z: float = field(default=0)
...
@dataclass
class BodyLinearVelocity(PythonMsg):
v_long: float = field(default=0)
v_tran: float = field(default=0)
v_n: float = field(default=0)
@dataclass
class BodyAngularVelocity(PythonMsg):
w_phi: float = field(default=0)
w_theta: float = field(default=0)
w_psi: float = field(default=0)
@dataclass
class OrientationEuler(PythonMsg):
phi: float = field(default=0)
theta: float = field(default=0)
psi: float = field(default=0)
@dataclass
class ParametricPose(PythonMsg):
s: float = field(default=0)
x_tran: float = field(default=0)
n: float = field(default=0)
e_psi: float = field(default=0)
@dataclass
class VehicleActuation(PythonMsg):
t: float = field(default=0)
u_a: float = field(default=0)
u_steer: float = field(default=0)
...
@dataclass
class VehicleState(PythonMsg):
'''
Complete vehicle state (local, global, and input)
'''
t: float = field(default=None) # time in seconds
x: Position = field(default=None) # global position
v: BodyLinearVelocity = field(default=None) # body linear velocity
w: BodyAngularVelocity = field(default=None) # body angular velocity
e: OrientationEuler = field(default=None) # global orientation (phi, theta, psi)
p: ParametricPose = field(default=None) # parametric position (s, y, ths)
u: VehicleActuation = field(default=None) # control inputs (u_a, u_steer)
lap_num: int = field(default=None)
...
Use the dot notation to access an attribute of the data structure above. For example, to access the global y coordinate given a state object vehicle_state, you should do vehicle_state.x.y.
Defining the PID Parameters
from mpclab_common.pytypes import PythonMsg
from dataclasses import dataclass, field
@dataclass
class PIDParams(PythonMsg):
"""
This is a template for PID Parameters.
Make sure you understand what each parameter means,
but DON'T MODIFY ANYTHING HERE!
"""
dt: float = field(default=0.1) # The frequency of the controller
Kp: float = field(default=2.0) # The P component.
Ki: float = field(default=0.0) # The I component.
Kd: float = field(default=0.0) # The D component.
# Constraints
int_e_max: float = field(default=100)
int_e_min: float = field(default=-100)
u_max: float = field(default=None)
u_min: float = field(default=None)
du_max: float = field(default=None)
du_min: float = field(default=None)
# References
u_ref: float = field(default=0.0) # Input reference
x_ref: float = field(default=0.0) # PID state referenceImplementing the base class for PID controller
TODO: Reference the slides or the book.
from typing import Tuple
class PID():
'''
Base class for PID controller
Meant to be packaged for use in actual controller (eg. ones that operate directly on vehicle state) since a PID controller by itself is not sufficient for vehicle control
See PIDLaneFollower for a PID controller that is an actual controller
'''
def __init__(self, params: PIDParams = PIDParams()):
self.dt = params.dt
self.Kp = params.Kp # proportional gain
self.Ki = params.Ki # integral gain
self.Kd = params.Kd # derivative gain
# Integral action and control action saturation limits
self.int_e_max = params.int_e_max
self.int_e_min = params.int_e_min
self.u_max = params.u_max
self.u_min = params.u_min
self.du_max = params.du_max
self.du_min = params.du_min
self.x_ref = params.x_ref # PID State reference
self.u_ref = params.u_ref # Input reference
self.u_prev = 0 # Internal buffer for previous input
self.e = 0 # error
self.de = 0 # finite time error difference
self.ei = 0 # accumulated error
self.initialized = True
def solve(self, x: float,
u_prev: float = None) -> Tuple[float, dict]:
if not self.initialized:
raise(RuntimeError('PID controller is not initialized, run PID.initialize() before calling PID.solve()'))
if self.u_prev is None and u_prev is None: u_prev = 0
elif u_prev is None: u_prev = self.u_prev
info = {'success' : True}
# Compute error terms
e_t = x - self.x_ref
de_t = (e_t - self.e)/self.dt
ei_t = self.ei + e_t*self.dt
# Anti-windup
if ei_t > self.int_e_max:
ei_t = self.int_e_max
elif ei_t < self.int_e_min:
ei_t = self.int_e_min
# Compute control action terms
P_val = self.Kp * e_t
I_val = self.Ki * ei_t
D_val = self.Kd * de_t
u = -(P_val + I_val + D_val) + self.u_ref
# Compute change in control action from previous timestep
du = u - u_prev
# Saturate change in control action
if self.du_max is not None:
du = self._saturate_rel_high(du)
if self.du_min is not None:
du = self._saturate_rel_low(du)
u = du + u_prev
# Saturate absolute control action
if self.u_max is not None:
u = self._saturate_abs_high(u)
if self.u_min is not None:
u = self._saturate_abs_low(u)
# Update error terms
self.e = e_t
self.de = de_t
self.ei = ei_t
self.u_prev = u
return u, info
def set_x_ref(self, x_ref: float):
self.x_ref = x_ref
# reset error integrator
self.ei = 0
# reset error, otherwise de/dt will skyrocket
self.e = 0
def set_u_ref(self, u_ref: float):
self.u_ref = u_ref
def clear_errors(self):
self.ei = 0
self.de = 0
def set_params(self, params: PIDParams):
self.dt = params.dt
self.Kp = params.Kp # proportional gain
self.Ki = params.Ki # integral gain
self.Kd = params.Kd # derivative gain
# Integral action and control action saturation limits
self.int_e_max = params.int_e_max
self.int_e_min = params.int_e_min
self.u_max = params.u_max
self.u_min = params.u_min
self.du_max = params.du_max
self.du_min = params.du_min
# Below are helper functions that might be helpful in debugging.
def get_refs(self) -> Tuple[float, float]:
return (self.x_ref, self.u_ref)
def get_errors(self) -> Tuple[float, float, float]:
return (self.e, self.de, self.ei)
# Below are clipping functions to enforce hard constraints.
def _saturate_abs_high(self, u: float) -> float:
return np.minimum(u, self.u_max)
def _saturate_abs_low(self, u: float) -> float:
return np.maximum(u, self.u_min)
def _saturate_rel_high(self, du: float) -> float:
return np.minimum(du, self.du_max)
def _saturate_rel_low(self, du: float) -> float:
return np.maximum(du, self.du_min)Part 1: Implementing and tuning a PID lane follower
TODO: Introductions. Example:
In this implementation, we follow a simple idea to control the speed and steering control separately by designing two separate PID controllers.
Specifically, the speed control PID tries to follow a reference speed, while the steering control PID tries to minimize the lateral deviation from the reference path.
Note that this PID controller only follows the center line of the track at a constant velocity. Later, we will implement a better controller that can follow a race line.
from mpclab_controllers.abstract_controller import AbstractController
from mpclab_common.pytypes import VehicleState
class PIDLaneFollower(AbstractController):
'''
Class for PID throttle and steering control of a vehicle
Incorporates separate PID controllers for maintaining a constant speed and a constant lane offset
target speed: v_ref
target lane offset_ x_ref
'''
def __init__(self, dt: float,
steer_pid_params: PIDParams = None,
speed_pid_params: PIDParams = None):
if steer_pid_params is None:
steer_pid_params = PIDParams()
steer_pid_params.dt = dt
steer_pid_params.default_steer_params()
if speed_pid_params is None:
speed_pid_params = PIDParams()
speed_pid_params.dt = dt
speed_pid_params.default_speed_params() # these may use dt so it is updated first
self.dt = dt
steer_pid_params.dt = dt
speed_pid_params.dt = dt
self.steer_pid_params = steer_pid_params
self.speed_pid_params = speed_pid_params
self.steer_pid = PID(self.steer_pid_params)
self.speed_pid = PID(self.speed_pid_params)
self.lat_ref = steer_pid_params.x_ref
self.steer_pid.set_x_ref(0) # For simplicity, we will just follow the center line in this problem. DO NOT MODIFY!
self.requires_env_state = False
return
def reset(self):
# Reinstantiate the two PID controllers.
self.steer_pid = PID(self.steer_pid_params)
self.speed_pid = PID(self.speed_pid_params)
def initialize(self, **args):
return
def solve(self, *args, **kwargs):
raise NotImplementedError
def step(self, vehicle_state: VehicleState, env_state = None):
v = vehicle_state.v.v_long
vehicle_state.u.u_a, _ = self.speed_pid.solve(v)
# Weighting factor: alpha*x_trans + beta*psi_diff
alpha = 5.0
beta = 1.0
vehicle_state.u.u_steer, _ = self.steer_pid.solve(alpha*(vehicle_state.p.x_tran - self.lat_ref) + beta*vehicle_state.p.e_psi)
return np.array([vehicle_state.u.u_a, vehicle_state.u.u_steer])
def get_prediction(self):
return None
def get_safe_set(self):
return NoneCreate an instance of your PID controller with the parameters
Initialize/Reset your controller. If you decide to implement your own controller, it must have a step function with the following method signature:
def step(state: VehicleState) -> np.ndarray:
...
Also, make sure your controller also modify the action fields (state.u.u_a and state.u.u_steer) in place, so that it will work in the hardware experiments later.
#@markdown ## Set PID parameters
#@markdown ### Steering PID Parameters
Kp_steer = 0.85 #@param {type:"slider", min:0.0, max:2.0, step:0.05}
Ki_steer = 0 #@param {type:"slider", min:0.0, max:2.0, step:0.05}
Kd_steer = 0.05 #@param {type:"slider", min:0.0, max:2.0, step:0.05}
#@markdown ### Speed PID Parameters
Kp_speed = 0.3 #@param {type:"slider", min:0.0, max:2.0, step:0.05}
Ki_speed = 0 #@param {type:"slider", min:0.0, max:2.0, step:0.05}
Kd_speed = 0 #@param {type:"slider", min:0.0, max:2.0, step:0.05}
reference_speed = 1.0 #@param
# Some other global parameters. Don't modify them!
seed = 42
dt = 0.1pid_steer_params = PIDParams(dt=dt,
Kp=Kp_steer, # Use the parameters from the sliders above.
Ki=Ki_steer,
Kd=Kd_steer,
u_max=0.436,
u_min=-0.436,
du_max=4.5,
du_min=-4.5,
x_ref=0.0, # Offset from the center line. Set to 0 to follow the center line.
)
pid_speed_params = PIDParams(dt=dt,
Kp=Kp_speed,
Ki=Ki_speed,
Kd=Kd_speed,
u_max=2.0,
u_min=-2.0,
du_max=20.0,
du_min=-20.0,
x_ref=reference_speed, # Reference speed.
)
pid_controller = PIDLaneFollower(dt, pid_steer_params, pid_speed_params)Simulate the controller
The environment is implemented following the standard definition of OpenAI gym.
Explanation of the parameters:
truncatedindicates whether it is at a terminal state (collision with the boundary, going too slow (slower than 0.25 m/s), going in the wrong way, or finished the requested number of laps)terminatedindicates whether the car just finished a lap.infois a dictionary that contains the ground truth vehicle_state and other helpful debugging information.
# Create an instance of the simulator.
env = gym.make('barc-v0',
track_name='L_track_barc',
do_render=True,
max_n_laps=2, # Feel free to modify this.
in_colab=True,
)
env.unwrapped.bind_controller(pid_controller)
# Reset the car to the starting line, with an initial velocity of 0.5 m/s.
_, info = env.reset(seed=seed, options={'spawning': 'fixed'})
truncated = False # Flag indicating whether the car is at a terminal state.
# Reset the PID controller (clear the errors).
pid_controller.reset()
lap_time = [] # Keep track of the lap times.
while not truncated:
action = pid_controller.step(vehicle_state=info['vehicle_state']) # Call the controller to get the steering and speed commands.
_, _, terminated, truncated, info = env.step(action) # Apply the action on the car and get the updated state.
if terminated:
lap_time.append(info['lap_time']) # Keep track of the time it took to finish a lap.
logger.info(f"Average lap time: {np.mean(lap_time):.1f} s. Std: {np.std(lap_time):.1f} s.")
logger.info("Rollout truncated.")[32m2025-04-22 22:16:25.310[0m | [34m[1mDEBUG [0m | [36mgym_carla.envs.barc.barc_env[0m:[36mreset[0m:[36m181[0m - [34m[1mRespawning at fixed location.[0m
[32m2025-04-22 22:16:33.717[0m | [1mINFO [0m | [36mgym_carla.envs.barc.barc_env[0m:[36mstep[0m:[36m258[0m - [1mLap 0 finished in 19.8 s. avg_v = 0.8974, max_v = 0.9836, min_v = 0.5000[0m
[32m2025-04-22 22:16:40.149[0m | [1mINFO [0m | [36mgym_carla.envs.barc.barc_env[0m:[36mstep[0m:[36m258[0m - [1mLap 1 finished in 18.3 s. avg_v = 0.9733, max_v = 0.9895, min_v = 0.9358[0m
[32m2025-04-22 22:16:40.181[0m | [1mINFO [0m | [36m__main__[0m:[36m<cell line: 0>[0m:[36m27[0m - [1mAverage lap time: 19.1 s. Std: 0.8 s.[0m
[32m2025-04-22 22:16:40.185[0m | [1mINFO [0m | [36m__main__[0m:[36m<cell line: 0>[0m:[36m28[0m - [1mRollout truncated.[0m
Playback of the race
This cell generates a matplotlib figure of the trajectory and statistics of the race that is just simulated. Use this to debug and tune your controller!
env.unwrapped.show_debug_plot()Animation (Optional)
Render an animation of the race.
Note that this may take a long time (about 4 frames per second). (FYI the simulation runs at 10 frames per second)
The playback speed is 2x compared to real time.
render_animation = False #@param {type:"boolean"}if render_animation:
from IPython.display import HTML
animation = env.unwrapped.visualizer.get_animation()
print(f"Please wait for the animation to be rendered. Estimated total wait time: {len(env.unwrapped.v_buffer) * 0.25:.1f} s. ")
display(HTML(animation.to_html5_video()))Compare with hardware data (Optional)
Warning: Upload the csv file converted from the ROS bag that is generated on your hardware test first!
Replace TODO with your group name. Your csv file should have the same name.
compare_with_hardware_data = False #@param {type:"boolean"}
group_name = "group_1" #@param {type:"string"}if compare_with_hardware_data:
import pandas as pd
from google.colab import files
import os
while not os.path.exists(f'{group_name}.csv'):
print(f"Data file {group_name}.csv not found. Please upload it here.")
uploaded = files.upload()
df = pd.read_csv(f'{group_name}.csv', delimiter=',')
# df = pd.read_csv(f'{group_name}.csv', delimiter=',')
fig, axes = plt.subplots(2, 2, figsize=(9, 9))
((ax_traj, ax_v), (ax_u_a, ax_u_steer)) = axes
start_idx = np.where((df['u_a'] != 0) & (df['u_steer'] != 0) & (df['v_long'] >= 0.5))[0][0]
t = df['t'][start_idx:]
t = (t - t.min()) / 1e9
env.unwrapped.get_track().plot_map(ax=ax_traj)
ax_traj.plot(df['x'][start_idx:], df['y'][start_idx:], label='hardware')
ax_traj.set_title("Trajectory Playback")
ax_traj.set_xlabel('x(m)')
ax_traj.set_ylabel('y(m)')
ax_v.plot(t, df['v_long'][start_idx:], label='hardware')
ax_v.set_title("Velocity playback")
ax_v.set_xlabel('t(s)')
ax_v.set_ylabel('v(m/s)')
ax_u_a.plot(t, df['u_a'][start_idx:], label='hardware')
ax_u_a.set_title("Acceleration input playback")
ax_u_a.set_xlabel('t(s)')
ax_u_a.set_ylabel('$u_a$')
ax_u_steer.plot(t, df['u_steer'][start_idx:], label='hardware')
ax_u_steer.set_title('Steering input playback')
ax_u_steer.set_xlabel('t(s)')
ax_u_steer.set_ylabel('$u_{steer}$')
env.unwrapped.show_debug_plot(axes)
for ax in axes.flatten():
ax.legend()
plt.show()Part 2: Raceline Tracking experiment
**TODO:**Introduction.
Example: Now we redesign the PID controller to track any reference, so that it can follow the raceline we derived and go in high speed.
%%writefile my_script.py
from mpclab_controllers.abstract_controller import AbstractController
from mpclab_common.pytypes import VehicleState
class PIDRacelineFollower(AbstractController):
'''
Class for PID throttle and steering control of a vehicle
Incorporates separate PID controllers for maintaining a constant speed and a constant lane offset
target speed: v_ref
target lane offset_ x_ref
'''
def __init__(self, dt: float,
steer_pid_params: PIDParams = None,
speed_pid_params: PIDParams = None):
if steer_pid_params is None:
steer_pid_params = PIDParams()
steer_pid_params.dt = dt
steer_pid_params.default_steer_params()
if speed_pid_params is None:
speed_pid_params = PIDParams()
speed_pid_params.dt = dt
speed_pid_params.default_speed_params() # these may use dt so it is updated first
self.dt = dt
steer_pid_params.dt = dt
speed_pid_params.dt = dt
self.steer_pid_params = steer_pid_params
self.speed_pid_params = speed_pid_params
self.steer_pid_params.x_ref = 0 # TODO: This is temporary. Properly implement this.
self.speed_pid_params.x_ref = 0 # TODO: This is temporary. Properly implement this.
self.steer_pid = PID(self.steer_pid_params)
self.speed_pid = PID(self.speed_pid_params)
self.lat_ref = steer_pid_params.x_ref
self.requires_env_state = False
return
def reset(self):
# Reinstantiate the two PID controllers.
self.steer_pid = PID(self.steer_pid_params)
self.speed_pid = PID(self.speed_pid_params)
def initialize(self, **args):
return
def solve(self, *args, **kwargs):
raise NotImplementedError
def step(self, vehicle_state: VehicleState, reference=None):
# """
# Reference solution (needs more test to get it to work):
v_ref, x_tran_ref, psi_ref = reference['x0'], reference['x5'], reference['x3']
u0_ref, u1_ref = reference['u0'], reference['u1']
v = vehicle_state.v.v_long
vehicle_state.u.u_a, _ = self.speed_pid.solve(v - v_ref)
# Weighting factor: alpha*x_trans + beta*psi_diff
alpha = 5.0
beta = 1.0
vehicle_state.u.u_steer, _ = self.steer_pid.solve(alpha*(vehicle_state.p.x_tran - x_tran_ref) + beta* (vehicle_state.p.e_psi - psi_ref))
return np.array([vehicle_state.u.u_a, vehicle_state.u.u_steer])
# """
raise NotImplementedError
# ============YOUR CODE HERE===============
vehicle_state.u.u_a = TODO
vehicle_state.u.u_steer = TODO
# ============END YOUR CODE================
return np.array([vehicle_state.u.u_a, vehicle_state.u_steer])
def get_prediction(self):
return None
def get_safe_set(self):
return NoneOverwriting my_script.py
from google.colab import files
files.download('my_script.py')<IPython.core.display.Javascript object><IPython.core.display.Javascript object>import numpy as np
import os
from google.colab import files
class Raceline:
def __init__(self, raceline_file='raceline.npz'):
while not os.path.exists(raceline_file):
print(f"Raceline data file {raceline_file} is not found. Please upload it here. ")
uploaded = files.upload()
data = np.load(raceline_file)
self.s, self.ey, self.epsi, self.vx, self.vy, self.epsi_dot, self.t, self.u0, self.u1 = map(np.array, (data['s'], data['e_y'], data['e_psi'], data['v_long'], data['v_tran'], data['psidot'], data['t'], data['u_a'], data['u_s']))
def get_reference(self, _s, speed_scaling=1.):
# interp = lambda yp: np.interp(_s, self.s, yp)
# ey_ref, epsi_ref, vx_ref, vy_ref, epsi_dot_ref, t_ref, u0_ref, u1_ref = map(interp, (self.ey, self.epsi, self.vx, self.vy, self.epsi_dot, self.t, self.u0, self.u1))
ey_ref = np.interp(_s, self.s, self.ey)
epsi_ref = np.interp(_s, self.s, self.epsi)
vx_ref = np.interp(_s, self.s, self.vx) * speed_scaling # TODO: This is just a trick to scale down the speed to make the raceline easier to track. PROPERLY IMPLEMENT THIS!
vy_ref = np.interp(_s, self.s, self.vy)
epsi_dot_ref = np.interp(_s, self.s, self.epsi_dot)
t_ref = np.interp(_s, self.s, self.t)
u0_ref = np.interp(_s, self.s, self.u0)
u1_ref = np.interp(_s, self.s, self.u1)
return {
's': _s,
'x0': vx_ref,
'x1': vy_ref,
'x2': epsi_dot_ref,
'x3': epsi_ref,
'x4': t_ref,
'x5': ey_ref,
'u0': u0_ref,
'u1': u1_ref,
}
def plot_raceline(self, ax):
svec = self.s
Psi = self.epsi[0]
X, Y = [0], [self.ey[0]]
for j in range(1,len(svec)):
sj = svec[j]
deltaT = self.t[j] - self.t[j-1]
Psi = Psi + deltaT * self.epsi_dot[j]
X.append(X[j-1] + deltaT * (self.vx[j] * np.cos(Psi) - self.vy[j] * np.sin(Psi)))
Y.append(Y[j-1] + deltaT * (self.vx[j] * np.sin(Psi) + self.vy[j] * np.cos(Psi)))
ax.plot(X, Y, c='r', ls='--', label='raceline')
Raceline().get_reference(1) # TODO: Remove this debugging command.Raceline data file raceline.npz is not found. Please upload it here.
Raceline data file raceline.npz is not found. Please upload it here.
[0;31m---------------------------------------------------------------------------[0m
[0;31mKeyboardInterrupt[0m Traceback (most recent call last)
[0;32m<ipython-input-21-b526ae40bded>[0m in [0;36m<cell line: 0>[0;34m()[0m
[1;32m 48[0m [0max[0m[0;34m.[0m[0mplot[0m[0;34m([0m[0mX[0m[0;34m,[0m [0mY[0m[0;34m,[0m [0mc[0m[0;34m=[0m[0;34m'r'[0m[0;34m,[0m [0mls[0m[0;34m=[0m[0;34m'--'[0m[0;34m,[0m [0mlabel[0m[0;34m=[0m[0;34m'raceline'[0m[0;34m)[0m[0;34m[0m[0;34m[0m[0m
[1;32m 49[0m [0;34m[0m[0m
[0;32m---> 50[0;31m [0mRaceline[0m[0;34m([0m[0;34m)[0m[0;34m.[0m[0mget_reference[0m[0;34m([0m[0;36m1[0m[0;34m)[0m [0;31m# TODO: Remove this debugging command.[0m[0;34m[0m[0;34m[0m[0m
[0m
[0;32m<ipython-input-21-b526ae40bded>[0m in [0;36m__init__[0;34m(self, raceline_file)[0m
[1;32m 8[0m [0;32mwhile[0m [0;32mnot[0m [0mos[0m[0;34m.[0m[0mpath[0m[0;34m.[0m[0mexists[0m[0;34m([0m[0mraceline_file[0m[0;34m)[0m[0;34m:[0m[0;34m[0m[0;34m[0m[0m
[1;32m 9[0m [0mprint[0m[0;34m([0m[0;34mf"Raceline data file {raceline_file} is not found. Please upload it here. "[0m[0;34m)[0m[0;34m[0m[0;34m[0m[0m
[0;32m---> 10[0;31m [0muploaded[0m [0;34m=[0m [0mfiles[0m[0;34m.[0m[0mupload[0m[0;34m([0m[0;34m)[0m[0;34m[0m[0;34m[0m[0m
[0m[1;32m 11[0m [0mdata[0m [0;34m=[0m [0mnp[0m[0;34m.[0m[0mload[0m[0;34m([0m[0mraceline_file[0m[0;34m)[0m[0;34m[0m[0;34m[0m[0m
[1;32m 12[0m [0mself[0m[0;34m.[0m[0ms[0m[0;34m,[0m [0mself[0m[0;34m.[0m[0mey[0m[0;34m,[0m [0mself[0m[0;34m.[0m[0mepsi[0m[0;34m,[0m [0mself[0m[0;34m.[0m[0mvx[0m[0;34m,[0m [0mself[0m[0;34m.[0m[0mvy[0m[0;34m,[0m [0mself[0m[0;34m.[0m[0mepsi_dot[0m[0;34m,[0m [0mself[0m[0;34m.[0m[0mt[0m[0;34m,[0m [0mself[0m[0;34m.[0m[0mu0[0m[0;34m,[0m [0mself[0m[0;34m.[0m[0mu1[0m [0;34m=[0m [0mmap[0m[0;34m([0m[0mnp[0m[0;34m.[0m[0marray[0m[0;34m,[0m [0;34m([0m[0mdata[0m[0;34m[[0m[0;34m's'[0m[0;34m][0m[0;34m,[0m [0mdata[0m[0;34m[[0m[0;34m'e_y'[0m[0;34m][0m[0;34m,[0m [0mdata[0m[0;34m[[0m[0;34m'e_psi'[0m[0;34m][0m[0;34m,[0m [0mdata[0m[0;34m[[0m[0;34m'v_long'[0m[0;34m][0m[0;34m,[0m [0mdata[0m[0;34m[[0m[0;34m'v_tran'[0m[0;34m][0m[0;34m,[0m [0mdata[0m[0;34m[[0m[0;34m'psidot'[0m[0;34m][0m[0;34m,[0m [0mdata[0m[0;34m[[0m[0;34m't'[0m[0;34m][0m[0;34m,[0m [0mdata[0m[0;34m[[0m[0;34m'u_a'[0m[0;34m][0m[0;34m,[0m [0mdata[0m[0;34m[[0m[0;34m'u_s'[0m[0;34m][0m[0;34m)[0m[0;34m)[0m[0;34m[0m[0;34m[0m[0m
[0;32m/usr/local/lib/python3.11/dist-packages/google/colab/files.py[0m in [0;36mupload[0;34m(target_dir)[0m
[1;32m 70[0m """
[1;32m 71[0m [0;34m[0m[0m
[0;32m---> 72[0;31m [0muploaded_files[0m [0;34m=[0m [0m_upload_files[0m[0;34m([0m[0mmultiple[0m[0;34m=[0m[0;32mTrue[0m[0;34m)[0m[0;34m[0m[0;34m[0m[0m
[0m[1;32m 73[0m [0;31m# Mapping from original filename to filename as saved locally.[0m[0;34m[0m[0;34m[0m[0m
[1;32m 74[0m [0mlocal_filenames[0m [0;34m=[0m [0mdict[0m[0;34m([0m[0;34m)[0m[0;34m[0m[0;34m[0m[0m
[0;32m/usr/local/lib/python3.11/dist-packages/google/colab/files.py[0m in [0;36m_upload_files[0;34m(multiple)[0m
[1;32m 162[0m [0;34m[0m[0m
[1;32m 163[0m [0;31m# First result is always an indication that the file picker has completed.[0m[0;34m[0m[0;34m[0m[0m
[0;32m--> 164[0;31m result = _output.eval_js(
[0m[1;32m 165[0m 'google.colab._files._uploadFiles("{input_id}", "{output_id}")'.format(
[1;32m 166[0m [0minput_id[0m[0;34m=[0m[0minput_id[0m[0;34m,[0m [0moutput_id[0m[0;34m=[0m[0moutput_id[0m[0;34m[0m[0;34m[0m[0m
[0;32m/usr/local/lib/python3.11/dist-packages/google/colab/output/_js.py[0m in [0;36meval_js[0;34m(script, ignore_result, timeout_sec)[0m
[1;32m 38[0m [0;32mif[0m [0mignore_result[0m[0;34m:[0m[0;34m[0m[0;34m[0m[0m
[1;32m 39[0m [0;32mreturn[0m[0;34m[0m[0;34m[0m[0m
[0;32m---> 40[0;31m [0;32mreturn[0m [0m_message[0m[0;34m.[0m[0mread_reply_from_input[0m[0;34m([0m[0mrequest_id[0m[0;34m,[0m [0mtimeout_sec[0m[0;34m)[0m[0;34m[0m[0;34m[0m[0m
[0m[1;32m 41[0m [0;34m[0m[0m
[1;32m 42[0m [0;34m[0m[0m
[0;32m/usr/local/lib/python3.11/dist-packages/google/colab/_message.py[0m in [0;36mread_reply_from_input[0;34m(message_id, timeout_sec)[0m
[1;32m 94[0m [0mreply[0m [0;34m=[0m [0m_read_next_input_message[0m[0;34m([0m[0;34m)[0m[0;34m[0m[0;34m[0m[0m
[1;32m 95[0m [0;32mif[0m [0mreply[0m [0;34m==[0m [0m_NOT_READY[0m [0;32mor[0m [0;32mnot[0m [0misinstance[0m[0;34m([0m[0mreply[0m[0;34m,[0m [0mdict[0m[0;34m)[0m[0;34m:[0m[0;34m[0m[0;34m[0m[0m
[0;32m---> 96[0;31m [0mtime[0m[0;34m.[0m[0msleep[0m[0;34m([0m[0;36m0.025[0m[0;34m)[0m[0;34m[0m[0;34m[0m[0m
[0m[1;32m 97[0m [0;32mcontinue[0m[0;34m[0m[0;34m[0m[0m
[1;32m 98[0m if (
[0;31mKeyboardInterrupt[0m: %%writefile config.py
#@markdown ## Set PID parameters
#@markdown ### Steering PID Parameters
Kp_steer = 0.25 #@param {type:"slider", min:0.0, max:2.0, step:0.05}
Ki_steer = 0.1 #@param {type:"slider", min:0.0, max:2.0, step:0.05}
Kd_steer = 0.05 #@param {type:"slider", min:0.0, max:2.0, step:0.05}
#@markdown ### Speed PID Parameters
Kp_speed = 0.3 #@param {type:"slider", min:0.0, max:2.0, step:0.05}
Ki_speed = 0 #@param {type:"slider", min:0.0, max:2.0, step:0.05}
Kd_speed = 0 #@param {type:"slider", min:0.0, max:2.0, step:0.05}
# Some other global parameters. Don't modify them!
seed = 42
dt = 0.1pid_steer_params_raceline_follower = PIDParams(dt=dt,
Kp=Kp_steer, # Use the parameters from the sliders above.
Ki=Ki_steer,
Kd=Kd_steer,
u_max=0.436,
u_min=-0.436,
du_max=4.5,
du_min=-4.5,
x_ref=0.0, # Offset from the center line. Set to 0 to follow the center line.
)
pid_speed_params_raceline_follower = PIDParams(dt=dt,
Kp=Kp_speed,
Ki=Ki_speed,
Kd=Kd_speed,
u_max=2.0,
u_min=-2.0,
du_max=20.0,
du_min=-20.0,
x_ref=reference_speed, # Reference speed.
)
pid_controller = PIDLaneFollower(dt, pid_steer_params, pid_speed_params)# Create an instance of the simulator.
path_follower = PIDRacelineFollower(dt=dt, steer_pid_params=pid_steer_params_raceline_follower, speed_pid_params=pid_speed_params_raceline_follower)
raceline = Raceline()
env = gym.make('barc-v0',
track_name='L_track_barc',
do_render=True,
max_n_laps=50, # Feel free to modify this.
in_colab=True,
)
env.unwrapped.bind_controller(path_follower)
# Reset the car to the starting line, with an initial velocity of 0.5 m/s.
_, info = env.reset(seed=seed, options={'spawning': 'fixed'})
truncated = False # Flag indicating whether the car is at a terminal state.
# Reset the PID controller (clear the errors).
path_follower.reset()
lap_time = [] # Keep track of the lap times.
while not truncated:
reference = raceline.get_reference(info['vehicle_state'].p.s, speed_scaling=0.7)
action = path_follower.step(vehicle_state=info['vehicle_state'], reference=reference) # Call the controller to get the steering and speed commands.
_, _, terminated, truncated, info = env.step(action) # Apply the action on the car and get the updated state.
if terminated:
lap_time.append(info['lap_time']) # Keep track of the time it took to finish a lap.
logger.info(f"Average lap time: {np.mean(lap_time):.1f} s. Std: {np.std(lap_time):.1f} s.")
logger.info("Rollout truncated.")fig, axes = plt.subplots(2, 2, figsize=(9, 9))
env.unwrapped.show_debug_plot(axes)
raceline.plot_raceline(ax=axes[0][0])
for ax in axes.flatten():
ax.legend()
plt.show()Compute a new raceline (Optional)
import sys
IN_COLAB = 'google.colab' in sys.modules
if IN_COLAB:
# !pip install -q pyomo
!apt-get install -y -qq glpk-utils
!wget -N -q "https://matematica.unipv.it/gualandi/solvers/ipopt-linux64.zip"
!unzip -o -q ipopt-linux64Selecting previously unselected package libsuitesparseconfig5:amd64.
(Reading database ... 126333 files and directories currently installed.)
Preparing to unpack .../libsuitesparseconfig5_1%3a5.10.1+dfsg-4build1_amd64.deb ...
Unpacking libsuitesparseconfig5:amd64 (1:5.10.1+dfsg-4build1) ...
Selecting previously unselected package libamd2:amd64.
Preparing to unpack .../libamd2_1%3a5.10.1+dfsg-4build1_amd64.deb ...
Unpacking libamd2:amd64 (1:5.10.1+dfsg-4build1) ...
Selecting previously unselected package libcolamd2:amd64.
Preparing to unpack .../libcolamd2_1%3a5.10.1+dfsg-4build1_amd64.deb ...
Unpacking libcolamd2:amd64 (1:5.10.1+dfsg-4build1) ...
Selecting previously unselected package libglpk40:amd64.
Preparing to unpack .../libglpk40_5.0-1_amd64.deb ...
Unpacking libglpk40:amd64 (5.0-1) ...
Selecting previously unselected package glpk-utils.
Preparing to unpack .../glpk-utils_5.0-1_amd64.deb ...
Unpacking glpk-utils (5.0-1) ...
Setting up libsuitesparseconfig5:amd64 (1:5.10.1+dfsg-4build1) ...
Setting up libamd2:amd64 (1:5.10.1+dfsg-4build1) ...
Setting up libcolamd2:amd64 (1:5.10.1+dfsg-4build1) ...
Setting up libglpk40:amd64 (5.0-1) ...
Setting up glpk-utils (5.0-1) ...
Processing triggers for man-db (2.10.2-1) ...
Processing triggers for libc-bin (2.35-0ubuntu3.8) ...
/sbin/ldconfig.real: /usr/local/lib/libtcm.so.1 is not a symbolic link
/sbin/ldconfig.real: /usr/local/lib/libtbbbind.so.3 is not a symbolic link
/sbin/ldconfig.real: /usr/local/lib/libtcm_debug.so.1 is not a symbolic link
/sbin/ldconfig.real: /usr/local/lib/libur_adapter_level_zero.so.0 is not a symbolic link
/sbin/ldconfig.real: /usr/local/lib/libumf.so.0 is not a symbolic link
/sbin/ldconfig.real: /usr/local/lib/libtbbbind_2_0.so.3 is not a symbolic link
/sbin/ldconfig.real: /usr/local/lib/libur_loader.so.0 is not a symbolic link
/sbin/ldconfig.real: /usr/local/lib/libtbbbind_2_5.so.3 is not a symbolic link
/sbin/ldconfig.real: /usr/local/lib/libhwloc.so.15 is not a symbolic link
/sbin/ldconfig.real: /usr/local/lib/libtbbmalloc_proxy.so.2 is not a symbolic link
/sbin/ldconfig.real: /usr/local/lib/libur_adapter_opencl.so.0 is not a symbolic link
/sbin/ldconfig.real: /usr/local/lib/libtbbmalloc.so.2 is not a symbolic link
/sbin/ldconfig.real: /usr/local/lib/libtbb.so.12 is not a symbolic link
# Original code at: https://github.com/MPC-Berkeley/barc_gym/blob/main/mpclab_common/mpclab_common/tracks/solve_raceline.py
import numpy as np
from pyomo.environ import *
from pyomo.dae import *
import pandas as pd
from mpclab_common.pytypes import BodyAngularVelocity, BodyLinearVelocity, ParametricPose, VehicleState, VehicleActuation
# from mpclab_common.track import get_track
def compute_raceline(track, vehicle_config, vehicle_constraints,
obj_num=0,
raceline_to_avoid=None,
track_tightening=0,
constant_width=False):
#SYSTEM STATES: vx=x[0], vy=x[1], wz=x[2] ,e_psi=x[3], t=x[4], e_y=x[5]
#SYSTEM INPUTS: ax[m/s^2]=u0, steering(rad)=u1
#INDEPENDENT VARIABLE IS s (space)
lf = vehicle_config.wheel_dist_front
lr = vehicle_config.wheel_dist_rear
mass = vehicle_config.mass
Iz = vehicle_config.yaw_inertia
tire_model = vehicle_config.tire_model
mu = vehicle_config.wheel_friction
Br = vehicle_config.pacejka_b_rear
Bf = vehicle_config.pacejka_b_front
Cr = vehicle_config.pacejka_c_rear
Cf = vehicle_config.pacejka_c_front
Dr = vehicle_config.pacejka_d_rear
Df = vehicle_config.pacejka_d_front
xu_ub = vehicle_constraints['xu_ub']
xu_lb = vehicle_constraints['xu_lb']
du_ub = vehicle_constraints['du_ub']
du_lb = vehicle_constraints['du_lb']
v_long_max = xu_ub.v.v_long
v_long_min = xu_lb.v.v_long
v_tran_max = xu_ub.v.v_tran
v_tran_min = xu_lb.v.v_tran
psidot_max = xu_ub.w.w_psi
psidot_min = xu_lb.w.w_psi
e_psi_max = xu_ub.p.e_psi
e_psi_min = xu_lb.p.e_psi
if constant_width:
e_y_max = track.half_width - track_tightening
e_y_min = -(track.half_width - track_tightening)
else:
# e_y_max = xu_ub.p.x_tran
# e_y_min = xu_lb.p.x_tran
e_y_max = 1e9
e_y_min = -1e9
a_max = xu_ub.u.u_a
a_min = xu_lb.u.u_a
steer_max = xu_ub.u.u_steer
steer_min = xu_lb.u.u_steer
a_rate_max = du_ub.u_a
a_rate_min = du_lb.u_a
steer_rate_max = du_ub.u_steer
steer_rate_min = du_lb.u_steer
print('=================== Raceline computation ===================')
if track.circuit:
print('Track is a circuit')
else:
print('Track is not a circuit')
if constant_width:
print('Using constant width')
else:
print('Using varying width')
# print('Track name: %s' % track_name)
# print(' - length: %g m' % track.track_length)
# print(' - avg width: %g m' % (track.half_width*2))
print('Vehicle configuration:')
print(' - center of mass to front axle: %g m' % lf)
print(' - center of mass to rear axle: %g m' % lr)
print(' - mass: %g kg' % mass)
print(' - yaw inertia: %g kg m^2' % Iz)
print(' - tire model: %s' % tire_model)
print(' - tire friction coefficient: %g' % mu)
print(' - Front tires:')
print(' - B: %g' % Bf)
print(' - C: %g' % Cf)
print(' - D: %g' % Df)
print(' - Rear tires:')
print(' - B: %g' % Br)
print(' - C: %g' % Cr)
print(' - D: %g' % Dr)
print('Vehicle input constraints:')
print(' - max acceleration: %g m/s^2' % a_max)
print(' - min acceleration: %g m/s^2' % a_min)
print(' - max steering angle: %g rad' % steer_max)
print(' - min steering angle: %g rad' % steer_min)
print('Vehicle state constraints:')
print(' - max longitudinal velocity: %g m/s' % v_long_max)
print(' - min longitudinal velocity: %g m/s' % v_long_min)
print(' - max lateral velocity: %g m/s' % v_tran_max)
print(' - min lateral velocity: %g m/s' % v_tran_min)
print(' - max yaw rate: %g rad/s' % psidot_max)
print(' - min yaw rate: %g rad/s' % psidot_min)
print('============================================================')
m = ConcreteModel()
m.sf = Param(initialize=track.track_length)
m.s = ContinuousSet(bounds=(0, m.sf))
m.alpha_f = Var(m.s, initialize=0)
m.alpha_r = Var(m.s, initialize=0)
m.Fyf = Var(m.s,bounds=(-mass*9.8, mass*9.8), initialize=0)
m.Fyr = Var(m.s,bounds=(-mass*9.8, mass*9.8), initialize=0)
m.x0 = Var(m.s, bounds=(v_long_min, v_long_max), initialize=v_long_min)
m.x1 = Var(m.s, bounds=(v_tran_min, v_tran_max), initialize=0)
m.x2 = Var(m.s, bounds=(psidot_min, psidot_max), initialize=0)
m.x3 = Var(m.s, bounds=(e_psi_min, e_psi_max))
m.x4 = Var(m.s, bounds=(0, 20000), initialize=0)
m.x5 = Var(m.s, bounds=(e_y_min, e_y_max), initialize=0)
m.u0 = Var(m.s, bounds=(a_min, a_max), initialize=0)
m.u1 = Var(m.s, bounds=(steer_min, steer_max), initialize=0)
m.du0 = Var(m.s, bounds=(a_rate_min, a_rate_max), initialize=0)
# m.du0 = Var(m.s, initialize=0)
m.du1 = Var(m.s, bounds=(steer_rate_min, steer_rate_max), initialize=0)
# m.du1 = Var(m.s, initialize=0)
m.dx0ds = DerivativeVar(m.x0, wrt=m.s)
m.dx1ds = DerivativeVar(m.x1, wrt=m.s)
m.dx2ds = DerivativeVar(m.x2, wrt=m.s)
m.dx3ds = DerivativeVar(m.x3, wrt=m.s)
m.dx4ds = DerivativeVar(m.x4, wrt=m.s)
m.dx5ds = DerivativeVar(m.x5, wrt=m.s)
m.du0ds = DerivativeVar(m.u0, wrt=m.s)
m.du1ds = DerivativeVar(m.u1, wrt=m.s)
# to avoid divide by 0
eps=0.000001
#Objective function
if obj_num == 0:
m.obj = Objective(expr=m.x4[m.sf], sense=minimize)
elif obj_num == 1:
m.obj = Objective(expr=m.x4[m.sf] + 0.1*sum(m.du1ds[i] for i in m.s), sense=minimize)
elif obj_num == 2:
m.obj = Objective(expr=m.x4[m.sf] + 0.01*sum(m.du1ds[i] for i in m.s), sense=minimize)
elif obj_num == 3:
m.obj = Objective(expr=m.x4[m.sf] + 0.001*sum(m.du1ds[i] for i in m.s), sense=minimize)
elif obj_num == 4:
m.obj = Objective(expr=m.x4[m.sf] + 0.005*sum(m.du1ds[i] for i in m.s), sense=minimize)
elif obj_num == 5:
# Penalized lap time, inputs, and input rates
m.obj = Objective(expr=m.x4[m.sf] + 0.01*sum(m.du0ds[i]**2 for i in m.s) + 0.01*sum(m.du1ds[i]**2 for i in m.s), sense=minimize)
# m.obj = Objective(expr=m.x4[m.sf] + 0.01*sum(m.du0[i]**2 for i in m.s) + 0.01*sum(m.du1[i]**2 for i in m.s), sense=minimize)
# m.obj = Objective(expr=m.x4[m.sf] + 0.001*sum(m.u0[i]**2 for i in m.s) + 0.001*sum(m.u1[i]**2 for i in m.s) + 0.01*sum(m.du0ds[i]**2 for i in m.s) + 0.01*sum(m.du1ds[i]**2 for i in m.s), sense=minimize)
#sideslip and lateral force
def _alphafc(m, s):
return m.alpha_f[s] == m.u1[s] - atan((m.x1[s] + lf * m.x2[s])/ (m.x0[s]))
m.c4 = Constraint(m.s, rule=_alphafc)
def _alpharc(m, s):
return m.alpha_r[s] == -atan((m.x1[s] - lr * m.x2[s])/ (m.x0[s]))
m.c3 = Constraint(m.s, rule=_alpharc)
if tire_model == 'linear':
def _Fyfc(m, s):
return m.Fyf[s] == Df * Cf * Bf * m.alpha_f[s]
m.c2 = Constraint(m.s, rule=_Fyfc)
def _Fyrc(m, s):
return m.Fyr[s] == Dr * Cr * Br * m.alpha_r[s]
m.c1 = Constraint(m.s, rule=_Fyrc)
elif tire_model == 'pacejka':
def _Fyfc(m, s):
return m.Fyf[s] == Df * sin(Cf * atan(Bf * m.alpha_f[s]))
m.c2 = Constraint(m.s, rule=_Fyfc)
def _Fyrc(m, s):
return m.Fyr[s] == Dr * sin(Cr * atan(Br * m.alpha_r[s]))
m.c1 = Constraint(m.s, rule=_Fyrc)
#Differential model definition
def _x0dot(m, s):
cur = track.get_curvature(s)
return m.dx0ds[s] == (m.u0[s] - 1 / mass * m.Fyf[s] * sin(m.u1[s]) + m.x2[s]*m.x1[s])*((1 - cur * m.x5[s]) /(eps+m.x0[s] * cos(m.x3[s]) - m.x1[s] * sin(m.x3[s])))
m.x0dot = Constraint(m.s, rule=_x0dot)
def _x1dot(m, s):
cur = track.get_curvature(s)
return m.dx1ds[s] == (1 / mass * (m.Fyf[s] * cos(m.u1[s]) + m.Fyr[s]) - m.x2[s] * m.x0[s])*((1 - cur * m.x5[s]) /(eps+m.x0[s] * cos(m.x3[s]) - m.x1[s] * sin(m.x3[s])))
m.x1dot = Constraint(m.s, rule=_x1dot)
def _x2dot(m, s):
cur = track.get_curvature(s)
return m.dx2ds[s] == (1 / Iz *(lf*m.Fyf[s] * cos(m.u1[s]) - lr * m.Fyr[s]) )*((1 - cur * m.x5[s]) /(eps+m.x0[s] * cos(m.x3[s]) - m.x1[s] * sin(m.x3[s])))
m.x2dot = Constraint(m.s, rule=_x2dot)
def _x3dot(m, s):
cur = track.get_curvature(s)
return m.dx3ds[s] == ( m.x2[s]*(1 - cur * m.x5[s])/(eps+m.x0[s] * cos(m.x3[s]) - m.x1[s] * sin(m.x3[s])) - cur)
m.x3dot = Constraint(m.s, rule=_x3dot)
def _x4dot(m, s):
cur = track.get_curvature(s)
return m.dx4ds[s] == ((1 - cur * m.x5[s]) /(eps+m.x0[s] * cos(m.x3[s]) - m.x1[s] * sin(m.x3[s])))
m.x4dot = Constraint(m.s, rule=_x4dot)
def _x5dot(m, s):
cur = track.get_curvature(s)
return m.dx5ds[s] == (m.x0[s] * sin(m.x3[s]) + m.x1[s] * cos(m.x3[s]))*((1 - cur * m.x5[s]) /(eps+m.x0[s] * cos(m.x3[s]) - m.x1[s] * sin(m.x3[s])))
m.x5dot = Constraint(m.s, rule=_x5dot)
def _u0dot(m, s):
cur = track.get_curvature(s)
return m.du0ds[s] == m.du0[s] * ((1 - cur * m.x5[s]) /(eps+m.x0[s] * cos(m.x3[s]) - m.x1[s] * sin(m.x3[s])))
m.u0dot = Constraint(m.s, rule=_u0dot)
def _u1dot(m, s):
cur = track.get_curvature(s)
return m.du1ds[s] == m.du1[s] * ((1 - cur * m.x5[s]) /(eps+m.x0[s] * cos(m.x3[s]) - m.x1[s] * sin(m.x3[s])))
m.u1dot = Constraint(m.s, rule=_u1dot)
# # rate constraints on acceleration
# def _u0dotmax(m, s):
# cur = track.get_curvature(s)
# return m.du0ds[s] <= a_rate_max*((1 - cur * m.x5[s]) /(eps+m.x0[s] * cos(m.x3[s]) - m.x1[s] * sin(m.x3[s])))
# m.i0dotub = Constraint(m.s, rule=_u0dotmax)
# def _u0dotmin(m, s):
# cur = track.get_curvature(s)
# return m.du0ds[s] >= a_rate_min*((1 - cur * m.x5[s]) /(eps+m.x0[s] * cos(m.x3[s]) - m.x1[s] * sin(m.x3[s])))
# m.i0dotlb = Constraint(m.s, rule=_u0dotmin)
# # rate constraints on steering
# def _u1dotmax(m, s):
# cur = track.get_curvature(s)
# return m.du1ds[s] <= steer_rate_max*((1 - cur * m.x5[s]) /(eps+m.x0[s] * cos(m.x3[s]) - m.x1[s] * sin(m.x3[s])))
# m.i1dotub = Constraint(m.s, rule=_u1dotmax)
# def _u1dotmin(m, s):
# cur = track.get_curvature(s)
# return m.du1ds[s] >= steer_rate_min*((1 - cur * m.x5[s]) /(eps+m.x0[s] * cos(m.x3[s]) - m.x1[s] * sin(m.x3[s])))
# m.i1dotlb = Constraint(m.s, rule=_u1dotmin)
# Variable track width
if not constant_width:
def _eymax(m, s):
_w = float(track.left_width(s)) - track_tightening
return m.x5[s] <= _w
m.eymax = Constraint(m.s, rule=_eymax)
def _eymin(m, s):
_w = -(float(track.right_width(s)) - track_tightening)
return m.x5[s] >= _w
m.eymin = Constraint(m.s, rule=_eymin)
if raceline_to_avoid is not None:
# racing constraints: t2(s) + ey2(s) >= t1(s) + ey1(s) + buffer
buffer = 0.5
def _raceConstraint(m,s):
m1x4, m1x5 = getValues(raceline_to_avoid, s)
#return m.x4[s] + m.x5[s] >= m1x4 + m1x5 + buffer
#return m.x5[s] <= m1x5 - 0.1
return m.x5[s]+m1x4 <= m1x5 + m.x4[s] - 0.1
m.rc = Constraint(m.s, rule=_raceConstraint)
# inital and terminal conditions
def _init(m):
if track.circuit:
yield m.x0[0] == m.x0[track.track_length]
yield m.x1[0] == m.x1[track.track_length]
yield m.x2[0] == m.x2[track.track_length]
yield m.x3[0] == m.x3[track.track_length]
yield m.x5[0] == m.x5[track.track_length]
yield m.x4[0] == 0
m.init_conditions = ConstraintList(rule=_init)
# Discretize model using radau or finite difference collocation
TransformationFactory('dae.collocation').apply_to(m, nfe=200, ncp=10, scheme='LAGRANGE-LEGENDRE') #STANDARD METHOD
# TransformationFactory('dae.collocation').apply_to(m, nfe=500, ncp=50, scheme='LAGRANGE-LEGENDRE' )
# TransformationFactory('dae.collocation').apply_to(m, nfe=200, ncp=10, scheme='LAGRANGE-RADAU' )
#TransformationFactory('dae.collocation').apply_to(m, nfe=100, ncp=10, scheme='LAGRANGE-RADAU')
# TransformationFactory('dae.finite_difference').apply_to(m, nfe=1000)
# TransformationFactory('dae.finite_difference').apply_to(m, nfe=3000)
# TransformationFactory('dae.finite_difference').apply_to(m, nfe=10000)
# Solve algebraic model
solver = SolverFactory('ipopt')
# Solver options
solver.options['max_iter'] = 1000
# solver.options['linear_solver'] = 'ma27'
# solver.options['mu_strategy'] = 'adaptive'
#solver.options = {'tol': 1e-6,
# 'mu_init': 1e-8,
# 'bound_push': 1e-8,
# 'halt_on_ampl_error': 'yes'}
solver.options = {'tol': 1e-2,
'mu_init': 1e-6}
results = solver.solve(m, tee=True)
# Evaluate solution at discretization points
s_vec = list(sorted(m.s.data()))
raceline = {'t': np.zeros(len(s_vec)),
'x' : np.zeros(len(s_vec)),
'y' : np.zeros(len(s_vec)),
'psi' : np.zeros(len(s_vec)),
's' : np.zeros(len(s_vec)),
'e_y' : np.zeros(len(s_vec)),
'e_psi' : np.zeros(len(s_vec)),
'v_long' : np.zeros(len(s_vec)),
'v_tran' : np.zeros(len(s_vec)),
'psidot' : np.zeros(len(s_vec)),
'u_a': np.zeros(len(s_vec)),
'u_s': np.zeros(len(s_vec))}
for j in range(len(s_vec)):
s = s_vec[j]
local_pos = (s, value(m.x5[s]), 0)
(x, y, psi) = track.local_to_global(local_pos)
raceline['t'][j] = value(m.x4[s])
raceline['v_long'][j] = value(m.x0[s])
raceline['v_tran'][j] = value(m.x1[s])
raceline['psidot'][j] = value(m.x2[s])
raceline['s'][j] = s
raceline['e_y'][j] = value(m.x5[s])
raceline['e_psi'][j] = value(m.x3[s])
raceline['x'][j] = x
raceline['y'][j] = y
raceline['psi'][j] = psi
raceline['u_a'][j] = value(m.u0[s])
raceline['u_s'][j] = value(m.u1[s])
DF = pd.DataFrame()
# for v in m.component_objects(Var, active=True):
# for index in v:
# DF.at[index, v.name] = value(v[index])
return raceline, DF
def getValues(DF, s):
m1x4 = np.interp(s, DF.index, DF['x4'].values)
m1x5 = np.interp(s, DF.index, DF['x5'].values)
return m1x4, m1x5
def plot_racelines(track, racelines, colors):
import matplotlib.pyplot as plt
import os
fig_xy = plt.figure(figsize=(20, 20))
ax = fig_xy.gca()
track.plot_map(ax)
fig_ts = plt.figure(figsize=(50, 30))
ax_vx = fig_ts.add_subplot(6, 1, 1)
ax_vx.set_ylabel('vel long')
ax_vy = fig_ts.add_subplot(6, 1, 2)
ax_vy.set_ylabel('vel lat')
ax_pd = fig_ts.add_subplot(6, 1, 3)
ax_pd.set_ylabel('yaw rate')
ax_ey = fig_ts.add_subplot(6, 1, 4)
ax_ey.set_ylabel('e_y')
ax_a = fig_ts.add_subplot(6, 1, 5)
ax_a.set_ylabel('accel cmd')
ax_s = fig_ts.add_subplot(6, 1, 6)
ax_s.set_ylabel('steer cmd')
ax_s.set_xlabel('s')
for r, c in zip(racelines, colors):
vs = np.cos(r['v_long']) - np.sin(r['v_tran'])
speed = np.sqrt(np.power(r['v_long'],2)+np.power(r['v_tran'],2))
sc = ax.scatter(r['x'], r['y'], c=speed)
ax.plot(r['x'], r['y'], c)
ax_vx.plot(r['s'], r['v_long'], c)
ax_vy.plot(r['s'], r['v_tran'], c)
ax_pd.plot(r['s'], r['psidot'], c)
ax_ey.plot(r['s'], r['e_y'], c)
ax_a.plot(r['s'], r['u_a'], c)
ax_s.plot(r['s'], r['u_s'], c)
laptime = r['t'][-1]
print(f'Lap time: {laptime}')
ax.set_aspect('equal')
plt.colorbar(mappable=sc, ax=ax)
ax.set_title(f'time: {laptime:.2f} s')
save_plot = False
if save_plot:
fig_xy.savefig(os.path.join(os.path.expanduser('~'), 'Pictures', 'raceline.png'))
fig_ts.savefig(os.path.join(os.path.expanduser('~'), 'Pictures', 'raceline_state_input.png'))
plt.draw()
plt.show()from google.colab import files
import os
from mpclab_common.models.model_types import DynamicBicycleConfig
from mpclab_common.track import get_track
track_name = 'L_track_barc'
track_obj = get_track(track_name)
vehicle_config = DynamicBicycleConfig(wheel_dist_front=0.132,
wheel_dist_rear=0.128,
mass=2.258, #2.158,
yaw_inertia=0.01771, #0.026188,
drag_coefficient=0.0,
tire_model='pacejka',
wheel_friction=0.9, #0.96,
pacejka_b_front=5.0, #0.99,
pacejka_b_rear=575055782097995, #0.99,
pacejka_c_front=2.28, #11.04,
pacejka_c_rear=2.0524659447890445, #11.04,
simple_slip=False)
VW = 0.195
track_tightening = VW/2
state_input_ub = VehicleState(p=ParametricPose(x_tran=1e9, e_psi=np.pi/2),
v=BodyLinearVelocity(v_long=5, v_tran=1),
w=BodyAngularVelocity(w_psi=10),
u=VehicleActuation(u_a=2.0, u_steer=0.436))
state_input_lb = VehicleState(p=ParametricPose(x_tran=-1e9, e_psi=-np.pi/2),
v=BodyLinearVelocity(v_long=0.1, v_tran=-1),
w=BodyAngularVelocity(w_psi=-10),
u=VehicleActuation(u_a=-2.0, u_steer=-0.436))
input_rate_ub = VehicleActuation(u_a=40, u_steer=4.5)
input_rate_lb = VehicleActuation(u_a=-40, u_steer=-4.5)
vehicle_constraints = {'xu_ub': state_input_ub, 'xu_lb': state_input_lb, 'du_ub': input_rate_ub, 'du_lb': input_rate_lb}
obj_num = 0
raceline_1, raceline_data = compute_raceline(track_obj, vehicle_config, vehicle_constraints,
obj_num=obj_num,
track_tightening=track_tightening,
constant_width=True)
# raceline_2, _ = compute_raceline(track_name, vehicle_config, vehicle_constraints, raceline_to_avoid=raceline_data)
plot = True
save = True
if save:
# save_dir = os.path.expanduser('~/barc_data/racelines')
# if not os.path.exists(save_dir):
# os.makedirs(save_dir)
save_dir = ''
# filename = track_name + '_raceline.npz'
filename = 'raceline.npz'
save_path = filename
# save_path = os.path.join(save_dir, filename)
print('Saving computed raceline to %s' % save_path)
np.savez(save_path, **raceline_1)
files.download(save_path)
# filename = track_name + '_raceline_2.npz'
# save_path = os.path.join(save_dir, filename)
# print('Saving computed raceline to %s' % save_path)
# np.savez(save_path, **raceline_2)
if plot:
# plot_racelines(track_name, [raceline_1, raceline_2], ['b', 'r'])
plot_racelines(track_obj, [raceline_1], ['b'])=================== Raceline computation ===================
Track is a circuit
Using constant width
Vehicle configuration:
- center of mass to front axle: 0.132 m
- center of mass to rear axle: 0.128 m
- mass: 2.258 kg
- yaw inertia: 0.01771 kg m^2
- tire model: pacejka
- tire friction coefficient: 0.9
- Front tires:
- B: 5
- C: 2.28
- D: 9.81459
- Rear tires:
- B: 5.75056e+14
- C: 2.05247
- D: 10.1213
Vehicle input constraints:
- max acceleration: 2 m/s^2
- min acceleration: -2 m/s^2
- max steering angle: 0.436 rad
- min steering angle: -0.436 rad
Vehicle state constraints:
- max longitudinal velocity: 5 m/s
- min longitudinal velocity: 0.1 m/s
- max lateral velocity: 1 m/s
- min lateral velocity: -1 m/s
- max yaw rate: 10 rad/s
- min yaw rate: -10 rad/s
============================================================
Ipopt 3.12.13: tol=0.01
mu_init=1e-06
******************************************************************************
This program contains Ipopt, a library for large-scale nonlinear optimization.
Ipopt is released as open source code under the Eclipse Public License (EPL).
For more information visit http://projects.coin-or.org/Ipopt
******************************************************************************
This is Ipopt version 3.12.13, running with linear solver mumps.
NOTE: Other linear solvers might be more efficient (see Ipopt documentation).
Number of nonzeros in equality constraint Jacobian...: 352166
Number of nonzeros in inequality constraint Jacobian.: 0
Number of nonzeros in Lagrangian Hessian.............: 84302
Total number of variables............................: 48422
variables with only lower bounds: 0
variables with lower and upper bounds: 26412
variables with only upper bounds: 0
Total number of equality constraints.................: 44018
Total number of inequality constraints...............: 0
inequality constraints with only lower bounds: 0
inequality constraints with lower and upper bounds: 0
inequality constraints with only upper bounds: 0
iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls
0 9.9999900e-03 9.09e+00 1.00e+00 -6.0 0.00e+00 - 0.00e+00 0.00e+00 0
1 3.1572035e-02 6.69e+00 3.51e+00 -6.0 1.01e+01 - 7.19e-02 4.43e-01h 1
2 4.4869538e-02 4.63e+00 4.15e+00 -6.0 6.52e+00 - 8.04e-02 4.17e-01h 1
3 7.3945501e-02 2.82e+00 6.66e+00 -6.0 7.66e+00 - 1.06e-01 7.71e-01h 1
4 7.3911159e-02 2.82e+00 6.81e+00 -6.0 4.28e+00 - 8.34e-02 7.67e-04h 1
5 7.1934154e-02 2.26e+00 6.32e+01 -6.0 4.54e+00 - 5.88e-03 2.52e-01h 1
6 7.1930295e-02 2.24e+00 6.28e+01 -6.0 3.15e+00 - 1.96e-03 6.05e-03h 1
7 7.1927916e-02 2.23e+00 6.49e+01 -6.0 3.13e+00 - 1.15e-05 4.32e-03h 1
8 7.1809601e-02 1.85e+00 5.34e+01 -6.0 3.13e+00 - 2.96e-03 2.08e-01h 1
9 7.1263996e-02 1.78e+00 4.91e+01 -6.0 2.70e+00 - 1.60e-01 3.95e-02h 1
iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls
10 8.4345128e-02 1.46e+00 4.30e+01 -6.0 2.76e+00 - 7.35e-02 2.16e-01h 1
11 1.0531832e-01 1.32e+00 3.83e+01 -6.0 2.90e+00 - 1.07e-01 1.07e-01h 1
12r 1.0531832e-01 1.32e+00 9.99e+02 0.1 0.00e+00 - 0.00e+00 3.80e-07R 4
13r 1.0848182e-01 2.22e+00 9.99e+02 0.1 5.27e+04 - 1.09e-07 2.10e-05f 1
14r 2.1846817e-01 2.11e+00 9.99e+02 0.1 9.44e+02 - 7.84e-05 6.34e-04f 1
15r 4.5584060e-01 1.74e+00 9.98e+02 0.1 4.83e+02 - 1.46e-04 1.15e-03f 1
16r 5.1600325e-01 1.60e+00 9.98e+02 0.1 3.51e+02 - 4.72e-04 3.96e-04f 1
17r 7.5965114e-01 1.10e+00 9.97e+02 0.1 3.92e+02 - 4.63e-04 1.66e-03f 1
18 7.6887630e-01 9.94e-01 1.00e+03 -6.0 6.44e+00 - 3.87e-02 1.04e-01h 1
19 7.6896332e-01 9.92e-01 9.99e+02 -6.0 1.08e+01 - 6.38e-02 1.92e-03h 1
iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls
20 7.7707682e-01 9.45e-01 1.11e+03 -6.0 1.04e+01 - 4.45e-02 4.96e-02h 1
21 8.3571486e-01 7.80e-01 3.96e+03 -6.0 9.58e+00 - 9.13e-03 2.02e-01h 1
22 8.6211344e-01 7.30e-01 3.95e+03 -6.0 1.15e+01 - 3.42e-03 6.82e-02h 1
23 8.8033905e-01 7.01e-01 3.87e+03 -6.0 1.31e+01 - 1.20e-03 4.07e-02h 1
24 8.8160833e-01 6.99e-01 3.86e+03 -6.0 1.42e+01 - 2.60e-03 2.62e-03h 1
25 8.8396643e-01 6.96e-01 3.84e+03 -6.0 1.43e+01 - 4.25e-03 4.79e-03h 1
26 9.0179059e-01 6.72e-01 3.75e+03 -6.0 1.46e+01 - 1.25e-03 3.52e-02h 1
27 9.0468934e-01 6.68e-01 3.73e+03 -6.0 1.51e+01 - 2.25e-02 5.36e-03h 1
28 9.2716114e-01 6.44e-01 3.62e+03 -6.0 1.52e+01 - 1.60e-03 3.71e-02h 1
29 9.2719227e-01 6.44e-01 3.62e+03 -6.0 1.58e+01 - 9.27e-04 4.80e-05h 1
iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls
30 9.2852529e-01 6.43e-01 3.62e+03 -6.0 1.59e+01 - 3.14e-06 2.01e-03h 1
31 9.4099349e-01 6.31e-01 3.55e+03 -6.0 1.59e+01 - 9.33e-03 1.86e-02h 1
32 9.5352287e-01 6.20e-01 3.49e+03 -6.0 1.62e+01 - 4.79e-03 1.73e-02h 1
33 9.5834565e-01 6.16e-01 3.47e+03 -6.0 1.72e+01 - 3.17e-03 6.27e-03h 1
34 9.6146076e-01 6.14e-01 3.46e+03 -6.0 1.71e+01 - 1.29e-03 3.92e-03h 1
35 1.0116767e+00 5.96e-01 3.23e+03 -6.0 1.71e+01 - 2.07e-03 6.21e-02h 1
36 1.0996134e+00 7.29e-01 1.18e+04 -6.0 1.79e+01 - 2.78e-04 9.20e-02h 1
37 1.1046400e+00 7.27e-01 1.18e+04 -6.0 1.54e+01 - 5.33e-04 3.72e-03h 1
38 1.1151869e+00 7.22e-01 1.17e+04 -6.0 1.52e+01 - 1.03e-02 7.56e-03h 1
39 1.1186232e+00 7.20e-01 1.16e+04 -6.0 1.43e+01 - 3.36e-03 2.14e-03h 1
iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls
40 1.1363301e+00 7.14e-01 1.12e+04 -6.0 1.39e+01 - 3.32e-03 1.06e-02h 1
41 1.1452951e+00 7.11e-01 1.11e+04 -6.0 1.30e+01 - 1.80e-03 4.79e-03h 1
42 1.1491988e+00 7.09e-01 1.11e+04 -6.0 1.25e+01 - 7.46e-04 1.98e-03h 1
43 1.1663316e+00 7.04e-01 1.10e+04 -6.0 1.24e+01 - 2.78e-03 8.51e-03h 1
44 1.2638871e+00 6.85e-01 1.05e+04 -6.0 1.16e+01 - 4.21e-04 4.48e-02h 1
45 1.3986007e+00 6.59e-01 1.00e+04 -6.0 8.79e+00 - 4.38e-04 4.94e-02h 1
46 1.7365862e+00 6.04e-01 8.97e+03 -6.0 6.50e+00 - 8.95e-04 1.08e-01h 1
47 1.8099186e+00 5.91e-01 8.79e+03 -6.0 3.83e+00 - 2.17e-03 2.18e-02h 1
48 1.9276290e+00 5.71e-01 8.48e+03 -6.0 3.52e+00 - 1.79e-02 3.50e-02h 1
49 2.0517348e+00 5.51e-01 8.20e+03 -6.0 3.38e+00 - 5.39e-03 3.68e-02h 1
iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls
50 2.2660329e+00 5.16e-01 7.69e+03 -6.0 3.32e+00 - 1.39e-02 6.45e-02h 1
51 4.6559408e+00 2.01e-01 5.76e+03 -6.0 3.21e+00 - 1.17e-02 7.46e-01h 1
52 5.1631507e+00 9.59e-02 2.90e+03 -6.0 9.05e-01 - 1.41e-01 5.61e-01h 1
53 5.5608979e+00 5.31e-03 2.59e+02 -6.0 3.98e-01 - 1.70e-01 1.00e+00h 1
54 5.5576026e+00 1.93e-05 1.35e+00 -6.0 1.13e-02 - 9.83e-01 1.00e+00h 1
55 5.4825841e+00 5.46e-03 6.03e+01 -6.0 7.20e-01 - 2.45e-01 3.89e-01f 1
56 5.2802986e+00 4.19e-02 5.35e+02 -6.0 8.98e-01 - 1.11e-01 8.89e-01h 1
57 5.1678749e+00 3.23e-02 5.15e+02 -6.0 8.11e-01 - 9.39e-02 6.10e-01h 1
58 4.9922355e+00 4.01e-02 9.23e+02 -6.0 8.06e-01 - 3.32e-01 1.00e+00h 1
59 4.8949123e+00 3.86e-02 9.18e+02 -6.0 1.06e+00 - 3.27e-01 4.89e-01h 1
iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls
60 4.8412097e+00 3.73e-02 8.81e+02 -6.0 1.58e+00 - 1.88e-01 2.15e-01h 1
61 4.6658455e+00 1.00e-01 2.40e+03 -6.0 1.95e+00 - 4.86e-02 6.31e-01h 1
62 4.6202012e+00 9.03e-02 2.13e+03 -6.0 1.92e+00 - 1.15e-01 2.23e-01h 1
63 4.5484614e+00 9.37e-02 2.11e+03 -6.0 2.09e+00 - 1.45e-01 3.39e-01h 1
64 4.5308277e+00 8.82e-02 1.98e+03 -6.0 2.44e+00 - 1.05e-01 8.33e-02h 1
65 4.5132220e+00 8.36e-02 1.88e+03 -6.0 2.66e+00 - 1.48e-01 7.80e-02h 1
66 4.4750013e+00 8.06e-02 1.84e+03 -6.0 3.19e+00 - 1.30e-01 1.52e-01h 1
67 4.4579902e+00 7.70e-02 1.77e+03 -6.0 3.26e+00 - 5.68e-02 6.57e-02h 1
68 4.3846131e+00 1.08e-01 2.78e+03 -6.0 3.67e+00 - 4.04e-02 2.80e-01h 1
69 4.3140100e+00 1.69e-01 3.81e+03 -6.0 2.94e+00 - 7.24e-02 3.21e-01h 1
iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls
70 4.2117666e+00 3.59e-01 6.02e+03 -6.0 2.52e+00 - 1.77e-01 5.53e-01h 1
71 4.1764605e+00 2.81e-01 4.53e+03 -6.0 2.75e+00 - 2.66e-01 2.46e-01h 1
72 4.1333333e+00 2.09e-01 3.88e+03 -6.0 3.56e+00 - 1.35e-01 2.66e-01h 1
73 4.1090372e+00 1.80e-01 3.56e+03 -6.0 3.41e+00 - 2.70e-01 1.62e-01h 1
74 4.0909993e+00 1.67e-01 3.34e+03 -6.0 4.12e+00 - 8.68e-02 1.06e-01h 1
75 4.0600329e+00 1.67e-01 3.25e+03 -6.0 4.10e+00 - 1.70e-02 1.88e-01h 1
76 4.0403447e+00 1.65e-01 3.01e+03 -6.0 4.03e+00 - 1.83e-02 1.41e-01h 1
77 4.0391952e+00 1.64e-01 2.98e+03 -6.0 4.27e+00 - 5.36e-02 9.16e-03h 1
78 4.0307625e+00 1.57e-01 2.82e+03 -6.0 4.63e+00 - 1.65e-02 6.48e-02h 1
79 3.9918892e+00 2.45e-01 3.40e+03 -6.0 3.74e+00 - 1.65e-02 3.19e-01h 1
iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls
80 3.9784878e+00 2.31e-01 3.10e+03 -6.0 4.62e+00 - 5.33e-02 1.39e-01h 1
81 3.9607947e+00 2.17e-01 2.81e+03 -6.0 3.82e+00 - 4.23e-02 2.00e-01h 1
82 3.9577566e+00 2.10e-01 2.71e+03 -6.0 4.67e+00 - 9.01e-02 3.77e-02h 1
83 3.9423369e+00 1.99e-01 2.40e+03 -6.0 4.28e+00 - 5.63e-02 1.87e-01h 1
84 3.9145730e+00 2.74e-01 3.19e+03 -6.0 3.59e+00 - 3.41e-02 3.80e-01h 1
85 3.9062579e+00 2.49e-01 2.84e+03 -6.0 4.75e+00 - 8.43e-02 1.42e-01h 1
86 3.8968108e+00 2.20e-01 2.52e+03 -6.0 4.18e+00 - 8.15e-02 1.72e-01h 1
87 3.8842809e+00 1.99e-01 2.13e+03 -6.0 4.07e+00 - 6.19e-02 2.46e-01h 1
88 3.8592982e+00 3.01e-01 3.23e+03 -6.0 3.53e+00 - 4.89e-03 5.75e-01h 1
89 3.8575554e+00 2.82e-01 3.02e+03 -6.0 3.59e+00 - 1.52e-01 6.77e-02h 1
iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls
90 3.8572684e+00 2.79e-01 2.98e+03 -6.0 3.27e+00 - 8.01e-03 1.15e-02h 1
91 3.8527871e+00 2.33e-01 2.62e+03 -6.0 3.49e+00 - 1.03e-01 1.78e-01f 1
92 3.8513272e+00 2.17e-01 2.47e+03 -6.0 3.42e+00 - 3.10e-01 6.84e-02h 1
93 3.8476457e+00 1.86e-01 2.21e+03 -6.0 3.84e+00 - 2.75e-02 1.51e-01h 1
94 3.8426879e+00 1.50e-01 2.01e+03 -6.0 3.88e+00 - 2.96e-02 2.06e-01h 1
95 3.8337078e+00 1.01e-01 1.93e+03 -6.0 3.70e+00 - 5.09e-02 4.54e-01h 1
96 3.8297528e+00 7.99e-02 1.57e+03 -6.0 3.56e+00 - 2.34e-01 2.58e-01f 1
97 3.8273275e+00 7.05e-02 1.36e+03 -6.0 3.69e+00 - 1.29e-01 1.69e-01h 1
98 3.8253628e+00 6.38e-02 1.20e+03 -6.0 4.56e+00 - 3.21e-01 1.41e-01h 1
99 3.8216824e+00 6.01e-02 1.00e+03 -6.0 5.30e+00 - 1.04e-01 2.49e-01h 1
iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls
100 3.8139765e+00 1.17e-01 1.32e+03 -6.0 5.18e+00 - 6.09e-02 6.18e-01h 1
101 3.8128674e+00 1.01e-01 1.11e+03 -6.0 5.56e+00 - 2.68e-01 1.70e-01h 1
102 3.8114498e+00 8.55e-02 9.25e+02 -6.0 6.55e+00 - 1.59e-01 2.11e-01h 1
103 3.8074545e+00 1.01e-01 1.39e+03 -6.0 6.25e+00 - 8.28e-02 6.51e-01h 1
104 3.8065533e+00 7.53e-02 1.18e+03 -6.0 6.64e+00 - 4.34e-01 2.91e-01h 1
105 3.8044431e+00 1.19e-01 3.16e+03 -6.0 9.60e+00 - 2.15e-01 5.93e-01h 1
106 3.8034220e+00 1.24e-01 3.55e+03 -6.0 9.90e+00 - 6.39e-01 4.69e-01h 1
107 3.8034681e+00 4.80e-03 6.41e+01 -6.0 9.94e-02 -4.0 8.60e-01 1.00e+00h 1
108 3.8034160e+00 4.42e-04 2.66e+00 -6.0 9.29e-02 -4.5 1.00e+00 1.00e+00h 1
109 3.8033221e+00 1.51e-03 1.29e+01 -6.0 2.12e-01 -5.0 1.00e+00 1.00e+00h 1
iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls
110 3.8031530e+00 3.99e-03 5.44e+01 -6.0 5.63e-01 -5.4 1.00e+00 1.00e+00h 1
111 3.8028225e+00 1.02e-02 8.38e+01 -6.0 1.63e+00 -5.9 1.00e+00 1.00e+00h 1
112 3.8022769e+00 4.31e-02 1.60e+02 -6.0 4.13e+00 -6.4 1.00e+00 8.00e-01h 1
113 3.8016232e+00 1.27e-01 4.97e+02 -6.0 1.07e+01 -6.9 1.00e+00 4.98e-01h 1
114 3.8013055e+00 8.24e-02 3.41e+02 -6.0 3.45e+00 -6.4 8.29e-01 1.00e+00h 1
115 3.8008742e+00 2.55e-01 7.85e+02 -6.0 7.40e+00 -6.9 1.00e+00 6.70e-01h 1
116 3.8007376e+00 1.53e-01 2.62e+02 -6.0 2.32e+00 -6.5 8.93e-01 1.00e+00h 1
117 3.8004190e+00 6.87e-01 6.75e+02 -6.0 5.61e+00 -7.0 1.00e+00 1.00e+00h 1
118 3.8001741e+00 3.18e-01 2.78e+02 -6.0 1.12e+01 -7.4 1.00e+00 7.95e-01h 1
119 3.8001645e+00 4.79e-03 4.54e+01 -6.0 2.76e+00 -7.0 1.00e+00 1.00e+00h 1
iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls
120 3.8000691e+00 7.12e-03 6.03e+01 -6.0 7.44e+00 -7.5 1.00e+00 1.00e+00h 1
121 3.8000519e+00 1.18e-03 1.16e+01 -6.0 1.74e+00 -7.1 1.00e+00 1.00e+00h 1
122 3.8000092e+00 5.37e-03 3.67e+01 -6.0 3.90e+00 -7.5 1.00e+00 1.00e+00h 1
123 3.8000004e+00 5.14e-04 5.24e+00 -6.0 1.19e+00 -7.1 1.00e+00 1.00e+00h 1
124 3.7999904e+00 4.19e-04 5.05e+00 -6.0 1.17e+00 -7.2 1.00e+00 1.00e+00h 1
125 3.7999706e+00 1.21e-03 2.26e+01 -6.0 2.80e+00 -7.6 1.00e+00 1.00e+00h 1
126 3.7999667e+00 1.13e-04 2.24e+00 -6.0 9.09e-01 -7.2 1.00e+00 1.00e+00h 1
127 3.7999563e+00 5.90e-04 1.84e+01 -6.0 1.96e+00 -7.7 1.00e+00 1.00e+00h 1
128 3.7999541e+00 6.97e-05 2.35e+00 -6.0 6.72e-01 -7.3 1.00e+00 1.00e+00h 1
129 3.7999481e+00 5.38e-04 1.90e+01 -6.0 1.66e+00 -7.7 1.00e+00 1.00e+00h 1
iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls
130 3.7999397e+00 2.97e-03 1.14e+02 -6.0 3.83e+00 -8.2 1.00e+00 1.00e+00h 1
131 3.7999360e+00 1.06e-03 1.24e+01 -6.0 3.90e+00 -7.8 1.00e+00 1.00e+00h 1
132 3.7999368e+00 1.26e-04 1.66e+00 -6.0 1.07e+00 -7.4 1.00e+00 1.00e+00h 1
133 3.7999356e+00 2.77e-04 1.32e+01 -6.0 7.93e-01 -7.9 1.00e+00 1.00e+00h 1
134 3.7999357e+00 3.62e-05 1.67e+00 -6.0 3.69e-01 -7.4 1.00e+00 1.00e+00h 1
135 3.7999342e+00 2.40e-04 1.27e+01 -6.0 6.38e-01 -7.9 1.00e+00 1.00e+00h 1
136 3.7999340e+00 2.88e-05 1.61e+00 -6.0 2.06e-01 -7.5 1.00e+00 1.00e+00h 1
137 3.7999337e+00 3.85e-06 2.22e-01 -6.0 8.06e-02 -7.1 1.00e+00 1.00e+00h 1
138 3.7999334e+00 3.14e-05 1.86e+00 -6.0 2.18e-01 -7.5 1.00e+00 1.00e+00h 1
139 3.7999323e+00 2.10e-04 1.32e+01 -6.0 5.61e-01 -8.0 1.00e+00 1.00e+00h 1
iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls
140 3.7999320e+00 2.43e-05 1.65e+00 -6.0 1.91e-01 -7.6 1.00e+00 1.00e+00h 1
141 3.7999312e+00 1.58e-04 1.16e+01 -6.0 4.87e-01 -8.1 1.00e+00 1.00e+00h 1
142 3.7999309e+00 1.80e-05 1.45e+00 -6.0 1.68e-01 -7.6 1.00e+00 1.00e+00h 1
143 3.7999303e+00 1.13e-04 9.86e+00 -6.0 4.10e-01 -8.1 1.00e+00 1.00e+00h 1
144 3.7999300e+00 1.26e-05 1.22e+00 -6.0 1.48e-01 -7.7 1.00e+00 1.00e+00h 1
145 3.7999295e+00 7.52e-05 8.02e+00 -6.0 3.74e-01 -8.2 1.00e+00 1.00e+00h 1
146 3.7999292e+00 9.56e-06 9.89e-01 -6.0 3.88e-01 -7.7 1.00e+00 1.00e+00h 1
147 3.7999291e+00 4.66e-05 2.40e+00 -6.0 1.18e+00 -8.2 1.00e+00 5.00e-01h 2
148 3.7999290e+00 6.77e-05 2.41e+00 -6.0 2.49e+03 -8.7 7.16e-03 4.31e-04h 5
149 3.7999287e+00 9.63e-06 1.46e-01 -6.0 1.44e-01 -7.4 1.00e+00 1.00e+00h 1
iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls
150 3.7999288e+00 7.53e-06 1.09e+00 -6.0 1.71e-01 -7.8 1.00e+00 1.00e+00h 1
151 3.7999287e+00 9.34e-07 1.43e-01 -6.0 3.61e-02 -7.4 1.00e+00 1.00e+00h 1
152 3.7999285e+00 6.69e-06 1.08e+00 -6.0 2.36e-01 -7.9 1.00e+00 1.00e+00h 1
153 3.7999285e+00 2.22e-05 1.03e+00 -6.0 1.08e+01 -8.4 1.00e+00 6.25e-02h 5
154 3.7999280e+00 2.02e-05 1.09e+00 -6.0 5.56e-01 -7.9 1.00e+00 1.00e+00h 1
155 3.7999282e+00 4.08e-05 8.15e-01 -6.0 1.44e+00 -8.0 1.00e+00 5.00e-01h 2
156 3.7999281e+00 5.04e-06 1.60e-01 -6.0 2.66e-01 -7.6 1.00e+00 1.00e+00h 1
157 3.7999282e+00 6.75e-06 1.11e+00 -6.0 3.17e-01 -8.0 1.00e+00 1.00e+00h 1
158 3.7999281e+00 3.51e-05 1.09e+00 -6.0 9.76e+00 -8.1 1.00e+00 1.56e-02h 7
159 3.7999281e+00 1.42e-05 2.14e-01 -6.0 3.00e-01 -7.7 1.00e+00 1.00e+00h 1
iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls
160 3.7999281e+00 1.11e-05 2.08e-01 -6.0 1.47e+00 -8.1 1.00e+00 2.50e-01h 3
161 3.7999279e+00 2.26e-06 1.87e-01 -6.0 2.04e-01 -7.7 1.00e+00 1.00e+00h 1
162 3.7999280e+00 8.90e-08 2.47e-02 -6.0 2.74e-02 -7.3 1.00e+00 1.00e+00h 1
Number of Iterations....: 162
(scaled) (unscaled)
Objective...............: 3.7999279551688945e+00 3.7999279551688945e+00
Dual infeasibility......: 2.4688370976946317e-02 2.4688370976946317e-02
Constraint violation....: 8.6727506065642501e-08 8.8979141721345898e-08
Complementarity.........: 1.0000000520168491e-06 1.0000000520168491e-06
Overall NLP error.......: 3.0869858031594439e-03 2.4688370976946317e-02
Number of objective function evaluations = 194
Number of objective gradient evaluations = 159
Number of equality constraint evaluations = 194
Number of inequality constraint evaluations = 0
Number of equality constraint Jacobian evaluations = 164
Number of inequality constraint Jacobian evaluations = 0
Number of Lagrangian Hessian evaluations = 162
Total CPU secs in IPOPT (w/o function evaluations) = 110.476
Total CPU secs in NLP function evaluations = 19.853
EXIT: Optimal Solution Found.
Saving computed raceline to raceline.npz
<IPython.core.display.Javascript object><IPython.core.display.Javascript object>Lap time: 3.7999279551688945
