robometrics 0.1.0__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.
- robometrics-0.1.0/LICENSE +21 -0
- robometrics-0.1.0/PKG-INFO +271 -0
- robometrics-0.1.0/README.md +232 -0
- robometrics-0.1.0/pyproject.toml +87 -0
- robometrics-0.1.0/robometrics/__init__.py +85 -0
- robometrics-0.1.0/robometrics/comfort.py +72 -0
- robometrics-0.1.0/robometrics/evaluator.py +294 -0
- robometrics-0.1.0/robometrics/geometry.py +164 -0
- robometrics-0.1.0/robometrics/io.py +142 -0
- robometrics-0.1.0/robometrics/physics.py +129 -0
- robometrics-0.1.0/robometrics/prediction.py +59 -0
- robometrics-0.1.0/robometrics/py.typed +1 -0
- robometrics-0.1.0/robometrics/registry.py +419 -0
- robometrics-0.1.0/robometrics/results.py +223 -0
- robometrics-0.1.0/robometrics/safety.py +122 -0
- robometrics-0.1.0/robometrics/schemas.py +85 -0
- robometrics-0.1.0/robometrics/trajectory.py +114 -0
- robometrics-0.1.0/robometrics.egg-info/PKG-INFO +271 -0
- robometrics-0.1.0/robometrics.egg-info/SOURCES.txt +33 -0
- robometrics-0.1.0/robometrics.egg-info/dependency_links.txt +1 -0
- robometrics-0.1.0/robometrics.egg-info/requires.txt +11 -0
- robometrics-0.1.0/robometrics.egg-info/top_level.txt +1 -0
- robometrics-0.1.0/setup.cfg +4 -0
- robometrics-0.1.0/tests/test_comfort.py +49 -0
- robometrics-0.1.0/tests/test_evaluator.py +112 -0
- robometrics-0.1.0/tests/test_examples.py +19 -0
- robometrics-0.1.0/tests/test_io_schemas.py +97 -0
- robometrics-0.1.0/tests/test_physics.py +56 -0
- robometrics-0.1.0/tests/test_prediction.py +65 -0
- robometrics-0.1.0/tests/test_public_api.py +42 -0
- robometrics-0.1.0/tests/test_readme_examples.py +47 -0
- robometrics-0.1.0/tests/test_registry.py +53 -0
- robometrics-0.1.0/tests/test_results.py +78 -0
- robometrics-0.1.0/tests/test_safety.py +81 -0
- robometrics-0.1.0/tests/test_trajectory.py +72 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 RoboMetrics contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: robometrics
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Lightweight robotics metrics for Python.
|
|
5
|
+
Author: RoboMetrics contributors
|
|
6
|
+
Maintainer: RoboMetrics contributors
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Project-URL: Homepage, https://github.com/robometrics/robometrics
|
|
9
|
+
Project-URL: Documentation, https://github.com/robometrics/robometrics/tree/main/docs
|
|
10
|
+
Project-URL: Source, https://github.com/robometrics/robometrics
|
|
11
|
+
Project-URL: Issues, https://github.com/robometrics/robometrics/issues
|
|
12
|
+
Project-URL: Changelog, https://github.com/robometrics/robometrics/blob/main/CHANGELOG.md
|
|
13
|
+
Keywords: robotics,metrics,trajectories,prediction,safety,numpy
|
|
14
|
+
Classifier: Development Status :: 3 - Alpha
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Intended Audience :: Science/Research
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Topic :: Scientific/Engineering
|
|
24
|
+
Classifier: Typing :: Typed
|
|
25
|
+
Requires-Python: >=3.10
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
License-File: LICENSE
|
|
28
|
+
Requires-Dist: numpy>=1.23
|
|
29
|
+
Requires-Dist: scipy>=1.10
|
|
30
|
+
Requires-Dist: pandas>=1.5
|
|
31
|
+
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: coverage[toml]>=7; extra == "dev"
|
|
33
|
+
Requires-Dist: mypy>=1.10; extra == "dev"
|
|
34
|
+
Requires-Dist: pre-commit>=3.5; extra == "dev"
|
|
35
|
+
Requires-Dist: pytest-cov>=5; extra == "dev"
|
|
36
|
+
Requires-Dist: pytest>=8; extra == "dev"
|
|
37
|
+
Requires-Dist: ruff>=0.5; extra == "dev"
|
|
38
|
+
Dynamic: license-file
|
|
39
|
+
|
|
40
|
+
# RoboMetrics
|
|
41
|
+
|
|
42
|
+
[](https://github.com/robometrics/robometrics/actions/workflows/ci.yml)
|
|
43
|
+

|
|
44
|
+

|
|
45
|
+

|
|
46
|
+
|
|
47
|
+
Lightweight robotics metrics for Python.
|
|
48
|
+
|
|
49
|
+
RoboMetrics is a small local Python library for computing robotics trajectory,
|
|
50
|
+
prediction, safety, comfort, and physics metrics from NumPy arrays and simple
|
|
51
|
+
CSV/JSON trajectory files.
|
|
52
|
+
|
|
53
|
+
## Why This Exists
|
|
54
|
+
|
|
55
|
+
Robotics projects often grow scattered metric functions across notebooks,
|
|
56
|
+
experiments, and scripts. RoboMetrics keeps common metrics in one typed,
|
|
57
|
+
dependency-light package that can be imported directly inside local robotics
|
|
58
|
+
codebases.
|
|
59
|
+
|
|
60
|
+
## Installation
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
pip install robometrics
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
For local development:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
python -m venv .venv
|
|
70
|
+
source .venv/bin/activate
|
|
71
|
+
pip install -e ".[dev]"
|
|
72
|
+
pytest
|
|
73
|
+
ruff check .
|
|
74
|
+
mypy robometrics
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Quickstart
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
import numpy as np
|
|
81
|
+
|
|
82
|
+
from robometrics import ade, fde, path_length
|
|
83
|
+
|
|
84
|
+
prediction = np.array([[0.0, 0.0], [1.0, 0.0], [2.0, 0.0]])
|
|
85
|
+
ground_truth = np.array([[0.0, 0.0], [1.1, 0.0], [2.2, 0.0]])
|
|
86
|
+
|
|
87
|
+
print("ADE (m):", ade(prediction, ground_truth))
|
|
88
|
+
print("FDE (m):", fde(prediction, ground_truth))
|
|
89
|
+
print("Path length (m):", path_length(ground_truth))
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Runnable examples:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
python examples/trajectory_metrics.py
|
|
96
|
+
python examples/prediction_metrics.py
|
|
97
|
+
python examples/safety_metrics.py
|
|
98
|
+
python examples/comfort_metrics.py
|
|
99
|
+
python examples/load_from_csv.py
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Input Shapes
|
|
103
|
+
|
|
104
|
+
Trajectory-like inputs are NumPy-compatible arrays with finite numeric values:
|
|
105
|
+
|
|
106
|
+
- Single trajectory: `Nx2` or `Nx3`
|
|
107
|
+
- Multimodal predictions: `KxTx2` or `KxTx3`
|
|
108
|
+
- Ground-truth trajectory for prediction metrics: `Tx2` or `Tx3`
|
|
109
|
+
- Actor trajectories for safety metrics: a list of `Nx2` or `Nx3` arrays
|
|
110
|
+
|
|
111
|
+
`N` or `T` is the number of timesteps, `K` is the number of prediction modes,
|
|
112
|
+
and columns are position coordinates in meters. `Nx3` inputs are supported by
|
|
113
|
+
metrics that operate on positions; distance-based metrics use all available
|
|
114
|
+
coordinate dimensions unless documented otherwise.
|
|
115
|
+
|
|
116
|
+
Empty arrays, NaN/inf values, bad ranks, mismatched trajectory lengths, invalid
|
|
117
|
+
`dt`, and incompatible dimensions raise `ValueError` with a targeted message.
|
|
118
|
+
|
|
119
|
+
## Units
|
|
120
|
+
|
|
121
|
+
- Position and distance inputs: meters
|
|
122
|
+
- Time step `dt`: seconds
|
|
123
|
+
- Speed: meters per second
|
|
124
|
+
- Acceleration: meters per second squared
|
|
125
|
+
- Jerk: meters per second cubed
|
|
126
|
+
- Curvature: inverse meters
|
|
127
|
+
- Rates and scores: unitless floats
|
|
128
|
+
|
|
129
|
+
## Return Types
|
|
130
|
+
|
|
131
|
+
Most public metric functions return `float` or `numpy.ndarray`. Threshold-style
|
|
132
|
+
physics helpers return `MetricResult`, which stores a value, unit, optional
|
|
133
|
+
threshold, pass/fail status, and metadata.
|
|
134
|
+
|
|
135
|
+
`EvaluationResult` is a small container for local batches of metric results and
|
|
136
|
+
can export dictionaries, strict JSON, Markdown tables, and pandas DataFrames.
|
|
137
|
+
Non-finite metric values are serialized as `null` in JSON.
|
|
138
|
+
|
|
139
|
+
## Metric Examples
|
|
140
|
+
|
|
141
|
+
### Trajectory
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
import numpy as np
|
|
145
|
+
|
|
146
|
+
from robometrics import curvature, path_length
|
|
147
|
+
|
|
148
|
+
trajectory = np.array([[0.0, 0.0], [1.0, 0.2], [2.0, 0.5]])
|
|
149
|
+
|
|
150
|
+
print(path_length(trajectory)) # meters
|
|
151
|
+
print(curvature(trajectory)) # 1/meters
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Prediction
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
import numpy as np
|
|
158
|
+
|
|
159
|
+
from robometrics import min_ade, min_fde, miss_rate
|
|
160
|
+
|
|
161
|
+
predictions = np.array(
|
|
162
|
+
[
|
|
163
|
+
[[0.0, 0.0], [1.0, 0.0], [2.0, 0.0]],
|
|
164
|
+
[[0.0, 0.0], [1.1, 0.0], [2.1, 0.0]],
|
|
165
|
+
]
|
|
166
|
+
)
|
|
167
|
+
ground_truth = np.array([[0.0, 0.0], [1.0, 0.1], [2.0, 0.2]])
|
|
168
|
+
|
|
169
|
+
print(min_ade(predictions, ground_truth))
|
|
170
|
+
print(min_fde(predictions, ground_truth))
|
|
171
|
+
print(miss_rate(predictions, ground_truth, threshold=0.5))
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Safety
|
|
175
|
+
|
|
176
|
+
```python
|
|
177
|
+
import numpy as np
|
|
178
|
+
|
|
179
|
+
from robometrics import collision_rate, min_distance_to_actors
|
|
180
|
+
|
|
181
|
+
ego = np.array([[0.0, 0.0], [1.0, 0.0], [2.0, 0.0]])
|
|
182
|
+
actors = [np.array([[0.0, 2.0], [1.0, 1.0], [2.0, 0.4]])]
|
|
183
|
+
|
|
184
|
+
print(min_distance_to_actors(ego, actors)) # meters
|
|
185
|
+
print(collision_rate(ego, actors, ego_radius=0.3, actor_radius=0.3))
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Comfort
|
|
189
|
+
|
|
190
|
+
```python
|
|
191
|
+
import numpy as np
|
|
192
|
+
|
|
193
|
+
from robometrics import acceleration, jerk, jerk_cost
|
|
194
|
+
|
|
195
|
+
trajectory = np.array([[0.0, 0.0], [1.0, 0.1], [2.0, 0.4], [3.0, 0.9]])
|
|
196
|
+
dt = 0.5
|
|
197
|
+
|
|
198
|
+
print(acceleration(trajectory, dt=dt)) # m/s^2
|
|
199
|
+
print(jerk(trajectory, dt=dt)) # m/s^3
|
|
200
|
+
print(jerk_cost(trajectory, dt=dt))
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Physics
|
|
204
|
+
|
|
205
|
+
```python
|
|
206
|
+
import numpy as np
|
|
207
|
+
|
|
208
|
+
from robometrics import acceleration_limits_violated, dynamic_feasibility_score
|
|
209
|
+
|
|
210
|
+
trajectory = np.array([[0.0, 0.0], [1.0, 0.1], [2.0, 0.3], [3.0, 0.6]])
|
|
211
|
+
|
|
212
|
+
print(acceleration_limits_violated(trajectory, dt=0.5, max_accel=5.0))
|
|
213
|
+
print(dynamic_feasibility_score(trajectory, dt=0.5))
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## CSV And JSON
|
|
217
|
+
|
|
218
|
+
```python
|
|
219
|
+
from robometrics.io import load_trajectory_csv, load_trajectory_json
|
|
220
|
+
|
|
221
|
+
csv_traj = load_trajectory_csv("trajectory.csv")
|
|
222
|
+
json_traj = load_trajectory_json("trajectory.json")
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
CSV files must include `x` and `y` columns, and may include `z`. JSON files may
|
|
226
|
+
contain either a raw list of points or an object with a `points` field.
|
|
227
|
+
|
|
228
|
+
## Lightweight Evaluator And Registry
|
|
229
|
+
|
|
230
|
+
The evaluator and registry are intentionally small helpers for local scripts.
|
|
231
|
+
Use them when named metric selection or threshold reporting is useful:
|
|
232
|
+
|
|
233
|
+
```python
|
|
234
|
+
from robometrics import Evaluator, registry
|
|
235
|
+
|
|
236
|
+
print(registry.list_metrics())
|
|
237
|
+
|
|
238
|
+
result = Evaluator().evaluate(
|
|
239
|
+
prediction=prediction,
|
|
240
|
+
ground_truth=ground_truth,
|
|
241
|
+
metrics=["ade", "fde"],
|
|
242
|
+
thresholds={"ade": 0.5, "fde": 1.0},
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
print(result.to_json())
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Unknown metric names raise `UnknownMetricError` before evaluation starts.
|
|
249
|
+
Metric execution failures are returned as failed `MetricResult` entries with
|
|
250
|
+
`metadata["error"]`.
|
|
251
|
+
|
|
252
|
+
## Contributing
|
|
253
|
+
|
|
254
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) and [docs/contributing.md](docs/contributing.md).
|
|
255
|
+
|
|
256
|
+
Before opening a pull request:
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
ruff check .
|
|
260
|
+
mypy robometrics
|
|
261
|
+
pytest --cov=robometrics --cov-report=term-missing
|
|
262
|
+
python examples/trajectory_metrics.py
|
|
263
|
+
python examples/prediction_metrics.py
|
|
264
|
+
python examples/safety_metrics.py
|
|
265
|
+
python examples/comfort_metrics.py
|
|
266
|
+
python examples/load_from_csv.py
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
## License
|
|
270
|
+
|
|
271
|
+
MIT. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
# RoboMetrics
|
|
2
|
+
|
|
3
|
+
[](https://github.com/robometrics/robometrics/actions/workflows/ci.yml)
|
|
4
|
+

|
|
5
|
+

|
|
6
|
+

|
|
7
|
+
|
|
8
|
+
Lightweight robotics metrics for Python.
|
|
9
|
+
|
|
10
|
+
RoboMetrics is a small local Python library for computing robotics trajectory,
|
|
11
|
+
prediction, safety, comfort, and physics metrics from NumPy arrays and simple
|
|
12
|
+
CSV/JSON trajectory files.
|
|
13
|
+
|
|
14
|
+
## Why This Exists
|
|
15
|
+
|
|
16
|
+
Robotics projects often grow scattered metric functions across notebooks,
|
|
17
|
+
experiments, and scripts. RoboMetrics keeps common metrics in one typed,
|
|
18
|
+
dependency-light package that can be imported directly inside local robotics
|
|
19
|
+
codebases.
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pip install robometrics
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
For local development:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
python -m venv .venv
|
|
31
|
+
source .venv/bin/activate
|
|
32
|
+
pip install -e ".[dev]"
|
|
33
|
+
pytest
|
|
34
|
+
ruff check .
|
|
35
|
+
mypy robometrics
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Quickstart
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
import numpy as np
|
|
42
|
+
|
|
43
|
+
from robometrics import ade, fde, path_length
|
|
44
|
+
|
|
45
|
+
prediction = np.array([[0.0, 0.0], [1.0, 0.0], [2.0, 0.0]])
|
|
46
|
+
ground_truth = np.array([[0.0, 0.0], [1.1, 0.0], [2.2, 0.0]])
|
|
47
|
+
|
|
48
|
+
print("ADE (m):", ade(prediction, ground_truth))
|
|
49
|
+
print("FDE (m):", fde(prediction, ground_truth))
|
|
50
|
+
print("Path length (m):", path_length(ground_truth))
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Runnable examples:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
python examples/trajectory_metrics.py
|
|
57
|
+
python examples/prediction_metrics.py
|
|
58
|
+
python examples/safety_metrics.py
|
|
59
|
+
python examples/comfort_metrics.py
|
|
60
|
+
python examples/load_from_csv.py
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Input Shapes
|
|
64
|
+
|
|
65
|
+
Trajectory-like inputs are NumPy-compatible arrays with finite numeric values:
|
|
66
|
+
|
|
67
|
+
- Single trajectory: `Nx2` or `Nx3`
|
|
68
|
+
- Multimodal predictions: `KxTx2` or `KxTx3`
|
|
69
|
+
- Ground-truth trajectory for prediction metrics: `Tx2` or `Tx3`
|
|
70
|
+
- Actor trajectories for safety metrics: a list of `Nx2` or `Nx3` arrays
|
|
71
|
+
|
|
72
|
+
`N` or `T` is the number of timesteps, `K` is the number of prediction modes,
|
|
73
|
+
and columns are position coordinates in meters. `Nx3` inputs are supported by
|
|
74
|
+
metrics that operate on positions; distance-based metrics use all available
|
|
75
|
+
coordinate dimensions unless documented otherwise.
|
|
76
|
+
|
|
77
|
+
Empty arrays, NaN/inf values, bad ranks, mismatched trajectory lengths, invalid
|
|
78
|
+
`dt`, and incompatible dimensions raise `ValueError` with a targeted message.
|
|
79
|
+
|
|
80
|
+
## Units
|
|
81
|
+
|
|
82
|
+
- Position and distance inputs: meters
|
|
83
|
+
- Time step `dt`: seconds
|
|
84
|
+
- Speed: meters per second
|
|
85
|
+
- Acceleration: meters per second squared
|
|
86
|
+
- Jerk: meters per second cubed
|
|
87
|
+
- Curvature: inverse meters
|
|
88
|
+
- Rates and scores: unitless floats
|
|
89
|
+
|
|
90
|
+
## Return Types
|
|
91
|
+
|
|
92
|
+
Most public metric functions return `float` or `numpy.ndarray`. Threshold-style
|
|
93
|
+
physics helpers return `MetricResult`, which stores a value, unit, optional
|
|
94
|
+
threshold, pass/fail status, and metadata.
|
|
95
|
+
|
|
96
|
+
`EvaluationResult` is a small container for local batches of metric results and
|
|
97
|
+
can export dictionaries, strict JSON, Markdown tables, and pandas DataFrames.
|
|
98
|
+
Non-finite metric values are serialized as `null` in JSON.
|
|
99
|
+
|
|
100
|
+
## Metric Examples
|
|
101
|
+
|
|
102
|
+
### Trajectory
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
import numpy as np
|
|
106
|
+
|
|
107
|
+
from robometrics import curvature, path_length
|
|
108
|
+
|
|
109
|
+
trajectory = np.array([[0.0, 0.0], [1.0, 0.2], [2.0, 0.5]])
|
|
110
|
+
|
|
111
|
+
print(path_length(trajectory)) # meters
|
|
112
|
+
print(curvature(trajectory)) # 1/meters
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Prediction
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
import numpy as np
|
|
119
|
+
|
|
120
|
+
from robometrics import min_ade, min_fde, miss_rate
|
|
121
|
+
|
|
122
|
+
predictions = np.array(
|
|
123
|
+
[
|
|
124
|
+
[[0.0, 0.0], [1.0, 0.0], [2.0, 0.0]],
|
|
125
|
+
[[0.0, 0.0], [1.1, 0.0], [2.1, 0.0]],
|
|
126
|
+
]
|
|
127
|
+
)
|
|
128
|
+
ground_truth = np.array([[0.0, 0.0], [1.0, 0.1], [2.0, 0.2]])
|
|
129
|
+
|
|
130
|
+
print(min_ade(predictions, ground_truth))
|
|
131
|
+
print(min_fde(predictions, ground_truth))
|
|
132
|
+
print(miss_rate(predictions, ground_truth, threshold=0.5))
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Safety
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
import numpy as np
|
|
139
|
+
|
|
140
|
+
from robometrics import collision_rate, min_distance_to_actors
|
|
141
|
+
|
|
142
|
+
ego = np.array([[0.0, 0.0], [1.0, 0.0], [2.0, 0.0]])
|
|
143
|
+
actors = [np.array([[0.0, 2.0], [1.0, 1.0], [2.0, 0.4]])]
|
|
144
|
+
|
|
145
|
+
print(min_distance_to_actors(ego, actors)) # meters
|
|
146
|
+
print(collision_rate(ego, actors, ego_radius=0.3, actor_radius=0.3))
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Comfort
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
import numpy as np
|
|
153
|
+
|
|
154
|
+
from robometrics import acceleration, jerk, jerk_cost
|
|
155
|
+
|
|
156
|
+
trajectory = np.array([[0.0, 0.0], [1.0, 0.1], [2.0, 0.4], [3.0, 0.9]])
|
|
157
|
+
dt = 0.5
|
|
158
|
+
|
|
159
|
+
print(acceleration(trajectory, dt=dt)) # m/s^2
|
|
160
|
+
print(jerk(trajectory, dt=dt)) # m/s^3
|
|
161
|
+
print(jerk_cost(trajectory, dt=dt))
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Physics
|
|
165
|
+
|
|
166
|
+
```python
|
|
167
|
+
import numpy as np
|
|
168
|
+
|
|
169
|
+
from robometrics import acceleration_limits_violated, dynamic_feasibility_score
|
|
170
|
+
|
|
171
|
+
trajectory = np.array([[0.0, 0.0], [1.0, 0.1], [2.0, 0.3], [3.0, 0.6]])
|
|
172
|
+
|
|
173
|
+
print(acceleration_limits_violated(trajectory, dt=0.5, max_accel=5.0))
|
|
174
|
+
print(dynamic_feasibility_score(trajectory, dt=0.5))
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## CSV And JSON
|
|
178
|
+
|
|
179
|
+
```python
|
|
180
|
+
from robometrics.io import load_trajectory_csv, load_trajectory_json
|
|
181
|
+
|
|
182
|
+
csv_traj = load_trajectory_csv("trajectory.csv")
|
|
183
|
+
json_traj = load_trajectory_json("trajectory.json")
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
CSV files must include `x` and `y` columns, and may include `z`. JSON files may
|
|
187
|
+
contain either a raw list of points or an object with a `points` field.
|
|
188
|
+
|
|
189
|
+
## Lightweight Evaluator And Registry
|
|
190
|
+
|
|
191
|
+
The evaluator and registry are intentionally small helpers for local scripts.
|
|
192
|
+
Use them when named metric selection or threshold reporting is useful:
|
|
193
|
+
|
|
194
|
+
```python
|
|
195
|
+
from robometrics import Evaluator, registry
|
|
196
|
+
|
|
197
|
+
print(registry.list_metrics())
|
|
198
|
+
|
|
199
|
+
result = Evaluator().evaluate(
|
|
200
|
+
prediction=prediction,
|
|
201
|
+
ground_truth=ground_truth,
|
|
202
|
+
metrics=["ade", "fde"],
|
|
203
|
+
thresholds={"ade": 0.5, "fde": 1.0},
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
print(result.to_json())
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Unknown metric names raise `UnknownMetricError` before evaluation starts.
|
|
210
|
+
Metric execution failures are returned as failed `MetricResult` entries with
|
|
211
|
+
`metadata["error"]`.
|
|
212
|
+
|
|
213
|
+
## Contributing
|
|
214
|
+
|
|
215
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) and [docs/contributing.md](docs/contributing.md).
|
|
216
|
+
|
|
217
|
+
Before opening a pull request:
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
ruff check .
|
|
221
|
+
mypy robometrics
|
|
222
|
+
pytest --cov=robometrics --cov-report=term-missing
|
|
223
|
+
python examples/trajectory_metrics.py
|
|
224
|
+
python examples/prediction_metrics.py
|
|
225
|
+
python examples/safety_metrics.py
|
|
226
|
+
python examples/comfort_metrics.py
|
|
227
|
+
python examples/load_from_csv.py
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## License
|
|
231
|
+
|
|
232
|
+
MIT. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "robometrics"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Lightweight robotics metrics for Python."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
authors = [{ name = "RoboMetrics contributors" }]
|
|
13
|
+
maintainers = [{ name = "RoboMetrics contributors" }]
|
|
14
|
+
keywords = ["robotics", "metrics", "trajectories", "prediction", "safety", "numpy"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 3 - Alpha",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"Intended Audience :: Science/Research",
|
|
19
|
+
"Operating System :: OS Independent",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
22
|
+
"Programming Language :: Python :: 3.10",
|
|
23
|
+
"Programming Language :: Python :: 3.11",
|
|
24
|
+
"Programming Language :: Python :: 3.12",
|
|
25
|
+
"Topic :: Scientific/Engineering",
|
|
26
|
+
"Typing :: Typed",
|
|
27
|
+
]
|
|
28
|
+
dependencies = [
|
|
29
|
+
"numpy>=1.23",
|
|
30
|
+
"scipy>=1.10",
|
|
31
|
+
"pandas>=1.5",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[project.optional-dependencies]
|
|
35
|
+
dev = [
|
|
36
|
+
"coverage[toml]>=7",
|
|
37
|
+
"mypy>=1.10",
|
|
38
|
+
"pre-commit>=3.5",
|
|
39
|
+
"pytest-cov>=5",
|
|
40
|
+
"pytest>=8",
|
|
41
|
+
"ruff>=0.5",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
[project.urls]
|
|
45
|
+
Homepage = "https://github.com/robometrics/robometrics"
|
|
46
|
+
Documentation = "https://github.com/robometrics/robometrics/tree/main/docs"
|
|
47
|
+
Source = "https://github.com/robometrics/robometrics"
|
|
48
|
+
Issues = "https://github.com/robometrics/robometrics/issues"
|
|
49
|
+
Changelog = "https://github.com/robometrics/robometrics/blob/main/CHANGELOG.md"
|
|
50
|
+
|
|
51
|
+
[tool.setuptools.packages.find]
|
|
52
|
+
include = ["robometrics*"]
|
|
53
|
+
|
|
54
|
+
[tool.setuptools.package-data]
|
|
55
|
+
robometrics = ["py.typed"]
|
|
56
|
+
|
|
57
|
+
[tool.pytest.ini_options]
|
|
58
|
+
testpaths = ["tests"]
|
|
59
|
+
addopts = "-ra"
|
|
60
|
+
|
|
61
|
+
[tool.ruff]
|
|
62
|
+
line-length = 100
|
|
63
|
+
target-version = "py310"
|
|
64
|
+
|
|
65
|
+
[tool.ruff.lint]
|
|
66
|
+
select = ["B", "E", "F", "I", "SIM", "UP"]
|
|
67
|
+
|
|
68
|
+
[tool.mypy]
|
|
69
|
+
python_version = "3.10"
|
|
70
|
+
packages = ["robometrics"]
|
|
71
|
+
disallow_untyped_defs = true
|
|
72
|
+
no_implicit_optional = true
|
|
73
|
+
warn_return_any = true
|
|
74
|
+
warn_unused_ignores = true
|
|
75
|
+
strict_equality = true
|
|
76
|
+
|
|
77
|
+
[[tool.mypy.overrides]]
|
|
78
|
+
module = ["pandas.*", "scipy.*"]
|
|
79
|
+
ignore_missing_imports = true
|
|
80
|
+
|
|
81
|
+
[tool.coverage.run]
|
|
82
|
+
branch = true
|
|
83
|
+
source = ["robometrics"]
|
|
84
|
+
|
|
85
|
+
[tool.coverage.report]
|
|
86
|
+
show_missing = true
|
|
87
|
+
skip_covered = true
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""RoboMetrics: lightweight robotics metrics for Python."""
|
|
2
|
+
|
|
3
|
+
from robometrics.comfort import (
|
|
4
|
+
acceleration,
|
|
5
|
+
jerk,
|
|
6
|
+
jerk_cost,
|
|
7
|
+
max_acceleration,
|
|
8
|
+
max_deceleration,
|
|
9
|
+
smoothness_score,
|
|
10
|
+
)
|
|
11
|
+
from robometrics.evaluator import EvaluationInputError, Evaluator
|
|
12
|
+
from robometrics.io import TrajectoryIOError
|
|
13
|
+
from robometrics.physics import (
|
|
14
|
+
acceleration_limits_violated,
|
|
15
|
+
curvature_limits_violated,
|
|
16
|
+
dynamic_feasibility_score,
|
|
17
|
+
jerk_limits_violated,
|
|
18
|
+
speed_profile,
|
|
19
|
+
)
|
|
20
|
+
from robometrics.prediction import min_ade, min_fde, miss_rate, topk_trajectory_error
|
|
21
|
+
from robometrics.registry import MetricDefinition, MetricRegistry, UnknownMetricError, registry
|
|
22
|
+
from robometrics.results import EvaluationResult, MetricResult
|
|
23
|
+
from robometrics.safety import (
|
|
24
|
+
collision_rate,
|
|
25
|
+
lane_departure_rate,
|
|
26
|
+
min_distance_to_actors,
|
|
27
|
+
time_to_collision,
|
|
28
|
+
)
|
|
29
|
+
from robometrics.schemas import AgentState, Trajectory
|
|
30
|
+
from robometrics.trajectory import (
|
|
31
|
+
average_displacement_error,
|
|
32
|
+
curvature,
|
|
33
|
+
final_displacement_error,
|
|
34
|
+
hausdorff_distance,
|
|
35
|
+
lateral_error,
|
|
36
|
+
longitudinal_error,
|
|
37
|
+
path_length,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
ade = average_displacement_error
|
|
41
|
+
fde = final_displacement_error
|
|
42
|
+
|
|
43
|
+
__all__ = [
|
|
44
|
+
"AgentState",
|
|
45
|
+
"EvaluationInputError",
|
|
46
|
+
"EvaluationResult",
|
|
47
|
+
"Evaluator",
|
|
48
|
+
"MetricDefinition",
|
|
49
|
+
"MetricRegistry",
|
|
50
|
+
"MetricResult",
|
|
51
|
+
"Trajectory",
|
|
52
|
+
"TrajectoryIOError",
|
|
53
|
+
"UnknownMetricError",
|
|
54
|
+
"acceleration",
|
|
55
|
+
"acceleration_limits_violated",
|
|
56
|
+
"ade",
|
|
57
|
+
"average_displacement_error",
|
|
58
|
+
"collision_rate",
|
|
59
|
+
"curvature",
|
|
60
|
+
"curvature_limits_violated",
|
|
61
|
+
"dynamic_feasibility_score",
|
|
62
|
+
"fde",
|
|
63
|
+
"final_displacement_error",
|
|
64
|
+
"hausdorff_distance",
|
|
65
|
+
"jerk",
|
|
66
|
+
"jerk_cost",
|
|
67
|
+
"jerk_limits_violated",
|
|
68
|
+
"lane_departure_rate",
|
|
69
|
+
"lateral_error",
|
|
70
|
+
"longitudinal_error",
|
|
71
|
+
"max_acceleration",
|
|
72
|
+
"max_deceleration",
|
|
73
|
+
"min_ade",
|
|
74
|
+
"min_distance_to_actors",
|
|
75
|
+
"min_fde",
|
|
76
|
+
"miss_rate",
|
|
77
|
+
"path_length",
|
|
78
|
+
"registry",
|
|
79
|
+
"smoothness_score",
|
|
80
|
+
"speed_profile",
|
|
81
|
+
"time_to_collision",
|
|
82
|
+
"topk_trajectory_error",
|
|
83
|
+
]
|
|
84
|
+
|
|
85
|
+
__version__ = "0.1.0"
|