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,123 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import sys
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
sys.path.append('.')
|
|
6
|
+
import c4dynamics as c4d
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class lowpass(c4d.state):
|
|
10
|
+
"""
|
|
11
|
+
A first-order low-pass filter for smoothing signals, supporting both discrete and continuous systems.
|
|
12
|
+
|
|
13
|
+
Parameters
|
|
14
|
+
----------
|
|
15
|
+
alpha : float, optional
|
|
16
|
+
Smoothing factor for a discrete system. Must be in the range (0, 1). Defaults to None.
|
|
17
|
+
dt : float, optional
|
|
18
|
+
Time step for a continuous system. Must be positive. Defaults to None.
|
|
19
|
+
tau : float, optional
|
|
20
|
+
Time constant for a continuous system. Must be positive. Defaults to None.
|
|
21
|
+
y0 : float, optional
|
|
22
|
+
Initial value of the state. Defaults to 0.
|
|
23
|
+
|
|
24
|
+
Raises
|
|
25
|
+
------
|
|
26
|
+
ValueError
|
|
27
|
+
If neither `alpha` nor both `dt` and `tau` are provided.
|
|
28
|
+
If `alpha` is out of the range (0, 1) for a discrete system.
|
|
29
|
+
If `dt` or `tau` is non-positive for a continuous system.
|
|
30
|
+
|
|
31
|
+
Notes
|
|
32
|
+
-----
|
|
33
|
+
- For a continuous system, `dt` and `tau` are required, and `alpha` is calculated as `dt / tau`.
|
|
34
|
+
- For a discrete system, `alpha` alone is required and directly specifies the smoothing factor.
|
|
35
|
+
|
|
36
|
+
Example
|
|
37
|
+
-------
|
|
38
|
+
|
|
39
|
+
.. code::
|
|
40
|
+
|
|
41
|
+
>>> filter_continuous = lowpass(dt=0.01, tau=0.1, y0=0)
|
|
42
|
+
>>> filter_discrete = lowpass(alpha=0.5, y0=1)
|
|
43
|
+
>>> filter_continuous.sample(1.0) # doctest: +ELLIPSIS
|
|
44
|
+
0.09...
|
|
45
|
+
>>> filter_discrete.sample(2.0)
|
|
46
|
+
1.5
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
def __init__(self, alpha: Optional[float] = None, dt: Optional[float] = None,
|
|
50
|
+
tau: Optional[float] = None, y0: float = 0) -> None:
|
|
51
|
+
# Initialize alpha based on the provided parameters
|
|
52
|
+
if dt is not None and tau is not None:
|
|
53
|
+
if dt <= 0 or tau <= 0:
|
|
54
|
+
raise ValueError("For a continuous system, `dt` and `tau` must be positive.")
|
|
55
|
+
self.alpha = dt / tau
|
|
56
|
+
elif alpha is not None:
|
|
57
|
+
if not (0 < alpha < 1):
|
|
58
|
+
raise ValueError("For a discrete system, `alpha` must be in the range (0, 1).")
|
|
59
|
+
self.alpha = alpha
|
|
60
|
+
else:
|
|
61
|
+
raise ValueError("Provide either `alpha` for a discrete system or both `dt` and `tau` for a continuous system.")
|
|
62
|
+
|
|
63
|
+
self.y = y0 # Initial state value
|
|
64
|
+
|
|
65
|
+
def sample(self, x: float) -> float:
|
|
66
|
+
"""
|
|
67
|
+
Applies the low-pass filter to the input value and returns the filtered output.
|
|
68
|
+
|
|
69
|
+
Parameters
|
|
70
|
+
----------
|
|
71
|
+
x : float
|
|
72
|
+
Input value to be filtered.
|
|
73
|
+
|
|
74
|
+
Returns
|
|
75
|
+
-------
|
|
76
|
+
float
|
|
77
|
+
The filtered output value after applying the low-pass filter.
|
|
78
|
+
|
|
79
|
+
Notes
|
|
80
|
+
-----
|
|
81
|
+
- For a continuous system: `y'(t) = -y(t) / tau + x(t) / tau`
|
|
82
|
+
- For a discrete system: `y[k] = (1 - alpha) * y[k-1] + alpha * x[k]`
|
|
83
|
+
- The filter's state (`self.y`) is updated in place.
|
|
84
|
+
|
|
85
|
+
Example
|
|
86
|
+
-------
|
|
87
|
+
|
|
88
|
+
.. code::
|
|
89
|
+
|
|
90
|
+
>>> lp_filter = lowpass(alpha=0.5)
|
|
91
|
+
>>> lp_filter.sample(2.0)
|
|
92
|
+
1.0
|
|
93
|
+
>>> lp_filter.sample(3.0)
|
|
94
|
+
2.0
|
|
95
|
+
"""
|
|
96
|
+
# Update the filter's state
|
|
97
|
+
self.y = (1 - self.alpha) * self.y + self.alpha * x
|
|
98
|
+
return self.y
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
if __name__ == "__main__":
|
|
102
|
+
import doctest
|
|
103
|
+
import contextlib
|
|
104
|
+
import os
|
|
105
|
+
from c4dynamics import IgnoreOutputChecker, cprint
|
|
106
|
+
|
|
107
|
+
# Register the custom OutputChecker
|
|
108
|
+
doctest.OutputChecker = IgnoreOutputChecker
|
|
109
|
+
|
|
110
|
+
tofile = False
|
|
111
|
+
optionflags = doctest.FAIL_FAST
|
|
112
|
+
|
|
113
|
+
if tofile:
|
|
114
|
+
with open('tests/_out/output.txt', 'w') as f:
|
|
115
|
+
with contextlib.redirect_stdout(f), contextlib.redirect_stderr(f):
|
|
116
|
+
result = doctest.testmod(optionflags=optionflags)
|
|
117
|
+
else:
|
|
118
|
+
result = doctest.testmod(optionflags=optionflags)
|
|
119
|
+
|
|
120
|
+
if result.failed == 0:
|
|
121
|
+
cprint(os.path.basename(__file__) + ": all tests passed!", 'g')
|
|
122
|
+
else:
|
|
123
|
+
print(f"{result.failed} test(s) failed.")
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
class luenberger:
|
|
4
|
+
'''
|
|
5
|
+
Luenberger (Asymptotic) Observer.
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
Parameters
|
|
9
|
+
==========
|
|
10
|
+
TODO complete
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
luenberger estimator
|
|
14
|
+
agranovich,
|
|
15
|
+
modern control 72
|
|
16
|
+
|
|
17
|
+
### Luenberger Filter
|
|
18
|
+
The Luenberger observer is given by:
|
|
19
|
+
|
|
20
|
+
\[ \dot{\hat{x}}(t) = A \hat{x}(t) + B u(t) + L (y(t) - C \hat{x}(t)) \]
|
|
21
|
+
|
|
22
|
+
where \( L \) is the observer gain matrix.
|
|
23
|
+
|
|
24
|
+
'''
|
|
25
|
+
|
|
26
|
+
A = 0
|
|
27
|
+
c = 0
|
|
28
|
+
obsv = 0
|
|
29
|
+
Aest = 0
|
|
30
|
+
|
|
31
|
+
def __init__(obj, A, c):
|
|
32
|
+
obj.A = A
|
|
33
|
+
obj.c = c
|
|
34
|
+
obj.obsv = np.copy(obj.c)
|
|
35
|
+
for n in range(len(obj.A) - 1):
|
|
36
|
+
obj.obsv = np.vstack((obj.obsv, obj.c @ obj.A**(n + 1))).copy()
|
|
37
|
+
|
|
38
|
+
def isobservable(obj):
|
|
39
|
+
return np.linalg.matrix_rank(obj.obsv) == len(obj.A)
|
|
40
|
+
|
|
41
|
+
def eig(obj):
|
|
42
|
+
return np.linalg.eig(obj.A)[0]
|
|
43
|
+
|
|
44
|
+
def setest(obj, s):
|
|
45
|
+
n = len(obj.A)
|
|
46
|
+
|
|
47
|
+
# coefficients from the desired eigenvalues
|
|
48
|
+
an_d = np.polynomial.polynomial.polyfromroots(s)
|
|
49
|
+
# the extended system eigenvalues are including the luenberger gains which are currently unknown.
|
|
50
|
+
# the polynomial that represents the system is given by the determinant of s*I-Aest. where Aest is the extended system matrix which inclueds the gains.
|
|
51
|
+
# the desired eigenvalues are given in the input argument s.
|
|
52
|
+
# assuming the prime coefficient is one in the both systems, there are n-1 coefficients to compare.
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
#
|
|
56
|
+
# the calculation of luenberger gains
|
|
57
|
+
# rational:
|
|
58
|
+
# 1 calculate the luenberger gains of the equivalent canonical model
|
|
59
|
+
# 2 find the model transform matrix
|
|
60
|
+
# 3 tranform the gains
|
|
61
|
+
##
|
|
62
|
+
|
|
63
|
+
# a canonical equivalent system matrix:
|
|
64
|
+
# | 0 0 .. -a0 |
|
|
65
|
+
# | 1 0 .. -a1 |
|
|
66
|
+
# | .. |
|
|
67
|
+
# |0 0 .. 1 -an-1|
|
|
68
|
+
# where a0..an-1 are the coefficients of the system polynomial.
|
|
69
|
+
|
|
70
|
+
an = np.polynomial.polynomial.polyfromroots(np.linalg.eig(obj.A)[0])
|
|
71
|
+
# canA = np.zeros((n, n))
|
|
72
|
+
# for i in range(n):
|
|
73
|
+
# canA[i, -1] = -an[i]
|
|
74
|
+
# if i == 0:
|
|
75
|
+
# continue
|
|
76
|
+
# canA[i, i - 1] = 1
|
|
77
|
+
|
|
78
|
+
# luenberger gains for the canonical system
|
|
79
|
+
Lc = np.zeros(n) # Lcanonical
|
|
80
|
+
for i in range(n):
|
|
81
|
+
Lc[i] = an_d[i] - an[i]
|
|
82
|
+
|
|
83
|
+
# model transformation matrix
|
|
84
|
+
cmu = np.zeros(n)
|
|
85
|
+
cmu[-1] = 1
|
|
86
|
+
mu = np.linalg.solve(obj.obsv, cmu.reshape((-1, 1)))
|
|
87
|
+
|
|
88
|
+
M = np.copy(mu)
|
|
89
|
+
for n in range(n - 1):
|
|
90
|
+
M = np.hstack((M, obj.A**(n + 1) @ mu)).copy()
|
|
91
|
+
|
|
92
|
+
# 3 tranform the gains
|
|
93
|
+
L = M @ Lc
|
|
94
|
+
|
|
95
|
+
obj.Aest = obj.A - L @ obj.c
|
|
96
|
+
|
|
97
|
+
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
'''
|
|
2
|
+
|
|
3
|
+
Rotational Matrix Operations
|
|
4
|
+
============================
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
Background Material
|
|
9
|
+
-------------------
|
|
10
|
+
|
|
11
|
+
A rotation matrix is a mathematical representation of a
|
|
12
|
+
rotation in three-dimensional space.
|
|
13
|
+
|
|
14
|
+
It's a 3x3 matrix that, when multiplied with a vector,
|
|
15
|
+
transforms the vector to represent a new orientation.
|
|
16
|
+
|
|
17
|
+
Each element of the matrix corresponds to a directional cosine,
|
|
18
|
+
capturing the rotation's effect on the x, y, and z axes.
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
Euler Angles Order
|
|
22
|
+
^^^^^^^^^^^^^^^^^^
|
|
23
|
+
|
|
24
|
+
Frame based vectors are related through a Direction Cosine Matrix (DCM). [HS]_
|
|
25
|
+
|
|
26
|
+
When Euler angles are employed in the transformation of
|
|
27
|
+
a vector expressed in one reference frame to the expression
|
|
28
|
+
of the vector in a different reference frame, any order of the
|
|
29
|
+
three Euler rotations is possible, but the resulting transformation
|
|
30
|
+
equations depend on the order selected. [MIs]_
|
|
31
|
+
|
|
32
|
+
In aerospace applications for example,
|
|
33
|
+
the common order is that the first Euler rotation is about the z-axis,
|
|
34
|
+
the second is about the y-axis, and the third is about the Xaxis.
|
|
35
|
+
Such a transformation order is called z-y-x, or 3-2-1.
|
|
36
|
+
With reference to a body orientation, the resulting
|
|
37
|
+
order is yaw, pitch, and roll.
|
|
38
|
+
With reference to geographical
|
|
39
|
+
orientation, the resulting order is azimuth (heading),
|
|
40
|
+
elevation (pitch), and roll (bank angle).
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
Right Hand Frame
|
|
44
|
+
^^^^^^^^^^^^^^^^
|
|
45
|
+
|
|
46
|
+
The positive directions of coordinate system
|
|
47
|
+
axes and the directions of positive rotations
|
|
48
|
+
about the axes are arbitrary.
|
|
49
|
+
In right-handed systems:
|
|
50
|
+
|
|
51
|
+
::
|
|
52
|
+
|
|
53
|
+
i x j = k
|
|
54
|
+
j x k = i
|
|
55
|
+
k x i = j
|
|
56
|
+
|
|
57
|
+
where i is the unit vector in the direction of the x-axis,
|
|
58
|
+
j is the unit vector in the direction of the y-axis,
|
|
59
|
+
k is the unit vector in the direction of the z-axis.
|
|
60
|
+
|
|
61
|
+
Positive rotations are clockwise
|
|
62
|
+
when viewed from the origin, looking out along the
|
|
63
|
+
positive direction of the axis.
|
|
64
|
+
|
|
65
|
+
These conventions are illustrated
|
|
66
|
+
in Fig-1.
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
.. figure:: /_architecture/frame_conventions.svg
|
|
70
|
+
|
|
71
|
+
Fig-1: Coordinate System Conventions
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
References
|
|
75
|
+
----------
|
|
76
|
+
|
|
77
|
+
.. [HS] Hanspeter Schaub, "Spacecraft Dynamics and Control" lecture notes, module 2: rigidbody kinematics.
|
|
78
|
+
.. [MIs] Ch 4 in "Missile Flight Simulation Part One Surface-to-Air Missiles", Military Handbook, 1995, MIL-HDBK-1211(MI).
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
Examples
|
|
83
|
+
--------
|
|
84
|
+
|
|
85
|
+
For examples, see the various functions.
|
|
86
|
+
|
|
87
|
+
'''
|
|
88
|
+
# spacecraft dyanmics and control
|
|
89
|
+
|
|
90
|
+
# Rotating Reference Frame
|
|
91
|
+
# ^^^^^^^^^^^^^^^^^^^^^^^^
|
|
92
|
+
|
|
93
|
+
# A vector resolved in a given reference frame is said to be
|
|
94
|
+
# **expressed** in that frame (sometimes said **referred to**).
|
|
95
|
+
|
|
96
|
+
# The rate of change of a vector, as viewed by an observer fixed to and moving
|
|
97
|
+
# with a given reference frame,
|
|
98
|
+
# is said to be **relative to** or **with respect to** that reference frame.
|
|
99
|
+
|
|
100
|
+
# It's important to note here that the rate of change of a vector must be
|
|
101
|
+
# relative to an inertial reference frame,
|
|
102
|
+
# but it can be expressed in any reference frame.
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
import sys
|
|
106
|
+
sys.path.append('.')
|
|
107
|
+
|
|
108
|
+
from c4dynamics.rotmat.rotmat import rotx, roty, rotz, dcm321, dcm321euler
|
|
109
|
+
from c4dynamics.rotmat.animate import animate
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
if __name__ == "__main__":
|
|
114
|
+
|
|
115
|
+
import doctest, contextlib, os
|
|
116
|
+
from c4dynamics import IgnoreOutputChecker, cprint
|
|
117
|
+
|
|
118
|
+
# Register the custom OutputChecker
|
|
119
|
+
doctest.OutputChecker = IgnoreOutputChecker
|
|
120
|
+
|
|
121
|
+
tofile = False
|
|
122
|
+
optionflags = doctest.FAIL_FAST
|
|
123
|
+
|
|
124
|
+
if tofile:
|
|
125
|
+
with open(os.path.join('tests', '_out', 'output.txt'), 'w') as f:
|
|
126
|
+
with contextlib.redirect_stdout(f), contextlib.redirect_stderr(f):
|
|
127
|
+
result = doctest.testmod(optionflags = optionflags)
|
|
128
|
+
else:
|
|
129
|
+
result = doctest.testmod(optionflags = optionflags)
|
|
130
|
+
|
|
131
|
+
if result.failed == 0:
|
|
132
|
+
cprint(os.path.basename(__file__) + ": all tests passed!", 'g')
|
|
133
|
+
else:
|
|
134
|
+
print(f"{result.failed}")
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
|