Skip to content
Snippets Groups Projects
linearization.m 4.30 KiB
% Implements the nonlinear model and creates state-space matrices A and B
% using MATLAB's symbolic differentiation.
%
% The model physics is taken from Matt Rich's "Model Development..."
% sections 4.1 and 4.2
%
% This linearization assumes the rotor orientation in the simulink model:
% 1     2
%   +dx
%      +dy
%
% 3     4
% In some of the internal simulink files, these are indexed 0, ..., 3
% instead of 1, .., 4. The order is unchanged. 
% Note that the final result should be the same regardless of rotor
% orientation, but everything internal to this function and its parameters
% need to be consistent.
function [A, B] = linearization(m, g, Kt, Kh, Kd, delta_T_z, rx, ry, rz, J, Jreq, Vb, Rm, Kv, Kq, If, signal_mixer)
    % Symbolic state variables
    syms u v w p q r x y z ph th ps
    
    % Rotation matrices. After linearization, these are equivalent to I
    L_EB = [...
        cos(th)*cos(ps), sin(ph)*sin(th)*cos(ps)-cos(ph)*sin(ps), cos(ph)*sin(th)*cos(ps)+sin(ph)*sin(ps)
        cos(th)*sin(ps), sin(ph)*sin(th)*sin(ps)+cos(ph)*cos(ps), cos(ph)*sin(th)*sin(ps)-sin(ph)*cos(ps)
        -sin(th),        sin(ph)*cos(th),                         cos(ph)*cos(th)];
    A_EB = [...
        1,  0,       -sin(th)
        0,  cos(ph),  sin(ph)*cos(th)
        0, -sin(ph),  cos(ph)*cos(th)];
    
    % Rotor positions
    r_h = [...
        -rx,  rx, -rx,  rx
         ry,  ry, -ry, -ry
        -rz, -rz, -rz, -rz
    ];

    % Gamma matrices select force dimensions
    Gamma_H = diag([1, 1, 0]);
    Gamma_Omega = [zeros(2,4); 1, -1, -1, 1];
    
    % uT, uA, uE, uR
    u_c = sym('u_c_%d', [1 4]);
    % Individual rotor commands
    u_p = (signal_mixer * u_c')';
    
    % Rotor angular velocities
    % These values assume no motor transient
    % (i.e. speeds reached immediately)
    omega = sym('w_%d', [1 4]);
    for i = 1:4
        omega(i) = (-1 + sqrt(1 - 4*Rm*Kv*Kq*Kd*(Kv*Rm*If - Kv*u_p(i)*Vb)))/(2*Rm*Kv*Kq*Kd);
    end
    
    % Variable vectors
    v_B_0   = [u; v; w];
    Omega_B = [p; q; r];
    
    % Rotor velocity
    for i = 1:4
        v_h(:,i) = v_B_0 + cross(Omega_B, r_h(:,i));
    end
    
    % Gravity
    G_B = m*g*[-sin(th); cos(th)*sin(ph); cos(th)*cos(ph)];
    
    % Force
    % Rotor Thrust (3 directions x 4 rotors matrix)
    T_B = [zeros(2, 4); -(Kt*omega.^2 + delta_T_z*v_h(3,:).*omega)];
    % Rotor In-plane drag (3 directions x 4 rotors matrix)
    H_B = -(Kh*ones(3,1)*omega).*(Gamma_H*v_h);
    % Total Force (Gravity + rotor force summed over all rotors)
    F_B = G_B + sum(T_B + H_B, 2);

    % Torque
    for i = 1:4
        % Force Lever-arm Torque
        Q_B_F(:,i) = cross(r_h(:,i), T_B(:,i) + H_B(:,i));
        % Change of Angular Momentum Torque (assuming no motor transient)
        Q_B_L(:,i) = -Jreq*cross(Omega_B, omega(i)*Gamma_Omega(:,i));
        % Drag/Induced Torque
        Q_B_d(:,i) = -Kd*omega(i)^2*Gamma_Omega(:,i);
    end
    % Total Torque
    Q_B = sum(Q_B_F + Q_B_d + Q_B_L, 2);
    
    % Define state vector Lambda and its time derivative, Lambda_dot
    Lambda = [u, v, w, p, q, r, x, y, z, ph, th, ps];
    Lambda_dot = [...
        1/m * F_B - cross(Omega_B, v_B_0)
        J^-1 * Q_B - cross(J^-1 * Omega_B, J * Omega_B)
        L_EB * v_B_0
        A_EB * Omega_B
    ];

    % A is partial of Lambda_dot wrt Lambda
    for i = 1:12
        for j = 1:12
            symA(i, j) = diff(Lambda_dot(i), Lambda(j));
        end
    end
    
    % B is partial of Lambda_dot wrt u_T, u_A, u_E, u_R
    for i = 1:12
        for j = 1:4
            symB(i, j) = diff(Lambda_dot(i), u_c(j)); 
        end
    end
    
    % Set equilibrium values
    u = 0; v = 0; w = 0; p = 0; q = 0; r = 0; x = 0; y = 0; z = 0; ph = 0; th = 0; ps = 0;
    u_c_1 = 0; u_c_2 = 0; u_c_3 = 0; u_c_4 = 0;
    
    % Binary search for equilibrium throttle value
    throttle_max = 1; throttle_min = 0;
    while (abs(double(subs(F_B(3)))) > 1e-7)
        u_c_1 = (throttle_max + throttle_min)/2;
        if (double(subs(F_B(3))) > 0)
            throttle_min = double(u_c_1);
        else
            throttle_max = double(u_c_1);
        end
    end
    
    % Convert symbolic linearization matrices to double values
    A = double(subs(symA));
    B = double(subs(symB));
end
%#ok<*AGROW>