""" Simulate a quadrotor following a 3D trajectory Author: Daniel Ingram (daniel-s-ingram) """ from math import cos, sin import numpy as np from Quadrotor import Quadrotor from TrajectoryGenerator import TrajectoryGenerator #Simulation parameters g = 9.81 m = 0.2 Ixx = 1 Iyy = 1 Izz = 1 T = 5 #Proportional coefficients Kp_x = 1 Kp_y = 1 Kp_z = 1 Kp_roll = 25 Kp_pitch = 25 Kp_yaw = 25 #Derivative coefficients Kd_x = 10 Kd_y = 10 Kd_z = 1 def quad_sim(x_c, y_c, z_c): """ Calculates the necessary thrust and torques for the quadrotor to follow the trajectory described by the sets of coefficients x_c, y_c, and z_c. """ x_pos = -5 y_pos = -5 z_pos = 5 x_vel = 0 y_vel = 0 z_vel = 0 x_acc = 0 y_acc = 0 z_acc = 0 roll = 0 pitch = 0 yaw = 0 roll_vel = 0 pitch_vel = 0 yaw_vel = 0 des_yaw = 0 dt = 0.1 t = 0 q = Quadrotor(x=x_pos, y=y_pos, z=z_pos, roll=roll, pitch=pitch, yaw=yaw, size=1) i = 0 while True: while t <= T: des_x_pos = calculate_position(x_c[i], t) des_y_pos = calculate_position(y_c[i], t) des_z_pos = calculate_position(z_c[i], t) des_x_vel = calculate_velocity(x_c[i], t) des_y_vel = calculate_velocity(y_c[i], t) des_z_vel = calculate_velocity(z_c[i], t) des_x_acc = calculate_acceleration(x_c[i], t) des_y_acc = calculate_acceleration(y_c[i], t) des_z_acc = calculate_acceleration(z_c[i], t) thrust = m*(g + des_z_acc + Kp_z*(des_z_pos - z_pos) + Kd_z*(des_z_vel - z_vel)) roll_torque = Kp_roll*(((des_x_acc*sin(des_yaw) - des_y_acc*cos(des_yaw))/g) - roll) pitch_torque = Kp_pitch*(((des_x_acc*cos(des_yaw) - des_y_acc*sin(des_yaw))/g) - pitch) yaw_torque = Kp_yaw*(des_yaw - yaw) roll_vel += roll_torque*dt/Ixx pitch_vel += pitch_torque*dt/Iyy yaw_vel += yaw_torque*dt/Izz roll += roll_vel*dt pitch += pitch_vel*dt yaw += yaw_vel*dt R = rotation_matrix(roll, pitch, yaw) acc = (np.matmul(R, np.array([0, 0, thrust]).T) - np.array([0, 0, m*g]).T)/m x_acc = acc[0] y_acc = acc[1] z_acc = acc[2] x_vel += x_acc*dt y_vel += y_acc*dt z_vel += z_acc*dt x_pos += x_vel*dt y_pos += y_vel*dt z_pos += z_vel*dt q.update_pose(x_pos, y_pos, z_pos, roll, pitch, yaw) t += dt t = 0 i = (i+1)%4 def calculate_position(c, t): """ Calculates a position given a set of quintic coefficients and a time. Args c: List of coefficients generated by a quintic polynomial trajectory generator. t: Time at which to calculate the position Returns Position """ return c[0]*t**5 + c[1]*t**4 + c[2]*t**3 + c[3]*t**2 + c[4]*t + c[5] def calculate_velocity(c, t): """ Calculates a velocity given a set of quintic coefficients and a time. Args c: List of coefficients generated by a quintic polynomial trajectory generator. t: Time at which to calculate the velocity Returns Velocity """ return 5*c[0]*t**4 + 4*c[1]*t**3 + 3*c[2]*t**2 + 2*c[3]*t + c[4] def calculate_acceleration(c, t): """ Calculates an acceleration given a set of quintic coefficients and a time. Args c: List of coefficients generated by a quintic polynomial trajectory generator. t: Time at which to calculate the acceleration Returns Acceleration """ return 20*c[0]*t**3 + 12*c[1]*t**2 + 6*c[2]*t + 2*c[3] def rotation_matrix(roll, pitch, yaw): """ Calculates the ZYX rotation matrix. Args Roll: Angular position about the x-axis in radians. Pitch: Angular position about the y-axis in radians. Yaw: Angular position about the z-axis in radians. Returns 3x3 rotation matrix as NumPy array """ return np.array( [[cos(yaw)*cos(pitch), -sin(yaw)*cos(roll)+cos(yaw)*sin(pitch)*sin(roll), sin(yaw)*sin(roll)+cos(yaw)*sin(pitch)*cos(roll)], [sin(yaw)*cos(pitch), cos(yaw)*cos(roll)+sin(yaw)*sin(pitch)*sin(roll), -cos(yaw)*sin(roll)+sin(yaw)*sin(pitch)*cos(roll)], [-sin(pitch), cos(pitch)*sin(roll), cos(pitch)*cos(yaw)] ]) def main(): """ Calculates the x, y, z coefficients for the four segments of the trajectory """ x_coeffs = [[], [], [], []] y_coeffs = [[], [], [], []] z_coeffs = [[], [], [], []] waypoints = [[-5, -5, 5], [5, -5, 5], [5, 5, 5], [-5, 5, 5]] for i in range(4): traj = TrajectoryGenerator(waypoints[i], waypoints[(i+1)%4], T) traj.solve() x_coeffs[i] = traj.x_c y_coeffs[i] = traj.y_c z_coeffs[i] = traj.z_c quad_sim(x_coeffs, y_coeffs, z_coeffs) if __name__ == "__main__": main()