Quadrotor Trajectory Optimization:
This tutorial walks through the setup, simulation and visuallization of a Quadrotor (Quadcopter) drone model controlled using a Geometric controller. Specifically the simulation will demonstrate the use of a Geometric controller to track an optimal trajectory between an array of custom waypoints.
Copy-pasteable code:
using JuliaSimControl
using AerialVehicles
using SparseArrays, OSQP
using ModelingToolkit
using ControlSystemsBase.Polynomials
using StaticArrays
using WGLMakie, Rotations
Ta = [3, 3, 2, 2]
x = [0; 0; 0; 0]
xwp = [10 10 -10 0; 10 20 -10 0; 0 10 -5 0; 0 0 0 0]
vel = 0
px, py, pz, pyaw = optimal_trajectory_gen(Ta, x, xwp, vel)
# Wrapper for polynomial trajectory
gen_optimal_trajectory(t) = SynthesizePolynomialTrajectory(t, px, py, pz, pyaw, Ta)
# Generating structs for model and control parameters
QuadModel = QuadrotorModel(
mass = 4.34,
I = cat(0.0820, 0.0845, 0.1377, dims = (1, 2)),
Ts = 0.01,
g = 9.81,
)
ControlGains =
GeometricControlParams(kx = 4 * 4.34, kv = 5.6 * 4.34, kr = 8.81, kOmega = 2.54);
GeometricControllers =
Controllers(geometric_force_controller, geometric_attitude_controller)
func_sys, mtk_quad = build_quad_dynamics(QuadModel)
quad_mtk = complete(mtk_quad)
# Initial conditions
x0 = [0, 0, 0]
v0 = [0, 0, 0]
R0 = [1.0 0.0 0.0; 0.0 1.0 0.0; 0.0 0.0 1.0]
ω0 = [0, 0, 0]
x0_vec = build_initial_conditions(x0, v0, R0, ω0; quad_mtk = quad_mtk, func_sys = func_sys)
#Initiallising sim
Tf = 10.0
state_arr = run_quadrotor_sim(
Tf,
x0_vec,
quadrotor_dynamics!,
func_sys,
QuadModel,
GeometricControllers,
ControlGains,
gen_optimal_trajectory,
)
time_arr = state_arr.t
x_arr = state_arr[1, 1, :]
y_arr = state_arr[2, 1, :]
z_arr = state_arr[3, 1, :]
# Generating Quaternion matrix for Makie animation
R_arr = state_arr[7:15, 1, :]
quat_arr = quadrotor_RtoQuat(R_arr)
# Setting limits of animation
Scenelim = Rect(Vec3f(-0.1, -0.1, -0.1), Vec3f(15, 15, 15))
quad_simulator(
time_arr,
x_arr,
y_arr,
z_arr,
quat_arr;
quad_scale = 2.0,
limit_scene = Scenelim,
)
┌ Warning: Using arrays or dicts to store parameters of different types can hurt performance.
│ Consider using tuples instead.
└ @ SciMLBase /home/github_actions/depots/deepsea1.2/packages/SciMLBase/GmToj/src/performance_warnings.jl:32
Step-by-Step Solution:
1. Import required packages:
JuliaSimControl
: Provides the control design frameworkAerialVehicles
: Provides the model componentsModelingToolkit
: Provides the acausal modeling frameworkStaticArrays
: provides a framework for implementing statically sized arraysWGLMakie
: Provides the visuallization and animation frameworkRotations
: Provides tools for coordinate transformtion
using JuliaSimControl
using AerialVehicles
using SparseArrays, OSQP
using ModelingToolkit
using ControlSystemsBase.Polynomials
using StaticArrays
using WGLMakie, Rotations
2. Specify trajectory properties and time segments
Specify:
- Array of time segments
- The starting point of the Quadrotor
- Array of waypoints to be traversed
- Starting velocity
We utillize JuliaSimControl
's polynomial trajectory generation function optimal_trajectory_gen
to compute the coefficients of the polynomial trajectory in time. Finally, we utillize the SynthesizePolynomialTrajectory
function in AerialVehicles
along with a wrapper function to define the desired trajectory properties.
Ta = [3, 3, 2, 2]
x = [0; 0; 0; 0]
xwp = [10 10 -10 0; 10 20 -10 0; 0 10 -5 0; 0 0 0 0]
vel = 0
px, py, pz, pyaw = optimal_trajectory_gen(Ta, x, xwp, vel)
# Wrapper for polynomial trajectory
gen_optimal_trajectory(t) = SynthesizePolynomialTrajectory(t, px, py, pz, pyaw, Ta)
3. Define required properties and build model:
Specify:
- Properties of the quadrotor to be simulated
- Control gains
- Choice of controllers
After defining the model, build the model and use ModellingToolkit.jl
's complete function
QuadModel = QuadrotorModel(
mass = 4.34,
I = cat(0.0820, 0.0845, 0.1377, dims = (1, 2)),
Ts = 0.01,
g = 9.81,
)
ControlGains =
GeometricControlParams(kx = 4 * 4.34, kv = 5.6 * 4.34, kr = 8.81, kOmega = 2.54);
GeometricControllers =
Controllers(geometric_force_controller, geometric_attitude_controller)
func_sys, mtk_quad = build_quad_dynamics(QuadModel)
quad_mtk = complete(mtk_quad)
5. Define initial conditions
Set up the initial conditions for the simulation. The states of the quadrotor are:
- (x,y,z) position of the quadrotor
- Velocity components in the x,y,z direction
- Rotation matrix of representing orientation of the quadrotor
- Angular velocity components along the axes
x0 = [0, 0, 0]
v0 = [0, 0, 0]
R0 = [1.0 0.0 0.0; 0.0 1.0 0.0; 0.0 0.0 1.0]
ω0 = [0, 0, 0]
x0_vec = build_initial_conditions(x0, v0, R0, ω0; quad_mtk = quad_mtk, func_sys = func_sys)
6. Running the simulation
Here we set up and run the simulation for 10 sec. We use the run_quadrotor_sim
function to obtain the states of the quadrotor. This function requires
- Final time of the simulation
- Initial conditions
- Quadrotor dynamics function
JuliaSimControl
's FunctionSystem model of the dynamics- Choice of controllers
- Gains of the controllers
- Desired trajectory function wrapped over
SynthesizePolynomialTrajectory
function
Tf = 10.0
state_arr = run_quadrotor_sim(
Tf,
x0_vec,
quadrotor_dynamics!,
func_sys,
QuadModel,
GeometricControllers,
ControlGains,
gen_optimal_trajectory,
)
7. Visualising the trajectory
We use WGLMakie.jl
to visualize the orientation and trajectory of the quadrotor being simulated. In order to input data into the WGLMakie.jl
visualization tool, we need the rotation matrix to be converted to quaternion data. To do this we use quadrotor_RtoQuat
function. Once the converted quaternion array is obtained, we can run the visualization tool
time_arr = state_arr.t
x_arr = state_arr[1, 1, :]
y_arr = state_arr[2, 1, :]
z_arr = state_arr[3, 1, :]
# Generating Quaternion matrix for Makie animation
R_arr = state_arr[7:15, 1, :]
quat_arr = quadrotor_RtoQuat(R_arr)
# Setting limits of animation
Scenelim = Rect(Vec3f(-0.1, -0.1, -0.1), Vec3f(15, 15, 15))
quad_simulator(
time_arr,
x_arr,
y_arr,
z_arr,
quat_arr;
quad_scale = 2.0,
limit_scene = Scenelim,
)