-
Notifications
You must be signed in to change notification settings - Fork 0
/
throttle.py
110 lines (83 loc) · 3.72 KB
/
throttle.py
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
import math
import numpy as np
from steering import PIDController
# from steering import PIDController as PID
maxLateral = 20 # assuming maximum lateral acceleration of 20 m/s/s
maxLongitudinal = 30 # assuming maximum longitudinal acceleration of 20 m/s/s
class Circle:
def perpBis(point1, point2):
m1 = -1 / ((point2[1] - point1[1]) / (point2[0] - point1[0]))
mp1 = (points[:, 0] + points[:, 1]) / 2
b1 = mp1[1] - m1 * mp1[0]
return m1, b1
def getCircle(points):
# find 2 perpendicular bisectors
# check if y1 == y0 or y2 == y0 or x1 == x0 or x2 == x0
if (
np.array_equal(points[:, 0], points[:, 1])
or np.array_equal(points[:, 0], points[:, 2])
or np.array_equal(points[:, 1], points[:, 2])
):
raise ValueError("Two or more points are the same")
# find intersection of 2 perpendicular bisectors, considering cases when slope = 0 or is undefined
if points[1, 0] == points[1, 1]:
xIntersect = (points[0, 0] + points[0, 1]) / 2
elif points[1, 0] == points[1, 2]:
xIntersect = (points[0, 0] + points[0, 2]) / 2
else:
m1, b1 = Circle.perpBis(points[:, 0], points[:, 1])
m2, b2 = Circle.perpBis(points[:, 0], points[:, 2])
xIntersect = (b2 - b1) / (m1 - m2)
if points[1, 0] == points[1, 1]:
yIntersect = (points[1, 0] + points[1, 1]) / 2
elif points[1, 0] == points[1, 2]:
yIntersect = (points[1, 0] + points[1, 2]) / 2
else:
m1, b1 = Circle.perpBis(points[:, 0], points[:, 1])
m2, b2 = Circle.perpBis(points[:, 0], points[:, 2])
yIntersect = (b2 - b1) / (m1 - m2)
radius = math.sqrt(
(points[0, 2] - xIntersect) ** 2 + (points[1, 2] - yIntersect) ** 2
)
return (xIntersect, yIntersect, radius)
def getCentripetal(
points, velocity
): # get centripetal acceleration from points given a velocity
radius = Circle.getCircle(points)[2]
return velocity * velocity / radius
def getVelo(
points, accel
): # get velocity from points given a centripetal acceleration
radius = Circle.getCircle(points)[2]
return math.sqrt(accel * radius)
class Throttle:
def __init__(self, maxLat, maxLon, points, lookAheadTime, Kp, Ki, Kd):
self.maxLat = maxLat # maximum lateral acceleration before capsizing
self.maxLon = maxLon # maximum longitudinal acceleration
self.points = points
self.lookAheadTime = lookAheadTime
self.current = 0
self.controller = PID(0, Kp, Ki, Kd)
def getAccel(self, velocity):
lookAheadDist = velocity * self.lookAheadTime
return Circle.getCentripetal(
self.points[:, lookAheadDist * 10 : lookAheadDist * 10 + 2]
) # multiply lookAheadDist by 10 because path point spacing is 10 cm. This can be changed to a variable in a path class, for example.
def getMaxVelo(self):
lookAheadDist = velocity * self.lookAheadTime
return Circle.getVelo(
self.points[:, lookAheadDist * 10 : lookAheadDist * 10 + 2]
)
def update(self, accel, velo, dt):
actualVelo = (
accel * self.lookAheadTime + velo
) # predict velocity at lookahead point
desiredVelo = self.getMaxVelo(
self.maxLat
) # get maximum velocity before capsizing at lookahead distance
self.controller.updateError(actualVelo, dt)
self.controller.updateSetpoint(desiredVelo)
return self.controller.evaluate()
points = np.array([(-5, 0, 5), (0, -5, 0)])
velocity = 10
print(Circle.getCentripetal(points, velocity))