Skip to content

Commit 833f93c

Browse files
authored
Merge pull request AtsushiSakai#98 from daniel-s-ingram/master
Close AtsushiSakai#98 Quadrotor simulation
2 parents d4d73ad + 5b0a60a commit 833f93c

File tree

3 files changed

+344
-0
lines changed

3 files changed

+344
-0
lines changed

AerialNavigation/Quadrotor.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
"""
2+
Class for plotting a quadrotor
3+
4+
Author: Daniel Ingram (daniel-s-ingram)
5+
"""
6+
7+
from math import cos, sin
8+
import numpy as np
9+
import matplotlib.pyplot as plt
10+
from mpl_toolkits.mplot3d import Axes3D
11+
12+
class Quadrotor():
13+
def __init__(self, x=0, y=0, z=0, roll=0, pitch=0, yaw=0, size=0.25):
14+
self.p1 = np.array([size/2, 0, 0, 1]).T
15+
self.p2 = np.array([-size/2, 0, 0, 1]).T
16+
self.p3 = np.array([0, size/2, 0, 1]).T
17+
self.p4 = np.array([0, -size/2, 0, 1]).T
18+
19+
self.x_data = []
20+
self.y_data = []
21+
self.z_data = []
22+
23+
plt.ion()
24+
25+
fig = plt.figure()
26+
self.ax = fig.add_subplot(111, projection='3d')
27+
28+
self.update_pose(x, y, z, roll, pitch, yaw)
29+
30+
def update_pose(self, x, y, z, roll, pitch, yaw):
31+
self.x = x
32+
self.y = y
33+
self.z = z
34+
self.roll = roll
35+
self.pitch = pitch
36+
self.yaw = yaw
37+
self.x_data.append(x)
38+
self.y_data.append(y)
39+
self.z_data.append(z)
40+
self.plot()
41+
42+
def transformation_matrix(self):
43+
x = self.x
44+
y = self.y
45+
z = self.z
46+
roll = self.roll
47+
pitch = self.pitch
48+
yaw = self.yaw
49+
return np.array(
50+
[[cos(yaw)*cos(pitch), -sin(yaw)*cos(roll)+cos(yaw)*sin(pitch)*sin(roll), sin(yaw)*sin(roll)+cos(yaw)*sin(pitch)*cos(roll), x],
51+
[sin(yaw)*cos(pitch), cos(yaw)*cos(roll)+sin(yaw)*sin(pitch)*sin(roll), -cos(yaw)*sin(roll)+sin(yaw)*sin(pitch)*cos(roll), y],
52+
[-sin(pitch), cos(pitch)*sin(roll), cos(pitch)*cos(yaw), z]
53+
])
54+
55+
def plot(self):
56+
T = self.transformation_matrix()
57+
58+
p1_t = np.matmul(T, self.p1)
59+
p2_t = np.matmul(T, self.p2)
60+
p3_t = np.matmul(T, self.p3)
61+
p4_t = np.matmul(T, self.p4)
62+
63+
plt.cla()
64+
65+
self.ax.plot([p1_t[0], p2_t[0], p3_t[0], p4_t[0]],
66+
[p1_t[1], p2_t[1], p3_t[1], p4_t[1]],
67+
[p1_t[2], p2_t[2], p3_t[2], p4_t[2]], 'k.')
68+
69+
self.ax.plot([p1_t[0], p2_t[0]], [p1_t[1], p2_t[1]], [p1_t[2], p2_t[2]], 'r-')
70+
self.ax.plot([p3_t[0], p4_t[0]], [p3_t[1], p4_t[1]], [p3_t[2], p4_t[2]], 'r-')
71+
72+
self.ax.plot(self.x_data, self.y_data, self.z_data, 'b:')
73+
74+
plt.xlim(-5, 5)
75+
plt.ylim(-5, 5)
76+
self.ax.set_zlim(0, 10)
77+
78+
plt.show()
79+
plt.pause(0.001)
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
"""
2+
Generates a quintic polynomial trajectory.
3+
4+
Author: Daniel Ingram (daniel-s-ingram)
5+
"""
6+
7+
import numpy as np
8+
9+
class TrajectoryGenerator():
10+
def __init__(self, start_pos, des_pos, T, start_vel=[0,0,0], des_vel=[0,0,0], start_acc=[0,0,0], des_acc=[0,0,0]):
11+
self.start_x = start_pos[0]
12+
self.start_y = start_pos[1]
13+
self.start_z = start_pos[2]
14+
15+
self.des_x = des_pos[0]
16+
self.des_y = des_pos[1]
17+
self.des_z = des_pos[2]
18+
19+
self.start_x_vel = start_vel[0]
20+
self.start_y_vel = start_vel[1]
21+
self.start_z_vel = start_vel[2]
22+
23+
self.des_x_vel = des_vel[0]
24+
self.des_y_vel = des_vel[1]
25+
self.des_z_vel = des_vel[2]
26+
27+
self.start_x_acc = start_acc[0]
28+
self.start_y_acc = start_acc[1]
29+
self.start_z_acc = start_acc[2]
30+
31+
self.des_x_acc = des_acc[0]
32+
self.des_y_acc = des_acc[1]
33+
self.des_z_acc = des_acc[2]
34+
35+
self.T = T
36+
37+
def solve(self):
38+
A = np.array(
39+
[[0, 0, 0, 0, 0, 1],
40+
[self.T**5, self.T**4, self.T**3, self.T**2, self.T, 1],
41+
[0, 0, 0, 0, 1, 0],
42+
[5*self.T**4, 4*self.T**3, 3*self.T**2, 2*self.T, 1, 0],
43+
[0, 0, 0, 2, 0, 0],
44+
[20*self.T**3, 12*self.T**2, 6*self.T, 2, 0, 0]
45+
])
46+
47+
b_x = np.array(
48+
[[self.start_x],
49+
[self.des_x],
50+
[self.start_x_vel],
51+
[self.des_x_vel],
52+
[self.start_x_acc],
53+
[self.des_x_acc]
54+
])
55+
56+
b_y = np.array(
57+
[[self.start_y],
58+
[self.des_y],
59+
[self.start_y_vel],
60+
[self.des_y_vel],
61+
[self.start_y_acc],
62+
[self.des_y_acc]
63+
])
64+
65+
b_z = np.array(
66+
[[self.start_z],
67+
[self.des_z],
68+
[self.start_z_vel],
69+
[self.des_z_vel],
70+
[self.start_z_acc],
71+
[self.des_z_acc]
72+
])
73+
74+
self.x_c = np.linalg.solve(A, b_x)
75+
self.y_c = np.linalg.solve(A, b_y)
76+
self.z_c = np.linalg.solve(A, b_z)

AerialNavigation/quad_sim.py

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
"""
2+
Simulate a quadrotor following a 3D trajectory
3+
4+
Author: Daniel Ingram (daniel-s-ingram)
5+
"""
6+
7+
from math import cos, sin
8+
import numpy as np
9+
from Quadrotor import Quadrotor
10+
from TrajectoryGenerator import TrajectoryGenerator
11+
12+
#Simulation parameters
13+
g = 9.81
14+
m = 0.2
15+
Ixx = 1
16+
Iyy = 1
17+
Izz = 1
18+
T = 5
19+
20+
#Proportional coefficients
21+
Kp_x = 1
22+
Kp_y = 1
23+
Kp_z = 1
24+
Kp_roll = 25
25+
Kp_pitch = 25
26+
Kp_yaw = 25
27+
28+
#Derivative coefficients
29+
Kd_x = 10
30+
Kd_y = 10
31+
Kd_z = 1
32+
33+
def quad_sim(x_c, y_c, z_c):
34+
"""
35+
Calculates the necessary thrust and torques for the quadrotor to
36+
follow the trajectory described by the sets of coefficients
37+
x_c, y_c, and z_c.
38+
"""
39+
x_pos = -5
40+
y_pos = -5
41+
z_pos = 5
42+
x_vel = 0
43+
y_vel = 0
44+
z_vel = 0
45+
x_acc = 0
46+
y_acc = 0
47+
z_acc = 0
48+
roll = 0
49+
pitch = 0
50+
yaw = 0
51+
roll_vel = 0
52+
pitch_vel = 0
53+
yaw_vel = 0
54+
55+
des_yaw = 0
56+
57+
dt = 0.1
58+
t = 0
59+
60+
q = Quadrotor(x=x_pos, y=y_pos, z=z_pos, roll=roll, pitch=pitch, yaw=yaw, size=1)
61+
62+
i = 0
63+
64+
while True:
65+
while t <= T:
66+
des_x_pos = calculate_position(x_c[i], t)
67+
des_y_pos = calculate_position(y_c[i], t)
68+
des_z_pos = calculate_position(z_c[i], t)
69+
des_x_vel = calculate_velocity(x_c[i], t)
70+
des_y_vel = calculate_velocity(y_c[i], t)
71+
des_z_vel = calculate_velocity(z_c[i], t)
72+
des_x_acc = calculate_acceleration(x_c[i], t)
73+
des_y_acc = calculate_acceleration(y_c[i], t)
74+
des_z_acc = calculate_acceleration(z_c[i], t)
75+
76+
thrust = m*(g + des_z_acc + Kp_z*(des_z_pos - z_pos) + Kd_z*(des_z_vel - z_vel))
77+
78+
roll_torque = Kp_roll*(((des_x_acc*sin(des_yaw) - des_y_acc*cos(des_yaw))/g) - roll)
79+
pitch_torque = Kp_pitch*(((des_x_acc*cos(des_yaw) - des_y_acc*sin(des_yaw))/g) - pitch)
80+
yaw_torque = Kp_yaw*(des_yaw - yaw)
81+
82+
roll_vel += roll_torque*dt/Ixx
83+
pitch_vel += pitch_torque*dt/Iyy
84+
yaw_vel += yaw_torque*dt/Izz
85+
86+
roll += roll_vel*dt
87+
pitch += pitch_vel*dt
88+
yaw += yaw_vel*dt
89+
90+
R = rotation_matrix(roll, pitch, yaw)
91+
acc = (np.matmul(R, np.array([0, 0, thrust]).T) - np.array([0, 0, m*g]).T)/m
92+
x_acc = acc[0]
93+
y_acc = acc[1]
94+
z_acc = acc[2]
95+
x_vel += x_acc*dt
96+
y_vel += y_acc*dt
97+
z_vel += z_acc*dt
98+
x_pos += x_vel*dt
99+
y_pos += y_vel*dt
100+
z_pos += z_vel*dt
101+
102+
q.update_pose(x_pos, y_pos, z_pos, roll, pitch, yaw)
103+
104+
t += dt
105+
106+
t = 0
107+
i = (i+1)%4
108+
109+
def calculate_position(c, t):
110+
"""
111+
Calculates a position given a set of quintic coefficients and a time.
112+
113+
Args
114+
c: List of coefficients generated by a quintic polynomial
115+
trajectory generator.
116+
t: Time at which to calculate the position
117+
118+
Returns
119+
Position
120+
"""
121+
return c[0]*t**5 + c[1]*t**4 + c[2]*t**3 + c[3]*t**2 + c[4]*t + c[5]
122+
123+
def calculate_velocity(c, t):
124+
"""
125+
Calculates a velocity given a set of quintic coefficients and a time.
126+
127+
Args
128+
c: List of coefficients generated by a quintic polynomial
129+
trajectory generator.
130+
t: Time at which to calculate the velocity
131+
132+
Returns
133+
Velocity
134+
"""
135+
return 5*c[0]*t**4 + 4*c[1]*t**3 + 3*c[2]*t**2 + 2*c[3]*t + c[4]
136+
137+
def calculate_acceleration(c, t):
138+
"""
139+
Calculates an acceleration given a set of quintic coefficients and a time.
140+
141+
Args
142+
c: List of coefficients generated by a quintic polynomial
143+
trajectory generator.
144+
t: Time at which to calculate the acceleration
145+
146+
Returns
147+
Acceleration
148+
"""
149+
return 20*c[0]*t**3 + 12*c[1]*t**2 + 6*c[2]*t + 2*c[3]
150+
151+
def rotation_matrix(roll, pitch, yaw):
152+
"""
153+
Calculates the ZYX rotation matrix.
154+
155+
Args
156+
Roll: Angular position about the x-axis in radians.
157+
Pitch: Angular position about the y-axis in radians.
158+
Yaw: Angular position about the z-axis in radians.
159+
160+
Returns
161+
3x3 rotation matrix as NumPy array
162+
"""
163+
return np.array(
164+
[[cos(yaw)*cos(pitch), -sin(yaw)*cos(roll)+cos(yaw)*sin(pitch)*sin(roll), sin(yaw)*sin(roll)+cos(yaw)*sin(pitch)*cos(roll)],
165+
[sin(yaw)*cos(pitch), cos(yaw)*cos(roll)+sin(yaw)*sin(pitch)*sin(roll), -cos(yaw)*sin(roll)+sin(yaw)*sin(pitch)*cos(roll)],
166+
[-sin(pitch), cos(pitch)*sin(roll), cos(pitch)*cos(yaw)]
167+
])
168+
169+
def main():
170+
"""
171+
Calculates the x, y, z coefficients for the four segments
172+
of the trajectory
173+
"""
174+
x_coeffs = [[], [], [], []]
175+
y_coeffs = [[], [], [], []]
176+
z_coeffs = [[], [], [], []]
177+
waypoints = [[-5, -5, 5], [5, -5, 5], [5, 5, 5], [-5, 5, 5]]
178+
179+
for i in range(4):
180+
traj = TrajectoryGenerator(waypoints[i], waypoints[(i+1)%4], T)
181+
traj.solve()
182+
x_coeffs[i] = traj.x_c
183+
y_coeffs[i] = traj.y_c
184+
z_coeffs[i] = traj.z_c
185+
186+
quad_sim(x_coeffs, y_coeffs, z_coeffs)
187+
188+
if __name__ == "__main__":
189+
main()

0 commit comments

Comments
 (0)