pySimBlocks 0.1.0__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.
- examples/advanced/hardware/block_diagram.py +162 -0
- examples/advanced/hardware/camera.py +93 -0
- examples/advanced/hardware/gui_emio.py +231 -0
- examples/advanced/hardware/lmi.py +69 -0
- examples/advanced/hardware/main.py +57 -0
- examples/advanced/hardware/parameters.py +54 -0
- examples/advanced/qp_problem/params.py +10 -0
- examples/advanced/qp_problem/run.py +14 -0
- examples/advanced/sofa/gui/finger/Finger.py +108 -0
- examples/advanced/sofa/gui/finger/FingerController.py +53 -0
- examples/advanced/sofa/gui/finger/parameters_auto.py +22 -0
- examples/advanced/sofa/gui/sofa_plant/run.py +14 -0
- examples/advanced/sofa/python/Finger.py +107 -0
- examples/advanced/sofa/python/FingerController.py +48 -0
- examples/advanced/sofa/python/closedloop.py +64 -0
- examples/advanced/sofa/python/openloop.py +58 -0
- examples/basics/algebraic_function/g.py +7 -0
- examples/basics/algebraic_function/run.py +14 -0
- examples/basics/dc_motor/gui/run.py +14 -0
- examples/basics/dc_motor/multiple_order.py +91 -0
- examples/basics/dc_motor/openloop.py +62 -0
- examples/basics/dc_motor/pi.py +99 -0
- examples/basics/dc_motor/pid.py +118 -0
- examples/basics/dc_motor/pid_block.py +82 -0
- examples/basics/dc_motor/pid_block_vs_manual.py +143 -0
- examples/basics/external/parameters.py +4 -0
- examples/basics/external/run.py +14 -0
- examples/basics/file_sources/temp.py +16 -0
- examples/basics/observer.py +76 -0
- examples/basics/state_feedback/gui/parameters.py +9 -0
- examples/basics/state_feedback/gui/run.py +14 -0
- examples/basics/state_feedback/state_feedback.py +102 -0
- examples/basics/state_space/linear/gui/run.py +14 -0
- examples/basics/state_space/linear/ss_block.py +89 -0
- examples/basics/state_space/linear/ss_manual.py +105 -0
- examples/basics/state_space/non_linear/nlss.py +47 -0
- examples/basics/state_space/non_linear/run.py +14 -0
- examples/basics/state_space/polytope/params.py +39 -0
- examples/basics/state_space/polytope/weights.py +12 -0
- examples/comparison/simulink/convert.py +29 -0
- examples/comparison/simulink/openloop.py +127 -0
- examples/comparison/simulink/pi.py +148 -0
- examples/comparison/simulink/pid.py +154 -0
- examples/comparison/simulink/pid_backward.py +155 -0
- examples/quick_start/filter.py +35 -0
- examples/quick_start/gui/run.py +14 -0
- examples/tutorials/tutorial_1_python/main.py +99 -0
- examples/tutorials/tutorial_3_sofa/finger/Finger.py +120 -0
- examples/tutorials/tutorial_3_sofa/finger/FingerController.py +37 -0
- pySimBlocks/__init__.py +35 -0
- pySimBlocks/blocks/__init__.py +68 -0
- pySimBlocks/blocks/controllers/__init__.py +27 -0
- pySimBlocks/blocks/controllers/pid.py +250 -0
- pySimBlocks/blocks/controllers/state_feedback.py +141 -0
- pySimBlocks/blocks/interfaces/__init__.py +27 -0
- pySimBlocks/blocks/interfaces/external_input.py +107 -0
- pySimBlocks/blocks/interfaces/external_output.py +113 -0
- pySimBlocks/blocks/observers/__init__.py +23 -0
- pySimBlocks/blocks/observers/luenberger.py +175 -0
- pySimBlocks/blocks/operators/__init__.py +47 -0
- pySimBlocks/blocks/operators/algebraic_function.py +258 -0
- pySimBlocks/blocks/operators/dead_zone.py +186 -0
- pySimBlocks/blocks/operators/delay.py +254 -0
- pySimBlocks/blocks/operators/demux.py +123 -0
- pySimBlocks/blocks/operators/discrete_derivator.py +237 -0
- pySimBlocks/blocks/operators/discrete_integrator.py +265 -0
- pySimBlocks/blocks/operators/gain.py +294 -0
- pySimBlocks/blocks/operators/mux.py +131 -0
- pySimBlocks/blocks/operators/product.py +194 -0
- pySimBlocks/blocks/operators/rate_limiter.py +214 -0
- pySimBlocks/blocks/operators/saturation.py +178 -0
- pySimBlocks/blocks/operators/sum.py +188 -0
- pySimBlocks/blocks/operators/zero_order_hold.py +141 -0
- pySimBlocks/blocks/optimizers/__init__.py +23 -0
- pySimBlocks/blocks/optimizers/quadratic_program.py +326 -0
- pySimBlocks/blocks/sources/__init__.py +37 -0
- pySimBlocks/blocks/sources/chirp.py +168 -0
- pySimBlocks/blocks/sources/constant.py +82 -0
- pySimBlocks/blocks/sources/file_source.py +294 -0
- pySimBlocks/blocks/sources/function_source.py +197 -0
- pySimBlocks/blocks/sources/ramp.py +99 -0
- pySimBlocks/blocks/sources/sinusoidal.py +113 -0
- pySimBlocks/blocks/sources/step.py +138 -0
- pySimBlocks/blocks/sources/white_noise.py +107 -0
- pySimBlocks/blocks/systems/__init__.py +27 -0
- pySimBlocks/blocks/systems/linear_state_space.py +180 -0
- pySimBlocks/blocks/systems/non_linear_state_space.py +309 -0
- pySimBlocks/blocks/systems/polytopic_state_space.py +193 -0
- pySimBlocks/blocks/systems/sofa/__init__.py +30 -0
- pySimBlocks/blocks/systems/sofa/sofa_controller.py +472 -0
- pySimBlocks/blocks/systems/sofa/sofa_exchange_i_o.py +120 -0
- pySimBlocks/blocks/systems/sofa/sofa_plant.py +306 -0
- pySimBlocks/cli.py +127 -0
- pySimBlocks/core/__init__.py +32 -0
- pySimBlocks/core/block.py +167 -0
- pySimBlocks/core/block_source.py +94 -0
- pySimBlocks/core/config.py +90 -0
- pySimBlocks/core/fixed_time_manager.py +46 -0
- pySimBlocks/core/model.py +250 -0
- pySimBlocks/core/scheduler.py +30 -0
- pySimBlocks/core/simulator.py +329 -0
- pySimBlocks/core/task.py +73 -0
- pySimBlocks/docs/blocks/controllers/pid.md +78 -0
- pySimBlocks/docs/blocks/controllers/state_feedback.md +60 -0
- pySimBlocks/docs/blocks/interfaces/external_input.md +39 -0
- pySimBlocks/docs/blocks/interfaces/external_output.md +39 -0
- pySimBlocks/docs/blocks/observers/luenberger.md +70 -0
- pySimBlocks/docs/blocks/operators/algebraic_function.md +76 -0
- pySimBlocks/docs/blocks/operators/dead_zone.md +67 -0
- pySimBlocks/docs/blocks/operators/delay.md +76 -0
- pySimBlocks/docs/blocks/operators/demux.md +64 -0
- pySimBlocks/docs/blocks/operators/discrete_derivator.md +61 -0
- pySimBlocks/docs/blocks/operators/discrete_integrator.md +87 -0
- pySimBlocks/docs/blocks/operators/gain.md +97 -0
- pySimBlocks/docs/blocks/operators/mux.md +64 -0
- pySimBlocks/docs/blocks/operators/product.md +51 -0
- pySimBlocks/docs/blocks/operators/rate_limiter.md +70 -0
- pySimBlocks/docs/blocks/operators/saturation.md +62 -0
- pySimBlocks/docs/blocks/operators/sum.md +49 -0
- pySimBlocks/docs/blocks/operators/zero_order_hold.md +61 -0
- pySimBlocks/docs/blocks/optimizers/quadratic_program.md +98 -0
- pySimBlocks/docs/blocks/sources/chirp.md +102 -0
- pySimBlocks/docs/blocks/sources/constant.md +53 -0
- pySimBlocks/docs/blocks/sources/file_source.md +51 -0
- pySimBlocks/docs/blocks/sources/function_source.md +52 -0
- pySimBlocks/docs/blocks/sources/ramp.md +58 -0
- pySimBlocks/docs/blocks/sources/sinusoidal.md +60 -0
- pySimBlocks/docs/blocks/sources/step.md +63 -0
- pySimBlocks/docs/blocks/sources/white_noise.md +58 -0
- pySimBlocks/docs/blocks/systems/linear_state_space.md +66 -0
- pySimBlocks/docs/blocks/systems/non_linear_state_space.md +90 -0
- pySimBlocks/docs/blocks/systems/polytopic_state_space.md +72 -0
- pySimBlocks/docs/blocks/systems/sofa/sofa_exchange_i_o.md +63 -0
- pySimBlocks/docs/blocks/systems/sofa/sofa_plant.md +62 -0
- pySimBlocks/gui/__init__.py +20 -0
- pySimBlocks/gui/addons/__init__.py +19 -0
- pySimBlocks/gui/addons/sofa/__init__.py +19 -0
- pySimBlocks/gui/addons/sofa/sofa_dialog.py +190 -0
- pySimBlocks/gui/addons/sofa/sofa_service.py +209 -0
- pySimBlocks/gui/blocks/block_dialog_session.py +47 -0
- pySimBlocks/gui/blocks/block_meta.py +408 -0
- pySimBlocks/gui/blocks/controllers/pid.py +116 -0
- pySimBlocks/gui/blocks/controllers/state_feedback.py +80 -0
- pySimBlocks/gui/blocks/interfaces/external_input.py +64 -0
- pySimBlocks/gui/blocks/interfaces/external_output.py +63 -0
- pySimBlocks/gui/blocks/observers/luenberger.py +108 -0
- pySimBlocks/gui/blocks/operators/algebraic_function.py +232 -0
- pySimBlocks/gui/blocks/operators/dead_zone.py +79 -0
- pySimBlocks/gui/blocks/operators/delay.py +79 -0
- pySimBlocks/gui/blocks/operators/demux.py +98 -0
- pySimBlocks/gui/blocks/operators/discrete_derivator.py +67 -0
- pySimBlocks/gui/blocks/operators/discrete_integrator.py +80 -0
- pySimBlocks/gui/blocks/operators/gain.py +79 -0
- pySimBlocks/gui/blocks/operators/mux.py +100 -0
- pySimBlocks/gui/blocks/operators/product.py +109 -0
- pySimBlocks/gui/blocks/operators/rate_limiter.py +79 -0
- pySimBlocks/gui/blocks/operators/saturation.py +72 -0
- pySimBlocks/gui/blocks/operators/sum.py +91 -0
- pySimBlocks/gui/blocks/operators/zero_order_hold.py +64 -0
- pySimBlocks/gui/blocks/optimizers/quadratic_program.py +128 -0
- pySimBlocks/gui/blocks/parameter_meta.py +34 -0
- pySimBlocks/gui/blocks/port_meta.py +31 -0
- pySimBlocks/gui/blocks/sources/chirp.py +146 -0
- pySimBlocks/gui/blocks/sources/constant.py +63 -0
- pySimBlocks/gui/blocks/sources/file_source.py +196 -0
- pySimBlocks/gui/blocks/sources/function_source.py +202 -0
- pySimBlocks/gui/blocks/sources/ramp.py +73 -0
- pySimBlocks/gui/blocks/sources/sinusoidal.py +81 -0
- pySimBlocks/gui/blocks/sources/step.py +83 -0
- pySimBlocks/gui/blocks/sources/white_noise.py +70 -0
- pySimBlocks/gui/blocks/systems/linear_state_space.py +102 -0
- pySimBlocks/gui/blocks/systems/non_linear_state_space.py +233 -0
- pySimBlocks/gui/blocks/systems/polytopic_state_space.py +109 -0
- pySimBlocks/gui/blocks/systems/sofa/sofa_exchange_i_o.py +221 -0
- pySimBlocks/gui/blocks/systems/sofa/sofa_plant.py +220 -0
- pySimBlocks/gui/dialogs/__init__.py +19 -0
- pySimBlocks/gui/dialogs/block_dialog.py +125 -0
- pySimBlocks/gui/dialogs/display_yaml_dialog.py +89 -0
- pySimBlocks/gui/dialogs/help_dialog.py +39 -0
- pySimBlocks/gui/dialogs/plot_dialog.py +230 -0
- pySimBlocks/gui/dialogs/settings/plots.py +187 -0
- pySimBlocks/gui/dialogs/settings/project.py +146 -0
- pySimBlocks/gui/dialogs/settings/simulation.py +137 -0
- pySimBlocks/gui/dialogs/settings_dialog.py +88 -0
- pySimBlocks/gui/dialogs/unsaved_dialog.py +92 -0
- pySimBlocks/gui/editor.py +49 -0
- pySimBlocks/gui/graphics/__init__.py +29 -0
- pySimBlocks/gui/graphics/block_item.py +289 -0
- pySimBlocks/gui/graphics/connection_item.py +266 -0
- pySimBlocks/gui/graphics/port_item.py +153 -0
- pySimBlocks/gui/graphics/theme.py +119 -0
- pySimBlocks/gui/main_window.py +162 -0
- pySimBlocks/gui/models/__init__.py +31 -0
- pySimBlocks/gui/models/block_instance.py +114 -0
- pySimBlocks/gui/models/connection_instance.py +43 -0
- pySimBlocks/gui/models/port_instance.py +52 -0
- pySimBlocks/gui/models/project_simulation_params.py +50 -0
- pySimBlocks/gui/models/project_state.py +114 -0
- pySimBlocks/gui/project_controller.py +302 -0
- pySimBlocks/gui/services/__init__.py +20 -0
- pySimBlocks/gui/services/project_loader.py +381 -0
- pySimBlocks/gui/services/project_saver.py +68 -0
- pySimBlocks/gui/services/simulation_runner.py +66 -0
- pySimBlocks/gui/services/yaml_tools.py +261 -0
- pySimBlocks/gui/widgets/__init__.py +19 -0
- pySimBlocks/gui/widgets/block_list.py +131 -0
- pySimBlocks/gui/widgets/diagram_view.py +312 -0
- pySimBlocks/gui/widgets/toolbar_view.py +161 -0
- pySimBlocks/project/__init__.py +35 -0
- pySimBlocks/project/build_model.py +92 -0
- pySimBlocks/project/generate_run_script.py +109 -0
- pySimBlocks/project/generate_sofa_controller.py +215 -0
- pySimBlocks/project/load_project_config.py +197 -0
- pySimBlocks/project/load_simulation_config.py +166 -0
- pySimBlocks/project/load_simulator.py +45 -0
- pySimBlocks/project/plot_from_config.py +174 -0
- pySimBlocks/project/pySimBlocks_blocks_index.yaml +106 -0
- pySimBlocks/real_time/__init__.py +23 -0
- pySimBlocks/real_time/real_time_runner.py +148 -0
- pySimBlocks/tools/__init__.py +19 -0
- pySimBlocks/tools/blocks_registry.py +141 -0
- pySimBlocks/tools/generate_blocks_index.py +124 -0
- pysimblocks-0.1.0.dist-info/METADATA +180 -0
- pysimblocks-0.1.0.dist-info/RECORD +262 -0
- pysimblocks-0.1.0.dist-info/WHEEL +5 -0
- pysimblocks-0.1.0.dist-info/entry_points.txt +2 -0
- pysimblocks-0.1.0.dist-info/licenses/LICENSE.md +165 -0
- pysimblocks-0.1.0.dist-info/top_level.txt +3 -0
- tests/blocks/controllers/test_pid.py +143 -0
- tests/blocks/controllers/test_state_feedback.py +193 -0
- tests/blocks/observers/test_luenberger.py +225 -0
- tests/blocks/operators/test_algebraic_function.py +230 -0
- tests/blocks/operators/test_dead_zone.py +106 -0
- tests/blocks/operators/test_delay.py +167 -0
- tests/blocks/operators/test_demux.py +111 -0
- tests/blocks/operators/test_discrete_derivator.py +160 -0
- tests/blocks/operators/test_discrete_integrator.py +149 -0
- tests/blocks/operators/test_gain.py +182 -0
- tests/blocks/operators/test_mux.py +101 -0
- tests/blocks/operators/test_product.py +171 -0
- tests/blocks/operators/test_rate_limiter.py +166 -0
- tests/blocks/operators/test_saturation.py +157 -0
- tests/blocks/operators/test_sum.py +183 -0
- tests/blocks/operators/test_zero_order_hold.py +106 -0
- tests/blocks/sources/test_chirp.py +157 -0
- tests/blocks/sources/test_constant.py +55 -0
- tests/blocks/sources/test_file_source.py +185 -0
- tests/blocks/sources/test_function_source.py +135 -0
- tests/blocks/sources/test_ramp.py +110 -0
- tests/blocks/sources/test_sinusoidal.py +113 -0
- tests/blocks/sources/test_step.py +76 -0
- tests/blocks/sources/test_white_noise.py +108 -0
- tests/blocks/systems/test_linear_state_space.py +135 -0
- tests/blocks/systems/test_polytopic_state_space.py +133 -0
- tests/core/test_core_errors_and_determinism.py +82 -0
- tests/core/test_core_multirate_tasks.py +88 -0
- tests/core/test_core_two_phase_and_propagation.py +132 -0
- tests/gui/main_window_open_test.py +162 -0
- tests/gui/test_block_meta_param_cache.py +16 -0
- tests/gui/test_file_source_meta.py +17 -0
- tests/gui/test_function_source_meta.py +28 -0
- tests/regression/test_simulink_dc_motor.py +217 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import matplotlib.pyplot as plt
|
|
3
|
+
from pySimBlocks import Model, Simulator, SimulationConfig
|
|
4
|
+
from pySimBlocks.blocks.sources import Step
|
|
5
|
+
from pySimBlocks.blocks.systems import LinearStateSpace
|
|
6
|
+
from pySimBlocks.blocks.operators import Sum, Gain, DiscreteIntegrator, DiscreteDerivator
|
|
7
|
+
from pySimBlocks.blocks.controllers import Pid
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def manual_sim(A, B, C, Kp, Ki, Kd, T, dt):
|
|
11
|
+
ref = Step("ref", start_time=1., value_before=0., value_after=1.)
|
|
12
|
+
motor = LinearStateSpace("motor", A, B, C)
|
|
13
|
+
error = Sum("error", "+-")
|
|
14
|
+
kp = Gain("Kp", Kp, multiplication="Matrix (K @ u)")
|
|
15
|
+
ki = Gain("Ki", Ki, multiplication="Matrix (K @ u)")
|
|
16
|
+
integrator = DiscreteIntegrator("integrator")
|
|
17
|
+
kd = Gain("Kd", Kd, multiplication="Matrix (K @ u)")
|
|
18
|
+
derivator = DiscreteDerivator("derivator")
|
|
19
|
+
sum = Sum("sum", "+++")
|
|
20
|
+
|
|
21
|
+
blocks = [ref, error, kp, integrator, ki, sum, motor, kd, derivator]
|
|
22
|
+
|
|
23
|
+
model = Model("DC Motor Control")
|
|
24
|
+
for block in blocks:
|
|
25
|
+
model.add_block(block)
|
|
26
|
+
model.connect("ref", "out", "error", "in1")
|
|
27
|
+
model.connect("motor", "y", "error", "in2")
|
|
28
|
+
model.connect("error", "out", "Kp", "in")
|
|
29
|
+
model.connect("error", "out", "Ki", "in")
|
|
30
|
+
model.connect("error", "out", "Kd", "in")
|
|
31
|
+
model.connect("Kp", "out", "sum", "in1")
|
|
32
|
+
model.connect("Ki", "out", "integrator", "in")
|
|
33
|
+
model.connect("integrator", "out", "sum", "in3")
|
|
34
|
+
model.connect("Kd", "out", "derivator", "in")
|
|
35
|
+
model.connect("derivator", "out", "sum", "in2")
|
|
36
|
+
model.connect("sum", "out", "motor", "u")
|
|
37
|
+
|
|
38
|
+
# Simulator
|
|
39
|
+
sim_cfg = SimulationConfig(dt, T, logging=[
|
|
40
|
+
"ref.outputs.out",
|
|
41
|
+
"motor.outputs.y",
|
|
42
|
+
"sum.outputs.out",
|
|
43
|
+
"derivator.outputs.out",
|
|
44
|
+
"integrator.outputs.out",
|
|
45
|
+
"Kp.outputs.out"])
|
|
46
|
+
sim = Simulator(model, sim_cfg, verbose=False)
|
|
47
|
+
logs = sim.run()
|
|
48
|
+
|
|
49
|
+
time = sim.get_data("time")
|
|
50
|
+
u = sim.get_data("sum.outputs.out").squeeze()
|
|
51
|
+
w = sim.get_data("motor.outputs.y").squeeze()
|
|
52
|
+
r = sim.get_data("ref.outputs.out").squeeze()
|
|
53
|
+
|
|
54
|
+
return time, u, w, r
|
|
55
|
+
|
|
56
|
+
def block_sim(A, B, C, Kp, Ki, Kd, T, dt):
|
|
57
|
+
ref = Step("ref", start_time=1., value_before=0., value_after=1.)
|
|
58
|
+
motor = LinearStateSpace("motor", A, B, C)
|
|
59
|
+
error = Sum("error", "+-")
|
|
60
|
+
pid = Pid("pid", Kp=Kp, Ki=Ki, Kd=Kd)
|
|
61
|
+
|
|
62
|
+
blocks = [ref, error, pid, motor]
|
|
63
|
+
|
|
64
|
+
# Model
|
|
65
|
+
model = Model("DC Motor Control")
|
|
66
|
+
for block in blocks:
|
|
67
|
+
model.add_block(block)
|
|
68
|
+
model.connect("ref", "out", "error", "in1")
|
|
69
|
+
model.connect("motor", "y", "error", "in2")
|
|
70
|
+
model.connect("error", "out", "pid", "e")
|
|
71
|
+
model.connect("pid", "u", "motor", "u")
|
|
72
|
+
|
|
73
|
+
# Simulator
|
|
74
|
+
sim_cfg = SimulationConfig(dt, T, logging=[
|
|
75
|
+
"ref.outputs.out",
|
|
76
|
+
"motor.outputs.y",
|
|
77
|
+
"pid.outputs.u"
|
|
78
|
+
])
|
|
79
|
+
sim = Simulator(model, sim_cfg, verbose=False)
|
|
80
|
+
logs = sim.run()
|
|
81
|
+
|
|
82
|
+
time = sim.get_data("time")
|
|
83
|
+
u = sim.get_data("pid.outputs.u").squeeze()
|
|
84
|
+
w = sim.get_data("motor.outputs.y").squeeze()
|
|
85
|
+
r = sim.get_data("ref.outputs.out").squeeze()
|
|
86
|
+
|
|
87
|
+
return time, u, w, r
|
|
88
|
+
|
|
89
|
+
def main():
|
|
90
|
+
# DC Motor parameters
|
|
91
|
+
R = 0.1
|
|
92
|
+
L = 0.5
|
|
93
|
+
J = 0.01
|
|
94
|
+
K = 0.1
|
|
95
|
+
a = 0.001
|
|
96
|
+
|
|
97
|
+
Kp = np.array([[.001]])
|
|
98
|
+
Ki = np.array([[.02]])
|
|
99
|
+
Kd = np.array([[.01]])
|
|
100
|
+
|
|
101
|
+
# Simulation parameters
|
|
102
|
+
dt = 0.1
|
|
103
|
+
T = 30.
|
|
104
|
+
|
|
105
|
+
# State-space matrices
|
|
106
|
+
A = np.array([[1-dt*R/L, -dt*K/L], [dt*K/J, 1-dt*a/J]])
|
|
107
|
+
B = np.array([[dt/L], [0]])
|
|
108
|
+
C = np.array([[0, 1]])
|
|
109
|
+
|
|
110
|
+
time, upid, wpid, rpid = manual_sim(A, B, C, Kp, Ki, Kd, T, dt)
|
|
111
|
+
time_block, upid_block, wpid_block, rpid_block = block_sim(A, B, C, Kp, Ki, Kd, T, dt)
|
|
112
|
+
|
|
113
|
+
# data from simulink
|
|
114
|
+
data = np.load("simulation.npz")
|
|
115
|
+
t_sim = data["time"]
|
|
116
|
+
upid_sim, wpid_sim, rpid_sim = data["upid"], data["wpid"], data['r']
|
|
117
|
+
|
|
118
|
+
print("Error simulink - manual: ", np.linalg.norm(wpid_sim - wpid))
|
|
119
|
+
print("Error simulink - block: ", np.linalg.norm(wpid_sim - wpid_block))
|
|
120
|
+
|
|
121
|
+
# Plot
|
|
122
|
+
plt.figure()
|
|
123
|
+
plt.step(t_sim, wpid_sim, '-r', label="w (simulink)", where="post")
|
|
124
|
+
plt.step(time, wpid, '--b', label="w (manual)", where="post")
|
|
125
|
+
plt.step(time_block, wpid_block, ':g', label="w (block)", where="post")
|
|
126
|
+
plt.xlabel("Time (s)")
|
|
127
|
+
plt.ylabel("Speed (rad/s)")
|
|
128
|
+
plt.legend()
|
|
129
|
+
plt.grid()
|
|
130
|
+
plt.title("PI DC Motor Response")
|
|
131
|
+
|
|
132
|
+
plt.figure()
|
|
133
|
+
plt.step(t_sim, upid_sim, '-r', label="u (simulink)", where="post")
|
|
134
|
+
plt.step(time, upid, '--b', label="u (Manual)", where="post")
|
|
135
|
+
plt.step(time_block, upid_block, ':g', label="u (block)", where="post")
|
|
136
|
+
plt.xlabel("Time (s)")
|
|
137
|
+
plt.ylabel("motor (V)")
|
|
138
|
+
plt.legend()
|
|
139
|
+
plt.grid()
|
|
140
|
+
plt.title("Command")
|
|
141
|
+
|
|
142
|
+
plt.figure()
|
|
143
|
+
plt.step(t_sim, rpid_sim, '-r', label="r (simulink)", where="post")
|
|
144
|
+
plt.step(time, rpid, '--b', label="r (Manual)", where="post")
|
|
145
|
+
plt.step(time_block, rpid_block, ':g', label="r (block)", where="post")
|
|
146
|
+
plt.xlabel("Time (s)")
|
|
147
|
+
plt.legend()
|
|
148
|
+
plt.grid()
|
|
149
|
+
plt.title("Reference")
|
|
150
|
+
|
|
151
|
+
plt.show()
|
|
152
|
+
|
|
153
|
+
if __name__ == "__main__":
|
|
154
|
+
main()
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import matplotlib.pyplot as plt
|
|
3
|
+
from pySimBlocks import Model, Simulator, SimulationConfig
|
|
4
|
+
from pySimBlocks.blocks.sources import Step
|
|
5
|
+
from pySimBlocks.blocks.systems import LinearStateSpace
|
|
6
|
+
from pySimBlocks.blocks.operators import Sum, Gain, DiscreteIntegrator, DiscreteDerivator
|
|
7
|
+
from pySimBlocks.blocks.controllers import Pid
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def manual_sim(A, B, C, Kp, Ki, Kd, T, dt):
|
|
11
|
+
ref = Step("ref", start_time=1., value_before=0., value_after=1.)
|
|
12
|
+
motor = LinearStateSpace("motor", A, B, C)
|
|
13
|
+
error = Sum("error", "+-")
|
|
14
|
+
kp = Gain("Kp", Kp, multiplication="Matrix (K @ u)")
|
|
15
|
+
ki = Gain("Ki", Ki, multiplication="Matrix (K @ u)")
|
|
16
|
+
integrator = DiscreteIntegrator("integrator", method="euler backward")
|
|
17
|
+
kd = Gain("Kd", Kd, multiplication="Matrix (K @ u)")
|
|
18
|
+
derivator = DiscreteDerivator("derivator")
|
|
19
|
+
sum = Sum("sum", "+++")
|
|
20
|
+
|
|
21
|
+
blocks = [ref, error, kp, integrator, ki, sum, motor, kd, derivator]
|
|
22
|
+
|
|
23
|
+
model = Model("DC Motor Control")
|
|
24
|
+
for block in blocks:
|
|
25
|
+
model.add_block(block)
|
|
26
|
+
model.connect("ref", "out", "error", "in1")
|
|
27
|
+
model.connect("motor", "y", "error", "in2")
|
|
28
|
+
model.connect("error", "out", "Kp", "in")
|
|
29
|
+
model.connect("error", "out", "Ki", "in")
|
|
30
|
+
model.connect("error", "out", "Kd", "in")
|
|
31
|
+
model.connect("Kp", "out", "sum", "in1")
|
|
32
|
+
model.connect("Ki", "out", "integrator", "in")
|
|
33
|
+
model.connect("integrator", "out", "sum", "in3")
|
|
34
|
+
model.connect("Kd", "out", "derivator", "in")
|
|
35
|
+
model.connect("derivator", "out", "sum", "in2")
|
|
36
|
+
model.connect("sum", "out", "motor", "u")
|
|
37
|
+
|
|
38
|
+
# Simulator
|
|
39
|
+
sim_cfg = SimulationConfig(dt, T, logging=[
|
|
40
|
+
"ref.outputs.out",
|
|
41
|
+
"motor.outputs.y",
|
|
42
|
+
"sum.outputs.out",
|
|
43
|
+
"derivator.outputs.out",
|
|
44
|
+
"integrator.outputs.out",
|
|
45
|
+
"Kp.outputs.out"
|
|
46
|
+
])
|
|
47
|
+
sim = Simulator(model, sim_cfg, verbose=False)
|
|
48
|
+
logs = sim.run()
|
|
49
|
+
|
|
50
|
+
time = sim.get_data("time")
|
|
51
|
+
u = sim.get_data("sum.outputs.out").squeeze()
|
|
52
|
+
w = sim.get_data("motor.outputs.y").squeeze()
|
|
53
|
+
r = sim.get_data("ref.outputs.out").squeeze()
|
|
54
|
+
|
|
55
|
+
return time, u, w, r
|
|
56
|
+
|
|
57
|
+
def block_sim(A, B, C, Kp, Ki, Kd, T, dt):
|
|
58
|
+
ref = Step("ref", start_time=1., value_before=0., value_after=1.)
|
|
59
|
+
motor = LinearStateSpace("motor", A, B, C)
|
|
60
|
+
error = Sum("error", "+-")
|
|
61
|
+
pid = Pid("pid", Kp=Kp, Ki=Ki, Kd=Kd, integration_method="euler backward")
|
|
62
|
+
|
|
63
|
+
blocks = [ref, error, pid, motor]
|
|
64
|
+
|
|
65
|
+
# Model
|
|
66
|
+
model = Model("DC Motor Control")
|
|
67
|
+
for block in blocks:
|
|
68
|
+
model.add_block(block)
|
|
69
|
+
model.connect("ref", "out", "error", "in1")
|
|
70
|
+
model.connect("motor", "y", "error", "in2")
|
|
71
|
+
model.connect("error", "out", "pid", "e")
|
|
72
|
+
model.connect("pid", "u", "motor", "u")
|
|
73
|
+
|
|
74
|
+
# Simulator
|
|
75
|
+
sim_cfg = SimulationConfig(dt, T, logging=[
|
|
76
|
+
"ref.outputs.out",
|
|
77
|
+
"motor.outputs.y",
|
|
78
|
+
"pid.outputs.u"
|
|
79
|
+
])
|
|
80
|
+
sim = Simulator(model, sim_cfg, verbose=False)
|
|
81
|
+
logs = sim.run()
|
|
82
|
+
|
|
83
|
+
time = sim.get_data("time")
|
|
84
|
+
u = sim.get_data("pid.outputs.u").squeeze()
|
|
85
|
+
w = sim.get_data("motor.outputs.y").squeeze()
|
|
86
|
+
r = sim.get_data("ref.outputs.out").squeeze()
|
|
87
|
+
|
|
88
|
+
return time, u, w, r
|
|
89
|
+
|
|
90
|
+
def main():
|
|
91
|
+
# DC Motor parameters
|
|
92
|
+
R = 0.1
|
|
93
|
+
L = 0.5
|
|
94
|
+
J = 0.01
|
|
95
|
+
K = 0.1
|
|
96
|
+
a = 0.001
|
|
97
|
+
|
|
98
|
+
Kp = np.array([[.001]])
|
|
99
|
+
Ki = np.array([[.02]])
|
|
100
|
+
Kd = np.array([[.01]])
|
|
101
|
+
|
|
102
|
+
# Simulation parameters
|
|
103
|
+
dt = 0.1
|
|
104
|
+
T = 30.
|
|
105
|
+
|
|
106
|
+
# State-space matrices
|
|
107
|
+
A = np.array([[1-dt*R/L, -dt*K/L], [dt*K/J, 1-dt*a/J]])
|
|
108
|
+
B = np.array([[dt/L], [0]])
|
|
109
|
+
C = np.array([[0, 1]])
|
|
110
|
+
|
|
111
|
+
time, upid, wpid, rpid = manual_sim(A, B, C, Kp, Ki, Kd, T, dt)
|
|
112
|
+
time_block, upid_block, wpid_block, rpid_block = block_sim(A, B, C, Kp, Ki, Kd, T, dt)
|
|
113
|
+
|
|
114
|
+
# data from simulink
|
|
115
|
+
data = np.load("simulation.npz")
|
|
116
|
+
t_sim = data["time"]
|
|
117
|
+
upid_sim, wpid_sim, rpid_sim = data["upid_backward"], data["wpid_backward"], data['r']
|
|
118
|
+
|
|
119
|
+
print("Error simulink - manual: ", np.linalg.norm(wpid_sim - wpid))
|
|
120
|
+
print("Error simulink - block: ", np.linalg.norm(wpid_sim - wpid_block))
|
|
121
|
+
|
|
122
|
+
# Plot
|
|
123
|
+
plt.figure()
|
|
124
|
+
plt.step(t_sim, wpid_sim, '-r', label="w (simulink)", where="post")
|
|
125
|
+
plt.step(time, wpid, '--b', label="w (manual)", where="post")
|
|
126
|
+
plt.step(time_block, wpid_block, ':g', label="w (block)", where="post")
|
|
127
|
+
plt.xlabel("Time (s)")
|
|
128
|
+
plt.ylabel("Speed (rad/s)")
|
|
129
|
+
plt.legend()
|
|
130
|
+
plt.grid()
|
|
131
|
+
plt.title("PI DC Motor Response")
|
|
132
|
+
|
|
133
|
+
plt.figure()
|
|
134
|
+
plt.step(t_sim, upid_sim, '-r', label="u (simulink)", where="post")
|
|
135
|
+
plt.step(time, upid, '--b', label="u (Manual)", where="post")
|
|
136
|
+
plt.step(time_block, upid_block, ':g', label="u (block)", where="post")
|
|
137
|
+
plt.xlabel("Time (s)")
|
|
138
|
+
plt.ylabel("motor (V)")
|
|
139
|
+
plt.legend()
|
|
140
|
+
plt.grid()
|
|
141
|
+
plt.title("Command")
|
|
142
|
+
|
|
143
|
+
plt.figure()
|
|
144
|
+
plt.step(t_sim, rpid_sim, '-r', label="r (simulink)", where="post")
|
|
145
|
+
plt.step(time, rpid, '--b', label="r (Manual)", where="post")
|
|
146
|
+
plt.step(time_block, rpid_block, ':g', label="r (block)", where="post")
|
|
147
|
+
plt.xlabel("Time (s)")
|
|
148
|
+
plt.legend()
|
|
149
|
+
plt.grid()
|
|
150
|
+
plt.title("Reference")
|
|
151
|
+
|
|
152
|
+
plt.show()
|
|
153
|
+
|
|
154
|
+
if __name__ == "__main__":
|
|
155
|
+
main()
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from pySimBlocks import Model, Simulator, SimulationConfig, PlotConfig
|
|
2
|
+
from pySimBlocks.blocks.operators import Gain, Sum, Delay
|
|
3
|
+
from pySimBlocks.blocks.sources import WhiteNoise
|
|
4
|
+
from pySimBlocks.project.plot_from_config import plot_from_config
|
|
5
|
+
|
|
6
|
+
# 1. Create the blocks
|
|
7
|
+
noise = WhiteNoise(name="noise", std=1.0)
|
|
8
|
+
delay = Delay(name="delay")
|
|
9
|
+
filtered = Sum("filtered", signs="++")
|
|
10
|
+
alpha_gain = Gain(name="alpha", gain=0.1)
|
|
11
|
+
complement = Gain(name="complement", gain=0.9)
|
|
12
|
+
|
|
13
|
+
# 2. Build the model
|
|
14
|
+
model = Model("Example")
|
|
15
|
+
for block in [noise, delay, filtered, alpha_gain, complement]:
|
|
16
|
+
model.add_block(block)
|
|
17
|
+
|
|
18
|
+
model.connect("noise", "out", "alpha", "in")
|
|
19
|
+
model.connect("delay", "out", "complement", "in")
|
|
20
|
+
model.connect("alpha", "out", "filtered", "in1")
|
|
21
|
+
model.connect("complement", "out", "filtered", "in2")
|
|
22
|
+
model.connect("filtered", "out", "delay", "in")
|
|
23
|
+
|
|
24
|
+
# 3. Simulate the model
|
|
25
|
+
sim_cfg = SimulationConfig(dt=0.05, T=30.)
|
|
26
|
+
sim = Simulator(model, sim_cfg)
|
|
27
|
+
logs = sim.run(logging=["noise.outputs.out", "filtered.outputs.out"])
|
|
28
|
+
|
|
29
|
+
# 4. Plot the results
|
|
30
|
+
plot_cfg = PlotConfig([
|
|
31
|
+
{"title": "Noisy signal vs Filtered",
|
|
32
|
+
"signals": ["noise.outputs.out", "filtered.outputs.out"],},
|
|
33
|
+
])
|
|
34
|
+
plot_from_config(logs, plot_cfg)
|
|
35
|
+
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from pySimBlocks.project import load_simulator_from_project
|
|
3
|
+
from pySimBlocks.project.plot_from_config import plot_from_config
|
|
4
|
+
|
|
5
|
+
try:
|
|
6
|
+
BASE_DIR = Path(__file__).parent.resolve()
|
|
7
|
+
except Exception:
|
|
8
|
+
BASE_DIR = Path("")
|
|
9
|
+
|
|
10
|
+
sim, plot_cfg = load_simulator_from_project(BASE_DIR / 'project.yaml')
|
|
11
|
+
|
|
12
|
+
logs = sim.run()
|
|
13
|
+
if True and plot_cfg is not None:
|
|
14
|
+
plot_from_config(logs, plot_cfg)
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import matplotlib.pyplot as plt
|
|
2
|
+
import numpy as np
|
|
3
|
+
|
|
4
|
+
from pySimBlocks import Model, SimulationConfig, Simulator
|
|
5
|
+
from pySimBlocks.blocks.controllers import Pid
|
|
6
|
+
from pySimBlocks.blocks.operators import Sum
|
|
7
|
+
from pySimBlocks.blocks.sources import Step
|
|
8
|
+
from pySimBlocks.blocks.systems import LinearStateSpace
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def main():
|
|
12
|
+
"""This example demonstrates how to use the pySimBlocks library to create a
|
|
13
|
+
simple closed-loop control system with a step reference input, a PI controller,
|
|
14
|
+
and a linear state-space system.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
A = np.array([[0.9]])
|
|
18
|
+
B = np.array([[0.5]])
|
|
19
|
+
C = np.array([[1.0]])
|
|
20
|
+
x0 = np.zeros((A.shape[0], 1))
|
|
21
|
+
|
|
22
|
+
Kp = np.array([[0.5]])
|
|
23
|
+
Ki = np.array([[2.]])
|
|
24
|
+
|
|
25
|
+
# -------------------------------------------------------
|
|
26
|
+
# 1. Create the blocks
|
|
27
|
+
# -------------------------------------------------------
|
|
28
|
+
step = Step(
|
|
29
|
+
name="ref",
|
|
30
|
+
value_before=np.array([[0.0]]),
|
|
31
|
+
value_after=np.array([[1.0]]),
|
|
32
|
+
start_time=0.5
|
|
33
|
+
)
|
|
34
|
+
sum = Sum(name="error", signs="+-")
|
|
35
|
+
pid = Pid(name="PID", controller="PI", Kp=Kp, Ki=Ki)
|
|
36
|
+
system = LinearStateSpace(name="system", A=A, B=B, C=C, x0=x0)
|
|
37
|
+
|
|
38
|
+
# -------------------------------------------------------
|
|
39
|
+
# 2. Build the model
|
|
40
|
+
# -------------------------------------------------------
|
|
41
|
+
model = Model("test")
|
|
42
|
+
for block in [step, sum, pid, system]:
|
|
43
|
+
model.add_block(block)
|
|
44
|
+
|
|
45
|
+
model.connect("ref", "out", "error", "in1")
|
|
46
|
+
model.connect("system", "y", "error", "in2")
|
|
47
|
+
model.connect("error", "out", "PID", "e")
|
|
48
|
+
model.connect("PID", "u", "system", "u")
|
|
49
|
+
|
|
50
|
+
# -------------------------------------------------------
|
|
51
|
+
# 3. Create the simulator
|
|
52
|
+
# -------------------------------------------------------
|
|
53
|
+
dt = 0.01 # seconds
|
|
54
|
+
T = 5. # seconds
|
|
55
|
+
sim_cfg = SimulationConfig(dt, T)
|
|
56
|
+
sim = Simulator(model, sim_cfg, verbose=False)
|
|
57
|
+
|
|
58
|
+
# -------------------------------------------------------
|
|
59
|
+
# 4. Run the simulation with logging
|
|
60
|
+
# -------------------------------------------------------
|
|
61
|
+
logs = sim.run(logging=[
|
|
62
|
+
"ref.outputs.out",
|
|
63
|
+
"PID.outputs.u",
|
|
64
|
+
"system.outputs.y"
|
|
65
|
+
]
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
# -------------------------------------------------------
|
|
69
|
+
# 5. Extract logged data
|
|
70
|
+
# -------------------------------------------------------
|
|
71
|
+
t = sim.get_data("time")
|
|
72
|
+
u = sim.get_data("PID.outputs.u").squeeze()
|
|
73
|
+
r = sim.get_data("ref.outputs.out").squeeze()
|
|
74
|
+
y = sim.get_data("system.outputs.y").squeeze()
|
|
75
|
+
|
|
76
|
+
# -------------------------------------------------------
|
|
77
|
+
# 6. Plot the result
|
|
78
|
+
# -------------------------------------------------------
|
|
79
|
+
fig, axs = plt.subplots(1, 2, sharex=True)
|
|
80
|
+
axs[0].step(t, r, "--r", label="ref", where="post")
|
|
81
|
+
axs[0].step(t, y, "--b", label="output", where="post")
|
|
82
|
+
axs[0].set_xlabel("Time [s]")
|
|
83
|
+
axs[0].set_ylabel("Amplitude")
|
|
84
|
+
axs[0].set_title("Closed-loop response")
|
|
85
|
+
axs[0].grid(True)
|
|
86
|
+
axs[0].legend()
|
|
87
|
+
|
|
88
|
+
axs[1].step(t, u, "--b", label="u[k] (pid output)", where="post")
|
|
89
|
+
axs[1].set_xlabel("Time [s]")
|
|
90
|
+
axs[1].set_ylabel("Amplitude")
|
|
91
|
+
axs[1].set_title("PID controller output")
|
|
92
|
+
axs[1].grid(True)
|
|
93
|
+
axs[1].legend()
|
|
94
|
+
|
|
95
|
+
plt.show()
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
if __name__ == "__main__":
|
|
99
|
+
main()
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
import Sofa
|
|
4
|
+
|
|
5
|
+
dir_path = Path(__file__).parent.absolute()
|
|
6
|
+
mesh_path = dir_path / "mesh"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def createScene(rootNode):
|
|
10
|
+
from FingerController import FingerController
|
|
11
|
+
|
|
12
|
+
# --------------------------------------------------------------------------
|
|
13
|
+
# Scene setup
|
|
14
|
+
# --------------------------------------------------------------------------
|
|
15
|
+
rootNode.addObject("RequiredPlugin", name='SoftRobots')
|
|
16
|
+
rootNode.addObject("RequiredPlugin", name='SofaPython3')
|
|
17
|
+
rootNode.addObject('RequiredPlugin', pluginName=[
|
|
18
|
+
"Sofa.Component.AnimationLoop", # Needed to use components FreeMotionAnimationLoop
|
|
19
|
+
"Sofa.Component.Constraint.Lagrangian.Correction", # Needed to use components GenericConstraintCorrection
|
|
20
|
+
"Sofa.Component.LinearSolver.Iterative",
|
|
21
|
+
"Sofa.Component.Constraint.Lagrangian.Solver", # Needed to use components GenericConstraintSolver
|
|
22
|
+
"Sofa.Component.Engine.Select", # Needed to use components BoxROI
|
|
23
|
+
"Sofa.Component.IO.Mesh", # Needed to use components MeshSTLLoader, MeshVTKLoader
|
|
24
|
+
"Sofa.Component.LinearSolver.Direct", # Needed to use components SparseLDLSolver
|
|
25
|
+
"Sofa.Component.Mass", # Needed to use components UniformMass
|
|
26
|
+
"Sofa.Component.ODESolver.Backward", # Needed to use components EulerImplicitSolver
|
|
27
|
+
"Sofa.Component.Setting", # Needed to use components BackgroundSetting
|
|
28
|
+
"Sofa.Component.SolidMechanics.FEM.Elastic", # Needed to use components TetrahedronFEMForceField
|
|
29
|
+
"Sofa.Component.SolidMechanics.Spring", # Needed to use components RestShapeSpringsForceField
|
|
30
|
+
"Sofa.Component.Topology.Container.Constant", # Needed to use components MeshTopology
|
|
31
|
+
"Sofa.Component.Visual", # Needed to use components VisualStyle
|
|
32
|
+
"Sofa.GL.Component.Rendering3D", # Needed to use components OglModel, OglSceneFrame
|
|
33
|
+
"Sofa.Component.StateContainer",
|
|
34
|
+
'Sofa.Component.Mapping.Linear'
|
|
35
|
+
])
|
|
36
|
+
rootNode.addObject('VisualStyle',
|
|
37
|
+
displayFlags='showVisualModels hideBehaviorModels showCollisionModels hideBoundingCollisionModels hideForceFields showInteractionForceFields hideWireframe')
|
|
38
|
+
|
|
39
|
+
rootNode.addObject('FreeMotionAnimationLoop')
|
|
40
|
+
rootNode.addObject('DefaultVisualManagerLoop')
|
|
41
|
+
|
|
42
|
+
try: # Compatible with SOFA <= 25.06
|
|
43
|
+
rootNode.addObject('GenericConstraintSolver', tolerance=1e-5, maxIterations=100)
|
|
44
|
+
except Exception as e: # Fallback for SOFA >= 25.12
|
|
45
|
+
print("GenericConstraintSolver not available, falling back to CGLinearSolver")
|
|
46
|
+
try:
|
|
47
|
+
rootNode.addObject('CGLinearSolver', name='solver', iterations=500, tolerance=1e-10, threshold=1e-10)
|
|
48
|
+
rootNode.addObject('BlockGaussSeidelConstraintSolver', maxIterations=100, tolerance=1e-5)
|
|
49
|
+
except Exception as e: # Error in both versions
|
|
50
|
+
print("Error adding CGLinearSolver:", e)
|
|
51
|
+
raise e
|
|
52
|
+
|
|
53
|
+
rootNode.gravity = [0, -9810, 0]
|
|
54
|
+
rootNode.dt = 0.01
|
|
55
|
+
|
|
56
|
+
# --------------------------------------------------------------------------
|
|
57
|
+
# FEM Model
|
|
58
|
+
# --------------------------------------------------------------------------
|
|
59
|
+
finger = rootNode.addChild('finger')
|
|
60
|
+
finger.addObject('EulerImplicitSolver', name='odesolver', rayleighMass=0.1, rayleighStiffness=0.1)
|
|
61
|
+
finger.addObject('SparseLDLSolver', template='CompressedRowSparseMatrixMat3x3d')
|
|
62
|
+
|
|
63
|
+
# Add a component to load a VTK tetrahedral mesh and expose the resulting topology in the scene .
|
|
64
|
+
finger.addObject('MeshVTKLoader', name='loader', filename=str(mesh_path / "finger.vtk"))
|
|
65
|
+
finger.addObject('MeshTopology', src='@loader', name='container')
|
|
66
|
+
|
|
67
|
+
# Create a MechanicaObject component to stores the DoFs of the model
|
|
68
|
+
finger.addObject('MechanicalObject', name='tetras', template='Vec3', showIndices=False, showIndicesScale=4e-5)
|
|
69
|
+
|
|
70
|
+
finger.addObject('UniformMass', totalMass=0.075)
|
|
71
|
+
finger.addObject('TetrahedronFEMForceField', template='Vec3', name='FEM', method='large', poissonRatio=0.45,
|
|
72
|
+
youngModulus=600)
|
|
73
|
+
finger.addObject('BoxROI', name='roi', box=[-15, 0, 0, 5, 10, 15], drawBoxes=True)
|
|
74
|
+
finger.addObject('RestShapeSpringsForceField', points=finger.roi.indices.getLinkPath(), stiffness=1e12)
|
|
75
|
+
finger.addObject('GenericConstraintCorrection')
|
|
76
|
+
|
|
77
|
+
# --------------------------------------------------------------------------
|
|
78
|
+
# Cable
|
|
79
|
+
# --------------------------------------------------------------------------
|
|
80
|
+
cable = finger.addChild('cable')
|
|
81
|
+
cable.addObject('MechanicalObject',
|
|
82
|
+
position=[
|
|
83
|
+
[-17.5, 12.5, 2.5],
|
|
84
|
+
[-32.5, 12.5, 2.5],
|
|
85
|
+
[-47.5, 12.5, 2.5],
|
|
86
|
+
[-62.5, 12.5, 2.5],
|
|
87
|
+
[-77.5, 12.5, 2.5],
|
|
88
|
+
|
|
89
|
+
[-85.5, 12.5, 6.5],
|
|
90
|
+
[-85.5, 12.5, 8.5],
|
|
91
|
+
[-83.5, 12.5, 4.5],
|
|
92
|
+
[-83.5, 12.5, 10.5],
|
|
93
|
+
|
|
94
|
+
[-77.5, 12.5, 12.5],
|
|
95
|
+
[-62.5, 12.5, 12.5],
|
|
96
|
+
[-47.5, 12.5, 12.5],
|
|
97
|
+
[-32.5, 12.5, 12.5],
|
|
98
|
+
[-17.5, 12.5, 12.5]])
|
|
99
|
+
|
|
100
|
+
cable.addObject('CableConstraint', name="aCableActuator",
|
|
101
|
+
indices=list(range(0, 14)),
|
|
102
|
+
minForce=0, # Set that the cable can't push
|
|
103
|
+
pullPoint=[0.0, 12.5, 2.5])
|
|
104
|
+
cable.addObject('BarycentricMapping')
|
|
105
|
+
|
|
106
|
+
# --------------------------------------------------------------------------
|
|
107
|
+
# Visualization
|
|
108
|
+
# --------------------------------------------------------------------------
|
|
109
|
+
fingerVisu = finger.addChild('visu')
|
|
110
|
+
fingerVisu.addObject('MeshSTLLoader', filename=str(mesh_path / "finger.stl"), name="loader")
|
|
111
|
+
fingerVisu.addObject('OglModel', src="@loader", color=[0.0, 0.7, 0.7, 1])
|
|
112
|
+
fingerVisu.addObject('BarycentricMapping')
|
|
113
|
+
|
|
114
|
+
# --------------------------------------------------------------------------
|
|
115
|
+
# Controller
|
|
116
|
+
# --------------------------------------------------------------------------
|
|
117
|
+
controller = FingerController(rootNode, cable.aCableActuator, finger.tetras)
|
|
118
|
+
finger.addObject(controller)
|
|
119
|
+
|
|
120
|
+
return rootNode, controller
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
from pySimBlocks.blocks.systems.sofa import SofaPysimBlocksController
|
|
6
|
+
|
|
7
|
+
BASE_DIR = Path(__file__).resolve().parent
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class FingerController(SofaPysimBlocksController):
|
|
11
|
+
|
|
12
|
+
def __init__(self, root, actuator, mo, tip_index=121, name="FingerController"):
|
|
13
|
+
super().__init__(root, name=name)
|
|
14
|
+
self.project_yaml = str((BASE_DIR / '../project.yaml').resolve())
|
|
15
|
+
|
|
16
|
+
self.mo = mo
|
|
17
|
+
self.actuator = actuator
|
|
18
|
+
self.tip_index = tip_index
|
|
19
|
+
self.dt = root.dt.value
|
|
20
|
+
self.verbose = False # Set to True to print debug information at each step
|
|
21
|
+
|
|
22
|
+
# Inputs & outputs dictionaries
|
|
23
|
+
self.inputs = { "cable": None }
|
|
24
|
+
self.outputs = { "tip": None, "measure": None }
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def get_outputs(self):
|
|
28
|
+
tip = self.mo.position[self.tip_index].copy()
|
|
29
|
+
self.outputs["tip"] = np.asarray(tip).reshape(-1, 1)
|
|
30
|
+
self.outputs["measure"] = np.asarray(tip[1]).reshape(-1, 1)
|
|
31
|
+
|
|
32
|
+
def set_inputs(self):
|
|
33
|
+
val = self.inputs["cable"]
|
|
34
|
+
if val is None:
|
|
35
|
+
raise ValueError("Input 'cable' is not set")
|
|
36
|
+
val = [val.item()]
|
|
37
|
+
self.actuator.value = val
|
pySimBlocks/__init__.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# ******************************************************************************
|
|
2
|
+
# pySimBlocks
|
|
3
|
+
# Copyright (c) 2026 Université de Lille & INRIA
|
|
4
|
+
# ******************************************************************************
|
|
5
|
+
# This program is free software: you can redistribute it and/or modify it
|
|
6
|
+
# under the terms of the GNU Lesser General Public License as published by
|
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or (at your
|
|
8
|
+
# option) any later version.
|
|
9
|
+
#
|
|
10
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT
|
|
11
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
12
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
|
13
|
+
# for more details.
|
|
14
|
+
#
|
|
15
|
+
# You should have received a copy of the GNU Lesser General Public License
|
|
16
|
+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
# ******************************************************************************
|
|
18
|
+
# Authors: see Authors.txt
|
|
19
|
+
# ******************************************************************************
|
|
20
|
+
|
|
21
|
+
from importlib.metadata import version, PackageNotFoundError
|
|
22
|
+
|
|
23
|
+
try:
|
|
24
|
+
__version__ = version("pySimBlocks")
|
|
25
|
+
except PackageNotFoundError:
|
|
26
|
+
__version__ = "unknown"
|
|
27
|
+
|
|
28
|
+
from pySimBlocks.core import Model, Simulator, SimulationConfig, PlotConfig
|
|
29
|
+
|
|
30
|
+
__all__ = [
|
|
31
|
+
"Model",
|
|
32
|
+
"Simulator",
|
|
33
|
+
"SimulationConfig",
|
|
34
|
+
"PlotConfig"
|
|
35
|
+
]
|