Onboard of the Dallara EAV24 at the Abu Dhabi circuit. The
feedforward controllers in this repo compute the feedforward
steering command δff to track the desired trajectory
provided by a high-level MPC.
Code accompanying the paper
Benchmarking Empirical and Learning-Based Approaches for Feedforward Steering Control in Autonomous Racing G. Jank, M. Piccinini, S. Wenk, P. Pitschi, J. Betz, B. Lohmann.
The repo contains the Python implementations used to fit / train the four feedforward steering controllers benchmarked in the paper, the open-loop evaluation harness, and a reference C++ port of the same controllers for ROS 2 deployment.
| Name | Type | Python module | C++ class |
|---|---|---|---|
| TAM baseline | empirical (linear) | tam_baseline_empirical_ff |
TamBaselineEmpiricalFF |
| EHD (proposed) | empirical (polynomial surface) | empirical_handling_diagram_ff |
EmpiricalHandlingDiagramFF |
| MS-NN | model-structured neural network | nnodely_ff |
NNodelyFF |
| LSTM | recurrent neural network | lstm_ff |
LstmFF |
The C++ side builds standalone — its dependencies (tum_types_cpp,
tum_helpers_cpp, param_management_cpp, tsl_logger_cpp,
controller_helpers_cpp) are vendored under external/tam_common/. A
ready-made ff_emulator CLI replays the paper test lap through any of
the four FFs and writes per-sample predictions next to the measured
steering, so users can verify the C++ matches the Python end-to-end
without needing the closed-loop ROS 2 stack. See
lat_acc_feedforward_cpp/README.md
for the exact invocations.
Where the feedforward block sits in the control stack; the four
FFs in this repo are drop-in replacements for the highlighted block
in this diagram.
.
├── config/ # public ROS-style parameter YAMLs
├── data_analysis/ # fitting / training / evaluation (Python)
│ ├── evaluate_ff_algorithms.py # open-loop benchmark (paper Fig. 5–7)
│ ├── delay_estimation.py # cross-correlation (paper Fig. 7 top)
│ ├── pyproject.toml
│ └── src/
│ ├── common/ # PT1 filter
│ ├── interface/ # FeedforwardBase + LearningFF base
│ ├── tam_baseline_empirical_ff/ # baseline
│ ├── empirical_handling_diagram_ff/ # EHD (proposed)
│ ├── nnodely_ff/ # MS-NN
│ └── lstm_ff/ # LSTM
├── lat_acc_feedforward_cpp/ # reference C++ port (ROS 2)
│ └── (incl. ff_emulator CLI for end-to-end replay)
├── external/tam_common/ # vendored C++ dependencies
├── trained_models/ # checkpoints + ONNX exports
├── data/example/ # CSV laps used in the paper (training/test)
├── docs/images/ # paper figures used by this README
└── LICENSE
Python 3.11 is required. The analysis stack uses PyTorch and nnodely.
cd data_analysis/src/lstm_ff && ./setup.sh && cd ../../..setup.sh auto-detects an NVIDIA GPU via lspci and installs a matching
PyTorch wheel; CPU-only is supported. Set CUDA_OVERRIDE=none|cu124|cu128
to skip autodetect.
After install, activate with:
source data_analysis/src/lstm_ff/venv/bin/activateAll commands below assume the venv is active and the working directory is
data_analysis/.
Each script accepts one or more processed-CSV paths via --data.
# EHD — polynomial surface fit (a few seconds)
python -m src.empirical_handling_diagram_ff.empirical_handling_diagram_ff \
--data ../data/example/training/*.csv
# MS-NN — nnodely-based model-structured NN
python -m src.nnodely_ff.nnodely_ff --data ../data/example/training/*.csv
# LSTM
python -m src.lstm_ff.lstm_ff --data ../data/example/training/*.csvEach model writes a timestamped folder under
trained_models/<ModelClass>/YYYY_MM_DD/HH_MM_SS/ containing the
checkpoint, exported ONNX, loss curves, and (for EHD) a ROS 2 parameter
YAML.
The TAM baseline has no training step — it reads its parameters
directly from config/baseline_ff_config.yml
together with config/vehicle_config.yml.
python evaluate_ff_algorithms.py \
--data_path ../data/example/test/EAV24_A2RL_2026-02-12_15-20_lap_8.csvLoads the checkpoints under trained_models/<…>/example/, computes RMSE /
MAE / FVU for all four controllers, and writes the per-sample prediction
plot, prediction-error plot, and the sign-corrected error vs. longitudinal
acceleration (median + IQR) to --save_dir (default:
data_analysis/logs/). A combined CSV with measured and predicted
steering for every algorithm is saved alongside as
model_comparison_results.csv.
Override --ehd_model_path, --msnn_model_path, --lstm_model_path to
benchmark your own retraining runs.
Add --feature_importance to additionally compute mean-absolute SHAP
values per input feature and time step for the two learning-based
controllers (MS-NN and LSTM). The resulting shap_summary.svg,
shap_importance_bar.svg, and shap_importance.csv are written into
each model's own folder (next to the checkpoint), not into --save_dir.
Uses 1000 random samples by default; expect several minutes per model.
Both LSTM_NN and NNodelyFF support iterative fine-tuning on
additional data with a reduced learning rate:
# LSTM
python src/lstm_ff/retrain.py \
--model <path/to/previous/run> \
--data <new_lap.csv>
# MS-NN
python src/nnodely_ff/retrain.py \
--model <path/to/previous/run> \
--data <new_lap.csv>The MS-NN script copies the original model folder into a fresh timestamped
output before resuming training; the LSTM script reloads weights into a
new folder. Both write their fine-tuned model under
trained_models/<ModelClass>/YYYY_MM_DD/HH_MM_SS/.
The checkpoints committed under trained_models/*/example/ are the ones
used to produce the paper figures. Re-training from scratch is not
guaranteed bit-for-bit reproducible because PyTorch is not seeded globally;
use the shipped checkpoints if you need the exact numbers in the paper.
End-to-end smoke test (open-loop benchmark on the paper's test lap):
cd data_analysis
python evaluate_ff_algorithms.pyOutputs land in data_analysis/logs/. The headline figure you should
get back is the per-FF prediction-error / FVU table from the paper:
Paper Fig. 5 — open-loop prediction error per FF on the test lap.
The evaluator reproduces this plot and the underlying numbers.
Once the workspace builds (colcon build --packages-up-to lat_acc_feedforward_cpp),
the ff_emulator binary can replay the same test lap through any of the
four C++ FFs. Example for the empirical handling diagram:
source install/setup.bash
ros2 run lat_acc_feedforward_cpp ff_emulator \
EmpiricalHandlingDiagram \
data/example/test/EAV24_A2RL_2026-02-12_15-20_lap_8.csv \
/tmp/ehd_pred.csv \
--param config/vehicle_config.yml \
--param trained_models/EmpiricalHandlingDiagramFF/example/config.yamlRequired arguments differ per FF (the neural FFs need only
vehicle_config.yml; the empirical ones need their parameter YAML too).
See lat_acc_feedforward_cpp/README.md
for the per-FF table and the invocations for the other three FFs
(EmpiricalBaseline, NNodely, LSTM).
The reported numbers were produced on:
- CPU: Intel-class workstation (any modern x86-64)
- GPU: NVIDIA, CUDA 12.4 (training only; evaluation runs CPU-only)
- OS: Ubuntu 22.04, Python 3.11
- Approximate training time: EHD <1 min, MS-NN ~30 min, LSTM ~45 min on a single mid-range NVIDIA GPU.
Evaluation runs on CPU in under a minute.
Pre-print citation. Until the ITSC 2026 proceedings publish on IEEEXplore, you can find the accompanying paper on arXiv (arXiv:2605.21111). The BibTeX below will be updated with the IEEEXplore DOI as soon as it is available. Nevertheless please re-check if an IEEExplore DOI is available before submitting a paper that cites this code.
% TODO: once ITSC 2026 proceedings publish, replace with the IEEEXplore DOI.
@inproceedings{Jank2026,
author = {Jank, Georg and Piccinini, Mattia and Wenk, Sebastian and
Pitschi, Phillip and Betz, Johannes and Lohmann, Boris},
title = {Benchmarking Empirical and Learning-Based Approaches for
Feedforward Steering Control in Autonomous Racing},
booktitle = {Accepted to the 29th IEEE International Conference on Intelligent Transportation Systems (ITSC 2026)},
year = {2026},
eprint = {2605.21111},
archivePrefix = {arXiv},
doi = {10.48550/arXiv.2605.21111}
}- Georg Jank - Chair of Automatic Control, TU Munich.
- Mattia Piccinini - Professorship of Autonomous Vehicle Systems, TU Munich.
- Sebastian Wenk - Chair of Automatic Control, TU Munich.
- Phillip Pitschi - Chair of Automatic Control, TU Munich.
- Johannes Betz - Professorship of Autonomous Vehicle Systems, TU Munich.
- Boris Lohmann - Chair of Automatic Control, TU Munich.
Apache-2.0. See LICENSE.
This project includes code from TAM_common, which is licensed under the Apache License 2.0.
AI usage disclaimer. All scripts in this repository were designed and implemented manually by the author. AI tools (Claude Code) were used as assistants for code review and for applying mechanical clean-up edits (import deduplication, indentation, docstring style, refactoring duplicated blocks, implementing smoke tests). Algorithmic logic, model architectures, parameter choices, and experimental decisions are the author's own.