diff --git a/controls/model/loggingAnalysis/logAnalysis.m b/controls/model/loggingAnalysis/logAnalysis.m index d713a65bc2e51180c5078de694ef94f3bfa6f53b..ae48f90734c220bf920b8367ca5fca88411d84c7 100644 --- a/controls/model/loggingAnalysis/logAnalysis.m +++ b/controls/model/loggingAnalysis/logAnalysis.m @@ -31,6 +31,12 @@ yawrate_setpoint_model_data = yawrate_setpoint_model.signals.values(indices_40ms yaw_command_model_data = yaw_command_model.signals.values(indices_40ms); yaw_value_model_data = yaw_value_model.signals.values(indices_40ms); +% Pull duty cycle commands from model +PWM0_model = motorCommands.signals.values(indices_40ms, 1); +PWM1_model = motorCommands.signals.values(indices_40ms, 2); +PWM2_model = motorCommands.signals.values(indices_40ms, 3); +PWM3_model = motorCommands.signals.values(indices_40ms, 4); + %% Plot x control structure % Plot lateral controller output @@ -154,4 +160,37 @@ stairs(time_model_40ms, yaw_value_model_data, '.-'); hold off; title('Yaw Position'); xlabel('Time (s)'); ylabel('Value (rad)'); +legend('Log', 'Model', 'location', 'northwest'); + +%% Plot PWM Commands +figure(5); subplot(2, 2, 1); +stairs(time, PWM0,'.-'); hold on; grid minor; +stairs(time_model_40ms, PWM0_model, '.-'); hold off; +title('PWM0 Value'); +xlabel('Time (s)'); +ylabel('PWM0 Command'); +legend('Log', 'Model', 'location', 'northwest'); + +subplot(2, 2, 2); +stairs(time, PWM1,'.-'); hold on; grid minor; +stairs(time_model_40ms, PWM1_model, '.-'); hold off; +title('PWM1 Value'); +xlabel('Time (s)'); +ylabel('PWM1 Command'); +legend('Log', 'Model', 'location', 'northwest'); + +subplot(2, 2, 3); +stairs(time, PWM2,'.-'); hold on; grid minor; +stairs(time_model_40ms, PWM2_model, '.-'); hold off; +title('PWM2 Value'); +xlabel('Time (s)'); +ylabel('PWM2 Command'); +legend('Log', 'Model', 'location', 'northwest'); + +subplot(2, 2, 4); +stairs(time, PWM3,'.-'); hold on; grid minor; +stairs(time_model_40ms, PWM3_model, '.-'); hold off; +title('PWM3 Value'); +xlabel('Time (s)'); +ylabel('PWM3 Command'); legend('Log', 'Model', 'location', 'northwest'); \ No newline at end of file diff --git a/controls/model/modelParameters.m b/controls/model/modelParameters.m index 9ed5d14109548d4115835d454df256cc834e82f8..97115a7c498158b6e5d7765cb3de075772fee193 100644 --- a/controls/model/modelParameters.m +++ b/controls/model/modelParameters.m @@ -1,6 +1,7 @@ +temp = 0; % Log Analysis Toggle logAnalysisToggle = 1; % 1 for log analysis, 0 for normal operation - + % Define Simulink Runtime (if logAnalysisToggle is selected, this will be % automatically set based on the log files time) runtime = 20; @@ -31,23 +32,50 @@ x_controlled_o = 0; % Equilibrium lateral controller output y_controlled_o = 0; % Equilibrium longitudinal controller output yaw_controlled_o = 0; % Equilibrium yaw controller output - omega_o = sqrt((m*g)/(4*Kt)); % Equilibrium Rotor Speed + + % Determine Initial Conditions + + % Equilibrium rotor speeds + omega0_o = sqrt((m*g)/(4*Kt)); + omega1_o = sqrt((m*g)/(4*Kt)); + omega2_o = sqrt((m*g)/(4*Kt)); + omega3_o = sqrt((m*g)/(4*Kt)); % Equilibrium height controller output height_controlled_o = (((Rm*If + ... - + (((omega_o * 2 * Rm * Kv * Kq ... + + (((omega0_o * 2 * Rm * Kv * Kq ... * Kd + 1)^2) - 1)/(4* Rm*Kv^2*Kq ... *Kd))/Vb)*(Pmax- Pmin)+Pmin); + % Equilibrium positions + x_o = 0; + y_o = 0; + z_o = 0; + + % Equilibrium velocities + x_vel_o = 0; + y_vel_o = 0; + z_vel_o = 0; + + % Equilibrium angles + roll_o = 0; + pitch_o = 0; + yaw_o = 0; + + % Equilibrium angular rates + rollrate_o = 0; + pitchrate_o = 0; + yawrate_o = 0; + % Import Data and determine errors -if logAnalysisToggle == 1 +if logAnalysisToggle == 1 && temp == 0 % Import Data to Workspace data = importdata('loggingAnalysis/logFiles/logData.csv'); % Set up time vector time = data.data(:, 1); runtime = max(time); - + % Determine x position error x_setpoint = data.data(:, 25); x_position = data.data(:, 20); @@ -75,7 +103,7 @@ if logAnalysisToggle == 1 % Determine yaw error yaw_setpoint = zeros(length(time), 1); % NEEDS UPDATED WHEN LOG FILE INCLUDES YAW SETPOINT - yaw_value = ones(length(time), 1); % NEEDS UPDATED WHEN LOG FILE INCLUDES YAW VALUE + yaw_value = data.data(:,10);%(length(time), 1); % NEEDS UPDATED WHEN LOG FILE INCLUDES YAW VALUE yaw_error = timeseries(yaw_setpoint - yaw_value, time); % Determine pitch rate error @@ -98,4 +126,134 @@ if logAnalysisToggle == 1 y_command = data.data(:, 15); z_command = data.data(:, 8); yaw_command = data.data(:, 16); + + % Determine signal mix PWM values + PWM0 = data.data(:, 28); + PWM1 = data.data(:, 29); + PWM2 = data.data(:, 30); + PWM3 = data.data(:, 31); + + % Determine the initial height controller command + % height_controlled_o = 1; % NEEDS UPDATED WHEN LOG FILES INCLUDE THROTTLE COMMAND + + % Determine the initial rotor speeds based on PWM inputs + u_P0 = (PWM0(1) - Pmin) / (Pmax - Pmin); + u_P1 = (PWM1(1) - Pmin) / (Pmax - Pmin); + u_P2 = (PWM2(1) - Pmin) / (Pmax - Pmin); + u_P3 = (PWM3(1) - Pmin) / (Pmax - Pmin); + + Vb_eff_0 = u_P0 * Vb; + Vb_eff_1 = u_P1 * Vb; + Vb_eff_2 = u_P2 * Vb; + Vb_eff_3 = u_P3 * Vb; + + omega0_o = (-1 + sqrt(1 - 4*Rm*Kv*Kq*Kd*( Kv*Rm*If - Kv*Vb_eff_0))) / (2*Rm*Kv*Kq*Kd); + omega1_o = (-1 + sqrt(1 - 4*Rm*Kv*Kq*Kd*( Kv*Rm*If - Kv*Vb_eff_1))) / (2*Rm*Kv*Kq*Kd); + omega2_o = (-1 + sqrt(1 - 4*Rm*Kv*Kq*Kd*( Kv*Rm*If - Kv*Vb_eff_2))) / (2*Rm*Kv*Kq*Kd); + omega3_o = (-1 + sqrt(1 - 4*Rm*Kv*Kq*Kd*( Kv*Rm*If - Kv*Vb_eff_3))) / (2*Rm*Kv*Kq*Kd); + + % Determine initial positions for x, y, and z + x_o = x_position(1); + y_o = y_position(1); + z_o = z_position(1); + + % Determine initial velocity for x, y, and z + x_vel_o = (x_position(2) - x_position(1)) / (time(2) - time(1)); + y_vel_o = (y_position(2) - y_position(1)) / (time(2) - time(1)); + z_vel_o = (z_position(2) - z_position(1)) / (time(2) - time(1)); + + % Determine initial angles + roll_o = roll_value(1); + pitch_o = pitch_value(1); + yaw_o = yaw_value(1); + + % Determine initial angular rates + rollrate_o = rollrate_value(1); + pitchrate_o = pitchrate_value(1); + yawrate_o = yawrate_value(1); + +elseif logAnalysisToggle == 1 && temp == 1 + %%%%%% Commented out section until logging is .txt file based %%%%%% + % FNAME + % if you know the name of the log file that you want to parse, set the it + % here only if it is in the working directory. Otherwise, you may leave it + % blank. You will be able to choose the file to parse through an explorer + % window. + % + %fname = 'sampleLogFile.txt'; + fname = 'sampleLogFile.txt'; + fpath = ''; + + if(isempty(fname)) + [fname, fpath] = uigetfile('.txt','Select log file'); + end + + params.file.name = fname; % file name only + params.file.path = fpath; % file path only + params.file.pathName = [fpath fname]; % file path with file name + + [dataStruct, headers] = parse_log_model(params.file.pathName); + + time = dataStruct.Time.data; + runtime = max(time); + + % Determine x position error + x_setpoint = dataStruct.X_Setpoint_Constant.data; + x_position = dataStruct.VRPN_X_Constant.data; + x_error = timeseries(x_setpoint - x_position, time); + + % Determine y position error + y_setpoint = dataStruct.Y_Setpoint_Constant.data; + y_position = dataStruct.VRPN_Y_Constant.data; + y_error = timeseries(y_setpoint - y_position, time); + + % Determine z position error + z_setpoint = dataStruct.Z_Setpoint_Constant.data; + z_position = dataStruct.VRPN_Z_Constant.data; + z_error = timeseries(z_setpoint - z_position, time); + + % Determine pitch error + pitch_setpoint = dataStruct.X_pos_PID_Correction.data; + pitch_value = dataStruct.VRPN_Pitch_Constant.data; + pitch_error = timeseries(pitch_setpoint - pitch_value, time); + + % Determine roll error + roll_setpoint = dataStruct.Y_pos_PID_Correction.data; + roll_value = dataStruct.VRPN_Roll_Constant.data; + roll_error = timeseries(roll_setpoint - roll_value, time); + + % Determine yaw error + yaw_setpoint = zeros(length(time), 1); % NEEDS UPDATED WHEN LOG FILE INCLUDES YAW SETPOINT + yaw_value = zeros(length(time), 1); % NEEDS UPDATED WHEN LOG FILE INCLUDES YAW VALUE + yaw_error = timeseries(yaw_setpoint - yaw_value, time); + + % Determine pitch rate error + pitchrate_setpoint = dataStruct.Pitch_PID_Correction.data; + pitchrate_value = dataStruct.gyro_y.data; + pitchrate_error = timeseries(pitchrate_setpoint - pitchrate_value, time); + + % Determine roll rate error + rollrate_setpoint = dataStruct.Roll_PID_Correction.data; + rollrate_value = dataStruct.gyro_x.data; + rollrate_error = timeseries(rollrate_setpoint - rollrate_value, time); + + % Determine yaw rate error + yawrate_setpoint = dataStruct.Yaw_PID_Correction.data; + yawrate_value = dataStruct.gyro_z.data; + yawrate_error = timeseries(yawrate_setpoint - yawrate_value, time); + + % Pull motor commands from log + x_command = dataStruct.Pitch_Rate_PID_Correction; + y_command = dataStruct.Roll_Rate_PID_Correction; + z_command = dataStruct.Altitude_PID_Correction; + yaw_command = dataStruct.Yaw_Rate_PID_Correction; + + % Determine signal mix PWM values + PWM0 = dataStruct.Signal_Mixer_PWM_0.data; + PWM1 = dataStruct.Signal_Mixer_PWM_1.data; + PWM2 = dataStruct.Signal_Mixer_PWM_2.data; + PWM3 = dataStruct.Signal_Mixer_PWM_3.data; + end + + diff --git a/controls/model/parse_log_model.m b/controls/model/parse_log_model.m new file mode 100644 index 0000000000000000000000000000000000000000..9fba92fa6d0d3d670370b06a035fd9ee802698d2 --- /dev/null +++ b/controls/model/parse_log_model.m @@ -0,0 +1,111 @@ +function [loggedData, headers] = parse_log_model(filename, params, expData) +%parse_log This independent function parses the data stored in the file and +%returns a structure containing the data +% filename - this is the complete path of the file with the filename +% params - this is the params structure which holds the analysis +% configuration options + +% Check if file exists +if (~exist(filename,'file')) + error(strcat(filename, ' does not exist')); +end + +% Open file +FileID = fopen(filename, 'r'); + +% Gets the first line of the file +string = fgetl(FileID); + +% Test first line, if not formatted correctly, reject +if(size(regexp(string, '^#')) == 0) + error(strcat(filename, ' is not properly formatted, and does not contain "#" headers')); +end + +% Loop through header lines +while( regexp(string, '^#') == 1 ) + + %Print out string and move on + disp(string) + old = string; + string = fgetl(FileID); + +end + +% Two possibilities for the next two lines: +% 1) line of headers +% 2) line of units +foundHeaders = 0; +foundUnits = 0; + +% Checking current line's type: +identifier = string(1); + +if (regexp(identifier,'%')) + foundHeaders = 1; + % this is a line of headers; extract headers: + headers = strsplit(string); + headers{1} = strrep(headers{1},'%', ''); + numOfHeaders = length(headers); +else + if (regexp(identifier,'&')) + foundUnits = 1; + % this is a line of units; extract units: + units = strsplit(string); + units{1} = strrep(units{1},'&',''); + else + error(strcat(filename, ' is not properly formatted, contains undefined line identifier.')); + end +end + +% Obtaining the next line +string = fgetl(FileID); +identifier = string(1); + +if(foundHeaders) + if(regexp(identifier,'&')) + foundUnits = 1; + % this is a line of units; extract units: + units = strsplit(string); + units{1} = strrep(units{1},'&',''); + else + error(strcat(filename, ' is not properly formatted, contains or undefined/excessive line identifiers.')); + end +else + if(foundUnits) + if(regexp(identifier,'%')) + % this is a line of headers; extract headers: + headers = strsplit(string); + headers{1} = strrep(headers{1},'%', ''); + numOfHeaders = length(headers); + end + else + error('Should never be able to get here'); + end +end + +% sanity check and clean up +if(numOfHeaders ~= length(units)) + error(strcat(filename, ' is not properly formatted, contains unmatched number of units and headers')); +end +clear foundHeaders foundUnits; + +% Get all data into a single matrix called "log" +log = []; +line = zeros(1,numOfHeaders); +while ~feof(FileID) + line = textscan(FileID, '%f', numOfHeaders); + line = transpose(cell2mat(line)); + log = [log;line]; +end + +% Converting the log matrix into a expData structure. +for i = 1:numOfHeaders + + eval(['loggedData.' headers{i} '.data = log(:,i);']); % adding data + eval(['loggedData.' headers{i} '.unit = cell2mat(units(i));']); % adding unit + +end + + +end + diff --git a/controls/model/test_model.slx b/controls/model/test_model.slx index 580cbfee5ee130885a96fc9f254099058bfb5e41..56975e7eba373b73a005d9451ddcb553bd36f882 100644 Binary files a/controls/model/test_model.slx and b/controls/model/test_model.slx differ