HybridVTOL Setup Instructions
Part A — One-time setup
1) Create the model & set solver
Open MATLAB → Home New Simulink Model.
Save as [Link].
Modeling Model Settings (gear icon)
Solver: Variable-step, ode45 (or ode23t), Max step = 0.005 (5 ms).
Stop time = 30.
2) Add the 6DOF plant (Aerospace Blockset)
In the Library Browser, search “Six DOF (Quaternion)” (Aerospace Blockset Motion). Drag it
Double-click it:
Mass m = 1.5
Inertia (diagonal) = [0.02 0.02 0.03] (kg·m2)
Gravity: keep on (block handles gravity automatically)
Initial position (NED) = [0 0 -2] (start 2 m above ground; Down is positive)
Initial velocity = [0 0 0]
Initial quaternion = [1 0 0 0] (level)
Note the two inputs to 6DOF: Forces (body) [Fx Fy Fz] and Moments (body) [L M N]. We will fe
3) Add sensors/utility conversions
From Aerospace Blockset Utilities, add:
Quaternion to Euler Angles (for , , )
(Optional) Direction Cosine Matrix if you need frame conversions later
Connect the 6DOF quaternion output to Quaternion to Euler; create scopes for:
1
Euler angles (phi, theta, psi)
Body rates [p q r] (from 6DOF)
Position NED [x y z] and Body velocities [u v w]
Part B — Parameters & signals
4) Create a parameter script
In MATLAB, make a new script vtol_params.m and paste:
% Physical
P.m = 1.5; % kg
P.J = diag([0.02 0.02 0.03]); % kg*m^2
P.g = 9.81;
% Geometry (quad arms in body frame, meters)
[Link] = 0.20;
P.r = [ +[Link], 0, 0; % front
0, +[Link], 0; % right
-[Link], 0, 0; % back
0, -[Link], 0]; % left
% Prop constants (example)
[Link] = 1.9e-5; % N/(rad/s)^2
[Link] = 2.6e-7; % N*m/(rad/s)^2
[Link] = [ +1; -1; +1; -1 ]; % rotor spin for reaction torque
% Limits
P.Fx_max = 6; P.Fy_max = 6; P.Fz_min = -15; % note Fz<0 is upward
P.L_max = 1.5; P.M_max = 1.5; P.N_max = 0.8;
% Controller gains (starter)
[Link]=1.2; [Link]=0.3; [Link]=0;
[Link]=1.2; [Link]=0.3; [Link]=0;
[Link]=3.5; [Link]=1.0; [Link]=0.5;
[Link]=0.08; [Link]=0.01; % roll rate
[Link]=0.08; [Link]=0.01; % pitch rate
[Link]=0.06; [Link]=0.01; % yaw rate
% Tilt-rotor (if using tilt): initial alphas (rad)
P.alpha0 = zeros(4,1);
Run vtol_params in MATLAB so variables enter the workspace.
5) Create setpoint sources
Add Constant blocks:
2
pos_ref = [10; 0; -2] (go 10 m forward, hold -2 m altitude)
psi_ref = 0 (yaw hold)
Add Mux if needed to bundle signals.
Part C — Controller (simple but works)
We’ll build a minimal two-layer controller:
Outer loop: position/velocity → desired body force [Fx* Fy* Fz*]
Inner loop: attitude rates → desired moments [L* M* N*]
6) Outer-loop (position → force)
Compute position error: add Sum block (pos_ref - pos_NED).
Convert NED velocity to body velocity (we’ll just use 6DOF body vel [u v w]).
For x-body speed command, a simple proportional on position error in x is okay if your headi
PID (velocity) on u: error = (u_cmd - u), where u_cmd = clamp( 0.8 * (pos_ref_x - x), -1.5,
Implement with Gain + Saturation + PID (P+D) to produce Fx*.
Side velocity v loop → Fy*.
Vertical controller on z (Down positive):
Altitude error ez = (z_ref - z) (remember z_ref = 2 for 2 m AGL).
PD+I on w (down velocity): produce Fz* (keep in mind upward lift is negative Fz).
Saturate forces: use Saturation blocks to enforce Fx, Fy, Fz within P.F* limits.
(If that feels like a lot: start only with z-hold (hover) first; add x, y later.)
7) Inner-loop (rates → moments)
Convert quaternion → Euler (phi, theta, psi) with the block you added.
Make simple rate commands from forces:
Roll cmd from Fy* (push right → roll right): p_cmd k_roll * Fy*
Pitch cmd from Fx* (push forward → pitch forward): q_cmd -k_pitch * Fx*
Yaw cmd: r_cmd = k_yaw * (psi_ref - psi)
Start with small constants: k_roll = 0.15, k_pitch = 0.15, k_yaw = 0.8 (Constants blocks).
3
Add PID (PD) controllers on p, q, r (body rates from 6DOF) to output L*, M*, N*.
Saturate moments with P.L_max, P.M_max, P.N_max.
Part D — Allocator (actuators → body forces/moments)
For hybrid VTOL you might have quad rotors, tilt angles, and a pusher. We’ll begin with a si
8) Add a MATLAB Function block
Simulink User-Defined Functions MATLAB Function; name it ForcesMomentsAllocator.
Double-click and paste:
function [Fx,Fy,Fz,L,M,N] = alloc_forces(omega,alpha,pusher,defl)
% omega: [4x1] rotor speeds (rad/s)
% alpha: [4x1] tilt angles (rad), tilt about y_b (positive tilts thrust toward +x_b)
% pusher: scalar throttle (0..1) -> Fx_pusher (simple)
% defl: [3x1] [aileron; elevator; rudder] (rad) (optional aero not used in this minimal stub
% Access params from base
P = evalin(’base’,’P’);
Fx=0; Fy=0; Fz=0; L=0; M=0; N=0;
% Rotor forces/moments
for i=1:4
Ti = [Link] * omega(i)^2; % thrust magnitude
Qi = [Link] * omega(i)^2; % reaction torque (about z_b)
% Thrust vector with tilt about y_b: [sin(a) 0 -cos(a)] * T
c = cos(alpha(i)); s = sin(alpha(i));
Fi = [ s*Ti; 0; -c*Ti ];
% moment from arm + reaction torque
r_i = P.r(i,:).’; tau_arm = cross(r_i,Fi);
tau_react = [0; 0; [Link](i)*Qi];
% accumulate
Fx = Fx + Fi(1); Fy = Fy + Fi(2); Fz = Fz + Fi(3);
L = L + tau_arm(1); M = M + tau_arm(2); N = N + tau_arm(3) + tau_react(3);
end
% Simple pusher model: map throttle to Fx (tune k_push)
k_push = 8.0; % N at full throttle (example)
Fx = Fx + k_push * saturate01(pusher);
% (Optional) basic control-surface aero could be added here
% Saturations (safety)
Fx = max(min(Fx, P.Fx_max), -P.Fx_max);
Fy = max(min(Fy, P.Fy_max), -P.Fy_max);
Fz = min(Fz, 0); % upward is negative; avoid adding down-force
4
L = max(min(L, P.L_max), -P.L_max);
M = max(min(M, P.M_max), -P.M_max);
N = max(min(N, P.N_max), -P.N_max);
function y=saturate01(u)
y = min(max(u,0),1);
end
Create input ports: omega [4x1], alpha [4x1], pusher scalar, defl [3x1].
Outputs: Fx,Fy,Fz,L,M,N.
9) Drive the allocator
For a quick first run, use Constant blocks:
omega = [350; 350; 350; 350] (rad/s) (hoverish guess)
alpha = [0; 0; 0; 0] (all lift)
pusher = 0
defl = [0;0;0]
Later, you’ll replace these with actuator commands coming from the controller:
Map [Fx*,Fy*,Fz*,L*,M*,N*] to desired omega/alpha/pusher (a second, inverse allocator). For
10) Connect allocator to 6DOF
Option A (actuator path) (keep for hybrid realism): use alloc_forces outputs → Mux [Fx Fy Fz
Option B (direct force path) (simpler for first test): skip the MATLAB function and directly
For a novice first success, do Option B now (controller → 6DOF). Keep the MATLAB Function in
Part E — Close the loop & test
11) Wire controller feedback
From 6DOF:
Body vel [u v w] → outer velocity PIDs
Pos NED [x y z] → position loop
Body rates [p q r] → inner rate PIDs
Quaternion → Euler (phi theta psi) → yaw loop
Set all PID sample times to -1 (inherit) and anti-windup ON.
5
12) Add scopes and logs
Scopes for: position, body velocity, Euler angles, body rates, forces/moments.
To Workspace (Array) for: min_d (if you add obstacle logic later), Fx,Fy,Fz, L,M,N, and omeg
13) First run: hover hold
Set pos_ref = [0; 0; -2], psi_ref = 0.
Comment out x/y force loops if needed; keep only z-loop → Fz*.
Connect [Fx Fy Fz L M N] = [0 0 Fz 0 0 0]* to 6DOF.
Run. Tune [Link].* until altitude holds with small overshoot (target: 10 cm).
14) Add forward motion
Set pos_ref = [10; 0; -2].
Enable x-loop → Fx*. Start with small gains (u_cmd limited to ~1 m/s).
Tune until it accelerates forward and stabilizes at 10 m without large pitch.
15) Add yaw control and y-loop
Enable psi_ref hold (r-loop → N*).
Enable y-loop → Fy* if you want lateral moves or path following.
Part F — Make it “Hybrid” (tilt + pusher)
16) Enable actuator path (allocator)
Uncomment / reconnect the MATLAB Function alloc_forces.
Build a simple inverse allocator (MATLAB Function) that maps desired forces/moments to omega
Use Fz* to set total rotor thrust
T=T
i
; split equally → omega.
Use Fx* to command tilt: alpha asin( clamp(Fx*/T, -0.4, 0.4) ).
6
Use Fy* initially ignored (or small roll bias via moment loop).
Use L*,M*,N* to add small differential thrusts / reaction torque trims.
Use pusher to offload Fx* when alpha grows beyond, say, 25° (blend).
Feed omega, alpha, pusher, defl to alloc_forces. Feed its [Fx Fy Fz L M N] to 6DOF.
(Tip: keep rate limiters on alpha (e.g., 30–60°/s) and on omega changes.)
17) Add a flight-mode/blender
Create a Mode value s [0,1] based on airspeed or tilt:
s=0 hover, s=1 forward flight.
Blend commands:
F* = (1-s)*F*hover + s*F*forward
* = (1-s)**hover + s**forward
During transitions:
Use External Reset on PID integrators.
Rate limit alpha and any setpoint changes.
Part G — Quality of life & safety
18) Safety checks (easy wins)
Saturation blocks on all commands.
Rate Limiters on v_cmd, omega, alpha.
Emergency hover: If tilt > 45° or moments saturate for > 0.5 s, set Fx*=Fy*=0, hold Fz* for
19) Ground contact (optional)
Add a simple ground plane: if z > 0, add a spring-damper upward force (NED Z down). This avo
20) Save a “working” version
File Save As: HybridVTOL_v1_hover_forward.slx.
Troubleshooting (common gotchas)
Drone shoots downward: You added gravity yourself. Remove any +mg; 6DOF adds gravity automat
Won’t rise: Remember upward lift is negative Fz (NED z positive Down). Your controller shoul
7
Spins uncontrollably: Check yaw moment signs and spin array; swap signs if necessary.
Crazy during mode change: Freeze integrators for 0.2–0.5 s, rate-limit tilt, bumpless transf