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
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
from typing import Dict, Optional, Union, Tuple
|
|
2
|
+
from numpy.typing import NDArray
|
|
3
|
+
import numpy as np
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
sys.path.append('.')
|
|
7
|
+
from c4dynamics.eqm.derivs import eqm3, eqm6
|
|
8
|
+
from c4dynamics import datapoint, rigidbody
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def int3(dp: 'datapoint', forces: Union[np.ndarray, list]
|
|
12
|
+
, dt: float, derivs_out: bool = False
|
|
13
|
+
) -> Union[NDArray[np.float64], Tuple[NDArray[np.float64], NDArray[np.float64]]]:
|
|
14
|
+
'''
|
|
15
|
+
A step integration of the equations of translational motion.
|
|
16
|
+
|
|
17
|
+
This method makes a numerical integration using the
|
|
18
|
+
fourth-order Runge-Kutta method.
|
|
19
|
+
|
|
20
|
+
The integrated derivatives are of three dimensional translational motion as
|
|
21
|
+
given by
|
|
22
|
+
:func:`eqm3 <c4dynamics.eqm.derivs.eqm3>`.
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
The result is an integrated state in a single interval of time where the
|
|
26
|
+
size of the step is determined by the parameter `dt`.
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
Parameters
|
|
30
|
+
----------
|
|
31
|
+
dp : :class:`datapoint <c4dynamics.states.lib.datapoint.datapoint>`
|
|
32
|
+
The datapoint which state vector is to be integrated.
|
|
33
|
+
forces : numpy.array or list
|
|
34
|
+
An external forces array acting on the body.
|
|
35
|
+
dt : float
|
|
36
|
+
Time step for integration.
|
|
37
|
+
derivs_out : bool, optional
|
|
38
|
+
If true, returns the last three derivatives as an estimation for
|
|
39
|
+
the acceleration of the datapoint.
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
Returns
|
|
43
|
+
-------
|
|
44
|
+
X : numpy.float64
|
|
45
|
+
An integrated state.
|
|
46
|
+
dxdt4 : numpy.float64, optional
|
|
47
|
+
The last three derivatives of the equations of motion.
|
|
48
|
+
These derivatives can use as an estimation for the acceleration of the datapoint.
|
|
49
|
+
Returned if `derivs_out` is set to `True`.
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
**Algorithm**
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
The integration steps follow the Runge-Kutta method:
|
|
57
|
+
|
|
58
|
+
1. Compute k1 = f(ti, yi)
|
|
59
|
+
|
|
60
|
+
2. Compute k2 = f(ti + dt / 2, yi + dt * k1 / 2)
|
|
61
|
+
|
|
62
|
+
3. Compute k3 = f(ti + dt / 2, yi + dt * k2 / 2)
|
|
63
|
+
|
|
64
|
+
4. Compute k4 = f(ti + dt, yi + dt * k3)
|
|
65
|
+
|
|
66
|
+
5. Update yi = yi + dt / 6 * (k1 + 2 * k2 + 2 * k3 + k4)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
Examples
|
|
71
|
+
--------
|
|
72
|
+
|
|
73
|
+
Runge-Kutta integration of the equations of motion on a mass in a free fall
|
|
74
|
+
(compare to the same example in :func:`eqm3 <c4dynamics.eqm.derivs.eqm3>` with Euler integration):
|
|
75
|
+
|
|
76
|
+
Import required packages
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
.. code::
|
|
80
|
+
|
|
81
|
+
>>> import c4dynamics as c4d
|
|
82
|
+
|
|
83
|
+
.. code::
|
|
84
|
+
|
|
85
|
+
>>> pt = c4d.datapoint(z = 10000)
|
|
86
|
+
>>> while pt.z > 0:
|
|
87
|
+
... pt.store()
|
|
88
|
+
... pt.X = c4d.eqm.int3(pt, [0, 0, -c4d.g_ms2], dt = 1)
|
|
89
|
+
|
|
90
|
+
.. figure:: /_examples/eqm/int3.png
|
|
91
|
+
|
|
92
|
+
'''
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
x0 = dp.X
|
|
96
|
+
X = dp.X
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
# step 1
|
|
100
|
+
dxdt1 = eqm3(dp, forces)
|
|
101
|
+
# dp.update(x0 + dt / 2 * dxdt1)
|
|
102
|
+
X = x0 + dt / 2 * dxdt1
|
|
103
|
+
|
|
104
|
+
# step 2
|
|
105
|
+
dxdt2 = eqm3(dp, forces)
|
|
106
|
+
# dp.update(x0 + dt / 2 * dxdt2)
|
|
107
|
+
X = x0 + dt / 2 * dxdt2
|
|
108
|
+
|
|
109
|
+
# step 3
|
|
110
|
+
dxdt3 = eqm3(dp, forces)
|
|
111
|
+
# dp.update(x0 + dt * dxdt3)
|
|
112
|
+
X = x0 + dt * dxdt3
|
|
113
|
+
dxdt3 += dxdt2
|
|
114
|
+
|
|
115
|
+
# step 4
|
|
116
|
+
dxdt4 = eqm3(dp, forces)
|
|
117
|
+
|
|
118
|
+
#
|
|
119
|
+
# dp.update(np.concatenate((x0 + dt / 6 * (dxdt1 + dxdt4 + 2 * dxdt3), dxdt4[-3:]), axis = 0))
|
|
120
|
+
X = x0 + dt / 6 * (dxdt1 + dxdt4 + 2 * dxdt3)
|
|
121
|
+
# dp.ax, dp.ay, dp.az = dxdt4[-3:]
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
if not derivs_out:
|
|
125
|
+
return X
|
|
126
|
+
|
|
127
|
+
# return also the derivatives.
|
|
128
|
+
return X, dxdt4[-3:]
|
|
129
|
+
|
|
130
|
+
##
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def int6(rb: 'rigidbody', forces: Union[np.ndarray, list], moments: Union[np.ndarray, list]
|
|
134
|
+
, dt: float, derivs_out: bool = False
|
|
135
|
+
) -> Union[NDArray[np.float64], Tuple[NDArray[np.float64], NDArray[np.float64]]]:
|
|
136
|
+
|
|
137
|
+
'''
|
|
138
|
+
A step integration of the equations of motion.
|
|
139
|
+
|
|
140
|
+
This method makes a numerical integration using the
|
|
141
|
+
fourth-order Runge-Kutta method.
|
|
142
|
+
|
|
143
|
+
The integrated derivatives are of three dimensional translational motion as
|
|
144
|
+
given by
|
|
145
|
+
:func:`eqm6 <c4dynamics.eqm.derivs.eqm6>`.
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
The result is an integrated state in a single interval of time where the
|
|
149
|
+
size of the step is determined by the parameter `dt`.
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
Parameters
|
|
153
|
+
----------
|
|
154
|
+
dp : :class:`datapoint <c4dynamics.states.lib.datapoint.datapoint>`
|
|
155
|
+
The datapoint which state vector is to be integrated.
|
|
156
|
+
forces : numpy.array or list
|
|
157
|
+
An external forces array acting on the body.
|
|
158
|
+
dt : float
|
|
159
|
+
Time step for integration.
|
|
160
|
+
derivs_out : bool, optional
|
|
161
|
+
If true, returns the last three derivatives as an estimation for
|
|
162
|
+
the acceleration of the datapoint.
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
Returns
|
|
166
|
+
-------
|
|
167
|
+
X : numpy.float64
|
|
168
|
+
An integrated state.
|
|
169
|
+
dxdt4 : numpy.float64, optional
|
|
170
|
+
The last six derivatives of the equations of motion.
|
|
171
|
+
These derivatives can use as an estimation for the
|
|
172
|
+
translational and angular acceleration of the datapoint.
|
|
173
|
+
Returned if `derivs_out` is set to `True`.
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
**Algorithm**
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
The integration steps follow the Runge-Kutta method:
|
|
180
|
+
|
|
181
|
+
1. Compute k1 = f(ti, yi)
|
|
182
|
+
|
|
183
|
+
2. Compute k2 = f(ti + dt / 2, yi + dt * k1 / 2)
|
|
184
|
+
|
|
185
|
+
3. Compute k3 = f(ti + dt / 2, yi + dt * k2 / 2)
|
|
186
|
+
|
|
187
|
+
4. Compute k4 = f(ti + dt, yi + dt * k3)
|
|
188
|
+
|
|
189
|
+
5. Update yi = yi + dt / 6 * (k1 + 2 * k2 + 2 * k3 + k4)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
Examples
|
|
193
|
+
--------
|
|
194
|
+
|
|
195
|
+
In the following example, the equations of motion of a
|
|
196
|
+
cylinderical body are integrated by using the
|
|
197
|
+
:func:`int6 <c4dynamics.eqm.integrate.int6>`.
|
|
198
|
+
|
|
199
|
+
The results are compared to the results of the same equations
|
|
200
|
+
integrated by using `scipy.odeint`.
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
1. `int6`
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
Import required packages
|
|
207
|
+
|
|
208
|
+
.. code::
|
|
209
|
+
|
|
210
|
+
>>> import c4dynamics as c4d
|
|
211
|
+
>>> from matplotlib import pyplot as plt
|
|
212
|
+
>>> from scipy.integrate import odeint
|
|
213
|
+
>>> import numpy as np
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
Settings and initial conditions
|
|
217
|
+
|
|
218
|
+
.. code::
|
|
219
|
+
|
|
220
|
+
>>> dt = 0.5e-3
|
|
221
|
+
>>> t = np.arange(0, 10, dt)
|
|
222
|
+
>>> theta0 = 80 * c4d.d2r # deg
|
|
223
|
+
>>> q0 = 0 * c4d.d2r # deg to sec
|
|
224
|
+
>>> Iyy = .4 # kg * m^2
|
|
225
|
+
>>> length = 1 # meter
|
|
226
|
+
>>> mass = 0.5 # kg
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
Define the cylinderical-rigidbody object
|
|
230
|
+
|
|
231
|
+
.. code::
|
|
232
|
+
|
|
233
|
+
>>> rb = c4d.rigidbody(theta = theta0, q = q0)
|
|
234
|
+
>>> rb.I = [0, Iyy, 0]
|
|
235
|
+
>>> rb.mass = mass
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
Main loop:
|
|
239
|
+
|
|
240
|
+
.. code::
|
|
241
|
+
|
|
242
|
+
>>> for ti in t:
|
|
243
|
+
... rb.store(ti)
|
|
244
|
+
... tau_g = -rb.mass * c4d.g_ms2 * length / 2 * c4d.cos(rb.theta)
|
|
245
|
+
... rb.X = c4d.eqm.int6(rb, np.zeros(3), [0, tau_g, 0], dt)
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
.. code::
|
|
249
|
+
|
|
250
|
+
>>> rb.plot('theta')
|
|
251
|
+
|
|
252
|
+
.. figure:: /_examples/eqm/int6.png
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
2. `scipy.odeint`
|
|
256
|
+
|
|
257
|
+
.. code::
|
|
258
|
+
|
|
259
|
+
>>> def pend(y, t):
|
|
260
|
+
... theta, omega = y
|
|
261
|
+
... dydt = [omega, -rb.mass * c4d.g_ms2 * length / 2 * c4d.cos(theta) / Iyy]
|
|
262
|
+
... return dydt
|
|
263
|
+
>>> sol = odeint(pend, [theta0, q0], t)
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
Compare to `int6`:
|
|
267
|
+
|
|
268
|
+
.. code::
|
|
269
|
+
|
|
270
|
+
>>> plt.plot(*rb.data('theta', c4d.r2d), 'm', label = 'c4dynamics.int6') # doctest: +IGNORE_OUTPUT
|
|
271
|
+
>>> plt.plot(t, sol[:, 0] * c4d.r2d, 'c', label = 'scipy.odeint') # doctest: +IGNORE_OUTPUT
|
|
272
|
+
>>> c4d.plotdefaults(plt.gca(), 'Equations of Motion Integration ($\\theta$)', 'Time', 'degrees', fontsize = 12)
|
|
273
|
+
>>> plt.legend() # doctest: +IGNORE_OUTPUT
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
.. figure:: /_examples/eqm/int6_vs_scipy.png
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
**Note - Differences Between Scipy and C4dynamics Integration**
|
|
280
|
+
|
|
281
|
+
The difference in the results derive from the method of delivering the
|
|
282
|
+
forces and moments.
|
|
283
|
+
scipy.odeint gets as input the function that caluclates the derivatives,
|
|
284
|
+
where the forces and moments are included in it:
|
|
285
|
+
|
|
286
|
+
`dydt = [omega, -rb.mass * c4d.g_ms2 * length / 2 * c4d.cos(theta) / Iyy]`
|
|
287
|
+
|
|
288
|
+
This way, the forces and moments are recalculated for each step of
|
|
289
|
+
the integration.
|
|
290
|
+
|
|
291
|
+
c4dynamics.eqm.int6 on the other hand, gets the vectors of forces and moments
|
|
292
|
+
only once when the function is called and therefore refer to them as constant
|
|
293
|
+
for the four steps of integration.
|
|
294
|
+
|
|
295
|
+
When external factors may vary quickly over time and a high level of
|
|
296
|
+
accuracy is required, using other methods, like scipy.odeint, is recommanded.
|
|
297
|
+
If computational resources are available, a decrement of the step-size
|
|
298
|
+
may be a workaround to achieve high accuracy results.
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
'''
|
|
302
|
+
|
|
303
|
+
# x, y, z, vx, vy, vz, phi, theta, psi, p, q, r
|
|
304
|
+
x0 = rb.X
|
|
305
|
+
X = rb.X
|
|
306
|
+
|
|
307
|
+
# step 1
|
|
308
|
+
h1 = eqm6(rb, forces, moments)
|
|
309
|
+
X = x0 + dt / 2 * h1
|
|
310
|
+
|
|
311
|
+
# step 2
|
|
312
|
+
h2 = eqm6(rb, forces, moments)
|
|
313
|
+
X = x0 + dt / 2 * h2
|
|
314
|
+
|
|
315
|
+
# step 3
|
|
316
|
+
h3 = eqm6(rb, forces, moments)
|
|
317
|
+
X = x0 + dt * h3
|
|
318
|
+
|
|
319
|
+
# step 4
|
|
320
|
+
h4 = eqm6(rb, forces, moments)
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
X = x0 + dt / 6 * (h1 + 2 * h2 + 2 * h3 + h4)
|
|
324
|
+
|
|
325
|
+
if not derivs_out:
|
|
326
|
+
return X
|
|
327
|
+
|
|
328
|
+
# return also the derivatives.
|
|
329
|
+
# 0 1 2 3 4 5 6 7 8 9 10 11
|
|
330
|
+
# h4 = np.array([dx, dy, dz, dvx, dvy, dvz, dphi, dtheta, dpsi, dp, dq, dr])
|
|
331
|
+
return X, np.concatenate([h4[3 : 6], h4[9 : 12]]) # X, [dvx, dvy, dvz, dp, dq, dr]
|
|
332
|
+
|
|
333
|
+
##
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
if __name__ == "__main__":
|
|
337
|
+
|
|
338
|
+
import doctest, contextlib, os
|
|
339
|
+
from c4dynamics import IgnoreOutputChecker, cprint
|
|
340
|
+
|
|
341
|
+
# Register the custom OutputChecker
|
|
342
|
+
doctest.OutputChecker = IgnoreOutputChecker
|
|
343
|
+
|
|
344
|
+
tofile = False
|
|
345
|
+
optionflags = doctest.FAIL_FAST
|
|
346
|
+
|
|
347
|
+
if tofile:
|
|
348
|
+
with open(os.path.join('tests', '_out', 'output.txt'), 'w') as f:
|
|
349
|
+
with contextlib.redirect_stdout(f), contextlib.redirect_stderr(f):
|
|
350
|
+
result = doctest.testmod(optionflags = optionflags)
|
|
351
|
+
else:
|
|
352
|
+
result = doctest.testmod(optionflags = optionflags)
|
|
353
|
+
|
|
354
|
+
if result.failed == 0:
|
|
355
|
+
cprint(os.path.basename(__file__) + ": all tests passed!", 'g')
|
|
356
|
+
else:
|
|
357
|
+
print(f"{result.failed}")
|
|
358
|
+
|
|
359
|
+
|