Skip to content

Commit

Permalink
publish release v2.0.0
Browse files Browse the repository at this point in the history
more refactoring


changelog/readme update


class documentation; convenience


fitting unit test


fitting improved; simulink example; function signatures progress; syntax of MagicFormulaTyre class changed


help added


unit tests; examples


merged FSAE TTC parsers (fool proof?)


syntax changes for lower-level functions


example updated


readme update


GettingStarted updated


toolbox packager updated


readme update


readme update


unit test updated


first draft of Mz0 integrated


tydex Measurements now can have model parameters stored


fix handling of model parameters


remove slprj from source control


fix tydex reader


README update; new `unpack()` method for measurements


moved a few unit tests to examples


removed ZETAs; added VX as input; MZ implemented (but untested)


added 5.2 parameter set and removed some 6.1.2 parameters from class


v52 stuff


bugfix Fy0; Mz now seems to work as intended


custom display for Parameters class


fix?


trying to get MZ0 fitting to work


init syntax changed for MagicFormulaTyre


unit tests fixed; fsae example now with MZ fit; generic plots; refactoring


removed function signatures for now


directory change (+tir; +exceptions); min/max/fixed settings can be saved


cosmetics


classes to (Hidden)


classes to (Hidden)


gitignore


Mx, My; lots of QoL-improvements; magicformula syntax changed; version handling


publish release v2.0.0
  • Loading branch information
teasit committed Mar 5, 2023
1 parent 43ac32b commit bfdf5a3
Show file tree
Hide file tree
Showing 238 changed files with 4,456 additions and 2,056 deletions.
Binary file removed +tests/BasicTests.mlx
Binary file not shown.
167 changes: 167 additions & 0 deletions +tests/FitterTest.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
classdef FitterTest < matlab.unittest.TestCase
properties
Fitter magicformula.v61.Fitter
Tyre MagicFormulaTyre
Measurements tydex.Measurement
MeasurementFileCornering char {mustBeFile} = 'doc/examples/fsae-ttc-data/fsaettc_obscured_testbench_cornering.mat'
MeasurementFileDriveBrake char {mustBeFile} = 'doc/examples/fsae-ttc-data/fsaettc_obscured_testbench_drivebrake.mat'
%Average RMSE of model versus data across fitted measurements must
%be lower than this value or test will fail.
ThresholdAverageRMSE = 0.3;
end
methods (TestClassSetup)
function setupMeasurements(testCase)
parser = tydex.parsers.FSAETTC_SI_ISO_Mat();
measurements1 = parser.run(testCase.MeasurementFileCornering);
measurements2 = parser.run(testCase.MeasurementFileDriveBrake);
measurements = [measurements1 measurements2];
measurements = measurements.downsample(10, 0);
testCase.Measurements = measurements;
end
function setupTyreModel(testCase)
tyre = MagicFormulaTyre();
tyre.Parameters.UNLOADED_RADIUS.Value = 0.20574;
testCase.Tyre = tyre;
end
function setupFitter(testCase)
tyre = testCase.Tyre;
params = tyre.Parameters;
measurements = testCase.Measurements;
fitter = magicformula.v61.Fitter(measurements, params);
fitter.Options.MaxFunctionEvaluations = 300; % TODO: temporary
testCase.Fitter = fitter;
end
end
methods (Test)
function fitFx0(testCase)
fitmode = magicformula.FitMode.Fx0;

fitter = testCase.Fitter;
fitter.FitModes = fitmode;
fitter.run()

tyre = testCase.Tyre;
tyre.Parameters = fitter.ParametersFitted;

I = fitter.FitModeFlags(char(fitmode));
measurements = testCase.Measurements(I);
rmse = zeros(numel(measurements), 1);
for i = 1:numel(measurements)
measurement = measurements(i);
[SX,SA,FZ,IP,IA,VX,FX] = unpack(measurement);
FX_mdl = magicformula(tyre,SX,SA,FZ,IP,IA,VX);
FX_max = max(abs([FX FX_mdl]), [], 'all');
FX_nrm = [FX FX_mdl]/FX_max;
rmse(i) = sqrt(mean((FX_nrm(:,1)-FX_nrm(:,2)).^2));
end
rmseMean = mean(rmse);
rmseThrd = testCase.ThresholdAverageRMSE;
testCase.verifyLessThanOrEqual(rmseMean, rmseThrd, ...
sprintf('Average RMSE is larger than %.2f%%', rmseThrd))
end
function fitFy0(testCase)
fitmode = magicformula.FitMode.Fy0;

fitter = testCase.Fitter;
fitter.FitModes = fitmode;
fitter.run()

tyre = testCase.Tyre;
tyre.Parameters = fitter.ParametersFitted;

I = fitter.FitModeFlags(char(fitmode));
measurements = testCase.Measurements(I);
rmse = zeros(numel(measurements), 1);
for i = 1:numel(measurements)
measurement = measurements(i);
[SX,SA,FZ,IP,IA,VX,~,FY] = unpack(measurement);
[~, FY_mdl] = magicformula(tyre,SX,SA,FZ,IP,IA,VX);
FY_max = max(abs([FY FY_mdl]), [], 'all');
FY_nrm = [FY FY_mdl]/FY_max;
rmse(i) = sqrt(mean((FY_nrm(:,1)-FY_nrm(:,2)).^2));
end
rmseMean = mean(rmse);
rmseThrd = testCase.ThresholdAverageRMSE;
testCase.verifyLessThanOrEqual(rmseMean, rmseThrd, ...
sprintf('Average RMSE is larger than %.2f%%', rmseThrd))
end
function fitMz0(testCase)
fitmode = magicformula.FitMode.Mz0;

fitter = testCase.Fitter;
fitter.FitModes = fitmode;
fitter.run()

tyre = testCase.Tyre;
tyre.Parameters = fitter.ParametersFitted;

I = fitter.FitModeFlags(char(fitmode));
measurements = testCase.Measurements(I);
rmse = zeros(numel(measurements), 1);
for i = 1:numel(measurements)
measurement = measurements(i);
[SX,SA,FZ,IP,IA,VX,~,~,MZ] = unpack(measurement);
[~,~,MZ_mdl] = magicformula(tyre,SX,SA,FZ,IP,IA,VX);
MZ_max = max(abs([MZ MZ_mdl]), [], 'all');
MZ_nrm = [MZ MZ_mdl]/MZ_max;
rmse(i) = sqrt(mean((MZ_nrm(:,1)-MZ_nrm(:,2)).^2));
end
rmseMean = mean(rmse);
rmseThrd = testCase.ThresholdAverageRMSE;
testCase.verifyLessThanOrEqual(rmseMean, rmseThrd, ...
sprintf('Average RMSE is larger than %.2f%%', rmseThrd))
end
function fitFx(testCase)
fitmode = magicformula.FitMode.Fx;

fitter = testCase.Fitter;
fitter.FitModes = fitmode;
fitter.run()

tyre = testCase.Tyre;
tyre.Parameters = fitter.ParametersFitted;

I = fitter.FitModeFlags(char(fitmode));
measurements = testCase.Measurements(I);
rmse = zeros(numel(measurements), 1);
for i = 1:numel(measurements)
measurement = measurements(i);
[SX,SA,FZ,IP,IA,VX,FX] = unpack(measurement);
FX_mdl = magicformula(tyre,SX,SA,FZ,IP,IA,VX);
FX_max = max(abs([FX FX_mdl]), [], 'all');
FX_nrm = [FX FX_mdl]/FX_max;
rmse(i) = sqrt(mean((FX_nrm(:,1)-FX_nrm(:,2)).^2));
end
rmseMean = mean(rmse);
rmseThrd = testCase.ThresholdAverageRMSE;
testCase.verifyLessThanOrEqual(rmseMean, rmseThrd, ...
sprintf('Average RMSE is larger than %.2f%%', rmseThrd))
end
function fitFy(testCase)
fitmode = magicformula.FitMode.Fy;

fitter = testCase.Fitter;
fitter.FitModes = fitmode;
fitter.run()

tyre = testCase.Tyre;
tyre.Parameters = fitter.ParametersFitted;

I = fitter.FitModeFlags(char(fitmode));
measurements = testCase.Measurements(I);
rmse = zeros(numel(measurements), 1);
for i = 1:numel(measurements)
measurement = measurements(i);
[SX,SA,FZ,IP,IA,VX,~,FY] = unpack(measurement);
[~, FY_mdl] = magicformula(tyre,SX,SA,FZ,IP,IA,VX);
FY_max = max(abs([FY FY_mdl]), [], 'all');
FY_nrm = [FY FY_mdl]/FY_max;
rmse(i) = sqrt(mean((FY_nrm(:,1)-FY_nrm(:,2)).^2));
end
rmseMean = mean(rmse);
rmseThrd = testCase.ThresholdAverageRMSE;
testCase.verifyLessThanOrEqual(rmseMean, rmseThrd, ...
sprintf('Average RMSE is larger than %.2f%%', rmseThrd))
end
end
end
Binary file added +tests/FunctionSignatures.mlx
Binary file not shown.
Binary file added +tests/ParameterFittingSettings.mlx
Binary file not shown.
Binary file added +tests/TirePropertiesFile.mlx
Binary file not shown.
17 changes: 12 additions & 5 deletions +tests/TydexImportExport.m
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,18 @@
end
methods (TestClassSetup)
function parseMeasurements(testCase)
file = fullfile('doc', 'examples', 'fsae-ttc-data', ...
'fsaettc_obscured_testbench_drivebrake.mat');
parser = tydex.parsers.FSAETTC_SI_ISO_Mat_DriveBrake();
measurements = parser.run(file);
testCase.MeasurementsParsed = measurements;
folder = 'doc/examples/fsae-ttc-data';
files = fullfile(folder, {
'fsaettc_obscured_testbench_drivebrake.mat'
'fsaettc_obscured_testbench_cornering.mat'
});
parser = tydex.parsers.FSAETTC_SI_ISO_Mat();
for i = 1:numel(files)
file = files{i};
measurements = parser.run(file);
testCase.MeasurementsParsed = [
testCase.MeasurementsParsed measurements];
end
end
function createTempFolder(testCase)
savedir = testCase.TdxSaveDir;
Expand Down
Binary file added +tests/VerityCodeGenerationCompatible.mlx
Binary file not shown.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
*.asv
*.mltbx
*.mltbx
slprj/
32 changes: 31 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,33 @@
# Changelog

- Can now read TYDEX files instead of only exporting. See [this example](./doc/examples/Example_TYDEX_FSAETTC.mlx) for examples
In this big update I tried to refactor the library in a way that eases the
use of the functions at the command-line. The most notatble change is the
`magicformula()` function that acts as a version-independent convenience
function to evaluate parameter sets. To make the function-call easier, a few
input arguments have been declared optional. For this change, the order of
arguments has been changed as noted below.

- FIX: Within calculation of Fy0, the pacejka equation (4.E30) used a wrong
paramter (PKY5 instead of PPY5). This is now fixed!
- NEW: Convenience function `magicformula()`.
Intended to replace calling `magicformula.v61.eval()` directly.
In the future, this function is supposed to be able to evaluate
different versions of parameter sets (e.g. both v52 and v61).
- NEW: Convenience class `MagicFormulaTyre`. See examples.
- NEW: Plot package. Use `magicformula.plots` to access them.
- NEW: Added calculation of MZ, MY, MX. Can also be fitted. See examples.
- BREAKING: Moved `+tir` package into `+magicformula`.
- BREAKING: order of arguments changed for evaluation functions:
OLD `(params,SA,SX,IA,IP,FZ,side)` --> NEW `(params,SX,SA,FZ,IP,IA,side)`
Rationale was to put arguments that can more easily be ommited last
and make them optional. Slip ratio (SX) and slip angle (SA) are usually
known or assumed zero, so they are the only mandatory arguments.
All other inputs can be set to nominal values from parameter set or
set to zero.
- BREAKING: position of multiple functions and classes within the package
`+magicformula` has changed; subpackage `+v62` has been corrected to
`+v61`. If you have been using some functions or classes within the
`magicformula` package, make sure you update their path.
(e.g. `magicformula.v62.equations.Fx0` --> `magicformula.v61.Fx0`)
- BREAKING: order of outputs of `magicformula()` function changed:
[FX,FY,mux,muy] --> [FX,FY,MZ,MY,MX] (mue have been removed)
103 changes: 51 additions & 52 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,67 +3,74 @@
[![View on File Exchange](https://www.mathworks.com/matlabcentral/images/matlab-file-exchange.svg)](https://de.mathworks.com/matlabcentral/fileexchange/110955)
[![GitHub release (latest by date)](https://img.shields.io/github/v/release/teasit/magic-formula-tyre-library)](https://github.com/teasit/magic-formula-tyre-library/releases/latest)
[![GitHub downloads](https://img.shields.io/github/downloads/teasit/magic-formula-tyre-library/total)](https://github.com/teasit/magic-formula-tyre-library/releases/latest)
![MATLAB version compatability](https://img.shields.io/badge/compatibility-%E2%89%A5R2021a-orange)

![Fitting Example](doc/images/magic_formula_library_socialpreview.png)

```matlab
[Fx,Fy] = magicformula.v62.eval(p,slipangl,longslip,inclangl,inflpres,FZW,tyreSide)
[FX,FY,MZ,MY,MX] = MAGICFORMULA(params,__)
```

- Computationally efficient Magic Formula tyre model functions
- Computationally efficient
- Code generation compatible
- Automatically fit Magic Formula tyre models to data
- TIR import/export (Tyre Property File format)
- TYDEX import (Tyre Data Exchange format)

![Fitting Example](doc/images/magic_formula_library_socialpreview.png)
- Automated fitting to data
- TIR (*.tir) import/export
- TYDEX (*.tdx) import/export

## Requirements

- MATLAB Base (tested with R2021a)
- Optimization Toolbox (for fitting)
- Signal Processing Toolbox (for raw measurement import)
- MATLAB Base
- (Optional) Optimization Toolbox (fitting)
- (Optional) Signal Processing Toolbox (measurement import)

## Installation

There are several ways:

- Download latest Release from [MATLAB File Exchange](https://de.mathworks.com/matlabcentral/fileexchange/110955)
- Download latest Release from [GitHub](https://github.com/teasit/magic-formula-tyre-library/releases)
- Clone using Git and integrate into your projects using a [Project Reference](https://de.mathworks.com/help/simulink/ug/add-or-remove-a-reference-to-another-project.html)
- Download from [MATLAB File Exchange](https://de.mathworks.com/matlabcentral/fileexchange/110955)
- Download from [GitHub](https://github.com/teasit/magic-formula-tyre-library/releases)
- Clone and integrate into your MATLAB projects as a [Project Reference](https://de.mathworks.com/help/simulink/ug/add-or-remove-a-reference-to-another-project.html)

## Usage and Examples

To get started with interactive examples, open the MATLAB live script
[`doc/GettingStarted.mlx`](./doc/GettingStarted.mlx) in your editor.
See [`GettingStarted.mlx`](./doc/GettingStarted.mlx) to get started. If you install
the library as a toolbox, you will be prompted to open said Live Script. It contains
an overview of the library and is the recommended way to get started.

### Example: Model vs. Data

You can find further examples in the [`doc/examples`](./doc/examples) folder.
This code generates the title figure of this README. Either clone the library or install
it as a toolbox and try it yourself. You can download the example data
[here](https://github.com/teasit/magic-formula-tyre-library/tree/main/doc/examples/fsae-ttc-data).
The data has been de-identified to conform with the FSAE TTC's
[license agreement](https://www.millikenresearch.com/FSAE_TTC_agreement.pdf).

I also created an open-source GUI application in MATLAB for interactive fitting
of Magic Formula tyre models to measurement data.
It uses this library as a submodule, meaning that fitted parameter sets with the GUI can
be used with the equations provided by this CLI library.
You can find it here:
[Link to GUI](https://github.com/teasit/magic-formula-tyre-tool)!
```matlab
folder = 'doc/examples/fsae-ttc-data';
file = fullfile(folder, 'fsaettc_obfuscated.tir');
tyre = MagicFormulaTyre(file);
file = fullfile(folder, 'fsaettc_obscured_testbench_drivebrake.mat');
parser = tydex.parsers.FSAETTC_SI_ISO_Mat();
measurements = parser.run(file);
measurements = measurements([30 40 55 60 70 75]);
figure(); grid on; hold on
c = colororder();
for i = 1:numel(measurements)
[SX,SA,FZ,IP,IA,VX,FX] = unpack(measurements(i));
FX_mdl = magicformula(tyre,SX,SA,FZ,IP,IA,VX);
plot(SX, FX_mdl, 'Color', c(i,:), 'LineWidth', 2)
plot(SX, FX, '.', 'Color', c(i,:), 'MarkerSize', 4)
end
xlabel('SX / 1'); ylabel('FX / N')
```

Further examples can be found in the [`doc/examples`](./doc/examples) folder.

## Motivation

The project was motivated by my work in the Formula Student Team
[UPBracing](https://formulastudent.uni-paderborn.de/en/). As I required
a performant, easy-to-use and precise implementation of the Magic Formula,
and existing implementations were not satisfactory (either being commercialized
and therefore without source or [very slow](https://de.mathworks.com/matlabcentral/fileexchange/63618-mfeval)),
it made sense to make my own version.

This library is currently used in the team's Torque-Vectoring
algorithms and all vehicle-modeling applications. Therefore the model
evaluation functions are suitable for code-generation and have actually
proven to be only moderately slower than linear tire models.

I hope this project benefits some students passionate about Vehicle Dynamics.
If you have any questions, don't hesitate to contact me! I will try and keep this
project updated in the future. If you want to contribute, please do. But I
must warn you, I am still new to publishing code as open-source and do not yet
know the ins and outs of working collaboratively on GitHub.
[UPBracing](https://formulastudent.uni-paderborn.de/en/). My goal was to create an
easy-to-use MATLAB library for other students to use in their vehicle dynamics projects.

This project would not have been possible without the data provided by the
[Formula SAE Tire Test Consortium (FSAE TTC)](https://www.millikenresearch.com/fsaettc.html)
Expand All @@ -75,15 +82,7 @@ of the application, to conform to the
Special thanks to Dr. Edward M. Kasprzak for granting me permission to provide the used,
de-identified and obscured data for demonstration purposes.

## Known Issues, Notes and Bugs

- Currently only Magic Formula version 6.1.2 (62) is implemented
- Of v6.1.2 the turn slip parameters have been reduced to constant parameters
(noted by greek letter zeta in Pacejka's book). To remove the influence of
these parameters, simply set them to unity (=1). The default parameter
set automatically applies unity. This effectively ignores turn slip.
- Equations (4.E7) and (4.E8) are ignored (velocity-dependent scaling of
friction coefficient) to remove dependency on speed as input. Might be
added as an alternative implementation in the future.
- In some sub-equations a few scaling factors were not implemented
- Self-aligning torque is not calculated/implemented (MZW).
## Known Issues

- Currenlty only version 6.1.2 (61) is implemented
- Turnslip is ignored
Loading

0 comments on commit bfdf5a3

Please sign in to comment.