trajectopy 5.0.2__tar.gz → 5.0.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {trajectopy-5.0.2 → trajectopy-5.0.3}/PKG-INFO +22 -24
- {trajectopy-5.0.2 → trajectopy-5.0.3}/README.md +21 -23
- {trajectopy-5.0.2 → trajectopy-5.0.3}/test/test_fast_reading.py +30 -0
- trajectopy-5.0.3/trajectopy/__version__.py +1 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/cli/main.py +2 -2
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/core/trajectory.py +1 -1
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/gui/models/entries.py +1 -1
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/io/ascii.py +10 -9
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/results/ate_result.py +2 -1
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy-docs/docs/api/index.md +1 -0
- trajectopy-5.0.3/trajectopy-docs/docs/api/io/rosbag.md +3 -0
- trajectopy-5.0.3/trajectopy-docs/docs/file_formats.md +204 -0
- trajectopy-5.0.3/trajectopy-docs/docs/index.md +149 -0
- trajectopy-5.0.3/trajectopy-docs/docs/settings.md +201 -0
- trajectopy-5.0.3/trajectopy-docs/docs/user_guide.md +379 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy-docs/mkdocs.yml +1 -0
- trajectopy-5.0.2/trajectopy/__version__.py +0 -1
- trajectopy-5.0.2/trajectopy-docs/docs/file_formats.md +0 -178
- trajectopy-5.0.2/trajectopy-docs/docs/index.md +0 -145
- trajectopy-5.0.2/trajectopy-docs/docs/settings.md +0 -183
- trajectopy-5.0.2/trajectopy-docs/docs/user_guide.md +0 -270
- {trajectopy-5.0.2 → trajectopy-5.0.3}/.gitattributes +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/.github/workflows/ci.yml +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/.github/workflows/release.yml +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/.gitignore +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/.pre-commit-config.yaml +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/.pylintrc +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/CITATION.cff +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/LICENSE +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/example_data/KITTI_ORB.traj +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/example_data/KITTI_SPTAM.traj +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/example_data/KITTI_gt.traj +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/example_scripts/align.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/example_scripts/alignment.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/example_scripts/ate.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/example_scripts/deviation_plots.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/example_scripts/rpe.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/example_scripts/trajectory_plot.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/installer/trajectopy.iss +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/pyproject.toml +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/requirements.txt +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/test/__init__.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/test/data/abs_dev.result +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/test/data/align_entry.result +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/test/data/generated_trajectory.traj +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/test/data/noisy_trajectory.traj +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/test/data/open_loop_trajectory.traj +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/test/data/rel_dev_meter.result +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/test/data/rel_dev_second.result +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/test/test_alignment.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/test/test_approximation.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/test/test_architecture.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/test/test_comparison.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/test/test_context_menus.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/test/test_gui_models.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/test/test_gui_smoke.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/test/test_inplace_contract.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/test/test_io.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/test/test_kml.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/test/test_manual_alignment.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/test/test_matching.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/test/test_merging.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/test/test_pipeline.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/test/test_positions_rotations.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/test/test_processing_edge_cases.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/test/test_processing_errors.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/test/test_readers_edge_cases.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/test/test_readers_errors.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/test/test_regressions.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/test/test_settings.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/test/test_settings_edge_cases.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/test/test_settings_serialization.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/test/test_settings_validation.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/test/test_sorting.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/test/test_trajectory.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/test/test_trajectory_validation.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/test/test_update_check.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/test/test_visualization.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/test/testdata.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/test/util.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/LICENSE +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/__init__.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/__main__.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/cli/__init__.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/core/__init__.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/core/parameters.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/core/positions.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/core/rotations.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/core/settings.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/exceptions.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/gui/__init__.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/gui/app.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/gui/managers/__init__.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/gui/managers/file_manager.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/gui/managers/plot_manager.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/gui/managers/requests.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/gui/managers/session_manager.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/gui/managers/trajectory_manager.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/gui/managers/ui_manager.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/gui/models/__init__.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/gui/models/property_model.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/gui/models/result_model.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/gui/models/selection.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/gui/models/table_model.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/gui/models/trajectory_model.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/gui/resources/full-icon-poppins.png +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/gui/resources/icon-bg.ico +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/gui/resources/icon-bg.png +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/gui/resources/icon.png +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/gui/themes.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/gui/update_check.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/gui/utils.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/gui/views/__init__.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/gui/views/about_window.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/gui/views/alignment_edit_window.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/gui/views/dof_organizer.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/gui/views/entry_table_view.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/gui/views/json_settings_view.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/gui/views/kml_dialog.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/gui/views/main_window.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/gui/views/manual_alignment_window.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/gui/views/menu_helpers.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/gui/views/playback_window.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/gui/views/preview_plot.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/gui/views/progress_window.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/gui/views/properties_panel.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/gui/views/result_context_menu.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/gui/views/result_selection_window.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/gui/views/result_table_view.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/gui/views/timeline_widget.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/gui/views/trajectory_context_menu.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/gui/views/trajectory_table_view.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/gui/worker.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/io/__init__.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/io/header.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/io/kml.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/io/results_io.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/io/rosbag.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/io/rosmsg.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/io/trajectory_io.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/io/utils.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/processing/__init__.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/processing/alignment.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/processing/approximation.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/processing/evaluation.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/processing/interpolation.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/processing/lib/__init__.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/processing/lib/alignment/__init__.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/processing/lib/alignment/data.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/processing/lib/alignment/direct.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/processing/lib/alignment/egrad_interface.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/processing/lib/alignment/equations.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/processing/lib/alignment/estimation.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/processing/lib/alignment/parameters.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/processing/lib/approximation/__init__.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/processing/lib/approximation/cubic_approximation.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/processing/lib/approximation/line_approximation.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/processing/lib/approximation/voxelizer.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/processing/matching.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/processing/merging.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/processing/sorting.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/py.typed +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/readers/__init__.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/readers/ascii.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/readers/rosbag.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/readers/rosmsg.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/readers/utils.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/results/__init__.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/results/alignment_result.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/results/rpe_result.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/utils/__init__.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/utils/colors.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/utils/common.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/utils/definitions.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/utils/formatting.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/utils/geometry.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/utils/linalg.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/utils/numeric.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/utils/resources.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/visualization/__init__.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/visualization/_svg_export.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/visualization/axes.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/visualization/figure.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/visualization/plots.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy/visualization/style.py +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy-docs/docs/api/core/parameters.md +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy-docs/docs/api/core/positions.md +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy-docs/docs/api/core/rotations.md +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy-docs/docs/api/core/settings.md +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy-docs/docs/api/core/trajectory.md +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy-docs/docs/api/io/kml.md +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy-docs/docs/api/io/results_io.md +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy-docs/docs/api/io/trajectory_io.md +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy-docs/docs/api/processing/alignment.md +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy-docs/docs/api/processing/approximation.md +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy-docs/docs/api/processing/evaluation.md +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy-docs/docs/api/processing/interpolation.md +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy-docs/docs/api/processing/matching.md +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy-docs/docs/api/processing/merging.md +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy-docs/docs/api/processing/sorting.md +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy-docs/docs/api/results/alignment_result.md +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy-docs/docs/api/results/ate_result.md +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy-docs/docs/api/results/rpe_result.md +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy-docs/docs/api/visualization/display.md +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy-docs/docs/api/visualization/figures.md +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy-docs/docs/assets/icon.png +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy-docs/docs/javascripts/mathjax.js +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy-docs/docs/stylesheets/extra.css +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/trajectopy.spec +0 -0
- {trajectopy-5.0.2 → trajectopy-5.0.3}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: trajectopy
|
|
3
|
-
Version: 5.0.
|
|
3
|
+
Version: 5.0.3
|
|
4
4
|
Summary: Trajectory Evaluation in Python
|
|
5
5
|
Project-URL: Homepage, https://gereon-t.github.io/trajectopy/
|
|
6
6
|
Project-URL: Documentation, https://gereon-t.github.io/trajectopy/
|
|
@@ -62,44 +62,42 @@ Description-Content-Type: text/markdown
|
|
|
62
62
|
|
|
63
63
|
## Installation
|
|
64
64
|
|
|
65
|
-
Windows app
|
|
65
|
+
**Windows app**: download `Trajectopy-<version>-setup.exe` from the [latest release](https://github.com/gereon-t/trajectopy/releases/latest) and run it. No Python installation or administrator rights are needed; `.traj` files can then be opened by double-click, and the app reports new versions (Help → Check for Updates).
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
**Python package** (Python 3.10 or newer):
|
|
68
68
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
Python package
|
|
69
|
+
| Command | Contents |
|
|
70
|
+
| --- | --- |
|
|
71
|
+
| `pip install "trajectopy[gui]"` | Python package and GUI (recommended) |
|
|
72
|
+
| `pip install "trajectopy[plot]"` | Python package with plotting, without the GUI |
|
|
73
|
+
| `pip install trajectopy` | Python package only |
|
|
74
74
|
|
|
75
|
-
|
|
76
|
-
pip install "trajectopy[plot]"
|
|
77
|
-
```
|
|
75
|
+
Loading large trajectory files (millions of poses) is several times faster with the `fast` extra, which can be combined with the others: `pip install "trajectopy[gui,fast]"`.
|
|
78
76
|
|
|
79
|
-
|
|
77
|
+
## Quick Start
|
|
80
78
|
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
```
|
|
79
|
+
```python
|
|
80
|
+
import trajectopy as tpy
|
|
84
81
|
|
|
85
|
-
|
|
82
|
+
reference = tpy.Trajectory.from_file("./example_data/KITTI_gt.traj")
|
|
83
|
+
estimate = tpy.Trajectory.from_file("./example_data/KITTI_ORB.traj")
|
|
86
84
|
|
|
87
|
-
|
|
88
|
-
|
|
85
|
+
ate_result = tpy.ate(trajectory=estimate, other=reference) # aligns the estimate first
|
|
86
|
+
print(f"Position ATE: {ate_result.pos_ate:.3f} m")
|
|
89
87
|
```
|
|
90
88
|
|
|
89
|
+
Or start the GUI with `trajectopy` (or from the Start menu), drop trajectory files onto it, right-click the ground truth → *Set as Reference*, then right-click another trajectory → *Compare with Reference*.
|
|
90
|
+
|
|
91
91
|
## Documentation
|
|
92
92
|
|
|
93
93
|
<a href="https://gereon-t.github.io/trajectopy/" target="_blank">https://gereon-t.github.io/trajectopy/</a>
|
|
94
94
|
|
|
95
95
|
## Key Features
|
|
96
96
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
-
|
|
100
|
-
-
|
|
101
|
-
- __Comparison__: Absolute and relative trajectory comparison metrics (__ATE and RPE__) that can be computed using various pose-matching methods.
|
|
102
|
-
- __Data Import/Export__: Support for importing and exporting data, ensuring compatibility with your existing workflows.
|
|
97
|
+
- __Interactive GUI__: Load, plot, align and compare trajectories without writing code. Every action explains itself in the context menus, and trajectories can be aligned interactively if an automatic alignment is not possible.
|
|
98
|
+
- __Alignment__: A spatio-temporal least squares alignment that can be tailored to the application: similarity transformation, leverarm and time shift, each of them optional.
|
|
99
|
+
- __Comparison__: Absolute and relative trajectory error (__ATE and RPE__), computed with various pose-matching methods.
|
|
100
|
+
- __Data Import/Export__: ASCII files with configurable columns, time formats and coordinate systems (any EPSG code), and ROS bags.
|
|
103
101
|
- __Fast, Interactive Visualization__: Powered by [pyqtgraph](https://www.pyqtgraph.org/), the same plots are available in the GUI and in Python scripts, stay fluid for large trajectories, and can be exported to PNG, JPG or SVG. Georeferenced trajectories and ATE results can be exported to Google Earth, colored by speed, height or deviations, on the terrain or at their own heights, with playback.
|
|
104
102
|
|
|
105
103
|
## Web Application (Docker)
|
|
@@ -22,44 +22,42 @@
|
|
|
22
22
|
|
|
23
23
|
## Installation
|
|
24
24
|
|
|
25
|
-
Windows app
|
|
25
|
+
**Windows app**: download `Trajectopy-<version>-setup.exe` from the [latest release](https://github.com/gereon-t/trajectopy/releases/latest) and run it. No Python installation or administrator rights are needed; `.traj` files can then be opened by double-click, and the app reports new versions (Help → Check for Updates).
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
**Python package** (Python 3.10 or newer):
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
Python package
|
|
29
|
+
| Command | Contents |
|
|
30
|
+
| --- | --- |
|
|
31
|
+
| `pip install "trajectopy[gui]"` | Python package and GUI (recommended) |
|
|
32
|
+
| `pip install "trajectopy[plot]"` | Python package with plotting, without the GUI |
|
|
33
|
+
| `pip install trajectopy` | Python package only |
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
pip install "trajectopy[plot]"
|
|
37
|
-
```
|
|
35
|
+
Loading large trajectory files (millions of poses) is several times faster with the `fast` extra, which can be combined with the others: `pip install "trajectopy[gui,fast]"`.
|
|
38
36
|
|
|
39
|
-
|
|
37
|
+
## Quick Start
|
|
40
38
|
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
```
|
|
39
|
+
```python
|
|
40
|
+
import trajectopy as tpy
|
|
44
41
|
|
|
45
|
-
|
|
42
|
+
reference = tpy.Trajectory.from_file("./example_data/KITTI_gt.traj")
|
|
43
|
+
estimate = tpy.Trajectory.from_file("./example_data/KITTI_ORB.traj")
|
|
46
44
|
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
ate_result = tpy.ate(trajectory=estimate, other=reference) # aligns the estimate first
|
|
46
|
+
print(f"Position ATE: {ate_result.pos_ate:.3f} m")
|
|
49
47
|
```
|
|
50
48
|
|
|
49
|
+
Or start the GUI with `trajectopy` (or from the Start menu), drop trajectory files onto it, right-click the ground truth → *Set as Reference*, then right-click another trajectory → *Compare with Reference*.
|
|
50
|
+
|
|
51
51
|
## Documentation
|
|
52
52
|
|
|
53
53
|
<a href="https://gereon-t.github.io/trajectopy/" target="_blank">https://gereon-t.github.io/trajectopy/</a>
|
|
54
54
|
|
|
55
55
|
## Key Features
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
-
|
|
60
|
-
-
|
|
61
|
-
- __Comparison__: Absolute and relative trajectory comparison metrics (__ATE and RPE__) that can be computed using various pose-matching methods.
|
|
62
|
-
- __Data Import/Export__: Support for importing and exporting data, ensuring compatibility with your existing workflows.
|
|
57
|
+
- __Interactive GUI__: Load, plot, align and compare trajectories without writing code. Every action explains itself in the context menus, and trajectories can be aligned interactively if an automatic alignment is not possible.
|
|
58
|
+
- __Alignment__: A spatio-temporal least squares alignment that can be tailored to the application: similarity transformation, leverarm and time shift, each of them optional.
|
|
59
|
+
- __Comparison__: Absolute and relative trajectory error (__ATE and RPE__), computed with various pose-matching methods.
|
|
60
|
+
- __Data Import/Export__: ASCII files with configurable columns, time formats and coordinate systems (any EPSG code), and ROS bags.
|
|
63
61
|
- __Fast, Interactive Visualization__: Powered by [pyqtgraph](https://www.pyqtgraph.org/), the same plots are available in the GUI and in Python scripts, stay fluid for large trajectories, and can be exported to PNG, JPG or SVG. Georeferenced trajectories and ATE results can be exported to Google Earth, colored by speed, height or deviations, on the terrain or at their own heights, with playback.
|
|
64
62
|
|
|
65
63
|
## Web Application (Docker)
|
|
@@ -119,6 +119,36 @@ class TestReadTrajectory(unittest.TestCase):
|
|
|
119
119
|
self._assert_same_as_general_reader(text)
|
|
120
120
|
|
|
121
121
|
|
|
122
|
+
class TestDatetimeTimeSystems(unittest.TestCase):
|
|
123
|
+
TEMPLATE = (
|
|
124
|
+
"#fields t,px,py,pz\n#time_format datetime\n{header}"
|
|
125
|
+
"2024-01-01 12:00:00.000,0,0,0\n2024-01-01 12:00:00.100,1,0,0\n"
|
|
126
|
+
)
|
|
127
|
+
UTC_2024 = 1704110400.0 # 2024-01-01 12:00:00 UTC
|
|
128
|
+
|
|
129
|
+
def _timestamps(self, header: str) -> np.ndarray:
|
|
130
|
+
with tempfile.TemporaryDirectory() as directory:
|
|
131
|
+
filename = os.path.join(directory, "trajectory.traj")
|
|
132
|
+
with open(filename, "w", encoding="utf-8") as file:
|
|
133
|
+
file.write(self.TEMPLATE.format(header=header))
|
|
134
|
+
return read_trajectory(filename).timestamps
|
|
135
|
+
|
|
136
|
+
def test_utc(self):
|
|
137
|
+
for header_lines in ("", "#datetime_timezone UTC\n"):
|
|
138
|
+
np.testing.assert_allclose(self._timestamps(header_lines), [self.UTC_2024, self.UTC_2024 + 0.1])
|
|
139
|
+
|
|
140
|
+
def test_gps_time_subtracts_the_leap_seconds(self):
|
|
141
|
+
np.testing.assert_allclose(
|
|
142
|
+
self._timestamps("#datetime_timezone GPS\n"), [self.UTC_2024 - 18, self.UTC_2024 - 18 + 0.1]
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
def test_other_time_zones_are_read_as_utc_with_a_warning(self):
|
|
146
|
+
with self.assertLogs(ascii.logger, level="WARNING") as logs:
|
|
147
|
+
timestamps = self._timestamps("#datetime_timezone Europe/Berlin\n")
|
|
148
|
+
np.testing.assert_allclose(timestamps, [self.UTC_2024, self.UTC_2024 + 0.1])
|
|
149
|
+
self.assertTrue(any("Europe/Berlin" in message for message in logs.output))
|
|
150
|
+
|
|
151
|
+
|
|
122
152
|
class TestFastExtraHint(unittest.TestCase):
|
|
123
153
|
FILE = "test/data/generated_trajectory.traj"
|
|
124
154
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "5.0.3"
|
|
@@ -8,13 +8,13 @@ from trajectopy.__version__ import __version__ as VERSION
|
|
|
8
8
|
|
|
9
9
|
def main():
|
|
10
10
|
"""Main CLI entry point."""
|
|
11
|
-
parser = argparse.ArgumentParser(description="Trajectopy - Trajectory Evaluation in Python")
|
|
11
|
+
parser = argparse.ArgumentParser(prog="trajectopy", description="Trajectopy - Trajectory Evaluation in Python")
|
|
12
12
|
parser.add_argument(
|
|
13
13
|
"files",
|
|
14
14
|
nargs="*",
|
|
15
15
|
help="Trajectory (.traj, .bag) or result (.result) files to open (also used when opening files from Windows).",
|
|
16
16
|
)
|
|
17
|
-
parser.add_argument("--version", "-v", action="store_true")
|
|
17
|
+
parser.add_argument("--version", "-v", action="store_true", help="Show the version and exit")
|
|
18
18
|
parser.add_argument(
|
|
19
19
|
"--single-thread",
|
|
20
20
|
action="store_true",
|
|
@@ -644,7 +644,7 @@ class Trajectory:
|
|
|
644
644
|
|
|
645
645
|
write_trajectory(self, filename=filename, mode=mode)
|
|
646
646
|
|
|
647
|
-
def to_kml(self, filename: str, precision: float = 1e-6, color: str = "#ff0000", **options) -> None:
|
|
647
|
+
def to_kml(self, filename: str, precision: float = 1e-6, color: str = "#ff0000", **options: Any) -> None:
|
|
648
648
|
"""
|
|
649
649
|
Exports the trajectory to a Google Earth KML file, as a time-stamped track that the time slider of
|
|
650
650
|
Google Earth plays back.
|
|
@@ -141,7 +141,7 @@ class TrajectoryEntry(Entry):
|
|
|
141
141
|
traj_settings = ProcessingSettings.from_file(str(settings_filename))
|
|
142
142
|
else:
|
|
143
143
|
logger.info(
|
|
144
|
-
"No settings file found. Settings can be provided by storing a
|
|
144
|
+
"No settings file found. Settings can be provided by storing a JSON file with the same name in the same directory."
|
|
145
145
|
)
|
|
146
146
|
traj_settings = ProcessingSettings()
|
|
147
147
|
|
|
@@ -309,19 +309,20 @@ def _parse_datetime(trajectory_data: np.ndarray, time_columns: list[int], header
|
|
|
309
309
|
|
|
310
310
|
datetime_strings = np.apply_along_axis(concatenate_strings, 1, trajectory_data[:, time_columns])
|
|
311
311
|
|
|
312
|
-
|
|
312
|
+
# Date/time strings are UTC or GPS time; other time zones are not supported (and are read as UTC).
|
|
313
|
+
time_system = header_data.datetime_timezone.upper()
|
|
314
|
+
if time_system not in ("UTC", "GPS"):
|
|
315
|
+
logger.warning(
|
|
316
|
+
"Unsupported #datetime_timezone '%s': date/time strings must be UTC or GPS time. Reading them as UTC.",
|
|
317
|
+
header_data.datetime_timezone,
|
|
318
|
+
)
|
|
313
319
|
|
|
314
|
-
|
|
320
|
+
ts_datetime = pd.DatetimeIndex(pd.to_datetime(datetime_strings, format=header_data.datetime_format, utc=True))
|
|
321
|
+
if time_system == "GPS":
|
|
315
322
|
ts_datetime -= pd.Timedelta(seconds=GPS_LEAP_SECONDS)
|
|
316
|
-
time_zone = "UTC"
|
|
317
323
|
logger.info("Applied GPS leap seconds.")
|
|
318
|
-
else:
|
|
319
|
-
time_zone = header_data.datetime_timezone
|
|
320
|
-
|
|
321
|
-
ts_datetime = pd.DatetimeIndex(ts_datetime).tz_convert(tz=time_zone)
|
|
322
|
-
logger.info("Timezone: %s", time_zone)
|
|
323
324
|
|
|
324
|
-
return np.
|
|
325
|
+
return np.asarray((ts_datetime - pd.Timestamp(0, tz="UTC")) / pd.Timedelta(seconds=1), dtype=float)
|
|
325
326
|
|
|
326
327
|
|
|
327
328
|
def _parse_gps_sow(trajectory_data: np.ndarray, time_columns: list[int], header_data: HeaderData) -> np.ndarray:
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
2
|
from functools import cached_property
|
|
3
|
+
from typing import Any
|
|
3
4
|
|
|
4
5
|
import numpy as np
|
|
5
6
|
import pandas as pd
|
|
@@ -501,7 +502,7 @@ class ATEResult:
|
|
|
501
502
|
|
|
502
503
|
return pd.DataFrame(all_data, columns=self.columns)
|
|
503
504
|
|
|
504
|
-
def to_kml(self, filename: str, field: str = "position", max_std: float = 3.0, **options) -> None:
|
|
505
|
+
def to_kml(self, filename: str, field: str = "position", max_std: float = 3.0, **options: Any) -> None:
|
|
505
506
|
"""Exports the result to a Google Earth KML file: the trajectory colored by a deviation field, with
|
|
506
507
|
playback in Google Earth's time slider. Requires a trajectory with a known EPSG code.
|
|
507
508
|
|
|
@@ -25,6 +25,7 @@ Welcome to the Trajectopy API reference. This section provides detailed document
|
|
|
25
25
|
- **[Trajectory I/O](io/trajectory_io.md)** - Read and write trajectories (ASCII)
|
|
26
26
|
- **[Results I/O](io/results_io.md)** - Read and write ATE/RPE/alignment results and parameters
|
|
27
27
|
- **[Google Earth (KML)](io/kml.md)** - Export trajectories and ATE results to Google Earth
|
|
28
|
+
- **[ROS Bags](io/rosbag.md)** - Read trajectories from ROS1 bag files
|
|
28
29
|
|
|
29
30
|
## Results
|
|
30
31
|
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# File Formats
|
|
2
|
+
|
|
3
|
+
Trajectopy reads trajectories from:
|
|
4
|
+
|
|
5
|
+
- **ASCII files** (`.traj`, `.txt`, `.csv`, …): one pose per line; a header describes the columns.
|
|
6
|
+
- **ROS1 bag files** (`.bag`): `geometry_msgs/PoseStamped` and `geometry_msgs/PointStamped` messages.
|
|
7
|
+
|
|
8
|
+
## ASCII Files
|
|
9
|
+
|
|
10
|
+
### Structure
|
|
11
|
+
|
|
12
|
+
Each line holds one pose. Lines starting with `#` are header lines, which describe how the data is interpreted.
|
|
13
|
+
Without a header, the columns are `t,px,py,pz,qx,qy,qz,qw` (time, position, quaternion), separated by commas:
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
1000.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0
|
|
17
|
+
1000.1,0.1,0.0,0.0,0.0,0.0,0.0,1.0
|
|
18
|
+
1000.2,0.2,0.0,0.0,0.0,0.0,0.0,1.0
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
With a header:
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
#name MyTrajectory
|
|
25
|
+
#epsg 25832
|
|
26
|
+
#fields t,px,py,pz,qx,qy,qz,qw
|
|
27
|
+
1000.0,364000.0,5621000.0,60.0,0.0,0.0,0.0,1.0
|
|
28
|
+
1000.1,364000.1,5621000.0,60.0,0.0,0.0,0.0,1.0
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Header Lines
|
|
32
|
+
|
|
33
|
+
| Header | Description | Default | Options |
|
|
34
|
+
|--------|-------------|---------|---------|
|
|
35
|
+
| `#name` | Display name | `Trajectory` | Any string |
|
|
36
|
+
| `#epsg` | EPSG code of the positions | `0` | Any EPSG code; `0` = local coordinates without a known datum |
|
|
37
|
+
| `#fields` | Meaning of the columns | `t,px,py,pz,qx,qy,qz,qw` | Comma-separated [field names](#field-names) |
|
|
38
|
+
| `#delimiter` | Column separator, in quotes | `','` | Any single character, e.g. `#delimiter ';'` |
|
|
39
|
+
| `#nframe` | Navigation frame of the data | `enu` | Order of east, north and up/down, e.g. `enu`, `ned`, `neu` |
|
|
40
|
+
| `#rot_unit` | Unit of Euler angles | `rad` | `rad`, `deg` |
|
|
41
|
+
| `#euler_rot_seq` | Axis sequence of Euler angles | `xyz` | Up to 3 of `x`, `y`, `z` for extrinsic or `X`, `Y`, `Z` for intrinsic rotations (not mixed) |
|
|
42
|
+
| `#time_format` | Format of the time column(s) | `unix` | `unix`, `datetime`, `gps_sow` |
|
|
43
|
+
| `#time_offset` | Offset in seconds added to all timestamps | `0.0` | Any number |
|
|
44
|
+
| `#datetime_format` | Format of date/time strings | `%Y-%m-%d %H:%M:%S.%f` | [strftime format](https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes) |
|
|
45
|
+
| `#datetime_timezone` | Time system of date/time strings | `UTC` | `UTC` or `GPS` |
|
|
46
|
+
| `#gps_week` | GPS week of `gps_sow` timestamps | `0` | Integer |
|
|
47
|
+
| `#sorting` | Order of the poses | `time` | `time`, `path_length` (or `arc_length`) |
|
|
48
|
+
|
|
49
|
+
Notes:
|
|
50
|
+
|
|
51
|
+
- **Delimiter**: the character must be in quotes; without them, the line is ignored and a comma is used.
|
|
52
|
+
Whitespace-separated files (spaces or tabs) are detected automatically and need no `#delimiter` line.
|
|
53
|
+
- **Navigation frame**: positions, velocities and orientations are converted to ENU. Positions are only
|
|
54
|
+
converted if `#epsg` is `0`; with an EPSG code, their axes are defined by the coordinate system.
|
|
55
|
+
- **GPS time**: `gps_sow` timestamps are seconds of the GPS week given by `#gps_week`; they are converted to UTC
|
|
56
|
+
(including leap seconds). `#datetime_timezone GPS` does the same for date/time strings.
|
|
57
|
+
- **Time systems**: date/time strings must be UTC or GPS time; local time zones are not supported (other values
|
|
58
|
+
are read as UTC, with a warning). A constant offset can be corrected with `#time_offset`.
|
|
59
|
+
- Header lines may also appear after the data (e.g. comments), but files with all header lines at the top load
|
|
60
|
+
faster.
|
|
61
|
+
|
|
62
|
+
### Field Names
|
|
63
|
+
|
|
64
|
+
| Field | Description | Can repeat? |
|
|
65
|
+
|-------|-------------|-------------|
|
|
66
|
+
| `t` | Time | Yes, for date and time in separate columns (`#time_format datetime`) |
|
|
67
|
+
| `l` | Path length (arc length) in meters | No |
|
|
68
|
+
| `px`, `py`, `pz` | Position (in the order of the coordinate system, e.g. latitude, longitude, height for EPSG 4326) | No |
|
|
69
|
+
| `qx`, `qy`, `qz`, `qw` | Orientation as quaternion | No |
|
|
70
|
+
| `ex`, `ey`, `ez` | Orientation as Euler angles (roll, pitch, yaw; see `#rot_unit` and `#euler_rot_seq`) | No |
|
|
71
|
+
| `vx`, `vy`, `vz` | Velocity in meters per second | No |
|
|
72
|
+
| `-` | Column that is not used | Yes |
|
|
73
|
+
|
|
74
|
+
Only `px`, `py` and `pz` are required. Without time column, the line number is used as timestamp; without
|
|
75
|
+
orientation, the trajectory has positions only (no rotation deviations, no leverarm estimation). If both
|
|
76
|
+
quaternions and Euler angles are given, the quaternions are used.
|
|
77
|
+
|
|
78
|
+
### Examples
|
|
79
|
+
|
|
80
|
+
**Positions only**
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
#name PositionOnly
|
|
84
|
+
#fields t,px,py,pz
|
|
85
|
+
1000.0,0.0,0.0,0.0
|
|
86
|
+
1000.1,0.1,0.0,0.0
|
|
87
|
+
1000.2,0.2,0.0,0.0
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**Euler angles in degrees**
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
#name EulerTrajectory
|
|
94
|
+
#fields t,px,py,pz,ex,ey,ez
|
|
95
|
+
#rot_unit deg
|
|
96
|
+
1000.0,0.0,0.0,0.0,0.0,0.0,0.0
|
|
97
|
+
1000.1,0.1,0.0,0.0,0.0,0.0,5.0
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**Space-separated, with unused columns**
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
#name SpaceSeparated
|
|
104
|
+
#fields t,-,-,px,py,pz
|
|
105
|
+
1000.0 17 ok 0.0 0.0 0.0
|
|
106
|
+
1000.1 17 ok 0.1 0.0 0.0
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**Geographic coordinates (latitude, longitude, ellipsoidal height)**
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
#name GNSS
|
|
113
|
+
#epsg 4979
|
|
114
|
+
#fields t,px,py,pz
|
|
115
|
+
1000.0,50.7374,7.0982,120.5
|
|
116
|
+
1000.1,50.7375,7.0983,121.2
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
**Date and time strings**
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
#name DatetimeTrajectory
|
|
123
|
+
#fields t,px,py,pz,qx,qy,qz,qw
|
|
124
|
+
#time_format datetime
|
|
125
|
+
#datetime_format %Y-%m-%d %H:%M:%S.%f
|
|
126
|
+
#datetime_timezone UTC
|
|
127
|
+
2024-01-01 12:00:00.000,0.0,0.0,0.0,0.0,0.0,0.0,1.0
|
|
128
|
+
2024-01-01 12:00:00.100,0.1,0.0,0.0,0.0,0.0,0.0,1.0
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Date and time can also be in separate columns: `#fields t,t,px,py,pz` (they are joined with a space).
|
|
132
|
+
|
|
133
|
+
**GPS time (seconds of week), NED frame, semicolon-separated**
|
|
134
|
+
|
|
135
|
+
```
|
|
136
|
+
#name GPSTime
|
|
137
|
+
#fields t,px,py,pz,qx,qy,qz,qw
|
|
138
|
+
#delimiter ';'
|
|
139
|
+
#nframe ned
|
|
140
|
+
#time_format gps_sow
|
|
141
|
+
#gps_week 2250
|
|
142
|
+
345600.0;0.0;0.0;0.0;0.0;0.0;0.0;1.0
|
|
143
|
+
345600.1;0.1;0.0;0.0;0.0;0.0;0.0;1.0
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Large Files
|
|
147
|
+
|
|
148
|
+
Files with millions of poses load several times faster with the optional pyarrow reader:
|
|
149
|
+
`pip install "trajectopy[fast]"`. It is used automatically when installed, for files without date/time strings.
|
|
150
|
+
|
|
151
|
+
## ROS Bags
|
|
152
|
+
|
|
153
|
+
ROS1 bag files (`.bag`) can be opened in the GUI like ASCII files, or read in Python:
|
|
154
|
+
|
|
155
|
+
```python
|
|
156
|
+
from trajectopy.io.rosbag import trajectories_from_rosbag
|
|
157
|
+
|
|
158
|
+
trajectories = trajectories_from_rosbag("recording.bag")
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Every topic with `geometry_msgs/PoseStamped` (position and orientation) or `geometry_msgs/PointStamped`
|
|
162
|
+
(position only) messages becomes one trajectory, named after the topic; the timestamps are taken from the message
|
|
163
|
+
headers. Messages of other types are skipped.
|
|
164
|
+
|
|
165
|
+
## Files Written by Trajectopy
|
|
166
|
+
|
|
167
|
+
- **Trajectories** (`Trajectory.to_file`, *Export…* in the GUI) are ASCII files as described above, with the
|
|
168
|
+
fields `t,l,px,py,pz,qx,qy,qz,qw,vx,vy,vz` (without `qx`…`qw` for trajectories without orientation).
|
|
169
|
+
- **Results** (`.result`): ATE, RPE and alignment results (`ATEResult.to_file`, `RPEResult.to_file`,
|
|
170
|
+
`AlignmentResult.to_file`). They are text files with a header, too, and can be opened in the GUI.
|
|
171
|
+
- **Settings** (`.json`): [processing settings](settings.md#processing-settings) and
|
|
172
|
+
[plot settings](settings.md#plot-settings).
|
|
173
|
+
- **Sessions** (GUI): a folder with all trajectories, results and settings, see
|
|
174
|
+
[Using the GUI](user_guide.md#menu-bar).
|
|
175
|
+
|
|
176
|
+
## Creating Trajectories in Python
|
|
177
|
+
|
|
178
|
+
```python
|
|
179
|
+
import numpy as np
|
|
180
|
+
import trajectopy as tpy
|
|
181
|
+
|
|
182
|
+
timestamps = np.arange(0, 10, 0.1)
|
|
183
|
+
positions = np.column_stack(
|
|
184
|
+
[
|
|
185
|
+
timestamps, # x increases with time
|
|
186
|
+
np.sin(timestamps), # y is sinusoidal
|
|
187
|
+
np.zeros_like(timestamps), # z is zero
|
|
188
|
+
]
|
|
189
|
+
)
|
|
190
|
+
quaternions = np.tile([0, 0, 0, 1], (len(timestamps), 1)) # identity (x, y, z, w)
|
|
191
|
+
|
|
192
|
+
trajectory = tpy.Trajectory.from_arrays(
|
|
193
|
+
xyz=positions,
|
|
194
|
+
quat=quaternions, # or rpy=... (roll, pitch, yaw in radians)
|
|
195
|
+
timestamps=timestamps,
|
|
196
|
+
name="CustomTrajectory",
|
|
197
|
+
)
|
|
198
|
+
trajectory.to_file("custom.traj")
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## See Also
|
|
202
|
+
|
|
203
|
+
- [User Guide](user_guide.md): loading files in Python and the GUI
|
|
204
|
+
- [API Reference](api/io/trajectory_io.md): reading and writing trajectories
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<h1>Trajectopy - Trajectory Evaluation in Python</h1>
|
|
3
|
+
<a href="https://github.com/gereon-t/trajectopy/releases"><img src="https://img.shields.io/github/v/release/gereon-t/trajectopy?label=version" /></a>
|
|
4
|
+
<a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/python-3.10+-blue.svg" /></a>
|
|
5
|
+
<a href="https://github.com/gereon-t/trajectopy/blob/main/LICENSE"><img src="https://img.shields.io/github/license/gereon-t/trajectopy" /></a>
|
|
6
|
+
<a href="https://github.com/astral-sh/ruff"><img alt="Code style: ruff" src="https://img.shields.io/badge/code%20style-ruff-000000.svg"></a>
|
|
7
|
+
<br />
|
|
8
|
+
<a href="https://github.com/gereon-t/trajectopy"><img src="https://img.shields.io/badge/Windows-0078D6?st&logo=windows&logoColor=white" /></a>
|
|
9
|
+
<a href="https://github.com/gereon-t/trajectopy"><img src="https://img.shields.io/badge/Linux-FCC624?logo=linux&logoColor=black" /></a>
|
|
10
|
+
<a href="https://github.com/gereon-t/trajectopy"><img src="https://img.shields.io/badge/mac%20os-000000?&logo=apple&logoColor=white" /></a>
|
|
11
|
+
|
|
12
|
+
<h4>Trajectopy is a Python package with an optional graphical user interface for empirical trajectory evaluation. </h4>
|
|
13
|
+
|
|
14
|
+
<p align="center">
|
|
15
|
+
<img style="border-radius: 10px;" src="https://raw.githubusercontent.com/gereon-t/trajectopy/main/.images/trajectopy_gif_low_quality.gif">
|
|
16
|
+
</p>
|
|
17
|
+
|
|
18
|
+
</div>
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
## Key Features
|
|
22
|
+
|
|
23
|
+
- __Interactive GUI__: Load, plot, align and compare trajectories without writing code. Every action explains
|
|
24
|
+
itself in the context menus, and trajectories can be aligned interactively if an automatic alignment is not possible.
|
|
25
|
+
- __Alignment__: A spatio-temporal least squares alignment that can be tailored to the application: similarity
|
|
26
|
+
transformation, leverarm and time shift, each of them optional.
|
|
27
|
+
- __Comparison__: Absolute and relative trajectory error (__ATE and RPE__), computed with various pose-matching methods.
|
|
28
|
+
- __Data Import/Export__: ASCII files with configurable columns, time formats and coordinate systems
|
|
29
|
+
(any EPSG code), and ROS bags.
|
|
30
|
+
- __Fast, Interactive Visualization__: Powered by [pyqtgraph](https://www.pyqtgraph.org/), the same plots are
|
|
31
|
+
available in the GUI and in Python scripts, stay fluid for large trajectories, and can be exported to PNG, JPG
|
|
32
|
+
or SVG. Georeferenced trajectories and ATE results can be exported to Google Earth, colored by speed, height or
|
|
33
|
+
deviations, on the terrain or at their own heights, with playback.
|
|
34
|
+
|
|
35
|
+
## Installation
|
|
36
|
+
|
|
37
|
+
### Windows App
|
|
38
|
+
|
|
39
|
+
Download `Trajectopy-<version>-setup.exe` from the [latest release](https://github.com/gereon-t/trajectopy/releases/latest)
|
|
40
|
+
and run it. No Python installation or administrator rights are needed. The installer adds Trajectopy to the Start
|
|
41
|
+
menu and, optionally, opens `.traj` files with it on double-click. The app reports new versions (Help → Check for
|
|
42
|
+
Updates); installing a newer version over an existing one updates it.
|
|
43
|
+
|
|
44
|
+
As the installer is not signed, Windows SmartScreen may warn about an unknown publisher: choose
|
|
45
|
+
*More info → Run anyway*.
|
|
46
|
+
|
|
47
|
+
### Python Package
|
|
48
|
+
|
|
49
|
+
| Command | Contents |
|
|
50
|
+
| --- | --- |
|
|
51
|
+
| `pip install "trajectopy[gui]"` | Python package and GUI (recommended) |
|
|
52
|
+
| `pip install "trajectopy[plot]"` | Python package with plotting, without the GUI |
|
|
53
|
+
| `pip install trajectopy` | Python package only |
|
|
54
|
+
|
|
55
|
+
Trajectopy requires Python 3.10 or newer.
|
|
56
|
+
|
|
57
|
+
**Faster loading of large files**: trajectory files with millions of poses load several times faster with the
|
|
58
|
+
optional `fast` extra, which installs [pyarrow](https://arrow.apache.org/docs/python/) for reading. It can be
|
|
59
|
+
combined with the other extras:
|
|
60
|
+
|
|
61
|
+
```console
|
|
62
|
+
pip install "trajectopy[gui,fast]"
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Quick Start
|
|
66
|
+
|
|
67
|
+
### Python
|
|
68
|
+
|
|
69
|
+
Load two trajectories and compute the absolute and relative trajectory error. The example files are in the
|
|
70
|
+
[`example_data`](https://github.com/gereon-t/trajectopy/tree/main/example_data) folder of the repository.
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
import trajectopy as tpy
|
|
74
|
+
|
|
75
|
+
reference = tpy.Trajectory.from_file("./example_data/KITTI_gt.traj")
|
|
76
|
+
estimate = tpy.Trajectory.from_file("./example_data/KITTI_ORB.traj")
|
|
77
|
+
|
|
78
|
+
# ATE: the estimate is aligned to the reference first (similarity transformation)
|
|
79
|
+
ate_result = tpy.ate(trajectory=estimate, other=reference)
|
|
80
|
+
print(f"Position ATE: {ate_result.pos_ate:.3f} m")
|
|
81
|
+
|
|
82
|
+
# RPE: drift per 100 m to 800 m (see the relative comparison settings)
|
|
83
|
+
rpe_result = tpy.rpe(trajectory=estimate, other=reference)
|
|
84
|
+
print(f"Position RPE: {rpe_result.pos_rpe:.3f} {rpe_result.pos_drift_unit}")
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
The [User Guide](user_guide.md) covers alignment, evaluation, plots and the GUI in detail.
|
|
88
|
+
|
|
89
|
+
### GUI
|
|
90
|
+
|
|
91
|
+
Start the Windows app from the Start menu, or run:
|
|
92
|
+
|
|
93
|
+
```console
|
|
94
|
+
trajectopy
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Drop trajectory files onto the trajectory table, right-click the ground truth and choose *Set as Reference*,
|
|
98
|
+
then right-click another trajectory and choose *Compare with Reference*. See [Using the GUI](user_guide.md#6-using-the-gui).
|
|
99
|
+
|
|
100
|
+
## Command Line
|
|
101
|
+
|
|
102
|
+
```console
|
|
103
|
+
trajectopy [-h] [--version] [--single-thread] [--plot-settings PLOT_SETTINGS] [files ...]
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
| Argument | Description |
|
|
107
|
+
| --- | --- |
|
|
108
|
+
| `files` | Trajectory (`.traj`, `.bag`) or result (`.result`) files to open at start. |
|
|
109
|
+
| `--version`, `-v` | Show the version and exit. |
|
|
110
|
+
| `--single-thread` | Process in the GUI thread instead of a background thread, e.g. for debugging. The GUI does not respond while processing. |
|
|
111
|
+
| `--plot-settings PLOT_SETTINGS` | JSON file with [plot settings](settings.md#plot-settings); settings missing in the file keep their default values. |
|
|
112
|
+
| `-h`, `--help` | Show the help and exit. |
|
|
113
|
+
|
|
114
|
+
Examples:
|
|
115
|
+
|
|
116
|
+
```console
|
|
117
|
+
trajectopy ground_truth.traj slam.traj
|
|
118
|
+
trajectopy --plot-settings my_session/plot_settings.json
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
A session saved from the GUI contains its plot settings in `plot_settings.json`.
|
|
122
|
+
|
|
123
|
+
## Citation
|
|
124
|
+
|
|
125
|
+
If you use Trajectopy for academic work, please cite our [paper](https://www.degruyter.com/document/doi/10.1515/jag-2024-0040/html):
|
|
126
|
+
|
|
127
|
+
```bibtex
|
|
128
|
+
@article{Tombrink2024,
|
|
129
|
+
title = {Spatio-temporal trajectory alignment for trajectory evaluation},
|
|
130
|
+
author = {Gereon Tombrink and Ansgar Dreier and Lasse Klingbeil and Heiner Kuhlmann},
|
|
131
|
+
journal = {Journal of Applied Geodesy},
|
|
132
|
+
year = {2024},
|
|
133
|
+
doi = {10.1515/jag-2024-0040},
|
|
134
|
+
url = {https://doi.org/10.1515/jag-2024-0040},
|
|
135
|
+
codeurl = {https://github.com/gereon-t/trajectopy}
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## License
|
|
140
|
+
|
|
141
|
+
Trajectopy is released under the GNU General Public License v3.0.
|
|
142
|
+
|
|
143
|
+
## Contributing
|
|
144
|
+
|
|
145
|
+
Contributions are welcome! Please see our [GitHub repository](https://github.com/gereon-t/trajectopy) for details.
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
*Developed at the Institute of Geodesy and Geoinformation, University of Bonn*
|