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.
Files changed (262) hide show
  1. examples/advanced/hardware/block_diagram.py +162 -0
  2. examples/advanced/hardware/camera.py +93 -0
  3. examples/advanced/hardware/gui_emio.py +231 -0
  4. examples/advanced/hardware/lmi.py +69 -0
  5. examples/advanced/hardware/main.py +57 -0
  6. examples/advanced/hardware/parameters.py +54 -0
  7. examples/advanced/qp_problem/params.py +10 -0
  8. examples/advanced/qp_problem/run.py +14 -0
  9. examples/advanced/sofa/gui/finger/Finger.py +108 -0
  10. examples/advanced/sofa/gui/finger/FingerController.py +53 -0
  11. examples/advanced/sofa/gui/finger/parameters_auto.py +22 -0
  12. examples/advanced/sofa/gui/sofa_plant/run.py +14 -0
  13. examples/advanced/sofa/python/Finger.py +107 -0
  14. examples/advanced/sofa/python/FingerController.py +48 -0
  15. examples/advanced/sofa/python/closedloop.py +64 -0
  16. examples/advanced/sofa/python/openloop.py +58 -0
  17. examples/basics/algebraic_function/g.py +7 -0
  18. examples/basics/algebraic_function/run.py +14 -0
  19. examples/basics/dc_motor/gui/run.py +14 -0
  20. examples/basics/dc_motor/multiple_order.py +91 -0
  21. examples/basics/dc_motor/openloop.py +62 -0
  22. examples/basics/dc_motor/pi.py +99 -0
  23. examples/basics/dc_motor/pid.py +118 -0
  24. examples/basics/dc_motor/pid_block.py +82 -0
  25. examples/basics/dc_motor/pid_block_vs_manual.py +143 -0
  26. examples/basics/external/parameters.py +4 -0
  27. examples/basics/external/run.py +14 -0
  28. examples/basics/file_sources/temp.py +16 -0
  29. examples/basics/observer.py +76 -0
  30. examples/basics/state_feedback/gui/parameters.py +9 -0
  31. examples/basics/state_feedback/gui/run.py +14 -0
  32. examples/basics/state_feedback/state_feedback.py +102 -0
  33. examples/basics/state_space/linear/gui/run.py +14 -0
  34. examples/basics/state_space/linear/ss_block.py +89 -0
  35. examples/basics/state_space/linear/ss_manual.py +105 -0
  36. examples/basics/state_space/non_linear/nlss.py +47 -0
  37. examples/basics/state_space/non_linear/run.py +14 -0
  38. examples/basics/state_space/polytope/params.py +39 -0
  39. examples/basics/state_space/polytope/weights.py +12 -0
  40. examples/comparison/simulink/convert.py +29 -0
  41. examples/comparison/simulink/openloop.py +127 -0
  42. examples/comparison/simulink/pi.py +148 -0
  43. examples/comparison/simulink/pid.py +154 -0
  44. examples/comparison/simulink/pid_backward.py +155 -0
  45. examples/quick_start/filter.py +35 -0
  46. examples/quick_start/gui/run.py +14 -0
  47. examples/tutorials/tutorial_1_python/main.py +99 -0
  48. examples/tutorials/tutorial_3_sofa/finger/Finger.py +120 -0
  49. examples/tutorials/tutorial_3_sofa/finger/FingerController.py +37 -0
  50. pySimBlocks/__init__.py +35 -0
  51. pySimBlocks/blocks/__init__.py +68 -0
  52. pySimBlocks/blocks/controllers/__init__.py +27 -0
  53. pySimBlocks/blocks/controllers/pid.py +250 -0
  54. pySimBlocks/blocks/controllers/state_feedback.py +141 -0
  55. pySimBlocks/blocks/interfaces/__init__.py +27 -0
  56. pySimBlocks/blocks/interfaces/external_input.py +107 -0
  57. pySimBlocks/blocks/interfaces/external_output.py +113 -0
  58. pySimBlocks/blocks/observers/__init__.py +23 -0
  59. pySimBlocks/blocks/observers/luenberger.py +175 -0
  60. pySimBlocks/blocks/operators/__init__.py +47 -0
  61. pySimBlocks/blocks/operators/algebraic_function.py +258 -0
  62. pySimBlocks/blocks/operators/dead_zone.py +186 -0
  63. pySimBlocks/blocks/operators/delay.py +254 -0
  64. pySimBlocks/blocks/operators/demux.py +123 -0
  65. pySimBlocks/blocks/operators/discrete_derivator.py +237 -0
  66. pySimBlocks/blocks/operators/discrete_integrator.py +265 -0
  67. pySimBlocks/blocks/operators/gain.py +294 -0
  68. pySimBlocks/blocks/operators/mux.py +131 -0
  69. pySimBlocks/blocks/operators/product.py +194 -0
  70. pySimBlocks/blocks/operators/rate_limiter.py +214 -0
  71. pySimBlocks/blocks/operators/saturation.py +178 -0
  72. pySimBlocks/blocks/operators/sum.py +188 -0
  73. pySimBlocks/blocks/operators/zero_order_hold.py +141 -0
  74. pySimBlocks/blocks/optimizers/__init__.py +23 -0
  75. pySimBlocks/blocks/optimizers/quadratic_program.py +326 -0
  76. pySimBlocks/blocks/sources/__init__.py +37 -0
  77. pySimBlocks/blocks/sources/chirp.py +168 -0
  78. pySimBlocks/blocks/sources/constant.py +82 -0
  79. pySimBlocks/blocks/sources/file_source.py +294 -0
  80. pySimBlocks/blocks/sources/function_source.py +197 -0
  81. pySimBlocks/blocks/sources/ramp.py +99 -0
  82. pySimBlocks/blocks/sources/sinusoidal.py +113 -0
  83. pySimBlocks/blocks/sources/step.py +138 -0
  84. pySimBlocks/blocks/sources/white_noise.py +107 -0
  85. pySimBlocks/blocks/systems/__init__.py +27 -0
  86. pySimBlocks/blocks/systems/linear_state_space.py +180 -0
  87. pySimBlocks/blocks/systems/non_linear_state_space.py +309 -0
  88. pySimBlocks/blocks/systems/polytopic_state_space.py +193 -0
  89. pySimBlocks/blocks/systems/sofa/__init__.py +30 -0
  90. pySimBlocks/blocks/systems/sofa/sofa_controller.py +472 -0
  91. pySimBlocks/blocks/systems/sofa/sofa_exchange_i_o.py +120 -0
  92. pySimBlocks/blocks/systems/sofa/sofa_plant.py +306 -0
  93. pySimBlocks/cli.py +127 -0
  94. pySimBlocks/core/__init__.py +32 -0
  95. pySimBlocks/core/block.py +167 -0
  96. pySimBlocks/core/block_source.py +94 -0
  97. pySimBlocks/core/config.py +90 -0
  98. pySimBlocks/core/fixed_time_manager.py +46 -0
  99. pySimBlocks/core/model.py +250 -0
  100. pySimBlocks/core/scheduler.py +30 -0
  101. pySimBlocks/core/simulator.py +329 -0
  102. pySimBlocks/core/task.py +73 -0
  103. pySimBlocks/docs/blocks/controllers/pid.md +78 -0
  104. pySimBlocks/docs/blocks/controllers/state_feedback.md +60 -0
  105. pySimBlocks/docs/blocks/interfaces/external_input.md +39 -0
  106. pySimBlocks/docs/blocks/interfaces/external_output.md +39 -0
  107. pySimBlocks/docs/blocks/observers/luenberger.md +70 -0
  108. pySimBlocks/docs/blocks/operators/algebraic_function.md +76 -0
  109. pySimBlocks/docs/blocks/operators/dead_zone.md +67 -0
  110. pySimBlocks/docs/blocks/operators/delay.md +76 -0
  111. pySimBlocks/docs/blocks/operators/demux.md +64 -0
  112. pySimBlocks/docs/blocks/operators/discrete_derivator.md +61 -0
  113. pySimBlocks/docs/blocks/operators/discrete_integrator.md +87 -0
  114. pySimBlocks/docs/blocks/operators/gain.md +97 -0
  115. pySimBlocks/docs/blocks/operators/mux.md +64 -0
  116. pySimBlocks/docs/blocks/operators/product.md +51 -0
  117. pySimBlocks/docs/blocks/operators/rate_limiter.md +70 -0
  118. pySimBlocks/docs/blocks/operators/saturation.md +62 -0
  119. pySimBlocks/docs/blocks/operators/sum.md +49 -0
  120. pySimBlocks/docs/blocks/operators/zero_order_hold.md +61 -0
  121. pySimBlocks/docs/blocks/optimizers/quadratic_program.md +98 -0
  122. pySimBlocks/docs/blocks/sources/chirp.md +102 -0
  123. pySimBlocks/docs/blocks/sources/constant.md +53 -0
  124. pySimBlocks/docs/blocks/sources/file_source.md +51 -0
  125. pySimBlocks/docs/blocks/sources/function_source.md +52 -0
  126. pySimBlocks/docs/blocks/sources/ramp.md +58 -0
  127. pySimBlocks/docs/blocks/sources/sinusoidal.md +60 -0
  128. pySimBlocks/docs/blocks/sources/step.md +63 -0
  129. pySimBlocks/docs/blocks/sources/white_noise.md +58 -0
  130. pySimBlocks/docs/blocks/systems/linear_state_space.md +66 -0
  131. pySimBlocks/docs/blocks/systems/non_linear_state_space.md +90 -0
  132. pySimBlocks/docs/blocks/systems/polytopic_state_space.md +72 -0
  133. pySimBlocks/docs/blocks/systems/sofa/sofa_exchange_i_o.md +63 -0
  134. pySimBlocks/docs/blocks/systems/sofa/sofa_plant.md +62 -0
  135. pySimBlocks/gui/__init__.py +20 -0
  136. pySimBlocks/gui/addons/__init__.py +19 -0
  137. pySimBlocks/gui/addons/sofa/__init__.py +19 -0
  138. pySimBlocks/gui/addons/sofa/sofa_dialog.py +190 -0
  139. pySimBlocks/gui/addons/sofa/sofa_service.py +209 -0
  140. pySimBlocks/gui/blocks/block_dialog_session.py +47 -0
  141. pySimBlocks/gui/blocks/block_meta.py +408 -0
  142. pySimBlocks/gui/blocks/controllers/pid.py +116 -0
  143. pySimBlocks/gui/blocks/controllers/state_feedback.py +80 -0
  144. pySimBlocks/gui/blocks/interfaces/external_input.py +64 -0
  145. pySimBlocks/gui/blocks/interfaces/external_output.py +63 -0
  146. pySimBlocks/gui/blocks/observers/luenberger.py +108 -0
  147. pySimBlocks/gui/blocks/operators/algebraic_function.py +232 -0
  148. pySimBlocks/gui/blocks/operators/dead_zone.py +79 -0
  149. pySimBlocks/gui/blocks/operators/delay.py +79 -0
  150. pySimBlocks/gui/blocks/operators/demux.py +98 -0
  151. pySimBlocks/gui/blocks/operators/discrete_derivator.py +67 -0
  152. pySimBlocks/gui/blocks/operators/discrete_integrator.py +80 -0
  153. pySimBlocks/gui/blocks/operators/gain.py +79 -0
  154. pySimBlocks/gui/blocks/operators/mux.py +100 -0
  155. pySimBlocks/gui/blocks/operators/product.py +109 -0
  156. pySimBlocks/gui/blocks/operators/rate_limiter.py +79 -0
  157. pySimBlocks/gui/blocks/operators/saturation.py +72 -0
  158. pySimBlocks/gui/blocks/operators/sum.py +91 -0
  159. pySimBlocks/gui/blocks/operators/zero_order_hold.py +64 -0
  160. pySimBlocks/gui/blocks/optimizers/quadratic_program.py +128 -0
  161. pySimBlocks/gui/blocks/parameter_meta.py +34 -0
  162. pySimBlocks/gui/blocks/port_meta.py +31 -0
  163. pySimBlocks/gui/blocks/sources/chirp.py +146 -0
  164. pySimBlocks/gui/blocks/sources/constant.py +63 -0
  165. pySimBlocks/gui/blocks/sources/file_source.py +196 -0
  166. pySimBlocks/gui/blocks/sources/function_source.py +202 -0
  167. pySimBlocks/gui/blocks/sources/ramp.py +73 -0
  168. pySimBlocks/gui/blocks/sources/sinusoidal.py +81 -0
  169. pySimBlocks/gui/blocks/sources/step.py +83 -0
  170. pySimBlocks/gui/blocks/sources/white_noise.py +70 -0
  171. pySimBlocks/gui/blocks/systems/linear_state_space.py +102 -0
  172. pySimBlocks/gui/blocks/systems/non_linear_state_space.py +233 -0
  173. pySimBlocks/gui/blocks/systems/polytopic_state_space.py +109 -0
  174. pySimBlocks/gui/blocks/systems/sofa/sofa_exchange_i_o.py +221 -0
  175. pySimBlocks/gui/blocks/systems/sofa/sofa_plant.py +220 -0
  176. pySimBlocks/gui/dialogs/__init__.py +19 -0
  177. pySimBlocks/gui/dialogs/block_dialog.py +125 -0
  178. pySimBlocks/gui/dialogs/display_yaml_dialog.py +89 -0
  179. pySimBlocks/gui/dialogs/help_dialog.py +39 -0
  180. pySimBlocks/gui/dialogs/plot_dialog.py +230 -0
  181. pySimBlocks/gui/dialogs/settings/plots.py +187 -0
  182. pySimBlocks/gui/dialogs/settings/project.py +146 -0
  183. pySimBlocks/gui/dialogs/settings/simulation.py +137 -0
  184. pySimBlocks/gui/dialogs/settings_dialog.py +88 -0
  185. pySimBlocks/gui/dialogs/unsaved_dialog.py +92 -0
  186. pySimBlocks/gui/editor.py +49 -0
  187. pySimBlocks/gui/graphics/__init__.py +29 -0
  188. pySimBlocks/gui/graphics/block_item.py +289 -0
  189. pySimBlocks/gui/graphics/connection_item.py +266 -0
  190. pySimBlocks/gui/graphics/port_item.py +153 -0
  191. pySimBlocks/gui/graphics/theme.py +119 -0
  192. pySimBlocks/gui/main_window.py +162 -0
  193. pySimBlocks/gui/models/__init__.py +31 -0
  194. pySimBlocks/gui/models/block_instance.py +114 -0
  195. pySimBlocks/gui/models/connection_instance.py +43 -0
  196. pySimBlocks/gui/models/port_instance.py +52 -0
  197. pySimBlocks/gui/models/project_simulation_params.py +50 -0
  198. pySimBlocks/gui/models/project_state.py +114 -0
  199. pySimBlocks/gui/project_controller.py +302 -0
  200. pySimBlocks/gui/services/__init__.py +20 -0
  201. pySimBlocks/gui/services/project_loader.py +381 -0
  202. pySimBlocks/gui/services/project_saver.py +68 -0
  203. pySimBlocks/gui/services/simulation_runner.py +66 -0
  204. pySimBlocks/gui/services/yaml_tools.py +261 -0
  205. pySimBlocks/gui/widgets/__init__.py +19 -0
  206. pySimBlocks/gui/widgets/block_list.py +131 -0
  207. pySimBlocks/gui/widgets/diagram_view.py +312 -0
  208. pySimBlocks/gui/widgets/toolbar_view.py +161 -0
  209. pySimBlocks/project/__init__.py +35 -0
  210. pySimBlocks/project/build_model.py +92 -0
  211. pySimBlocks/project/generate_run_script.py +109 -0
  212. pySimBlocks/project/generate_sofa_controller.py +215 -0
  213. pySimBlocks/project/load_project_config.py +197 -0
  214. pySimBlocks/project/load_simulation_config.py +166 -0
  215. pySimBlocks/project/load_simulator.py +45 -0
  216. pySimBlocks/project/plot_from_config.py +174 -0
  217. pySimBlocks/project/pySimBlocks_blocks_index.yaml +106 -0
  218. pySimBlocks/real_time/__init__.py +23 -0
  219. pySimBlocks/real_time/real_time_runner.py +148 -0
  220. pySimBlocks/tools/__init__.py +19 -0
  221. pySimBlocks/tools/blocks_registry.py +141 -0
  222. pySimBlocks/tools/generate_blocks_index.py +124 -0
  223. pysimblocks-0.1.0.dist-info/METADATA +180 -0
  224. pysimblocks-0.1.0.dist-info/RECORD +262 -0
  225. pysimblocks-0.1.0.dist-info/WHEEL +5 -0
  226. pysimblocks-0.1.0.dist-info/entry_points.txt +2 -0
  227. pysimblocks-0.1.0.dist-info/licenses/LICENSE.md +165 -0
  228. pysimblocks-0.1.0.dist-info/top_level.txt +3 -0
  229. tests/blocks/controllers/test_pid.py +143 -0
  230. tests/blocks/controllers/test_state_feedback.py +193 -0
  231. tests/blocks/observers/test_luenberger.py +225 -0
  232. tests/blocks/operators/test_algebraic_function.py +230 -0
  233. tests/blocks/operators/test_dead_zone.py +106 -0
  234. tests/blocks/operators/test_delay.py +167 -0
  235. tests/blocks/operators/test_demux.py +111 -0
  236. tests/blocks/operators/test_discrete_derivator.py +160 -0
  237. tests/blocks/operators/test_discrete_integrator.py +149 -0
  238. tests/blocks/operators/test_gain.py +182 -0
  239. tests/blocks/operators/test_mux.py +101 -0
  240. tests/blocks/operators/test_product.py +171 -0
  241. tests/blocks/operators/test_rate_limiter.py +166 -0
  242. tests/blocks/operators/test_saturation.py +157 -0
  243. tests/blocks/operators/test_sum.py +183 -0
  244. tests/blocks/operators/test_zero_order_hold.py +106 -0
  245. tests/blocks/sources/test_chirp.py +157 -0
  246. tests/blocks/sources/test_constant.py +55 -0
  247. tests/blocks/sources/test_file_source.py +185 -0
  248. tests/blocks/sources/test_function_source.py +135 -0
  249. tests/blocks/sources/test_ramp.py +110 -0
  250. tests/blocks/sources/test_sinusoidal.py +113 -0
  251. tests/blocks/sources/test_step.py +76 -0
  252. tests/blocks/sources/test_white_noise.py +108 -0
  253. tests/blocks/systems/test_linear_state_space.py +135 -0
  254. tests/blocks/systems/test_polytopic_state_space.py +133 -0
  255. tests/core/test_core_errors_and_determinism.py +82 -0
  256. tests/core/test_core_multirate_tasks.py +88 -0
  257. tests/core/test_core_two_phase_and_propagation.py +132 -0
  258. tests/gui/main_window_open_test.py +162 -0
  259. tests/gui/test_block_meta_param_cache.py +16 -0
  260. tests/gui/test_file_source_meta.py +17 -0
  261. tests/gui/test_function_source_meta.py +28 -0
  262. 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
@@ -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
+ ]