-
Notifications
You must be signed in to change notification settings - Fork 453
Expand file tree
/
Copy pathkincar.py
More file actions
112 lines (90 loc) · 3.25 KB
/
kincar.py
File metadata and controls
112 lines (90 loc) · 3.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# kincar.py - planar vehicle model (with flatness)
# RMM, 16 Jan 2022
import numpy as np
import matplotlib.pyplot as plt
import control.flatsys as fs
#
# Vehicle dynamics (bicycle model)
#
# Function to take states, inputs and return the flat flag
def _kincar_flat_forward(x, u, params={}):
# Get the parameter values
b = params.get('wheelbase', 3.)
#! TODO: add dir processing
# Create a list of arrays to store the flat output and its derivatives
zflag = [np.zeros(3), np.zeros(3)]
# Flat output is the x, y position of the rear wheels
zflag[0][0] = x[0]
zflag[1][0] = x[1]
# First derivatives of the flat output
zflag[0][1] = u[0] * np.cos(x[2]) # dx/dt
zflag[1][1] = u[0] * np.sin(x[2]) # dy/dt
# First derivative of the angle
thdot = (u[0]/b) * np.tan(u[1])
# Second derivatives of the flat output (setting vdot = 0)
zflag[0][2] = -u[0] * thdot * np.sin(x[2])
zflag[1][2] = u[0] * thdot * np.cos(x[2])
return zflag
# Function to take the flat flag and return states, inputs
def _kincar_flat_reverse(zflag, params={}):
# Get the parameter values
b = params.get('wheelbase', 3.)
dir = params.get('dir', 'f')
# Create a vector to store the state and inputs
x = np.zeros(3)
u = np.zeros(2)
# Given the flat variables, solve for the state
x[0] = zflag[0][0] # x position
x[1] = zflag[1][0] # y position
if dir == 'f':
x[2] = np.arctan2(zflag[1][1], zflag[0][1]) # tan(theta) = ydot/xdot
elif dir == 'r':
# Angle is flipped by 180 degrees (since v < 0)
x[2] = np.arctan2(-zflag[1][1], -zflag[0][1])
else:
raise ValueError("unknown direction:", dir)
# And next solve for the inputs
u[0] = zflag[0][1] * np.cos(x[2]) + zflag[1][1] * np.sin(x[2])
thdot_v = zflag[1][2] * np.cos(x[2]) - zflag[0][2] * np.sin(x[2])
u[1] = np.arctan2(thdot_v, u[0]**2 / b)
return x, u
# Function to compute the RHS of the system dynamics
def _kincar_update(t, x, u, params):
b = params.get('wheelbase', 3.) # get parameter values
#! TODO: add dir processing
dx = np.array([
np.cos(x[2]) * u[0],
np.sin(x[2]) * u[0],
(u[0]/b) * np.tan(u[1])
])
return dx
def _kincar_output(t, x, u, params):
return x # return x, y, theta (full state)
# Create differentially flat input/output system
kincar = fs.FlatSystem(
_kincar_flat_forward, _kincar_flat_reverse, name="kincar",
updfcn=_kincar_update, outfcn=_kincar_output,
inputs=('v', 'delta'), outputs=('x', 'y', 'theta'),
states=('x', 'y', 'theta'))
#
# Utility function to plot lane change maneuver
#
def plot_lanechange(t, y, u, figure=None, yf=None):
# Plot the xy trajectory
plt.subplot(3, 1, 1, label='xy')
plt.plot(y[0], y[1])
plt.xlabel("x [m]")
plt.ylabel("y [m]")
if yf is not None:
plt.plot(yf[0], yf[1], 'ro')
# Plot the inputs as a function of time
plt.subplot(3, 1, 2, label='v')
plt.plot(t, u[0])
plt.xlabel("Time $t$ [sec]")
plt.ylabel("$v$ [m/s]")
plt.subplot(3, 1, 3, label='delta')
plt.plot(t, u[1])
plt.xlabel("Time $t$ [sec]")
plt.ylabel("$\\delta$ [rad]")
plt.suptitle("Lane change maneuver")
plt.tight_layout()