fmdtools 2.2.1__tar.gz → 2.2.3__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.
- {fmdtools-2.2.1 → fmdtools-2.2.3}/LICENSE +1 -1
- {fmdtools-2.2.1/fmdtools.egg-info → fmdtools-2.2.3}/PKG-INFO +7 -7
- {fmdtools-2.2.1 → fmdtools-2.2.3}/README.md +4 -4
- {fmdtools-2.2.1 → fmdtools-2.2.3}/examples/eps/eps.py +6 -5
- {fmdtools-2.2.1 → fmdtools-2.2.3}/examples/eps/test_eps.py +1 -1
- {fmdtools-2.2.1 → fmdtools-2.2.3}/examples/multiflow_demo/multiflow_demo.py +6 -4
- {fmdtools-2.2.1 → fmdtools-2.2.3}/examples/multirotor/drone_mdl_dynamic.py +11 -10
- {fmdtools-2.2.1 → fmdtools-2.2.3}/examples/multirotor/drone_mdl_hierarchical.py +163 -115
- {fmdtools-2.2.1 → fmdtools-2.2.3}/examples/multirotor/drone_mdl_rural.py +62 -52
- {fmdtools-2.2.1 → fmdtools-2.2.3}/examples/multirotor/drone_mdl_static.py +25 -21
- {fmdtools-2.2.1 → fmdtools-2.2.3}/examples/multirotor/drone_mdl_urban.py +9 -9
- {fmdtools-2.2.1 → fmdtools-2.2.3}/examples/multirotor/opt_drone_rural.py +1 -1
- {fmdtools-2.2.1 → fmdtools-2.2.3}/examples/pump/ex_pump.py +49 -35
- {fmdtools-2.2.1 → fmdtools-2.2.3}/examples/pump/jointfaults_ex.py +3 -3
- {fmdtools-2.2.1 → fmdtools-2.2.3}/examples/pump/parallelism_methods.py +9 -9
- {fmdtools-2.2.1 → fmdtools-2.2.3}/examples/pump/pump_stochastic.py +6 -6
- {fmdtools-2.2.1 → fmdtools-2.2.3}/examples/pump/test_pump_stochastic.py +2 -2
- {fmdtools-2.2.1 → fmdtools-2.2.3}/examples/rover/optimization/search_rover.py +1 -1
- {fmdtools-2.2.1 → fmdtools-2.2.3}/examples/rover/rover_degradation.py +26 -26
- {fmdtools-2.2.1 → fmdtools-2.2.3}/examples/rover/rover_model.py +53 -53
- {fmdtools-2.2.1 → fmdtools-2.2.3}/examples/rover/rover_model_human.py +9 -7
- {fmdtools-2.2.1 → fmdtools-2.2.3}/examples/tank/tank_model.py +19 -17
- {fmdtools-2.2.1 → fmdtools-2.2.3}/examples/tank/tank_optimization_model.py +6 -6
- {fmdtools-2.2.1 → fmdtools-2.2.3}/examples/taxiway/asset.py +2 -2
- {fmdtools-2.2.1 → fmdtools-2.2.3}/examples/taxiway/common.py +17 -17
- {fmdtools-2.2.1 → fmdtools-2.2.3}/examples/taxiway/model.py +3 -3
- {fmdtools-2.2.1 → fmdtools-2.2.3}/fmdtools/__init__.py +1 -1
- {fmdtools-2.2.1 → fmdtools-2.2.3}/fmdtools/analyze/common.py +2 -2
- {fmdtools-2.2.1 → fmdtools-2.2.3}/fmdtools/analyze/phases.py +7 -7
- {fmdtools-2.2.1 → fmdtools-2.2.3}/fmdtools/analyze/result.py +9 -5
- {fmdtools-2.2.1 → fmdtools-2.2.3}/fmdtools/analyze/tabulate.py +47 -31
- {fmdtools-2.2.1 → fmdtools-2.2.3}/fmdtools/define/architecture/action.py +2 -2
- {fmdtools-2.2.1 → fmdtools-2.2.3}/fmdtools/define/architecture/base.py +21 -5
- {fmdtools-2.2.1 → fmdtools-2.2.3}/fmdtools/define/architecture/component.py +6 -6
- {fmdtools-2.2.1 → fmdtools-2.2.3}/fmdtools/define/architecture/function.py +11 -11
- {fmdtools-2.2.1 → fmdtools-2.2.3}/fmdtools/define/architecture/geom.py +2 -2
- {fmdtools-2.2.1 → fmdtools-2.2.3}/fmdtools/define/base.py +3 -1
- {fmdtools-2.2.1 → fmdtools-2.2.3}/fmdtools/define/block/action.py +6 -5
- {fmdtools-2.2.1 → fmdtools-2.2.3}/fmdtools/define/block/base.py +6 -5
- {fmdtools-2.2.1 → fmdtools-2.2.3}/fmdtools/define/block/component.py +5 -5
- {fmdtools-2.2.1 → fmdtools-2.2.3}/fmdtools/define/block/function.py +6 -6
- {fmdtools-2.2.1 → fmdtools-2.2.3}/fmdtools/define/container/base.py +9 -3
- {fmdtools-2.2.1 → fmdtools-2.2.3}/fmdtools/define/container/mode.py +5 -5
- {fmdtools-2.2.1 → fmdtools-2.2.3}/fmdtools/define/container/parameter.py +9 -9
- {fmdtools-2.2.1 → fmdtools-2.2.3}/fmdtools/define/container/rand.py +18 -10
- {fmdtools-2.2.1 → fmdtools-2.2.3}/fmdtools/define/container/state.py +5 -3
- {fmdtools-2.2.1 → fmdtools-2.2.3}/fmdtools/define/container/time.py +9 -8
- {fmdtools-2.2.1 → fmdtools-2.2.3}/fmdtools/define/environment.py +26 -6
- {fmdtools-2.2.1 → fmdtools-2.2.3}/fmdtools/define/flow/base.py +4 -4
- {fmdtools-2.2.1 → fmdtools-2.2.3}/fmdtools/define/flow/commsflow.py +12 -10
- {fmdtools-2.2.1 → fmdtools-2.2.3}/fmdtools/define/flow/multiflow.py +21 -14
- {fmdtools-2.2.1 → fmdtools-2.2.3}/fmdtools/define/object/base.py +15 -10
- {fmdtools-2.2.1 → fmdtools-2.2.3}/fmdtools/define/object/coords.py +5 -4
- {fmdtools-2.2.1 → fmdtools-2.2.3}/fmdtools/define/object/geom.py +1 -1
- {fmdtools-2.2.1 → fmdtools-2.2.3}/fmdtools/sim/propagate.py +20 -10
- {fmdtools-2.2.1 → fmdtools-2.2.3}/fmdtools/sim/sample.py +2 -2
- {fmdtools-2.2.1 → fmdtools-2.2.3}/fmdtools/sim/search.py +4 -4
- {fmdtools-2.2.1 → fmdtools-2.2.3/fmdtools.egg-info}/PKG-INFO +7 -7
- {fmdtools-2.2.1 → fmdtools-2.2.3}/fmdtools.egg-info/requires.txt +1 -1
- {fmdtools-2.2.1 → fmdtools-2.2.3}/pyproject.toml +2 -2
- {fmdtools-2.2.1 → fmdtools-2.2.3}/tests/test_graph.py +1 -1
- {fmdtools-2.2.1 → fmdtools-2.2.3}/examples/__init__.py +0 -0
- {fmdtools-2.2.1 → fmdtools-2.2.3}/examples/asg_demo/__init__.py +0 -0
- {fmdtools-2.2.1 → fmdtools-2.2.3}/examples/asg_demo/demo_model.py +0 -0
- {fmdtools-2.2.1 → fmdtools-2.2.3}/examples/eps/__init__.py +0 -0
- {fmdtools-2.2.1 → fmdtools-2.2.3}/examples/multiflow_demo/__init__.py +0 -0
- {fmdtools-2.2.1 → fmdtools-2.2.3}/examples/multiflow_demo/test_multiflow.py +0 -0
- {fmdtools-2.2.1 → fmdtools-2.2.3}/examples/multirotor/__init__.py +0 -0
- {fmdtools-2.2.1 → fmdtools-2.2.3}/examples/multirotor/test_multirotor.py +0 -0
- {fmdtools-2.2.1 → fmdtools-2.2.3}/examples/pump/__init__.py +0 -0
- {fmdtools-2.2.1 → fmdtools-2.2.3}/examples/pump/pump_indiv.py +0 -0
- {fmdtools-2.2.1 → fmdtools-2.2.3}/examples/pump/test_pump.py +0 -0
- {fmdtools-2.2.1 → fmdtools-2.2.3}/examples/pump/test_pump_example.py +0 -0
- {fmdtools-2.2.1 → fmdtools-2.2.3}/examples/rover/__init__.py +0 -0
- {fmdtools-2.2.1 → fmdtools-2.2.3}/examples/rover/optimization/ea_analysis.py +0 -0
- {fmdtools-2.2.1 → fmdtools-2.2.3}/examples/rover/rover_mode_space.py +0 -0
- {fmdtools-2.2.1 → fmdtools-2.2.3}/examples/rover/test_rover.py +0 -0
- {fmdtools-2.2.1 → fmdtools-2.2.3}/examples/tank/__init__.py +0 -0
- {fmdtools-2.2.1 → fmdtools-2.2.3}/examples/tank/test_tank.py +0 -0
- {fmdtools-2.2.1 → fmdtools-2.2.3}/examples/taxiway/ATC.py +0 -0
- {fmdtools-2.2.1 → fmdtools-2.2.3}/examples/taxiway/test_asset.py +0 -0
- {fmdtools-2.2.1 → fmdtools-2.2.3}/examples/taxiway/test_model.py +0 -0
- {fmdtools-2.2.1 → fmdtools-2.2.3}/fmdtools/analyze/__init__.py +0 -0
- {fmdtools-2.2.1 → fmdtools-2.2.3}/fmdtools/analyze/graph/__init__.py +0 -0
- {fmdtools-2.2.1 → fmdtools-2.2.3}/fmdtools/analyze/graph/base.py +0 -0
- {fmdtools-2.2.1 → fmdtools-2.2.3}/fmdtools/analyze/graph/label.py +0 -0
- {fmdtools-2.2.1 → fmdtools-2.2.3}/fmdtools/analyze/graph/model.py +0 -0
- {fmdtools-2.2.1 → fmdtools-2.2.3}/fmdtools/analyze/graph/style.py +0 -0
- {fmdtools-2.2.1 → fmdtools-2.2.3}/fmdtools/analyze/history.py +0 -0
- {fmdtools-2.2.1 → fmdtools-2.2.3}/fmdtools/define/__init__.py +0 -0
- {fmdtools-2.2.1 → fmdtools-2.2.3}/fmdtools/define/architecture/__init__.py +0 -0
- {fmdtools-2.2.1 → fmdtools-2.2.3}/fmdtools/define/block/__init__.py +0 -0
- {fmdtools-2.2.1 → fmdtools-2.2.3}/fmdtools/define/container/__init__.py +0 -0
- {fmdtools-2.2.1 → fmdtools-2.2.3}/fmdtools/define/flow/__init__.py +0 -0
- {fmdtools-2.2.1 → fmdtools-2.2.3}/fmdtools/define/object/__init__.py +0 -0
- {fmdtools-2.2.1 → fmdtools-2.2.3}/fmdtools/define/object/timer.py +0 -0
- {fmdtools-2.2.1 → fmdtools-2.2.3}/fmdtools/sim/__init__.py +0 -0
- {fmdtools-2.2.1 → fmdtools-2.2.3}/fmdtools/sim/scenario.py +0 -0
- {fmdtools-2.2.1 → fmdtools-2.2.3}/fmdtools.egg-info/SOURCES.txt +0 -0
- {fmdtools-2.2.1 → fmdtools-2.2.3}/fmdtools.egg-info/dependency_links.txt +0 -0
- {fmdtools-2.2.1 → fmdtools-2.2.3}/fmdtools.egg-info/top_level.txt +0 -0
- {fmdtools-2.2.1 → fmdtools-2.2.3}/setup.cfg +0 -0
- {fmdtools-2.2.1 → fmdtools-2.2.3}/tests/test_hierarchical.py +0 -0
- {fmdtools-2.2.1 → fmdtools-2.2.3}/tests/test_packages.py +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fmdtools
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.3
|
|
4
4
|
Summary: System resilience modelling, simulation, and assessment in Python
|
|
5
5
|
Author: fmdtools Developers
|
|
6
6
|
Maintainer-email: Daniel Hulse <daniel.e.hulse@nasa.gov>
|
|
7
|
-
License: fmdtools version 2.2.
|
|
7
|
+
License: fmdtools version 2.2.3
|
|
8
8
|
---------------------------
|
|
9
9
|
Apache License
|
|
10
10
|
Version 2.0, January 2004
|
|
@@ -250,7 +250,7 @@ Requires-Dist: tqdm
|
|
|
250
250
|
Requires-Dist: networkx
|
|
251
251
|
Requires-Dist: numpy>=2.0
|
|
252
252
|
Requires-Dist: matplotlib>=3.7.0
|
|
253
|
-
Requires-Dist: pandas
|
|
253
|
+
Requires-Dist: pandas[all]
|
|
254
254
|
Requires-Dist: ordered-set
|
|
255
255
|
Requires-Dist: dill
|
|
256
256
|
Requires-Dist: recordclass==0.23.1
|
|
@@ -367,7 +367,7 @@ This may then be installed using:
|
|
|
367
367
|
Note that this version reflects the latest version on the ``main`` git branch along with any local changes. To use a particular version of ``fmdtools`` you can checkout the tag corresponding to the version you want, e.g.:
|
|
368
368
|
|
|
369
369
|
```
|
|
370
|
-
git checkout v2.2.
|
|
370
|
+
git checkout v2.2.3
|
|
371
371
|
```
|
|
372
372
|
|
|
373
373
|
|
|
@@ -386,7 +386,7 @@ numpy < 2.0
|
|
|
386
386
|
# license: (BSD) https://numpy.org/doc/stable/license.html
|
|
387
387
|
matplotlib
|
|
388
388
|
# license: (mixed) https://matplotlib.org/stable/users/license.html
|
|
389
|
-
pandas
|
|
389
|
+
pandas[all]
|
|
390
390
|
# license: (BSD 3-clause) https://pandas.pydata.org/pandas-docs/stable/getting_started/overview.html#license
|
|
391
391
|
ordered-set
|
|
392
392
|
# license: (MIT) https://github.com/rspeer/ordered-set/blob/master/MIT-LICENSE
|
|
@@ -505,8 +505,8 @@ To cite a particular version of the fmdtools, you may use:
|
|
|
505
505
|
author = {{NASA}},
|
|
506
506
|
title = {fmdtools},
|
|
507
507
|
url = {https://github.com/nasa/fmdtools},
|
|
508
|
-
version = {2.2.
|
|
509
|
-
date = {2025-
|
|
508
|
+
version = {2.2.3},
|
|
509
|
+
date = {2025-12-4},
|
|
510
510
|
}
|
|
511
511
|
```
|
|
512
512
|
|
|
@@ -106,7 +106,7 @@ This may then be installed using:
|
|
|
106
106
|
Note that this version reflects the latest version on the ``main`` git branch along with any local changes. To use a particular version of ``fmdtools`` you can checkout the tag corresponding to the version you want, e.g.:
|
|
107
107
|
|
|
108
108
|
```
|
|
109
|
-
git checkout v2.2.
|
|
109
|
+
git checkout v2.2.3
|
|
110
110
|
```
|
|
111
111
|
|
|
112
112
|
|
|
@@ -125,7 +125,7 @@ numpy < 2.0
|
|
|
125
125
|
# license: (BSD) https://numpy.org/doc/stable/license.html
|
|
126
126
|
matplotlib
|
|
127
127
|
# license: (mixed) https://matplotlib.org/stable/users/license.html
|
|
128
|
-
pandas
|
|
128
|
+
pandas[all]
|
|
129
129
|
# license: (BSD 3-clause) https://pandas.pydata.org/pandas-docs/stable/getting_started/overview.html#license
|
|
130
130
|
ordered-set
|
|
131
131
|
# license: (MIT) https://github.com/rspeer/ordered-set/blob/master/MIT-LICENSE
|
|
@@ -244,8 +244,8 @@ To cite a particular version of the fmdtools, you may use:
|
|
|
244
244
|
author = {{NASA}},
|
|
245
245
|
title = {fmdtools},
|
|
246
246
|
url = {https://github.com/nasa/fmdtools},
|
|
247
|
-
version = {2.2.
|
|
248
|
-
date = {2025-
|
|
247
|
+
version = {2.2.3},
|
|
248
|
+
date = {2025-12-4},
|
|
249
249
|
}
|
|
250
250
|
```
|
|
251
251
|
|
|
@@ -35,11 +35,12 @@ from fmdtools.define.container.mode import Mode
|
|
|
35
35
|
from fmdtools.define.architecture.function import FunctionArchitecture
|
|
36
36
|
from fmdtools.define.container.state import State
|
|
37
37
|
from fmdtools.define.flow.base import Flow
|
|
38
|
+
import numpy as np
|
|
38
39
|
|
|
39
40
|
|
|
40
41
|
class GenericState(State):
|
|
41
|
-
rate:
|
|
42
|
-
effort:
|
|
42
|
+
rate: np.float64 = 1.0
|
|
43
|
+
effort: np.float64 = 1.0
|
|
43
44
|
|
|
44
45
|
|
|
45
46
|
class GenericFlow(Flow):
|
|
@@ -48,7 +49,7 @@ class GenericFlow(Flow):
|
|
|
48
49
|
|
|
49
50
|
|
|
50
51
|
class SigState(State):
|
|
51
|
-
value:
|
|
52
|
+
value: np.float64 = 1.0
|
|
52
53
|
|
|
53
54
|
|
|
54
55
|
class Signal(Flow):
|
|
@@ -482,14 +483,14 @@ if __name__ == "__main__":
|
|
|
482
483
|
to_return="graph")
|
|
483
484
|
|
|
484
485
|
result.get_faulty().tend.graph.draw()
|
|
485
|
-
#
|
|
486
|
+
# results, hists = propagate.single_faults(mdl)
|
|
486
487
|
|
|
487
488
|
# resgraph, mdlhists = propagate.one_fault(mdl, 'ee_to_me', 'toohigh_torque', to_return="fxnflowgraph")
|
|
488
489
|
# result.graph.draw()
|
|
489
490
|
ks = [*mdl.get_roles_as_dict("fxn", "flow", flex_prefixes=True)]
|
|
490
491
|
|
|
491
492
|
summary = mdlhists.get_fault_degradation_summary(*ks)
|
|
492
|
-
#
|
|
493
|
+
# results, hists = propagate.single_faults(mdl)
|
|
493
494
|
degradation = mdlhists.get_degraded_hist(*ks)
|
|
494
495
|
|
|
495
496
|
degtimemap = degradation.get_summary(operator=np.sum)
|
|
@@ -24,10 +24,12 @@ from fmdtools.define.flow.multiflow import MultiFlow
|
|
|
24
24
|
from fmdtools.define.flow.commsflow import CommsFlow
|
|
25
25
|
from fmdtools.define.container.state import State
|
|
26
26
|
|
|
27
|
+
import numpy as np
|
|
28
|
+
|
|
27
29
|
|
|
28
30
|
class LocationState(State):
|
|
29
|
-
x:
|
|
30
|
-
y:
|
|
31
|
+
x: np.float64 = 0.0
|
|
32
|
+
y: np.float64 = 0.0
|
|
31
33
|
|
|
32
34
|
|
|
33
35
|
class Communications(CommsFlow):
|
|
@@ -39,8 +41,8 @@ class Location(MultiFlow):
|
|
|
39
41
|
|
|
40
42
|
|
|
41
43
|
class MoveParam(Parameter):
|
|
42
|
-
x_up:
|
|
43
|
-
y_up:
|
|
44
|
+
x_up: np.float64 = 0.0
|
|
45
|
+
y_up: np.float64 = 0.0
|
|
44
46
|
|
|
45
47
|
|
|
46
48
|
class Mover(Function):
|
|
@@ -60,9 +60,9 @@ class DroneEnvironmentGridParam(CoordsParam):
|
|
|
60
60
|
Type of environment risk profile (urban or rural).
|
|
61
61
|
"""
|
|
62
62
|
|
|
63
|
-
x_size:
|
|
64
|
-
y_size:
|
|
65
|
-
blocksize:
|
|
63
|
+
x_size: np.int32 = 16
|
|
64
|
+
y_size: np.int32 = 16
|
|
65
|
+
blocksize: np.float64 = 10.0
|
|
66
66
|
loc: str = 'rural'
|
|
67
67
|
|
|
68
68
|
|
|
@@ -240,10 +240,10 @@ class PlanPath(Function):
|
|
|
240
240
|
e.g.::
|
|
241
241
|
>>> p = PlanPath()
|
|
242
242
|
>>> p.des_traj.s
|
|
243
|
-
DesTrajState(dx=
|
|
243
|
+
DesTrajState(dx=1.0, dy=0.0, dz=0.0, power=1.0)
|
|
244
244
|
>>> p.assign_vectdist_to_goal()
|
|
245
245
|
>>> p.des_traj.s
|
|
246
|
-
DesTrajState(dx=
|
|
246
|
+
DesTrajState(dx=0.0, dy=0.0, dz=50.0, power=1.0)
|
|
247
247
|
"""
|
|
248
248
|
loc = self.dofs.s.get('x', 'y', 'z')
|
|
249
249
|
vd = vectdist(self.s.goal, loc)
|
|
@@ -354,13 +354,13 @@ class AffectDOF(AffectDOFStatic):
|
|
|
354
354
|
>>> a.dofs.s.put(x=0.0, y=0.0, z=100.0, planvel=1.0, vertvel=0.0)
|
|
355
355
|
>>> a.inc_pos()
|
|
356
356
|
>>> a.dofs.s
|
|
357
|
-
DOFstate(vertvel=
|
|
357
|
+
DOFstate(vertvel=0.0, planvel=1.0, planpwr=1.0, uppwr=1.0, x=1.0, y=0.0, z=100.0)
|
|
358
358
|
|
|
359
359
|
or, for climbing::
|
|
360
360
|
>>> a.dofs.s.put(vertvel=1.0, planvel=0.0)
|
|
361
361
|
>>> a.inc_pos()
|
|
362
362
|
>>> a.dofs.s
|
|
363
|
-
DOFstate(vertvel=
|
|
363
|
+
DOFstate(vertvel=1.0, planvel=0.0, planpwr=1.0, uppwr=1.0, x=1.0, y=0.0, z=101.0)
|
|
364
364
|
"""
|
|
365
365
|
# increment x,y,z
|
|
366
366
|
vec_factor = self.des_traj.s.dist2d()
|
|
@@ -428,7 +428,7 @@ class Drone(FunctionArchitecture):
|
|
|
428
428
|
|
|
429
429
|
Returns
|
|
430
430
|
-------
|
|
431
|
-
|
|
431
|
+
result : dict
|
|
432
432
|
Rate, cost, and expected cost for scenario.
|
|
433
433
|
"""
|
|
434
434
|
if -5 > self.h.flows.dofs.s.x[-1] or 5 < self.h.flows.dofs.s.x[-1]:
|
|
@@ -439,7 +439,7 @@ class Drone(FunctionArchitecture):
|
|
|
439
439
|
lostcost = 50000
|
|
440
440
|
else:
|
|
441
441
|
lostcost = 0
|
|
442
|
-
|
|
442
|
+
|
|
443
443
|
if any(self.h.fxns.hold_payload.m.faults['break']):
|
|
444
444
|
crashcost = 100000
|
|
445
445
|
else:
|
|
@@ -451,7 +451,8 @@ class Drone(FunctionArchitecture):
|
|
|
451
451
|
totcost = repcost + crashcost + lostcost
|
|
452
452
|
rate = scen.rate
|
|
453
453
|
expcost = totcost*rate*1e5
|
|
454
|
-
return {'rate': rate, 'cost': totcost, 'expected_cost': expcost
|
|
454
|
+
return {'rate': rate, 'cost': totcost, 'expected_cost': expcost,
|
|
455
|
+
'lost_cost': lostcost, 'crashcost': crashcost}
|
|
455
456
|
|
|
456
457
|
|
|
457
458
|
def finddist(p1, p2):
|
|
@@ -16,7 +16,7 @@ under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
|
16
16
|
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
17
17
|
specific language governing permissions and limitations under the License.
|
|
18
18
|
"""
|
|
19
|
-
from examples.multirotor.drone_mdl_static import m2to1, DistEE, BaseLine
|
|
19
|
+
from examples.multirotor.drone_mdl_static import m2to1, DistEE, BaseLine, ControlState
|
|
20
20
|
from examples.multirotor.drone_mdl_static import Force, EE, Control, DOFs, DesTraj
|
|
21
21
|
from examples.multirotor.drone_mdl_static import AffectDOFMode, AffectDOFState
|
|
22
22
|
from examples.multirotor.drone_mdl_dynamic import StoreEE, CtlDOF, PlanPath, HoldPayload
|
|
@@ -25,16 +25,15 @@ from examples.multirotor.drone_mdl_dynamic import Drone as DynDrone
|
|
|
25
25
|
from examples.multirotor.drone_mdl_dynamic import AffectDOF as AffectDOFDynamic
|
|
26
26
|
|
|
27
27
|
from fmdtools.define.container.parameter import Parameter
|
|
28
|
-
from fmdtools.define.container.state import State
|
|
29
28
|
from fmdtools.define.block.component import Component
|
|
30
29
|
from fmdtools.define.architecture.component import ComponentArchitecture
|
|
31
|
-
from fmdtools.define.
|
|
30
|
+
from fmdtools.define.flow.multiflow import MultiFlow
|
|
32
31
|
|
|
33
32
|
import fmdtools.sim as fs
|
|
34
33
|
|
|
35
34
|
import numpy as np
|
|
36
35
|
|
|
37
|
-
class OverallAffectDOFState(
|
|
36
|
+
class OverallAffectDOFState(AffectDOFState):
|
|
38
37
|
"""
|
|
39
38
|
Overall states for the multirotor AffectDOF architecture.
|
|
40
39
|
|
|
@@ -48,9 +47,8 @@ class OverallAffectDOFState(State):
|
|
|
48
47
|
Amplification factor (for fault recovery)
|
|
49
48
|
"""
|
|
50
49
|
|
|
51
|
-
lrstab:
|
|
52
|
-
frstab:
|
|
53
|
-
amp_factor: float = 1.0
|
|
50
|
+
lrstab: np.float64 = 0.0
|
|
51
|
+
frstab: np.float64 = 0.0
|
|
54
52
|
|
|
55
53
|
|
|
56
54
|
class LineArchParam(Parameter):
|
|
@@ -117,144 +115,194 @@ class LineArchParam(Parameter):
|
|
|
117
115
|
lr_dict=lr_dict, fr_dict=fr_dict, opposite=opposite)
|
|
118
116
|
super().__init__(*args, strict_immutability=False)
|
|
119
117
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
118
|
+
def get_forward_factors(self):
|
|
119
|
+
if self.archtype == 'quad':
|
|
120
|
+
return {'rf': 0.5, 'lf': 0.5, 'lr': -0.5, 'rr': -0.5}
|
|
121
|
+
elif self.archtype == 'hex':
|
|
122
|
+
return {'rf': 0.5, 'lf': 0.5, 'lr': -0.5, 'rr': -0.5, 'r': -0.75, 'f': 0.75}
|
|
123
|
+
elif self.archtype == 'oct':
|
|
124
|
+
return {'rf': 0.5, 'lf': 0.5, 'lr': -0.5, 'rr': -0.5,
|
|
125
|
+
'rf2': 0.5, 'lf2': 0.5, 'lr2': -0.5, 'rr2': -0.5}
|
|
123
126
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
Correction factors for moving forward (based on which rotors are in front).
|
|
128
|
-
upward: dict
|
|
129
|
-
Correction factors for moving upward (1.0 unless in a recovery mode).
|
|
130
|
-
"""
|
|
131
|
-
forward: dict = dict()
|
|
132
|
-
upward: dict = dict()
|
|
127
|
+
|
|
128
|
+
class MultiControl(MultiFlow):
|
|
129
|
+
container_s = ControlState
|
|
133
130
|
|
|
134
131
|
|
|
135
132
|
class AffectDOFArch(ComponentArchitecture):
|
|
133
|
+
"""
|
|
134
|
+
Drone line architecture (quad/hex/octorotor).
|
|
135
|
+
|
|
136
|
+
Architecture reconfigures rotors when a fault is present, see:
|
|
137
|
+
|
|
138
|
+
Examples
|
|
139
|
+
--------
|
|
140
|
+
>>> adf = AffectDOFArch()
|
|
141
|
+
>>> adf.flows['ctl_in'].s.put(forward=1.0, upward=1.0)
|
|
142
|
+
>>> adf.static_behavior()
|
|
143
|
+
>>> adf.flows['ctl']
|
|
144
|
+
ctl MultiControl
|
|
145
|
+
- s=ControlState(forward=1.0, upward=1.0)
|
|
146
|
+
LOCALS:
|
|
147
|
+
- lf=(s=(forward=0.5, upward=1.0))
|
|
148
|
+
- lf_factors=(s=(forward=0.5, upward=1.0))
|
|
149
|
+
- lr=(s=(forward=-0.5, upward=1.0))
|
|
150
|
+
- lr_factors=(s=(forward=-0.5, upward=1.0))
|
|
151
|
+
- rf=(s=(forward=0.5, upward=1.0))
|
|
152
|
+
- rf_factors=(s=(forward=0.5, upward=1.0))
|
|
153
|
+
- rr=(s=(forward=-0.5, upward=1.0))
|
|
154
|
+
- rr_factors=(s=(forward=-0.5, upward=1.0))
|
|
155
|
+
>>> adf.comps['lf'].m.add_fault("mechbreak") # if one is broken, should adjust ctl
|
|
156
|
+
>>> adf.static_behavior()
|
|
157
|
+
>>> adf.flows['ctl']
|
|
158
|
+
ctl MultiControl
|
|
159
|
+
- s=ControlState(forward=1.0, upward=1.0)
|
|
160
|
+
LOCALS:
|
|
161
|
+
- lf=(s=(forward=0.0, upward=0.0))
|
|
162
|
+
- lf_factors=(s=(forward=0.0, upward=0.0))
|
|
163
|
+
- lr=(s=(forward=-1.0, upward=2.0))
|
|
164
|
+
- lr_factors=(s=(forward=-1.0, upward=2.0))
|
|
165
|
+
- rf=(s=(forward=1.0, upward=2.0))
|
|
166
|
+
- rf_factors=(s=(forward=1.0, upward=2.0))
|
|
167
|
+
- rr=(s=(forward=0.0, upward=0.0))
|
|
168
|
+
- rr_factors=(s=(forward=0.0, upward=0.0))
|
|
169
|
+
"""
|
|
170
|
+
|
|
136
171
|
container_p = LineArchParam
|
|
137
|
-
container_s = LineArchState
|
|
138
172
|
|
|
139
173
|
def init_architecture(self, **kwargs):
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
174
|
+
self.add_flow("ee_in", EE)
|
|
175
|
+
self.add_flow("force", Force)
|
|
176
|
+
self.add_flow("ctl_in", Control)
|
|
177
|
+
self.add_flow("ctl", MultiControl)
|
|
143
178
|
# add state configuration - relative throttle for each line
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
self.
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
179
|
+
forward = self.p.get_forward_factors()
|
|
180
|
+
# add lines
|
|
181
|
+
for cname in self.p.components:
|
|
182
|
+
self.add_comp(cname, Line, 'ee_in', 'ctl', 'force')
|
|
183
|
+
s={'forward': forward[cname], 'upward': 1.0}
|
|
184
|
+
self.flows['ctl'].create_local(cname+"_factors", s=s)
|
|
185
|
+
|
|
186
|
+
def static_behavior(self):
|
|
187
|
+
"""Trigger reconfiguration during static behavior step."""
|
|
188
|
+
if self.p.opposite and self.get_faults(with_base_faults=False):
|
|
189
|
+
self.reconfig_faults()
|
|
190
|
+
self.send_local_control_signals()
|
|
191
|
+
|
|
192
|
+
def send_local_control_signals(self):
|
|
193
|
+
"""Calc and send local control signals to lines."""
|
|
194
|
+
for cname in self.comps:
|
|
195
|
+
ctl = self.flows['ctl'].get_view(cname)
|
|
196
|
+
facts = self.flows['ctl'].get_view(cname+"_factors")
|
|
197
|
+
ctl.s.put(forward=facts.s.forward*self.flows['ctl_in'].s.forward,
|
|
198
|
+
upward=facts.s.upward*self.flows['ctl_in'].s.upward)
|
|
199
|
+
|
|
200
|
+
def reconfig_faults(self):
|
|
201
|
+
"""
|
|
202
|
+
Correct for individual line faultmodes.
|
|
203
|
+
|
|
204
|
+
Turns off the opposite rotor and upping the throttle (amp_factor).
|
|
205
|
+
"""
|
|
206
|
+
empty_comps = [c for c, comp in self.comps.items()
|
|
207
|
+
if comp.m.faults or self.comps[self.p.opposite[c]].m.faults]
|
|
208
|
+
num_remaining = len(self.comps)-len(empty_comps)
|
|
209
|
+
if num_remaining:
|
|
210
|
+
amp_factor = len(self.comps)/num_remaining
|
|
211
|
+
else:
|
|
212
|
+
amp_factor = 0.0
|
|
213
|
+
forward = self.p.get_forward_factors()
|
|
214
|
+
for cname, comp in self.comps.items():
|
|
215
|
+
if cname in empty_comps:
|
|
216
|
+
new_facts = dict(forward=0.0, upward=0.0)
|
|
217
|
+
else:
|
|
218
|
+
new_facts = dict(forward=forward[cname]*amp_factor, upward=amp_factor)
|
|
219
|
+
self.flows['ctl'].get_view(cname+'_factors').s.put(**new_facts)
|
|
153
220
|
|
|
154
221
|
|
|
155
222
|
class AffectDOF(AffectDOFDynamic):
|
|
156
|
-
"""
|
|
223
|
+
"""
|
|
224
|
+
Multirotor locomotion (multi-component extension).
|
|
157
225
|
|
|
158
|
-
|
|
159
|
-
|
|
226
|
+
Each rotor is simulated individually, producing current and air values that are
|
|
227
|
+
aggregated in the power output calculation.
|
|
160
228
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
229
|
+
Examples
|
|
230
|
+
--------
|
|
231
|
+
>>> a = AffectDOF()
|
|
232
|
+
>>> a.dofs.s.put(z=100.0)
|
|
233
|
+
>>> a.ctl_in.s.put(forward=0.0, upward=1.0) # ascent - only going up
|
|
234
|
+
>>> a.ca() # simulate component arch
|
|
235
|
+
>>> a.calc_pwr()
|
|
236
|
+
>>> a.dofs.s
|
|
237
|
+
DOFstate(vertvel=1.0, planvel=1.0, planpwr=-0.0, uppwr=1.0, x=0.0, y=0.0, z=100.0)
|
|
238
|
+
>>> a.ctl_in.s.put(forward=1.0, upward=1.0) # ascent and forward flight - both
|
|
239
|
+
>>> a.ca() # simulate component arch
|
|
240
|
+
>>> a.calc_pwr()
|
|
241
|
+
>>> a.dofs.s
|
|
242
|
+
DOFstate(vertvel=1.0, planvel=1.0, planpwr=1.0, uppwr=1.0, x=0.0, y=0.0, z=100.0)
|
|
243
|
+
"""
|
|
166
244
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
and upping the throttle (amp_factor)"""
|
|
170
|
-
if self.get_faults(with_base_faults=False, only_present=True):
|
|
171
|
-
for cname, comp in self.ca.comps.items():
|
|
172
|
-
if comp.m.faults:
|
|
173
|
-
oname = self.ca.p.opposite[cname]
|
|
174
|
-
if self.ca.s.forward[cname] != 0.0:
|
|
175
|
-
self.ca.s.forward[cname] = 0.0
|
|
176
|
-
self.ca.s.upward[cname] = 0.0
|
|
177
|
-
if self.ca.s.forward[oname] != 0.0:
|
|
178
|
-
self.ca.s.forward[oname] = 0.0
|
|
179
|
-
self.ca.s.upward[oname] = 0.0
|
|
180
|
-
tot_comps = len(self.ca.comps)
|
|
181
|
-
empty_comps = len([c for c in self.ca.s.forward if self.ca.s.forward[c] == 0.0])
|
|
182
|
-
try:
|
|
183
|
-
self.s.amp_factor = tot_comps / (tot_comps - empty_comps)
|
|
184
|
-
except ZeroDivisionError:
|
|
185
|
-
self.s.amp_factor = 1.0
|
|
245
|
+
container_s = OverallAffectDOFState
|
|
246
|
+
arch_ca = AffectDOFArch
|
|
186
247
|
|
|
187
248
|
def calc_pwr(self):
|
|
188
249
|
"""
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
>>> a.dofs.s.put(z=100.0)
|
|
194
|
-
>>> a.ctl_in.s.put(forward=0.0, upward=1.0)
|
|
195
|
-
>>> a.calc_pwr()
|
|
196
|
-
>>> a.dofs.s
|
|
197
|
-
DOFstate(vertvel=np.float64(1.0), planvel=np.float64(1.0), planpwr=np.float64(-0.0), uppwr=np.float64(1.0), x=np.float64(0.0), y=np.float64(0.0), z=np.float64(100.0))
|
|
198
|
-
>>> a.ctl_in.s.put(forward=1.0, upward=1.0)
|
|
199
|
-
>>> a.calc_pwr()
|
|
200
|
-
>>> a.dofs.s
|
|
201
|
-
DOFstate(vertvel=np.float64(1.0), planvel=np.float64(1.0), planpwr=np.float64(1.0), uppwr=np.float64(1.0), x=np.float64(0.0), y=np.float64(0.0), z=np.float64(100.0))
|
|
250
|
+
Calculate overall power and stability based on individual rotor output.
|
|
251
|
+
|
|
252
|
+
Aggregates current and air from contained component architecture lines to
|
|
253
|
+
calculate high-level output to dofs.
|
|
202
254
|
"""
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
for
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
self.ca.s.upward[linname] * self.s.amp_factor,
|
|
209
|
-
self.ca.s.forward[linname] * self.s.amp_factor,
|
|
210
|
-
self.force.s.support)
|
|
211
|
-
air[lin.name] = a
|
|
212
|
-
ee_in[lin.name] = ee
|
|
213
|
-
|
|
214
|
-
if any(value >= 10 for value in ee_in.values()):
|
|
255
|
+
over_state = self.s.mul("e_to", "e_ti", "ct", "mt", "pt")
|
|
256
|
+
airs = {cname: c.s.air for cname, c in self.ca.comps.items()}
|
|
257
|
+
currents = {cname: c.ee.s.rate for cname, c in self.ca.comps.items()}
|
|
258
|
+
|
|
259
|
+
if any(value >= 10 for value in currents.values()):
|
|
215
260
|
self.ee_in.s.rate = 10
|
|
216
|
-
elif any(value != 0.0 for value in
|
|
217
|
-
self.ee_in.s.rate = sum(
|
|
218
|
-
len(ee_in) # should it really be max?
|
|
261
|
+
elif any(value != 0.0 for value in currents.values()):
|
|
262
|
+
self.ee_in.s.rate = sum(currents.values()) / len(currents) # should it really be max?
|
|
219
263
|
else:
|
|
220
264
|
self.ee_in.s.rate = 0.0
|
|
221
265
|
|
|
222
|
-
self.s.lrstab = (sum([
|
|
223
|
-
sum([
|
|
224
|
-
self.s.frstab = (sum([
|
|
225
|
-
sum([
|
|
266
|
+
self.s.lrstab = (sum([airs[comp] for comp in self.ca.p.lr_dict['l']]) -
|
|
267
|
+
sum([airs[comp] for comp in self.ca.p.lr_dict['r']]))/len(airs)
|
|
268
|
+
self.s.frstab = (sum([airs[comp] for comp in self.ca.p.fr_dict['r']]) -
|
|
269
|
+
sum([airs[comp] for comp in self.ca.p.fr_dict['f']]))/len(airs)
|
|
226
270
|
if abs(self.s.lrstab) >= 0.4 or abs(self.s.frstab) >= 0.75:
|
|
227
271
|
self.dofs.s.put(uppwr=0.0, planpwr=0.0)
|
|
228
272
|
else:
|
|
229
|
-
|
|
230
|
-
self.dofs.s.
|
|
231
|
-
|
|
273
|
+
self.dofs.s.uppwr = np.mean(list(airs.values())) * over_state
|
|
274
|
+
self.dofs.s.planpwr = -2*self.s.frstab * over_state
|
|
275
|
+
|
|
232
276
|
|
|
277
|
+
class LineState(AffectDOFState):
|
|
278
|
+
"""AffectDOFState with air output for individual rotor."""
|
|
279
|
+
|
|
280
|
+
air : np.float64 = 0.0
|
|
233
281
|
|
|
234
282
|
class Line(Component, BaseLine):
|
|
235
283
|
"""Individual version of a line (extends BaseLine in static model)."""
|
|
236
284
|
|
|
237
|
-
container_s =
|
|
285
|
+
container_s = LineState
|
|
238
286
|
container_m = AffectDOFMode
|
|
287
|
+
flow_ee_in = EE
|
|
288
|
+
flow_ee = EE
|
|
289
|
+
flow_ctl = MultiControl
|
|
290
|
+
flow_force = Force
|
|
291
|
+
|
|
292
|
+
def init_block(self, **kwargs):
|
|
293
|
+
self.ctl = self.ctl.create_local(self.name)
|
|
239
294
|
|
|
240
|
-
def
|
|
241
|
-
|
|
242
|
-
if force <= 0.0:
|
|
295
|
+
def static_behavior(self):
|
|
296
|
+
if self.force.s.support <= 0.0:
|
|
243
297
|
self.m.add_fault('mechbreak', 'propbreak')
|
|
244
|
-
elif force <= 0.5:
|
|
298
|
+
elif self.force.s.support <= 0.5:
|
|
245
299
|
self.m.add_fault('mechfriction')
|
|
246
|
-
|
|
247
300
|
self.calc_faults()
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
self.s.ct,
|
|
254
|
-
self.s.mt,
|
|
255
|
-
self.s.pt])
|
|
256
|
-
ee_in = m2to1([ee_in, self.s.e_to, pwr])
|
|
257
|
-
return airout, ee_in
|
|
301
|
+
pwr = self.ctl.s.upward + self.ctl.s.forward
|
|
302
|
+
self.ee.s.effort = self.ee_in.s.effort
|
|
303
|
+
self.ee.s.rate = m2to1([self.ee_in.s.effort, self.s.e_to, pwr])
|
|
304
|
+
self.s.air = m2to1([self.ee_in.s.effort, self.s.e_ti, pwr, self.s.ct, self.s.mt,
|
|
305
|
+
self.s.pt])
|
|
258
306
|
|
|
259
307
|
|
|
260
308
|
class DroneParam(Parameter, readonly=True):
|
|
@@ -303,11 +351,11 @@ if __name__ == "__main__":
|
|
|
303
351
|
|
|
304
352
|
# check rf_mechbreack fault propagation in quad architecture:
|
|
305
353
|
hierarchical_model = Drone(p=DroneParam(arch='quad'))
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
354
|
+
result, mdlhist = fs.propagate.one_fault(hierarchical_model,
|
|
355
|
+
'affect_dof.ca.comps.rf',
|
|
356
|
+
'mechbreak',
|
|
357
|
+
time=5)
|
|
358
|
+
mdlhist.plot_line('flows.dofs.s.x', 'flows.dofs.s.y', 'flows.dofs.s.z', 'fxns.store_ee.s.soc')
|
|
311
359
|
|
|
312
360
|
# check rr2_propstuck fault in oct architecture over several times:
|
|
313
361
|
mdl = Drone(p=DroneParam(arch='oct'))
|