c4dynamics 2.0.3__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.
- c4dynamics/__init__.py +240 -0
- c4dynamics/datasets/__init__.py +95 -0
- c4dynamics/datasets/_manager.py +596 -0
- c4dynamics/datasets/_registry.py +80 -0
- c4dynamics/detectors/__init__.py +37 -0
- c4dynamics/detectors/yolo3_opencv.py +686 -0
- c4dynamics/detectors/yolo3_tf.py +124 -0
- c4dynamics/eqm/__init__.py +324 -0
- c4dynamics/eqm/derivs.py +212 -0
- c4dynamics/eqm/integrate.py +359 -0
- c4dynamics/filters/__init__.py +1373 -0
- c4dynamics/filters/a.py +48 -0
- c4dynamics/filters/ekf.py +320 -0
- c4dynamics/filters/kalman.py +725 -0
- c4dynamics/filters/kalman_v0.py +1071 -0
- c4dynamics/filters/kalman_v1.py +821 -0
- c4dynamics/filters/lowpass.py +123 -0
- c4dynamics/filters/luenberger.py +97 -0
- c4dynamics/rotmat/__init__.py +141 -0
- c4dynamics/rotmat/animate.py +465 -0
- c4dynamics/rotmat/rotmat.py +351 -0
- c4dynamics/sensors/__init__.py +72 -0
- c4dynamics/sensors/lineofsight.py +78 -0
- c4dynamics/sensors/radar.py +740 -0
- c4dynamics/sensors/seeker.py +1030 -0
- c4dynamics/states/__init__.py +327 -0
- c4dynamics/states/lib/__init__.py +320 -0
- c4dynamics/states/lib/datapoint.py +660 -0
- c4dynamics/states/lib/pixelpoint.py +776 -0
- c4dynamics/states/lib/rigidbody.py +677 -0
- c4dynamics/states/state.py +1486 -0
- c4dynamics/utils/__init__.py +44 -0
- c4dynamics/utils/_struct.py +6 -0
- c4dynamics/utils/const.py +130 -0
- c4dynamics/utils/cprint.py +80 -0
- c4dynamics/utils/gen_gif.py +142 -0
- c4dynamics/utils/idx2keys.py +4 -0
- c4dynamics/utils/images_loader.py +63 -0
- c4dynamics/utils/math.py +136 -0
- c4dynamics/utils/plottools.py +140 -0
- c4dynamics/utils/plottracks.py +304 -0
- c4dynamics/utils/printpts.py +36 -0
- c4dynamics/utils/slides_gen.py +64 -0
- c4dynamics/utils/tictoc.py +167 -0
- c4dynamics/utils/video_gen.py +300 -0
- c4dynamics/utils/vidgen.py +182 -0
- c4dynamics-2.0.3.dist-info/METADATA +242 -0
- c4dynamics-2.0.3.dist-info/RECORD +50 -0
- c4dynamics-2.0.3.dist-info/WHEEL +5 -0
- c4dynamics-2.0.3.dist-info/top_level.txt +1 -0
c4dynamics/filters/a.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
def add(a, b):
|
|
2
|
+
"""
|
|
3
|
+
Adds two numbers together.
|
|
4
|
+
|
|
5
|
+
Example:
|
|
6
|
+
>>> add(2, 3)
|
|
7
|
+
5
|
|
8
|
+
>>> add(0, 0)
|
|
9
|
+
0
|
|
10
|
+
>>> add(-1, 1)
|
|
11
|
+
0
|
|
12
|
+
"""
|
|
13
|
+
return a + b
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Calculator:
|
|
17
|
+
"""
|
|
18
|
+
A simple calculator class.
|
|
19
|
+
|
|
20
|
+
Example:
|
|
21
|
+
>>> calc = Calculator()
|
|
22
|
+
>>> calc.add(2, 3)
|
|
23
|
+
5
|
|
24
|
+
>>> calc.subtract(5, 3)
|
|
25
|
+
2
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def add(self, a, b):
|
|
29
|
+
return a + b
|
|
30
|
+
|
|
31
|
+
def subtract(self, a, b):
|
|
32
|
+
return a - b
|
|
33
|
+
|
|
34
|
+
if __name__ == "__main__":
|
|
35
|
+
# import doctest
|
|
36
|
+
# doctest.testmod()
|
|
37
|
+
|
|
38
|
+
import sys
|
|
39
|
+
sys.path.append('.')
|
|
40
|
+
from c4dynamics.datasets._manager import sha256
|
|
41
|
+
print(sha256(r'C:\Users\odely\Downloads\drifting_car.mp4'))
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import sys
|
|
3
|
+
sys.path.append('.')
|
|
4
|
+
# import c4dynamics as c4d
|
|
5
|
+
from c4dynamics.filters import kalman
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
class ekf(kalman):
|
|
9
|
+
'''
|
|
10
|
+
Extended Kalman Filter class for handling nonlinear dynamics by
|
|
11
|
+
incorporating functions for nonlinear state transitions and measurements.
|
|
12
|
+
|
|
13
|
+
This subclass extends the base
|
|
14
|
+
:class:`kalman <c4dynamics.filters.kalman.kalman>`
|
|
15
|
+
class to handle cases where
|
|
16
|
+
system dynamics or measurements are nonlinear. The Jacobian matrices
|
|
17
|
+
`F` and `H` can be dynamically updated as linearizations of the
|
|
18
|
+
nonlinear functions.
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
Parameters
|
|
22
|
+
----------
|
|
23
|
+
X : dict
|
|
24
|
+
Initial state estimate dictionary, where key-value pairs
|
|
25
|
+
represent state variables and their initial values.
|
|
26
|
+
P0 : np.ndarray
|
|
27
|
+
Initial error covariance matrix, defining the initial
|
|
28
|
+
uncertainty for each state variable.
|
|
29
|
+
F : np.ndarray, optional
|
|
30
|
+
State transition Jacobian matrix; defaults to an identity matrix
|
|
31
|
+
if not provided, assuming a linear system model.
|
|
32
|
+
H : np.ndarray, optional
|
|
33
|
+
Measurement Jacobian matrix; defaults to a zero matrix if not
|
|
34
|
+
provided.
|
|
35
|
+
G : np.ndarray, optional
|
|
36
|
+
Control input matrix, mapping control inputs to the state.
|
|
37
|
+
Q : np.ndarray, optional
|
|
38
|
+
Process noise covariance matrix.
|
|
39
|
+
R : np.ndarray, optional
|
|
40
|
+
Measurement noise covariance matrix.
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
Example
|
|
44
|
+
-------
|
|
45
|
+
A detailed example can be found in the introduction
|
|
46
|
+
to the c4dynamics.filters module.
|
|
47
|
+
The mechanism of this class is similar to
|
|
48
|
+
the :class:`kalman <c4dynamics.filters.kalman.kalman>`,
|
|
49
|
+
so the examples provided there may serve as
|
|
50
|
+
inspiration for using `ekf`.
|
|
51
|
+
|
|
52
|
+
'''
|
|
53
|
+
|
|
54
|
+
def __init__(self, X: dict, P0: np.ndarray
|
|
55
|
+
, F: Optional[np.ndarray] = None
|
|
56
|
+
, H: Optional[np.ndarray] = None
|
|
57
|
+
, G: Optional[np.ndarray] = None
|
|
58
|
+
, Q: Optional[np.ndarray] = None
|
|
59
|
+
, R: Optional[np.ndarray] = None):
|
|
60
|
+
# F and H are necessary also for ekf because they are required to the ricatti.
|
|
61
|
+
# yes but the can be delivered at each call in the immediate linearized form.
|
|
62
|
+
|
|
63
|
+
if F is None:
|
|
64
|
+
F = np.eye(P0.shape[0])
|
|
65
|
+
|
|
66
|
+
if H is None:
|
|
67
|
+
H = np.zeros(P0.shape[0])
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
super().__init__(X, F, H, P0 = P0, G = G, Q = Q, R = R)
|
|
71
|
+
self._ekf = True
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def predict(self, F: Optional[np.ndarray] = None, fx: Optional[np.ndarray] = None, dt = None # type: ignore
|
|
79
|
+
, u: Optional[np.ndarray] = None
|
|
80
|
+
, Q: Optional[np.ndarray] = None):
|
|
81
|
+
'''
|
|
82
|
+
Predicts the next state of the system based on the current state
|
|
83
|
+
and an optional nonlinear state transition function.
|
|
84
|
+
|
|
85
|
+
Parameters
|
|
86
|
+
----------
|
|
87
|
+
F : np.ndarray, optional
|
|
88
|
+
The state transition Jacobian matrix. If not provided, the
|
|
89
|
+
previously set `F` matrix is used.
|
|
90
|
+
fx : np.ndarray, optional
|
|
91
|
+
Nonlinear state transition function derivative. If specified,
|
|
92
|
+
this value is used for updating the state with nonlinear dynamics.
|
|
93
|
+
dt : float, optional
|
|
94
|
+
Time step duration. Must be provided if `fx` is specified.
|
|
95
|
+
u : np.ndarray, optional
|
|
96
|
+
Control input vector, affecting the state based on the `G` matrix.
|
|
97
|
+
Q : np.ndarray, optional
|
|
98
|
+
Process noise covariance matrix, representing uncertainty in
|
|
99
|
+
the model during prediction.
|
|
100
|
+
|
|
101
|
+
Raises
|
|
102
|
+
------
|
|
103
|
+
TypeError
|
|
104
|
+
If `fx` is provided without a corresponding `dt` value.
|
|
105
|
+
|
|
106
|
+
Examples
|
|
107
|
+
--------
|
|
108
|
+
The examples in this section are intended to
|
|
109
|
+
demonstrate the usage of the `ekf` class and specifically the `predict` method.
|
|
110
|
+
However, they are not limited to nonlinear dynamics.
|
|
111
|
+
For detailed usage that highlights the properties of nonlinear dynamics,
|
|
112
|
+
refer to the :mod:`filters <c4dynamics.filters>` module introduction.
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
Import required packages:
|
|
117
|
+
|
|
118
|
+
.. code::
|
|
119
|
+
|
|
120
|
+
>>> from c4dynamics.filters import ekf
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
Plain `predict` step
|
|
125
|
+
(predict in steady-state mode where the process variance matrix
|
|
126
|
+
remains constant
|
|
127
|
+
and is provided once to initialize the filter):
|
|
128
|
+
|
|
129
|
+
.. code::
|
|
130
|
+
|
|
131
|
+
>>> _ekf = ekf({'x': 0}, P0 = 0.5**2, F = 1, H = 1, Q = 0.05, R = 200)
|
|
132
|
+
>>> print(_ekf)
|
|
133
|
+
[ x ]
|
|
134
|
+
>>> _ekf.X # doctest: +NUMPY_FORMAT
|
|
135
|
+
[0]
|
|
136
|
+
>>> _ekf.P # doctest: +NUMPY_FORMAT
|
|
137
|
+
[[0.25]]
|
|
138
|
+
>>> _ekf.predict()
|
|
139
|
+
>>> _ekf.X # doctest: +NUMPY_FORMAT
|
|
140
|
+
[0]
|
|
141
|
+
>>> _ekf.P # doctest: +NUMPY_FORMAT
|
|
142
|
+
[[0.3]]
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
Predict with control input:
|
|
146
|
+
|
|
147
|
+
.. code::
|
|
148
|
+
|
|
149
|
+
>>> _ekf = ekf({'x': 0}, P0 = 0.5**2, F = 1, G = 150, H = 1, R = 200, Q = 0.05)
|
|
150
|
+
>>> _ekf.X # doctest: +NUMPY_FORMAT
|
|
151
|
+
[0]
|
|
152
|
+
>>> _ekf.P # doctest: +NUMPY_FORMAT
|
|
153
|
+
[[0.25]]
|
|
154
|
+
>>> _ekf.predict(u = 1)
|
|
155
|
+
>>> _ekf.X # doctest: +NUMPY_FORMAT
|
|
156
|
+
[150]
|
|
157
|
+
>>> _ekf.P # doctest: +NUMPY_FORMAT
|
|
158
|
+
[[0.3]]
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
Predict with updated process noise covariance matrix:
|
|
163
|
+
|
|
164
|
+
.. code::
|
|
165
|
+
|
|
166
|
+
>>> _ekf = ekf({'x': 0}, P0 = 0.5**2, F = 1, G = 150, H = 1, R = 200, Q = 0.05)
|
|
167
|
+
>>> _ekf.X # doctest: +NUMPY_FORMAT
|
|
168
|
+
[0]
|
|
169
|
+
>>> _ekf.P # doctest: +NUMPY_FORMAT
|
|
170
|
+
[[0.25]]
|
|
171
|
+
>>> _ekf.predict(u = 1, Q = 0.01)
|
|
172
|
+
>>> _ekf.X # doctest: +NUMPY_FORMAT
|
|
173
|
+
[150]
|
|
174
|
+
>>> _ekf.P # doctest: +NUMPY_FORMAT
|
|
175
|
+
[[0.26]]
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
'''
|
|
181
|
+
|
|
182
|
+
if fx is not None:
|
|
183
|
+
if dt is None:
|
|
184
|
+
raise TypeError('For nonlinear derivatives inpout (fx), dt must be provided.')
|
|
185
|
+
self.X += np.atleast_1d(fx).ravel() * dt
|
|
186
|
+
self._nonlinearF = True
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
if F is not None:
|
|
190
|
+
# "if F" is not enough because F is an array and
|
|
191
|
+
# the truth value of an array with more than one
|
|
192
|
+
# element is ambiguous.
|
|
193
|
+
self.F = np.atleast_2d(F)
|
|
194
|
+
|
|
195
|
+
super().predict(u = u, Q = Q)
|
|
196
|
+
|
|
197
|
+
self._nonlinearF = True
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def update(self, z: np.ndarray # type: ignore
|
|
202
|
+
, H: Optional[np.ndarray] = None
|
|
203
|
+
, hx: Optional[np.ndarray] = None
|
|
204
|
+
, R: Optional[np.ndarray] = None
|
|
205
|
+
):
|
|
206
|
+
|
|
207
|
+
'''
|
|
208
|
+
Updates the state estimate based on the latest measurement, using an
|
|
209
|
+
optional nonlinear measurement function.
|
|
210
|
+
|
|
211
|
+
Parameters
|
|
212
|
+
----------
|
|
213
|
+
z : np.ndarray
|
|
214
|
+
Measurement vector, representing observed values from the system.
|
|
215
|
+
H : np.ndarray, optional
|
|
216
|
+
Measurement Jacobian matrix. If provided, it overrides the
|
|
217
|
+
previously set `H` matrix for this update step.
|
|
218
|
+
hx : np.ndarray, optional
|
|
219
|
+
Nonlinear measurement function output. If provided, it is used
|
|
220
|
+
as the current estimate of the state based on measurement data.
|
|
221
|
+
R : np.ndarray, optional
|
|
222
|
+
Measurement noise covariance matrix, representing the uncertainty
|
|
223
|
+
in the measurements.
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
Examples
|
|
227
|
+
--------
|
|
228
|
+
The examples in this section are intended to
|
|
229
|
+
demonstrate the usage of the `ekf` class and specifically the `update` method.
|
|
230
|
+
However, they are not limited to nonlinear dynamics.
|
|
231
|
+
For detailed usage that highlights the properties of nonlinear dynamics,
|
|
232
|
+
refer to the :mod:`filters <c4dynamics.filters>` module introduction.
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
Import required packages:
|
|
236
|
+
|
|
237
|
+
.. code::
|
|
238
|
+
|
|
239
|
+
>>> from c4dynamics.filters import ekf
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
Plain update step:
|
|
244
|
+
|
|
245
|
+
.. code::
|
|
246
|
+
|
|
247
|
+
>>> _ekf = ekf({'x': 0}, P0 = 0.5**2, F = 1, H = 1, Q = 0.05, R = 200)
|
|
248
|
+
>>> print(_ekf)
|
|
249
|
+
[ x ]
|
|
250
|
+
>>> _ekf.X # doctest: +NUMPY_FORMAT
|
|
251
|
+
[0]
|
|
252
|
+
>>> _ekf.P # doctest: +NUMPY_FORMAT
|
|
253
|
+
[[0.25]]
|
|
254
|
+
>>> _ekf.update(z = 100) # returns Kalman gain # doctest: +NUMPY_FORMAT
|
|
255
|
+
[[0.001...]]
|
|
256
|
+
>>> _ekf.X # doctest: +NUMPY_FORMAT
|
|
257
|
+
[0.124...]
|
|
258
|
+
>>> _ekf.P # doctest: +NUMPY_FORMAT
|
|
259
|
+
[[0.249...]]
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
Update with modified measurement noise covariance matrix:
|
|
264
|
+
|
|
265
|
+
.. code::
|
|
266
|
+
|
|
267
|
+
>>> _ekf = ekf({'x': 0}, P0 = 0.5**2, F = 1, G = 150, H = 1, R = 200, Q = 0.05)
|
|
268
|
+
>>> _ekf.X # doctest: +NUMPY_FORMAT
|
|
269
|
+
[0]
|
|
270
|
+
>>> _ekf.P # doctest: +NUMPY_FORMAT
|
|
271
|
+
[[0.25]]
|
|
272
|
+
>>> K = _ekf.update(z = 150, R = 0)
|
|
273
|
+
>>> K # doctest: +NUMPY_FORMAT
|
|
274
|
+
[[1]]
|
|
275
|
+
>>> _ekf.X # doctest: +NUMPY_FORMAT
|
|
276
|
+
[150]
|
|
277
|
+
>>> _ekf.P # doctest: +NUMPY_FORMAT
|
|
278
|
+
[[0]]
|
|
279
|
+
|
|
280
|
+
'''
|
|
281
|
+
if hx is not None:
|
|
282
|
+
self.X = hx
|
|
283
|
+
self._nonlinearH = True
|
|
284
|
+
|
|
285
|
+
if H is not None:
|
|
286
|
+
self.H = np.atleast_2d(H)
|
|
287
|
+
|
|
288
|
+
K = super().update(z = z, R = R)
|
|
289
|
+
self._nonlinearH = False
|
|
290
|
+
return K
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
if __name__ == "__main__":
|
|
295
|
+
|
|
296
|
+
import doctest, contextlib, os
|
|
297
|
+
from c4dynamics import IgnoreOutputChecker, cprint
|
|
298
|
+
|
|
299
|
+
# Register the custom OutputChecker
|
|
300
|
+
doctest.OutputChecker = IgnoreOutputChecker
|
|
301
|
+
|
|
302
|
+
tofile = False
|
|
303
|
+
optionflags = doctest.FAIL_FAST
|
|
304
|
+
|
|
305
|
+
if tofile:
|
|
306
|
+
with open(os.path.join('tests', '_out', 'output.txt'), 'w') as f:
|
|
307
|
+
with contextlib.redirect_stdout(f), contextlib.redirect_stderr(f):
|
|
308
|
+
result = doctest.testmod(optionflags = optionflags)
|
|
309
|
+
else:
|
|
310
|
+
result = doctest.testmod(optionflags = optionflags)
|
|
311
|
+
|
|
312
|
+
if result.failed == 0:
|
|
313
|
+
cprint(os.path.basename(__file__) + ": all tests passed!", 'g')
|
|
314
|
+
else:
|
|
315
|
+
print(f"{result.failed}")
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
|