Skip to content

Commit bee232e

Browse files
committed
try implementing
1 parent 46b547d commit bee232e

5 files changed

Lines changed: 363 additions & 0 deletions

File tree

PathTracking/lqr/__init__.py

Whitespace-only changes.

PathTracking/lqr/lqr_tracking.py

Lines changed: 295 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,295 @@
1+
#! /usr/bin/python
2+
"""
3+
4+
Path tracking simulation with rear wheel feedback steering control and PID speed control.
5+
6+
author: Atsushi Sakai
7+
8+
"""
9+
import numpy as np
10+
import math
11+
import matplotlib.pyplot as plt
12+
import unicycle_model
13+
from pycubicspline import pycubicspline
14+
from matplotrecorder import matplotrecorder
15+
import scipy.linalg as la
16+
17+
Kp = 1.0 # speed propotional gain
18+
# steering control parameter
19+
KTH = 1.0
20+
KE = 0.5
21+
22+
Q = np.eye(4)
23+
R = np.eye(1)
24+
25+
animation = True
26+
# animation = False
27+
28+
matplotrecorder.donothing = True
29+
30+
31+
def PIDControl(target, current):
32+
a = Kp * (target - current)
33+
34+
return a
35+
36+
37+
def pi_2_pi(angle):
38+
while (angle > math.pi):
39+
angle = angle - 2.0 * math.pi
40+
41+
while (angle < -math.pi):
42+
angle = angle + 2.0 * math.pi
43+
44+
return angle
45+
46+
47+
def solve_DARE(A, B, Q, R):
48+
"""
49+
solve a discrete time_Algebraic Riccati equation (DARE)
50+
"""
51+
X = Q
52+
maxiter = 150
53+
eps = 0.01
54+
55+
for i in range(maxiter):
56+
Xn = A.T * X * A - A.T * X * B * \
57+
la.inv(R + B.T * X * B) * B.T * X * A + Q
58+
if (abs(Xn - X)).max() < eps:
59+
X = Xn
60+
break
61+
X = Xn
62+
63+
return Xn
64+
65+
66+
def dlqr(A, B, Q, R):
67+
"""Solve the discrete time lqr controller.
68+
x[k+1] = A x[k] + B u[k]
69+
cost = sum x[k].T*Q*x[k] + u[k].T*R*u[k]
70+
# ref Bertsekas, p.151
71+
"""
72+
73+
# first, try to solve the ricatti equation
74+
X = solve_DARE(A, B, Q, R)
75+
76+
# compute the LQR gain
77+
K = np.matrix(la.inv(B.T * X * B + R) * (B.T * X * A))
78+
79+
eigVals, eigVecs = la.eig(A - B * K)
80+
81+
return K, X, eigVals
82+
83+
84+
def rear_wheel_feedback_control(state, cx, cy, cyaw, ck, preind):
85+
ind, e = calc_nearest_index(state, cx, cy, cyaw)
86+
87+
k = ck[ind]
88+
v = state.v
89+
th_e = pi_2_pi(state.yaw - cyaw[ind])
90+
91+
omega = v * k * math.cos(th_e) / (1.0 - k * e) - \
92+
KTH * abs(v) * th_e - KE * v * math.sin(th_e) * e / th_e
93+
94+
if th_e == 0.0 or omega == 0.0:
95+
return 0.0, ind
96+
97+
delta = math.atan2(unicycle_model.L * omega / v, 1.0)
98+
# print(k, v, e, th_e, omega, delta)
99+
100+
return delta, ind
101+
102+
103+
def lqr_steering_control(state, cx, cy, cyaw, ck, target_ind):
104+
ind, e = calc_nearest_index(state, cx, cy, cyaw)
105+
106+
k = ck[ind]
107+
v = state.v
108+
th_e = pi_2_pi(state.yaw - cyaw[ind])
109+
110+
A = np.matrix(np.zeros((4, 4)))
111+
A[0, 0] = 1.0
112+
A[0, 1] = unicycle_model.dt
113+
A[1, 2] = v
114+
A[2, 2] = 1.0
115+
A[2, 3] = unicycle_model.dt
116+
# print(A)
117+
118+
B = np.matrix(np.zeros((4, 1)))
119+
B[3, 0] = v / unicycle_model.L
120+
121+
K, _, _ = dlqr(A, B, Q, R)
122+
123+
x = np.matrix(np.zeros((4, 1)))
124+
125+
x[0, 0] = e
126+
x[1, 0] = 0.0
127+
x[2, 0] = th_e
128+
x[3, 0] = 0.0
129+
130+
ff = math.atan2(unicycle_model.L * k, 1)
131+
fb = pi_2_pi((-K * x)[0, 0])
132+
print(math.degrees(th_e))
133+
# print(K, x)
134+
print(math.degrees(ff), math.degrees(fb))
135+
136+
delta = ff + fb
137+
# print(delta)
138+
return delta
139+
140+
141+
def calc_nearest_index(state, cx, cy, cyaw):
142+
dx = [state.x - icx for icx in cx]
143+
dy = [state.y - icy for icy in cy]
144+
145+
d = [abs(math.sqrt(idx ** 2 + idy ** 2)) for (idx, idy) in zip(dx, dy)]
146+
147+
mind = min(d)
148+
149+
ind = d.index(mind)
150+
151+
dxl = cx[ind] - state.x
152+
dyl = cy[ind] - state.y
153+
154+
angle = pi_2_pi(cyaw[ind] - math.atan2(dyl, dxl))
155+
if angle < 0:
156+
mind *= -1
157+
158+
return ind, mind
159+
160+
161+
def closed_loop_prediction(cx, cy, cyaw, ck, speed_profile, goal):
162+
T = 500.0 # max simulation time
163+
goal_dis = 0.3
164+
stop_speed = 0.05
165+
166+
state = unicycle_model.State(x=-0.0, y=-0.0, yaw=0.0, v=0.0)
167+
168+
time = 0.0
169+
x = [state.x]
170+
y = [state.y]
171+
yaw = [state.yaw]
172+
v = [state.v]
173+
t = [0.0]
174+
target_ind = calc_nearest_index(state, cx, cy, cyaw)
175+
176+
while T >= time:
177+
di, target_ind = rear_wheel_feedback_control(
178+
state, cx, cy, cyaw, ck, target_ind)
179+
180+
dl = lqr_steering_control(state, cx, cy, cyaw, ck, target_ind)
181+
# print(di, dl)
182+
183+
ai = PIDControl(speed_profile[target_ind], state.v)
184+
# state = unicycle_model.update(state, ai, di)
185+
state = unicycle_model.update(state, ai, dl)
186+
187+
if abs(state.v) <= stop_speed:
188+
target_ind += 1
189+
190+
time = time + unicycle_model.dt
191+
192+
# check goal
193+
dx = state.x - goal[0]
194+
dy = state.y - goal[1]
195+
if math.sqrt(dx ** 2 + dy ** 2) <= goal_dis:
196+
print("Goal")
197+
break
198+
199+
x.append(state.x)
200+
y.append(state.y)
201+
yaw.append(state.yaw)
202+
v.append(state.v)
203+
t.append(time)
204+
205+
if target_ind % 1 == 0 and animation:
206+
plt.cla()
207+
plt.plot(cx, cy, "-r", label="course")
208+
plt.plot(x, y, "ob", label="trajectory")
209+
plt.plot(cx[target_ind], cy[target_ind], "xg", label="target")
210+
plt.axis("equal")
211+
plt.grid(True)
212+
plt.title("speed[km/h]:" + str(round(state.v * 3.6, 2)) +
213+
",target index:" + str(target_ind))
214+
plt.pause(0.0001)
215+
matplotrecorder.save_frame() # save each frame
216+
217+
plt.close()
218+
return t, x, y, yaw, v
219+
220+
221+
def calc_speed_profile(cx, cy, cyaw, target_speed):
222+
speed_profile = [target_speed] * len(cx)
223+
224+
direction = 1.0
225+
226+
# Set stop point
227+
for i in range(len(cx) - 1):
228+
dyaw = cyaw[i + 1] - cyaw[i]
229+
switch = math.pi / 4.0 <= dyaw < math.pi / 2.0
230+
231+
if switch:
232+
direction *= -1
233+
234+
if direction != 1.0:
235+
speed_profile[i] = - target_speed
236+
else:
237+
speed_profile[i] = target_speed
238+
239+
if switch:
240+
speed_profile[i] = 0.0
241+
242+
speed_profile[-1] = 0.0
243+
244+
# flg, ax = plt.subplots(1)
245+
# plt.plot(speed_profile, "-r")
246+
# plt.show()
247+
248+
return speed_profile
249+
250+
251+
def main():
252+
print("rear wheel feedback tracking start!!")
253+
ax = [0.0, 6.0, 12.5, 5.0, 7.5, 3.0, -1.0]
254+
ay = [0.0, 0.0, 5.0, 6.5, 3.0, 5.0, -2.0]
255+
goal = [ax[-1], ay[-1]]
256+
257+
cx, cy, cyaw, ck, s = pycubicspline.calc_spline_course(ax, ay, ds=0.1)
258+
target_speed = 10.0 / 3.6
259+
260+
sp = calc_speed_profile(cx, cy, cyaw, target_speed)
261+
262+
t, x, y, yaw, v = closed_loop_prediction(cx, cy, cyaw, ck, sp, goal)
263+
264+
if animation:
265+
matplotrecorder.save_movie("animation.gif", 0.1) # gif is ok.
266+
267+
flg, _ = plt.subplots(1)
268+
plt.plot(ax, ay, "xb", label="input")
269+
plt.plot(cx, cy, "-r", label="spline")
270+
plt.plot(x, y, "-g", label="tracking")
271+
plt.grid(True)
272+
plt.axis("equal")
273+
plt.xlabel("x[m]")
274+
plt.ylabel("y[m]")
275+
plt.legend()
276+
277+
flg, ax = plt.subplots(1)
278+
plt.plot(s, [math.degrees(iyaw) for iyaw in cyaw], "-r", label="yaw")
279+
plt.grid(True)
280+
plt.legend()
281+
plt.xlabel("line length[m]")
282+
plt.ylabel("yaw angle[deg]")
283+
284+
flg, ax = plt.subplots(1)
285+
plt.plot(s, ck, "-r", label="curvature")
286+
plt.grid(True)
287+
plt.legend()
288+
plt.xlabel("line length[m]")
289+
plt.ylabel("curvature [1/m]")
290+
291+
plt.show()
292+
293+
294+
if __name__ == '__main__':
295+
main()

PathTracking/lqr/matplotrecorder

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Subproject commit aac964fe899f8ca52be11ffe0a95fdbc96f3efdc

PathTracking/lqr/pycubicspline

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Subproject commit 8563587146749449db982c7821c4c983fc760800

PathTracking/lqr/unicycle_model.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#! /usr/bin/python
2+
# -*- coding: utf-8 -*-
3+
"""
4+
5+
6+
author Atsushi Sakai
7+
"""
8+
9+
import math
10+
11+
dt = 0.1 # [s]
12+
L = 2.9 # [m]
13+
14+
15+
class State:
16+
def __init__(self, x=0.0, y=0.0, yaw=0.0, v=0.0):
17+
self.x = x
18+
self.y = y
19+
self.yaw = yaw
20+
self.v = v
21+
22+
23+
def update(state, a, delta):
24+
state.x = state.x + state.v * math.cos(state.yaw) * dt
25+
state.y = state.y + state.v * math.sin(state.yaw) * dt
26+
state.yaw = state.yaw + state.v / L * math.tan(delta) * dt
27+
state.v = state.v + a * dt
28+
29+
return state
30+
31+
32+
if __name__ == '__main__':
33+
print("start unicycle simulation")
34+
import matplotlib.pyplot as plt
35+
36+
T = 100
37+
a = [1.0] * T
38+
delta = [math.radians(1.0)] * T
39+
# print(delta)
40+
# print(a, delta)
41+
42+
state = State()
43+
44+
x = []
45+
y = []
46+
yaw = []
47+
v = []
48+
49+
for (ai, di) in zip(a, delta):
50+
state = update(state, ai, di)
51+
52+
x.append(state.x)
53+
y.append(state.y)
54+
yaw.append(state.yaw)
55+
v.append(state.v)
56+
57+
flg, ax = plt.subplots(1)
58+
plt.plot(x, y)
59+
plt.axis("equal")
60+
plt.grid(True)
61+
62+
flg, ax = plt.subplots(1)
63+
plt.plot(v)
64+
plt.grid(True)
65+
66+
plt.show()

0 commit comments

Comments
 (0)