pntos-api 2.1.0.post0.dev0__py3-none-any.whl
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.
- pntos/__init__.py +3 -0
- pntos/api/__init__.py +79 -0
- pntos/api/plugins/__init__.py +1 -0
- pntos/api/plugins/common.py +1046 -0
- pntos/api/plugins/controller.py +99 -0
- pntos/api/plugins/fusion.py +572 -0
- pntos/api/plugins/fusion_strategy.py +306 -0
- pntos/api/plugins/inertial.py +414 -0
- pntos/api/plugins/initialization.py +278 -0
- pntos/api/plugins/logging.py +68 -0
- pntos/api/plugins/orchestration.py +253 -0
- pntos/api/plugins/platform_integration.py +81 -0
- pntos/api/plugins/preprocessor.py +88 -0
- pntos/api/plugins/registry.py +46 -0
- pntos/api/plugins/state_modeling.py +581 -0
- pntos/api/plugins/transport.py +50 -0
- pntos/api/plugins/ui.py +43 -0
- pntos/api/plugins/utility.py +17 -0
- pntos/py.typed +0 -0
- pntos_api-2.1.0.post0.dev0.dist-info/METADATA +21 -0
- pntos_api-2.1.0.post0.dev0.dist-info/RECORD +24 -0
- pntos_api-2.1.0.post0.dev0.dist-info/WHEEL +5 -0
- pntos_api-2.1.0.post0.dev0.dist-info/licenses/LICENSE +177 -0
- pntos_api-2.1.0.post0.dev0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
"""Python API of pntOS."""
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from typing import Any, TypeVar
|
|
5
|
+
|
|
6
|
+
from numpy import float64
|
|
7
|
+
from numpy.typing import NDArray
|
|
8
|
+
|
|
9
|
+
from pntos.api import CommonPlugin
|
|
10
|
+
|
|
11
|
+
from .state_modeling import (
|
|
12
|
+
StandardDynamicsModel,
|
|
13
|
+
StandardMeasurementModel,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class StandardFusionStrategy(ABC):
|
|
18
|
+
"""
|
|
19
|
+
A Fusion strategy making linearized Bayesian assumptions.
|
|
20
|
+
|
|
21
|
+
An implementation of the standard fusion strategy that is capable of
|
|
22
|
+
Bayesian inference on a linearized discrete-time system with Gaussian noise
|
|
23
|
+
inputs (e.g. an EKF).
|
|
24
|
+
|
|
25
|
+
At a fundamental level, this class manages an estimate of a state space as
|
|
26
|
+
it propagates (changes over time) and updates (incorporates new measurements
|
|
27
|
+
and observations). An estimate of a set of values (states) is stored and
|
|
28
|
+
maintained within this object. The estimate is then mutated and updated over
|
|
29
|
+
time by the various method calls.
|
|
30
|
+
|
|
31
|
+
A typical usage pattern of this class is as follows:
|
|
32
|
+
|
|
33
|
+
1. The user adds a set of states by calling the :meth:`add_states` method. As part
|
|
34
|
+
of this step, the user all passes in initial conditions for the estimate.
|
|
35
|
+
The fusion strategy is now storing an estimate of the states, set to the
|
|
36
|
+
initial conditions.
|
|
37
|
+
2. The user propagates this estimate forward in time by calling the
|
|
38
|
+
:meth:`propagate` method. For example, if the initial conditions were the
|
|
39
|
+
estimates of the states at time 0.0s, but we now want to know the
|
|
40
|
+
estimate of the values at time 5.0s, the user would call :meth:`propagate` with
|
|
41
|
+
a dynamics model parameter that specifies how to take an estimate at time
|
|
42
|
+
0.0 and use it to compute the estimate at time 5.0.
|
|
43
|
+
3. The user updates this estimate by using observations of the states at the
|
|
44
|
+
current time. For example, if the filter is currently propagated to time
|
|
45
|
+
5.0s and a measurement is received that observes the states' values at
|
|
46
|
+
time 5.0s, the user would call :meth:`update` with a measurement model
|
|
47
|
+
parameter that describes how to take the current estimate at 5.0s and
|
|
48
|
+
incorporate the new information from the measurement.
|
|
49
|
+
4. At any point, when the user wants to know the latest estimate given all
|
|
50
|
+
the propagate/updates that have occurred, they may call :attr:`estimate`.
|
|
51
|
+
|
|
52
|
+
Note:
|
|
53
|
+
This class must have an operational ``__deepcopy__`` method. For most classes, the default
|
|
54
|
+
``__deepcopy__`` method provided by Python will be sufficient. However, if the class has a
|
|
55
|
+
field which does not properly implement its own ``__deepcopy__`` (such as a C object wrapped
|
|
56
|
+
to Python), then the class will need to implement a custom ``__deepcopy__`` which properly
|
|
57
|
+
copies all fields.
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
@abstractmethod
|
|
62
|
+
def num_states(self) -> int:
|
|
63
|
+
"""
|
|
64
|
+
Get the total number of states this filter is estimating.
|
|
65
|
+
|
|
66
|
+
The count will initially be zero, until :meth:`add_states` is called.
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
int: Number of states being estimated in this filter.
|
|
70
|
+
"""
|
|
71
|
+
...
|
|
72
|
+
|
|
73
|
+
@abstractmethod
|
|
74
|
+
def add_states(
|
|
75
|
+
self,
|
|
76
|
+
initial_estimate: NDArray[float64],
|
|
77
|
+
initial_covariance: NDArray[float64],
|
|
78
|
+
cross_covariance: NDArray[float64] | None = None,
|
|
79
|
+
) -> int:
|
|
80
|
+
"""
|
|
81
|
+
Add new states to this filter.
|
|
82
|
+
|
|
83
|
+
Increases number of filter states and set the initial conditions of the new states. Returns
|
|
84
|
+
index of the first added state. If ``cross_covariance`` is ``None``, cross covariance between
|
|
85
|
+
the existing states and the added states will be set to zeroes.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
initial_estimate (NDArray[float64]): The initial estimate to populate the new states with.
|
|
89
|
+
initial_covariance (NDArray[float64]): The initial covariance matrix used to initialize the
|
|
90
|
+
uncertainty of the new states.
|
|
91
|
+
cross_covariance (NDArray[float64] | None): A covariance matrix that describes the cross terms
|
|
92
|
+
between the previous states and the new states. If ``None``, the
|
|
93
|
+
cross-terms will be set to zero. Otherwise, if there are ``n`` existing states and
|
|
94
|
+
``m`` new states being added, this argument should have shape ``[n, m]``.
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
int: Index of first state being added to this filter (zero indexed).
|
|
98
|
+
"""
|
|
99
|
+
...
|
|
100
|
+
|
|
101
|
+
@abstractmethod
|
|
102
|
+
def remove_states(self, first_index: int, count: int) -> None:
|
|
103
|
+
"""
|
|
104
|
+
Removes a set of states from the filter.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
first_index (int): Index of the first state to be removed (zero indexed).
|
|
108
|
+
count (int): The number of states to be removed.
|
|
109
|
+
"""
|
|
110
|
+
...
|
|
111
|
+
|
|
112
|
+
@property
|
|
113
|
+
@abstractmethod
|
|
114
|
+
def estimate(self) -> NDArray[float64] | None:
|
|
115
|
+
"""
|
|
116
|
+
Get the current internal estimate managed by this strategy.
|
|
117
|
+
|
|
118
|
+
This class manages a current estimate that is initially populated by :meth:`add_states` and
|
|
119
|
+
then is modified iteratively by :meth:`propagate`, :meth:`update`, and other method calls.
|
|
120
|
+
This method returns the current estimate, incorporating all changes made by previous method
|
|
121
|
+
calls to this strategy.
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
NDArray[float64] | None: An estimate if available. Returns ``None`` if no states have been added
|
|
125
|
+
yet.
|
|
126
|
+
"""
|
|
127
|
+
|
|
128
|
+
@abstractmethod
|
|
129
|
+
def set_estimate_slice(
|
|
130
|
+
self, new_estimate: NDArray[float64], first_index: int
|
|
131
|
+
) -> None:
|
|
132
|
+
"""
|
|
133
|
+
Set a slice of the state estimates to a given set of values.
|
|
134
|
+
|
|
135
|
+
This class manages a current estimate that is initially populated by :meth:`add_states` and
|
|
136
|
+
then is modified iteratively by :meth:`propagate`, :meth:`update`, and other method calls.
|
|
137
|
+
This method allows for manually overriding the current estimate. Sets a block of states to
|
|
138
|
+
new values, starting with ``first_index`` and overwriting a number of states equal to the
|
|
139
|
+
length of ``new_estimate``.
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
new_estimate (NDArray[float64]): The new estimate values that will overwrite the previous values.
|
|
143
|
+
first_index (int): The index of the first state to overwrite.
|
|
144
|
+
"""
|
|
145
|
+
...
|
|
146
|
+
|
|
147
|
+
@property
|
|
148
|
+
@abstractmethod
|
|
149
|
+
def covariance(self) -> NDArray[float64] | None:
|
|
150
|
+
"""
|
|
151
|
+
Get the covariance of the current estimate.
|
|
152
|
+
|
|
153
|
+
This class manages a current estimate that is initially populated by :meth:`add_states` and
|
|
154
|
+
then is modified iteratively by :meth:`propagate`, :meth:`update`, and other method calls.
|
|
155
|
+
In addition to the estimate itself, a covariance of the current estimate is computed. This
|
|
156
|
+
method returns this covariance, incorporating all changes made by previous method calls to
|
|
157
|
+
this strategy.
|
|
158
|
+
|
|
159
|
+
Returns:
|
|
160
|
+
NDArray[float64] | None: The covariance of the current estimate. Returns ``None`` if no states
|
|
161
|
+
have been added yet.
|
|
162
|
+
"""
|
|
163
|
+
|
|
164
|
+
@abstractmethod
|
|
165
|
+
def set_covariance_slice(
|
|
166
|
+
self,
|
|
167
|
+
new_covariance: NDArray[float64],
|
|
168
|
+
first_row: int,
|
|
169
|
+
first_col: int | None = None,
|
|
170
|
+
) -> None:
|
|
171
|
+
"""
|
|
172
|
+
Set a slice of the covariance matrix to a given set of values.
|
|
173
|
+
|
|
174
|
+
This class manages a current estimate that is initially populated by :meth:`add_states` and
|
|
175
|
+
then is modified iteratively by :meth:`propagate`, :meth:`update`, and other method calls.
|
|
176
|
+
In addition to the estimate itself, a covariance of the current estimate is computed.
|
|
177
|
+
|
|
178
|
+
Allows for manually overriding the current covariance matrix. Sets a block of the covariance
|
|
179
|
+
matrix to new values. The overwritten values are those in a rectangular area defined by the
|
|
180
|
+
upper left corner at ``first_row``, ``first_col`` and extending down and right to cover an
|
|
181
|
+
area equal to the size of ``new_covariance``. If ``first_col`` is not set or set to ``None``, the value of
|
|
182
|
+
``first_row`` will be used as a column index as well.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
new_covariance (NDArray[float64]): The new covariance values that will overwrite a slice of the
|
|
186
|
+
previous covariance matrix.
|
|
187
|
+
first_row (int): The row of the first value to overwrite.
|
|
188
|
+
first_col (int | None, optional): The column of the first value to overwrite.
|
|
189
|
+
"""
|
|
190
|
+
...
|
|
191
|
+
|
|
192
|
+
@abstractmethod
|
|
193
|
+
def propagate(self, dynamics_model: StandardDynamicsModel) -> None:
|
|
194
|
+
"""
|
|
195
|
+
Propagates the estimate of the state space forward in time.
|
|
196
|
+
|
|
197
|
+
This method assumes that a state space is already initialized with a set
|
|
198
|
+
of states via the :meth:`add_states` method. The ``dynamics_model`` parameter
|
|
199
|
+
includes a description of how the current state estimate can be
|
|
200
|
+
propagated from the current time to a new time. Note that the actual
|
|
201
|
+
numerical values of the current/new times are not specified anywhere, as
|
|
202
|
+
that information is not needed to perform the computation. This method
|
|
203
|
+
then takes the current state space estimate and the ``dynamics_model`` and
|
|
204
|
+
uses both to compute an estimate at the new time. The new estimate
|
|
205
|
+
clobbers the old estimate managed by this class, and be acquired by
|
|
206
|
+
calling :attr:`estimate`.
|
|
207
|
+
|
|
208
|
+
Args:
|
|
209
|
+
dynamics_model (StandardDynamicsModel)
|
|
210
|
+
"""
|
|
211
|
+
...
|
|
212
|
+
|
|
213
|
+
@abstractmethod
|
|
214
|
+
def update(self, measurement_model: StandardMeasurementModel) -> None:
|
|
215
|
+
"""
|
|
216
|
+
Updates the estimate of the state space, incorporating a new measurement.
|
|
217
|
+
|
|
218
|
+
This method assumes that a state space is already initialized with a set of states via the
|
|
219
|
+
:meth:`add_states` method. The ``measurement_model`` parameter includes both the measurement
|
|
220
|
+
itself and a description of how the measurement relates to the state space. This method then
|
|
221
|
+
takes the current state space estimate and updates it using information from the
|
|
222
|
+
``measurement_model``. The updated estimate can be acquired by calling :attr:`estimate`.
|
|
223
|
+
|
|
224
|
+
Args:
|
|
225
|
+
measurement_model (StandardMeasurementModel): The measurement with which to update the
|
|
226
|
+
filter, as well as a model that describes how the measurement relates to the states
|
|
227
|
+
this strategy is estimating.
|
|
228
|
+
"""
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
FusionStrategyType = TypeVar('FusionStrategyType', StandardFusionStrategy, Any)
|
|
232
|
+
"""Enumerates the types of fusion strategies.
|
|
233
|
+
|
|
234
|
+
Currently only StandardFusionStrategy is defined, but this TypeVar also includes "Any" in the type
|
|
235
|
+
list for future compatibility.
|
|
236
|
+
"""
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
class FusionStrategyPlugin(CommonPlugin, ABC):
|
|
240
|
+
"""
|
|
241
|
+
A plugin that provides computational engines for estimation.
|
|
242
|
+
|
|
243
|
+
At the high level, a fusion strategy is a computation engine that knows how
|
|
244
|
+
to estimating one or more states, given a set of observations/measurements.
|
|
245
|
+
For example, the EKF equations are an implementation of a fusion strategy.
|
|
246
|
+
This plugin is a factory that produces fusion strategies on demand, which is
|
|
247
|
+
useful for multi-filter approaches where the system may need several fusion
|
|
248
|
+
strategies running simultaneously.
|
|
249
|
+
|
|
250
|
+
There are many ways to model sensor fusion, including very simple (linear
|
|
251
|
+
Kalman filter) and complex (neural networks, factor graphs, etc.). This
|
|
252
|
+
plugin aims to allow any and all of those models to be represented. It
|
|
253
|
+
achieves this by having multiple fusion strategies, so that the user may select
|
|
254
|
+
what kind of fusion they want. Currently, only the :class:`pntos.api.StandardFusionStrategy` is
|
|
255
|
+
implemented, which is suitable for EKFs and filters that have similar
|
|
256
|
+
interfaces to an EKF (such as a UKF). However, more strategy types are
|
|
257
|
+
planned to be added in the future.
|
|
258
|
+
"""
|
|
259
|
+
|
|
260
|
+
@abstractmethod
|
|
261
|
+
def is_fusion_type_supported(self, fusion_type: type[FusionStrategyType]) -> bool:
|
|
262
|
+
"""
|
|
263
|
+
Check if a particular fusion strategy is supported by :meth:`new_fusion_strategy`.
|
|
264
|
+
|
|
265
|
+
The :meth:`new_fusion_strategy` factory method on this class can create one or more types of
|
|
266
|
+
fusion strategy. However, :class:`pntos.api.FusionStrategyPlugin` may not support all types
|
|
267
|
+
(they must support at least one). Therefore, when a user receives a
|
|
268
|
+
:class:`pntos.api.FusionStrategyPlugin`, they should:
|
|
269
|
+
|
|
270
|
+
1. Initialize the plugin by calling :meth:`pntos.api.CommonPlugin.init_plugin` (see
|
|
271
|
+
:class:`pntos.api.CommonPlugin` for more information).
|
|
272
|
+
2. Call :meth:`is_fusion_type_supported` to check that the plugin supports the type that the
|
|
273
|
+
user wants to use.
|
|
274
|
+
3. If :meth:`is_fusion_type_supported` returned True, call the :meth:`new_fusion_strategy`
|
|
275
|
+
factory method to get a new fusion strategy of the desired fusion strategy.
|
|
276
|
+
4. Proceed to use the fusion strategy to do estimation.
|
|
277
|
+
|
|
278
|
+
Args:
|
|
279
|
+
fusion_type (type[FusionStrategyType]): The fusion strategy type we are checking.
|
|
280
|
+
|
|
281
|
+
Returns:
|
|
282
|
+
bool: Whether or not we support the requested ``fusion_type``.
|
|
283
|
+
"""
|
|
284
|
+
...
|
|
285
|
+
|
|
286
|
+
@abstractmethod
|
|
287
|
+
def new_fusion_strategy(
|
|
288
|
+
self, fusion_type: type[FusionStrategyType]
|
|
289
|
+
) -> FusionStrategyType | None:
|
|
290
|
+
"""
|
|
291
|
+
Create a new fusion strategy of the requested type.
|
|
292
|
+
|
|
293
|
+
Users must first ensure that the fusion strategy is supported by calling
|
|
294
|
+
:meth:`is_fusion_type_supported`.
|
|
295
|
+
|
|
296
|
+
Args:
|
|
297
|
+
fusion_type (type[FusionStrategyType]): The type of fusion strategy that we want
|
|
298
|
+
returned.
|
|
299
|
+
|
|
300
|
+
Returns:
|
|
301
|
+
FusionStrategyType | None: The newly created fusion strategy, which is an
|
|
302
|
+
implementation of the type specified by ``fusion_type``. For example, if the user calls
|
|
303
|
+
:meth:`new_fusion_strategy` with a ``fusion_type`` of ``StandardFusionStrategy``, then
|
|
304
|
+
the returned object will be an implementation of :class:`pntos.api.StandardFusionStrategy`.
|
|
305
|
+
"""
|
|
306
|
+
...
|