BIRDy/Utils/initBenchmark.m

442 lines
24 KiB
Mathematica
Raw Normal View History

2021-04-29 09:42:38 +00:00
function [robot, benchmarkSettings, experimentDataStruct, startBenchmark, progressBar] = initBenchmark(robotName, identificationMethods, getKinematicExpressions, recompileMexFiles, displayProgression, regenerateModel, ...
regenerateTrajectory, regenerateData, displayTrajectory, displayControlPerformance, t_i, t_f, integrationAlgorithm, interpolator, numberOfInitialEstimates, sdOfInitialEstimates, freq_decim, samplingBorder, buildList, ...
filter, generateROSTrajectory, experimentOnTrueRobot, identifyDriveGains, initialParamType, noiseLevel, gammaReg)
% Authors: Quentin Leboutet, Julien Roux, Alexandre Janot and Gordon Cheng
%
% This function automatically setup the benchmark parameters and data structures.
%% Recompile the mex files if desired or if required:
% Check mex files extension:
extensionMex = mexext;
% Check the operating system
if isunix
disp('Linux detected.')
OS = 'linux';
startBenchmark=true;
elseif ismac
disp('Mac detected.')
OS = 'mac';
startBenchmark=true;
elseif ispc
disp('Windows detected.')
OS = 'windows';
startBenchmark=true;
else
disp('Platform not recognized. So far only Linux and Windows are supported.')
OS = 'unknown';
startBenchmark=false;
decisionCompilation = 'N';
end
if ~any(identificationMethods.isActivated) % If no identification method is selected, do not start the benchmark...
startBenchmark=false;
end
disp('Starting Benchmark...');
benchmarkSettings.codeImplementation = 'none';
benchmarkSettings.outputPath = 'figures/Results';% '../LaTeX/Journal/figures/Results';
if sum(identificationMethods.isActivated)~=0
if license('test','distrib_computing_toolbox')==0 % If the parallel computing toolbox is not detected on the machine
disp('The distributed computing toolbox was not detected. Expect slower computations for the CLOE, and Kalman filtering identification routines...')
end
if license('test','matlab_coder')==0 % If the matlab coder is not detected
disp('The matlab coder toolbox was not detected. Switching to pure matlab computations (no mex).')
benchmarkSettings.codeImplementation = 'classic';
elseif (recompileMexFiles == true) && ~(license('test','matlab_coder')==0)
disp('Automatic recompileMexFiles option was selected: proceeding...')
compileTime = recompileMex(buildList,benchmarkSettings);
fprintf('The total compile time was: %d s\n', compileTime);
benchmarkSettings.codeImplementation = 'optim';
else
disp('The matlab coder toolbox was detected.')
disp('Using mex files will increase the execution speed.')
prompt1 = 'Do you want to use mex files ? Y/N [Y]';
flag1=false;
flag2=false;
flag3=false;
while flag1==false
decisionMex = input(prompt1,'s');
if isempty(decisionMex)
decisionMex = 'Y';
end
if strcmpi(decisionMex, 'Y')
flag1=true;
versionMatlab = ver('matlab');
if ~(exist('Utils/compiledVersionMEX.txt', 'file') == 2) % If the compilation status file does not exist
disp('The mex files were never compiled on this machine. If you want to use mex files, it will be necessary to recompile them.')
recompile = true;
else
fileID = fopen('Utils/compiledVersionMEX.txt','r');
if fileID ~= -1
versionMatlabMexFile = fscanf(fileID,'%s'); % Version of matlab used to compile the mex files.
else
versionMatlabMexFile = -1; % Version of matlab used to compile the mex files.
end
if (fileID ~= -1) && strcmpi(sprintf('%s,%s',versionMatlab.Release,OS),versionMatlabMexFile) % If same matlab version than the one used to compile the mex files
fprintf("You are using Matlab %s on a %s platform... \n", versionMatlab.Release, OS);
disp('Your matlab version and operating system are compatible with the provided mex files. Launching parameter identification without recompiling...')
benchmarkSettings.codeImplementation = 'optim';
recompile = false;
fclose(fileID);
elseif fileID == -1 % No track of previous mex build: rebuild
fprintf("You are using Matlab %s on a %s platform... \n", versionMatlab.Release, OS);
disp('Unable to detect the matlab version used for building the provided MEX files. If you want to use mex files, it will be necessary to recompile them.')
recompile = true;
elseif (fileID ~= -1) && ~strcmpi(sprintf('%s,%s',versionMatlab.Release,OS),versionMatlabMexFile)% If NOT same matlab version than the one used to compile the mex files
fprintf("You are using Matlab %s on a %s platform... \n", versionMatlab.Release, OS);
versionMatlabMex = extractBefore(versionMatlabMexFile,",");
versionOSMex = extractAfter(versionMatlabMexFile,",");
if ~strcmp(versionOSMex, OS) % Critical mismatch in OS version
fprintf('This benchmark was originally compiled for %s. If you want to use mex files, it will be necessary to recompile them for your operating system.\n', versionOSMex);
recompile = true;
end
if ~strcmp(versionMatlabMex, versionMatlab.Release) % Non-critical mismatch in matlab version
fprintf('This benchmark was originally compiled for matlab %s. You can decide to recompile the mex files but this should not be necessary in most cases.\n', versionMatlabMex);
while flag3==false
decisionRecomp = input('Do you want to recompile mex files ? Y/N [N]','s');
if isempty(decisionRecomp)
decisionRecomp = 'N';
end
if strcmpi(decisionRecomp, 'Y')
recompile = true;
flag3=true;
elseif strcmpi(decisionRecomp, 'N')
recompile = false;
benchmarkSettings.codeImplementation = 'optim';
flag3=true;
else
disp('Incorrect input !')
flag3=false;
end
end
end
fclose(fileID);
else
recompile = true;
end
end
if recompile == true
prompt2 = 'Do you want to recompile mex files ? Y/N [Y]';
while flag2==false
decisionCompilation = input(prompt2,'s');
if isempty(decisionCompilation)
decisionCompilation = 'Y';
end
if strcmpi(decisionCompilation, 'Y')
flag2=true;
compileTime = recompileMex(buildList, benchmarkSettings);
fprintf("The total compile time was: = %d minutes, %d seconds\n", floor(compileTime/60), ceil(rem(compileTime,60)));
fileID = fopen('Utils/compiledVersionMEX.txt','w+'); % Overwrite file
fprintf(fileID, '%s,%s\n', versionMatlab.Release, OS);
fclose(fileID);
benchmarkSettings.codeImplementation = 'optim';
elseif strcmpi(decisionCompilation, 'N')
flag2=true;
disp('Switching to pure matlab computations (no mex).')
benchmarkSettings.codeImplementation = 'classic';
else
disp('Incorrect input !')
flag2=false;
end
end
end
elseif strcmpi(decisionMex, 'N')
flag1=true;
disp('Switching to pure matlab computations (no mex).')
benchmarkSettings.codeImplementation = 'classic';
else
disp('Incorrect input !')
flag1=false; % looping
end
end
end
elseif (recompileMexFiles == true) && license('test','matlab_coder')
disp('Automatic recompileMexFiles option was selected: proceeding...')
compileTime = recompileMex(buildList, benchmarkSettings);
fprintf('The total compile time was: %d s\n', compileTime);
benchmarkSettings.codeImplementation = 'optim';
end
% Is the benchmark executed on a true robot ?
benchmarkSettings.experimentOnTrueRobot = experimentOnTrueRobot;
benchmarkSettings.identifyDriveGains = identifyDriveGains;
if benchmarkSettings.experimentOnTrueRobot
noiseLevel = 'oldNoise'; % Use the noise parameters found for the true robot.
end
benchmarkSettings.noiseLevel = noiseLevel;
benchmarkSettings.regFactor = gammaReg;
%% Symbolic model computation:
% Check if the robot symbolic dynamics has already been computed:
robotHasSymbolicModel = exist(sprintf('Benchmark/Robot_Generated_Data/%s', robotName), 'dir');
if (robotHasSymbolicModel == 0) || (regenerateModel == true) % If the robot folder does not exist, so does its dynamic model...
% Start by creating the folders which will contain the symbolic expressions:
disp('The robot symbolic model was not detected or you decided to rebuild it: starting new symbolic computation of the dynamic model...');
mkdir(sprintf('Benchmark/Robot_Generated_Data/%s', robotName))
mkdir(sprintf('Benchmark/Robot_Generated_Data/%s/Homogeneous_Transforms', robotName))
mkdir(sprintf('Benchmark/Robot_Generated_Data/%s/Jacobian_Matrices', robotName))
mkdir(sprintf('Benchmark/Robot_Generated_Data/%s/Dynamic_Model', robotName))
mkdir(sprintf('Benchmark/Robot_Generated_Data/%s/Experiment_Data', robotName))
mkdir(sprintf('Benchmark/Robot_Generated_Data/%s/Trajectory_Data', robotName))
addpath(genpath('Benchmark')); % Add the newly created directories to the matlab path
% Set the symbolic computation options:
optionsSymbolicComputation.computeKinematics = true; % If true, computes the robot symbolic kinematics (but does not save it as a file)
optionsSymbolicComputation.computeDynamics = true; % If true, computes the robot symbolic dynamics (but does not save it as a file)
optionsSymbolicComputation.optimizeKinematics = true; % If true, generate a set of optimized files containing robot kinematics.
optionsSymbolicComputation.optimizeDynamics = true; % If true, generate a set of optimized files containing robot dynamics.
% Load Robot Symbolic and Numerical Parameters:
disp('Loading robot numerical model parameters...');
options.loadSymbolic = true;
options.checkPhysicality = false;
options.noiseLevel = noiseLevel;
[robot] = loadRobotModelParameters(robotName, options);
if ~computeSymbolicDynamics(robot, optionsSymbolicComputation) % Generate robot symbolic model
error('An error occured during symbolic model computation.');
else
recompileMexFiles = true; % Recompile mex files as soon as dynamics model has been modified
end
elseif sum(identificationMethods.isActivated)==0 && getKinematicExpressions == true
% Load Robot Numerical and Symbolic Parameters for vizualization purpose only:
disp('Symbolic model detected, no need to recompute it.');
disp('Loading robot symbolic and numerical model parameters...');
options.loadSymbolic = true;
options.checkPhysicality = false;
options.noiseLevel = noiseLevel;
[robot] = loadRobotModelParameters(robotName, options);
displaySymbolicTransforms(robot);
else
% Just load Robot Numerical Parameters:
disp('Symbolic model detected, no need to recompute it.');
disp('Loading robot numerical model parameters...');
options.loadSymbolic = false;
options.checkPhysicality = true;
options.noiseLevel = noiseLevel;
[robot] = loadRobotModelParameters(robotName, options);
% Looking for inconsistency between the dynamic model and the identification model:
if checkDynamicsConsistancy(robotName)== true
disp('Everything seems to be fine ! Moving forward...');
else
error('Inconsistancy detected between the robot dynamic model and the robot identification model !');
end
end
%% Initialization:
% Total number of identification methods:
benchmarkSettings.nbAlg = numel(identificationMethods.isActivated);
benchmarkSettings.identificationMethods = identificationMethods;
% Important parameters:
benchmarkSettings.displayProgression = displayProgression; % Display progression GUI.
benchmarkSettings.regenerateTrajectory = regenerateTrajectory; % Generate a new trajectory if set to true.
benchmarkSettings.regenerateData = regenerateData; % Generate new robot simulation data if set to true.
benchmarkSettings.displayTrajectory = displayTrajectory; % Display the robot joint trajectory.
benchmarkSettings.displayControlPerformance = displayControlPerformance; % Display the robot tracking performance on the generated trajectory.
Beta = feval(sprintf('Regressor_Beta_%s', robot.name),robot.numericalParameters.Xhi);
robot.paramVectorSize = numel(Beta);
if benchmarkSettings.displayProgression == true
progressBar = waitbar(0,'Benchmark initialization', 'name', 'Progress of the parametric identification sub-process');
else
progressBar = 0;
end
% Identify the robot using a subset of the experiment trajectory [t_i, t_f]:
benchmarkSettings.robotName = robotName;
benchmarkSettings.t_i = t_i; % Start time
benchmarkSettings.t_f = t_f; % End time
if benchmarkSettings.t_i>benchmarkSettings.t_f
error('Incorrect simulation time horizon ! The simulation stop time t_f cannot be smaller than the simulation init time t_i!');
elseif benchmarkSettings.t_i<0
error('Incorrect simulation time horizon ! The simulation init time t_i should be greater than 0!');
end
benchmarkSettings.nbSamples = (t_f-t_i)*robot.controlParameters.samplingFrequency+1; % Number of experiment samples
benchmarkSettings.nbCtrlSamples = (t_f-t_i)*robot.controlParameters.controlFrequency+1; % Number of control samples
benchmarkSettings.dt = 1/robot.controlParameters.samplingFrequency; % Sampling time
benchmarkSettings.f = robot.controlParameters.samplingFrequency; % Sampling frequency
benchmarkSettings.f_ctrl = robot.controlParameters.controlFrequency; % Control frequency
benchmarkSettings.dt_ctrl = 1/robot.controlParameters.controlFrequency; % Control time
benchmarkSettings.numberOfInitialEstimates = numberOfInitialEstimates; % Number of different initial estimates
benchmarkSettings.sdOfInitialEstimates = sdOfInitialEstimates; % Standard deviation of the different initial estimates
benchmarkSettings.integrationAlgorithm = integrationAlgorithm; % Defines the integration algorithm used during Identification : 'rk1', 'rk2', 'rk4' or 'ode45'
benchmarkSettings.interpolationAlgorithm = interpolator.Algorithm; % Defines the interpolation algorithm used for trajectory data: 'linear', 'nearest', 'next', 'previous', 'pchip', 'cubic', 'v5cubic', 'makima', or 'spline'.
benchmarkSettings.interpolationAlgorithmExp = interpolator.expAlgorithm;% Defines the interpolation algorithm used for experiment data: 'linear', 'nearest', 'next', 'previous', 'pchip', 'cubic', 'v5cubic', 'makima', or 'spline'.
benchmarkSettings.Xhi_obj = robot.numericalParameters.Xhi;
benchmarkSettings.Beta_obj = feval(sprintf('Regressor_Beta_%s', robot.name),benchmarkSettings.Xhi_obj);
if interpolator.visualizeInterpolator == true
testInterpolationTrajectory();
end
% Decimate parameters
benchmarkSettings.fdecim = freq_decim;
benchmarkSettings.fnyq = benchmarkSettings.f/2; % Nyquist frequency
benchmarkSettings.decimRate = round(0.8*benchmarkSettings.fnyq/(benchmarkSettings.fdecim)); % Decimation rate (see the Matlab 'decimate' documentation)
benchmarkSettings.samplingBorder = samplingBorder; % Sampling border (number of samples to be removed from the beginning and the end of the data sequence, in order to mitigate filtering artefacts...)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
if benchmarkSettings.experimentOnTrueRobot
benchmarkSettings.numberOfExperimentsPerInitialPoint = 1;
else
benchmarkSettings.numberOfExperimentsPerInitialPoint = robot.paramVectorSize; % Number of experiments for each method and each initial point
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% if sum(identificationMethods.isActivated)~=0
%% Generate trajectory:
if benchmarkSettings.experimentOnTrueRobot == true
trajectoryDataFile = sprintf('Benchmark/Robot_Generated_Data/%s/Trajectory_Data/trajectoryData_%s_real.mat', benchmarkSettings.robotName, benchmarkSettings.robotName);
if ~exist(trajectoryDataFile, 'file')
% Trigger real experiment data preprocessing:
experimentData = feval(sprintf('preprocessExperimentData_%s', robot.name),robot, benchmarkSettings, options);
else
% Load existing robot trajectory data:
disp('Trajectory Data found: Loading Trajectory Data...');
load(trajectoryDataFile);
end
else
trajectoryParameterFile = sprintf('Benchmark/Robot_Generated_Data/%s/Trajectory_Data/trajectoryParameters_%s.mat', benchmarkSettings.robotName, benchmarkSettings.robotName);
trajectoryDataFile = sprintf('Benchmark/Robot_Generated_Data/%s/Trajectory_Data/trajectoryData_%s.mat', benchmarkSettings.robotName, benchmarkSettings.robotName);
if ~exist(trajectoryDataFile, 'file') || benchmarkSettings.regenerateTrajectory % If the robot trajectory data file is not detected or if the flag 'regenerateTrajectory' is set.
if ~exist(trajectoryParameterFile, 'file') || benchmarkSettings.regenerateTrajectory
% Optimize robot trajectory:
benchmarkSettings.max_traj_it = 1;
disp('Optimizing robot trajectory...');
[trajectoryParameters, trajectoryData] = generateExcitationTrajectory(robot, benchmarkSettings, true);
save(trajectoryParameterFile, 'trajectoryParameters', '-v7.3');
else
load(trajectoryParameterFile);
benchmarkSettings.trajectoryParameters = trajectoryParameters;
[~, trajectoryData] = generateExcitationTrajectory(robot, benchmarkSettings, false);
end
% Save trajectory data:
disp('Saving Trajectory Data...');
save(trajectoryDataFile, 'trajectoryData', '-v7.3');
else
% Load existing robot trajectory data:
disp('Trajectory Data found: Loading Trajectory Data...');
load(trajectoryDataFile);
end
end
% Store trajectory data in a dedicated object:
benchmarkSettings.trajectoryData = Trajectory;
benchmarkSettings.trajectoryData.setTrajectoryData(trajectoryData.t,trajectoryData.Q,trajectoryData.Qp,trajectoryData.Qpp);
if benchmarkSettings.displayTrajectory == true
plotTrajectory(robot, benchmarkSettings);
end
%% Generate experiment data:
if benchmarkSettings.experimentOnTrueRobot == true
dataFolder = sprintf('Benchmark/Robot_Generated_Data/%s/Experiment_Data/data_%s_real.mat', benchmarkSettings.robotName, benchmarkSettings.robotName);
else
dataFolder = sprintf('Benchmark/Robot_Generated_Data/%s/Experiment_Data/data_%s_sim_%s.mat', benchmarkSettings.robotName, benchmarkSettings.robotName, noiseLevel);
end
% [Gains_TCG, fval, output, exitflag, lambda, jacobian] = TuneControlGains(robot, benchmarkSettings, options);
% Gains_TCG
% pause
if ~exist(dataFolder, 'file') || benchmarkSettings.regenerateData % If the robot simulation data is not detected or if the flag 'regenerateData' is set
if benchmarkSettings.experimentOnTrueRobot == true
% Trigger real experiment data preprocessing:
experimentData = feval(sprintf('preprocessExperimentData_%s', robot.name),robot, benchmarkSettings, options);
else
% Generate simulated experiment data:
disp('Generating Experiment Data...');
options.jointFrictionData = false;
options.integrationAlgorithm = 'rk4';
experimentData = generateExperimentData(robot, benchmarkSettings, options);
end
% Save experiment data:
disp('Saving Experiment Data...');
save(dataFolder, 'experimentData', '-v7.3');
else
% Load existing robot experiment data:
disp('Experiment Data found: Loading Experiment Data...');
load(dataFolder);
end
% Store the experiment data in a dedicated container equiped with zero-order time interpolation routines so that it can be accessed at any epoch:
if benchmarkSettings.experimentOnTrueRobot == true
experimentDataStruct = RealExperiment;
else
experimentDataStruct = SimulatedExperiment;
end
experimentDataStruct.setExperimentData(experimentData);
if benchmarkSettings.displayControlPerformance == true && benchmarkSettings.experimentOnTrueRobot == false
plotControlPerformance(robot, benchmarkSettings, experimentDataStruct, 1)
end
if generateROSTrajectory == true
options.generateRealRobot = true;
options.useROS = false; % publish trajectories as a ROS topic for visualization purpose
Experiment_ROS_Trajectory(robot, benchmarkSettings, options); % Generate .yaml trajectory file to be used on a real robot
end
disp('Experiment Data Ready !');
%% Filtering
% Filter parameters
benchmarkSettings.filter = filter;
% Filter performance visualization
if filter.visualizeFilter == true
filterTuning(benchmarkSettings, experimentDataStruct);
end
% else
% experimentDataStruct = 0;
% end
%% Generate starting points:
% Generate a set of 'numberOfInitialEstimates' initial parameter estimates and store it in 'Initial_pts':
benchmarkSettings.initialParamType = initialParamType;
[benchmarkSettings.Xhi_U, benchmarkSettings.Xhi_L, ~, benchmarkSettings.Beta_U, benchmarkSettings.Beta_L, benchmarkSettings.Initial_Beta, benchmarkSettings.Initial_Xhi] = generateInitParamEst(robot, benchmarkSettings, experimentDataStruct);
end