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,108 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import Sofa
|
|
5
|
+
|
|
6
|
+
dir_path = os.path.dirname(os.path.abspath(__file__))
|
|
7
|
+
path = os.path.join(dir_path, 'mesh')
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def createScene(rootNode):
|
|
11
|
+
from FingerController import FingerController
|
|
12
|
+
|
|
13
|
+
rootNode.addObject("RequiredPlugin", name='SoftRobots')
|
|
14
|
+
rootNode.addObject("RequiredPlugin", name='SofaPython3')
|
|
15
|
+
rootNode.addObject('RequiredPlugin', pluginName=[
|
|
16
|
+
"Sofa.Component.AnimationLoop", # Needed to use components FreeMotionAnimationLoop
|
|
17
|
+
"Sofa.Component.Constraint.Lagrangian.Correction", # Needed to use components GenericConstraintCorrection
|
|
18
|
+
"Sofa.Component.Constraint.Lagrangian.Solver", # Needed to use components GenericConstraintSolver
|
|
19
|
+
"Sofa.Component.Engine.Select", # Needed to use components BoxROI
|
|
20
|
+
"Sofa.Component.IO.Mesh", # Needed to use components MeshSTLLoader, MeshVTKLoader
|
|
21
|
+
"Sofa.Component.LinearSolver.Direct", # Needed to use components SparseLDLSolver
|
|
22
|
+
"Sofa.Component.Mass", # Needed to use components UniformMass
|
|
23
|
+
"Sofa.Component.ODESolver.Backward", # Needed to use components EulerImplicitSolver
|
|
24
|
+
"Sofa.Component.Setting", # Needed to use components BackgroundSetting
|
|
25
|
+
"Sofa.Component.SolidMechanics.FEM.Elastic", # Needed to use components TetrahedronFEMForceField
|
|
26
|
+
"Sofa.Component.SolidMechanics.Spring", # Needed to use components RestShapeSpringsForceField
|
|
27
|
+
"Sofa.Component.Topology.Container.Constant", # Needed to use components MeshTopology
|
|
28
|
+
"Sofa.Component.Visual", # Needed to use components VisualStyle
|
|
29
|
+
"Sofa.GL.Component.Rendering3D", # Needed to use components OglModel, OglSceneFrame
|
|
30
|
+
"Sofa.Component.StateContainer",
|
|
31
|
+
'Sofa.Component.Mapping.Linear'
|
|
32
|
+
])
|
|
33
|
+
rootNode.addObject('VisualStyle',
|
|
34
|
+
displayFlags='showVisualModels hideBehaviorModels showCollisionModels hideBoundingCollisionModels hideForceFields showInteractionForceFields hideWireframe')
|
|
35
|
+
|
|
36
|
+
rootNode.addObject('FreeMotionAnimationLoop')
|
|
37
|
+
rootNode.addObject('DefaultVisualManagerLoop')
|
|
38
|
+
|
|
39
|
+
rootNode.addObject('GenericConstraintSolver', tolerance=1e-5, maxIterations=100)
|
|
40
|
+
|
|
41
|
+
rootNode.gravity = [0, -9810, 0]
|
|
42
|
+
rootNode.dt = 0.01
|
|
43
|
+
|
|
44
|
+
##########################################
|
|
45
|
+
# FEM Model #
|
|
46
|
+
##########################################
|
|
47
|
+
finger = rootNode.addChild('finger')
|
|
48
|
+
finger.addObject('EulerImplicitSolver', name='odesolver', rayleighMass=0.1, rayleighStiffness=0.1)
|
|
49
|
+
finger.addObject('SparseLDLSolver', template='CompressedRowSparseMatrixMat3x3d')
|
|
50
|
+
|
|
51
|
+
# Add a component to load a VTK tetrahedral mesh and expose the resulting topology in the scene .
|
|
52
|
+
finger.addObject('MeshVTKLoader', name='loader', filename=os.path.join(path, 'finger.vtk'))
|
|
53
|
+
finger.addObject('MeshTopology', src='@loader', name='container')
|
|
54
|
+
|
|
55
|
+
# Create a MechanicaObject component to stores the DoFs of the model
|
|
56
|
+
finger.addObject('MechanicalObject', name='tetras', template='Vec3', showIndices=False, showIndicesScale=4e-5)
|
|
57
|
+
|
|
58
|
+
finger.addObject('UniformMass', totalMass=0.075)
|
|
59
|
+
finger.addObject('TetrahedronFEMForceField', template='Vec3', name='FEM', method='large', poissonRatio=0.45,
|
|
60
|
+
youngModulus=600)
|
|
61
|
+
finger.addObject('BoxROI', name='roi', box=[-15, 0, 0, 5, 10, 15], drawBoxes=True)
|
|
62
|
+
finger.addObject('RestShapeSpringsForceField', points=finger.roi.indices.getLinkPath(), stiffness=1e12)
|
|
63
|
+
finger.addObject('GenericConstraintCorrection')
|
|
64
|
+
|
|
65
|
+
##########################################
|
|
66
|
+
# Cable #
|
|
67
|
+
##########################################
|
|
68
|
+
|
|
69
|
+
cable = finger.addChild('cable')
|
|
70
|
+
cable.addObject('MechanicalObject',
|
|
71
|
+
position=[
|
|
72
|
+
[-17.5, 12.5, 2.5],
|
|
73
|
+
[-32.5, 12.5, 2.5],
|
|
74
|
+
[-47.5, 12.5, 2.5],
|
|
75
|
+
[-62.5, 12.5, 2.5],
|
|
76
|
+
[-77.5, 12.5, 2.5],
|
|
77
|
+
|
|
78
|
+
[-85.5, 12.5, 6.5],
|
|
79
|
+
[-85.5, 12.5, 8.5],
|
|
80
|
+
[-83.5, 12.5, 4.5],
|
|
81
|
+
[-83.5, 12.5, 10.5],
|
|
82
|
+
|
|
83
|
+
[-77.5, 12.5, 12.5],
|
|
84
|
+
[-62.5, 12.5, 12.5],
|
|
85
|
+
[-47.5, 12.5, 12.5],
|
|
86
|
+
[-32.5, 12.5, 12.5],
|
|
87
|
+
[-17.5, 12.5, 12.5]])
|
|
88
|
+
|
|
89
|
+
cable.addObject('CableConstraint', name="aCableActuator",
|
|
90
|
+
indices=list(range(0, 14)),
|
|
91
|
+
minForce=0, # Set that the cable can't push
|
|
92
|
+
pullPoint=[0.0, 12.5, 2.5])
|
|
93
|
+
cable.addObject('BarycentricMapping')
|
|
94
|
+
|
|
95
|
+
controller = FingerController(rootNode, cable.aCableActuator, finger.tetras)
|
|
96
|
+
finger.addObject(controller)
|
|
97
|
+
|
|
98
|
+
##########################################
|
|
99
|
+
# Visualization #
|
|
100
|
+
##########################################
|
|
101
|
+
fingerVisu = finger.addChild('visu')
|
|
102
|
+
|
|
103
|
+
# Add to this empty node a rendering model made of triangles and loaded from a stl file.
|
|
104
|
+
fingerVisu.addObject('MeshSTLLoader', filename=os.path.join(path, "finger.stl"), name="loader")
|
|
105
|
+
fingerVisu.addObject('OglModel', src="@loader", color=[0.0, 0.7, 0.7, 1])
|
|
106
|
+
fingerVisu.addObject('BarycentricMapping')
|
|
107
|
+
|
|
108
|
+
return rootNode, controller
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from pySimBlocks.blocks.systems.sofa import SofaPysimBlocksController
|
|
3
|
+
|
|
4
|
+
import Sofa
|
|
5
|
+
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
BASE_DIR = Path(__file__).resolve().parent
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class FingerController(SofaPysimBlocksController):
|
|
14
|
+
|
|
15
|
+
def __init__(self, root, actuator, mo, tip_index=121, name="FingerController"):
|
|
16
|
+
super().__init__(root, name=name)
|
|
17
|
+
self.project_yaml = str((BASE_DIR / '../sofa_plant/project.yaml').resolve())
|
|
18
|
+
|
|
19
|
+
self.mo = mo
|
|
20
|
+
self.actuator = actuator
|
|
21
|
+
self.tip_index = tip_index
|
|
22
|
+
self.dt = root.dt.value
|
|
23
|
+
self.verbose = True
|
|
24
|
+
|
|
25
|
+
# Inputs & outputs dictionaries
|
|
26
|
+
self.inputs = { "cable": None }
|
|
27
|
+
self.outputs = { "tip": None, "measure": None }
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def get_outputs(self):
|
|
31
|
+
tip = self.mo.position[self.tip_index].copy()
|
|
32
|
+
self.outputs["tip"] = np.asarray(tip).reshape(-1, 1)
|
|
33
|
+
self.outputs["measure"] = np.asarray(tip[1]).reshape(-1, 1)
|
|
34
|
+
|
|
35
|
+
def set_inputs(self):
|
|
36
|
+
# 1. READ INPUT -------------------------------------
|
|
37
|
+
val = self.inputs["cable"]
|
|
38
|
+
|
|
39
|
+
# Safe default for first initialization call
|
|
40
|
+
if val is None:
|
|
41
|
+
# ↓↓↓ Valeur par défaut pour l’initialisation
|
|
42
|
+
val = 0.0
|
|
43
|
+
|
|
44
|
+
# Convert input to Sofa format
|
|
45
|
+
if isinstance(val, np.ndarray):
|
|
46
|
+
processed = val.flatten().tolist()
|
|
47
|
+
elif isinstance(val, (list, tuple)):
|
|
48
|
+
processed = val
|
|
49
|
+
else:
|
|
50
|
+
processed = [float(val)]
|
|
51
|
+
|
|
52
|
+
# Apply to actuator
|
|
53
|
+
self.actuator.value = processed
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
step_value_before = np.array([[0]])
|
|
4
|
+
step_value_after = np.array([[8.0]])
|
|
5
|
+
step_start_time = 0.4
|
|
6
|
+
|
|
7
|
+
error_num_inputs = 2
|
|
8
|
+
error_signs = np.array([1.0, -1.0])
|
|
9
|
+
|
|
10
|
+
Kp_gain = np.array([[0.5]])
|
|
11
|
+
|
|
12
|
+
Ki_gain = np.array([[0.8]])
|
|
13
|
+
|
|
14
|
+
discrete_integrator_initial_state = np.array([[0.0]])
|
|
15
|
+
|
|
16
|
+
sum_num_inputs = 2
|
|
17
|
+
|
|
18
|
+
sofa_input_keys = np.array(['cable'])
|
|
19
|
+
sofa_output_keys = np.array(['tip', 'measure'])
|
|
20
|
+
|
|
21
|
+
dt = 0.01
|
|
22
|
+
T = 5.0
|
|
@@ -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,107 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import Sofa
|
|
3
|
+
|
|
4
|
+
dir_path = os.path.dirname(os.path.abspath(__file__))
|
|
5
|
+
# sys.path.append(specific_path) # Si jamais besoin de fichier externe
|
|
6
|
+
path = os.path.join(dir_path, 'mesh')
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def createScene(rootNode):
|
|
10
|
+
from FingerController import FingerController
|
|
11
|
+
|
|
12
|
+
rootNode.addObject("RequiredPlugin", name='SoftRobots')
|
|
13
|
+
rootNode.addObject("RequiredPlugin", name='SofaPython3')
|
|
14
|
+
rootNode.addObject('RequiredPlugin', pluginName=[
|
|
15
|
+
"Sofa.Component.AnimationLoop", # Needed to use components FreeMotionAnimationLoop
|
|
16
|
+
"Sofa.Component.Constraint.Lagrangian.Correction", # Needed to use components GenericConstraintCorrection
|
|
17
|
+
"Sofa.Component.Constraint.Lagrangian.Solver", # Needed to use components GenericConstraintSolver
|
|
18
|
+
"Sofa.Component.Engine.Select", # Needed to use components BoxROI
|
|
19
|
+
"Sofa.Component.IO.Mesh", # Needed to use components MeshSTLLoader, MeshVTKLoader
|
|
20
|
+
"Sofa.Component.LinearSolver.Direct", # Needed to use components SparseLDLSolver
|
|
21
|
+
"Sofa.Component.Mass", # Needed to use components UniformMass
|
|
22
|
+
"Sofa.Component.ODESolver.Backward", # Needed to use components EulerImplicitSolver
|
|
23
|
+
"Sofa.Component.Setting", # Needed to use components BackgroundSetting
|
|
24
|
+
"Sofa.Component.SolidMechanics.FEM.Elastic", # Needed to use components TetrahedronFEMForceField
|
|
25
|
+
"Sofa.Component.SolidMechanics.Spring", # Needed to use components RestShapeSpringsForceField
|
|
26
|
+
"Sofa.Component.Topology.Container.Constant", # Needed to use components MeshTopology
|
|
27
|
+
"Sofa.Component.Visual", # Needed to use components VisualStyle
|
|
28
|
+
"Sofa.GL.Component.Rendering3D", # Needed to use components OglModel, OglSceneFrame
|
|
29
|
+
"Sofa.Component.StateContainer",
|
|
30
|
+
'Sofa.Component.Mapping.Linear'
|
|
31
|
+
])
|
|
32
|
+
rootNode.addObject('VisualStyle',
|
|
33
|
+
displayFlags='showVisualModels hideBehaviorModels showCollisionModels hideBoundingCollisionModels hideForceFields showInteractionForceFields hideWireframe')
|
|
34
|
+
|
|
35
|
+
rootNode.addObject('FreeMotionAnimationLoop')
|
|
36
|
+
rootNode.addObject('DefaultVisualManagerLoop')
|
|
37
|
+
|
|
38
|
+
rootNode.addObject('GenericConstraintSolver', tolerance=1e-5, maxIterations=100)
|
|
39
|
+
|
|
40
|
+
rootNode.gravity = [0, -9810, 0]
|
|
41
|
+
rootNode.dt = 0.01
|
|
42
|
+
|
|
43
|
+
##########################################
|
|
44
|
+
# FEM Model #
|
|
45
|
+
##########################################
|
|
46
|
+
finger = rootNode.addChild('finger')
|
|
47
|
+
finger.addObject('EulerImplicitSolver', name='odesolver', rayleighMass=0.1, rayleighStiffness=0.1)
|
|
48
|
+
finger.addObject('SparseLDLSolver', template='CompressedRowSparseMatrixMat3x3d')
|
|
49
|
+
|
|
50
|
+
# Add a component to load a VTK tetrahedral mesh and expose the resulting topology in the scene .
|
|
51
|
+
finger.addObject('MeshVTKLoader', name='loader', filename=os.path.join(path, 'finger.vtk'))
|
|
52
|
+
finger.addObject('MeshTopology', src='@loader', name='container')
|
|
53
|
+
|
|
54
|
+
# Create a MechanicaObject component to stores the DoFs of the model
|
|
55
|
+
finger.addObject('MechanicalObject', name='tetras', template='Vec3', showIndices=False, showIndicesScale=4e-5)
|
|
56
|
+
|
|
57
|
+
finger.addObject('UniformMass', totalMass=0.075)
|
|
58
|
+
finger.addObject('TetrahedronFEMForceField', template='Vec3', name='FEM', method='large', poissonRatio=0.45,
|
|
59
|
+
youngModulus=600)
|
|
60
|
+
finger.addObject('BoxROI', name='roi', box=[-15, 0, 0, 5, 10, 15], drawBoxes=True)
|
|
61
|
+
finger.addObject('RestShapeSpringsForceField', points=finger.roi.indices.getLinkPath(), stiffness=1e12)
|
|
62
|
+
finger.addObject('GenericConstraintCorrection')
|
|
63
|
+
|
|
64
|
+
##########################################
|
|
65
|
+
# Cable #
|
|
66
|
+
##########################################
|
|
67
|
+
|
|
68
|
+
cable = finger.addChild('cable')
|
|
69
|
+
cable.addObject('MechanicalObject',
|
|
70
|
+
position=[
|
|
71
|
+
[-17.5, 12.5, 2.5],
|
|
72
|
+
[-32.5, 12.5, 2.5],
|
|
73
|
+
[-47.5, 12.5, 2.5],
|
|
74
|
+
[-62.5, 12.5, 2.5],
|
|
75
|
+
[-77.5, 12.5, 2.5],
|
|
76
|
+
|
|
77
|
+
[-85.5, 12.5, 6.5],
|
|
78
|
+
[-85.5, 12.5, 8.5],
|
|
79
|
+
[-83.5, 12.5, 4.5],
|
|
80
|
+
[-83.5, 12.5, 10.5],
|
|
81
|
+
|
|
82
|
+
[-77.5, 12.5, 12.5],
|
|
83
|
+
[-62.5, 12.5, 12.5],
|
|
84
|
+
[-47.5, 12.5, 12.5],
|
|
85
|
+
[-32.5, 12.5, 12.5],
|
|
86
|
+
[-17.5, 12.5, 12.5]])
|
|
87
|
+
|
|
88
|
+
cable.addObject('CableConstraint', name="aCableActuator",
|
|
89
|
+
indices=list(range(0, 14)),
|
|
90
|
+
minForce=0, # Set that the cable can't push
|
|
91
|
+
pullPoint=[0.0, 12.5, 2.5])
|
|
92
|
+
cable.addObject('BarycentricMapping')
|
|
93
|
+
|
|
94
|
+
controller = FingerController(rootNode, cable.aCableActuator, finger.tetras)
|
|
95
|
+
finger.addObject(controller)
|
|
96
|
+
|
|
97
|
+
##########################################
|
|
98
|
+
# Visualization #
|
|
99
|
+
##########################################
|
|
100
|
+
fingerVisu = finger.addChild('visu')
|
|
101
|
+
|
|
102
|
+
# Add to this empty node a rendering model made of triangles and loaded from a stl file.
|
|
103
|
+
fingerVisu.addObject('MeshSTLLoader', filename=os.path.join(path, "finger.stl"), name="loader")
|
|
104
|
+
fingerVisu.addObject('OglModel', src="@loader", color=[0.0, 0.7, 0.7, 1])
|
|
105
|
+
fingerVisu.addObject('BarycentricMapping')
|
|
106
|
+
|
|
107
|
+
return rootNode, controller
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from pySimBlocks.blocks.systems.sofa import SofaPysimBlocksController
|
|
3
|
+
|
|
4
|
+
import Sofa
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class FingerController(SofaPysimBlocksController):
|
|
8
|
+
|
|
9
|
+
def __init__(self, root, actuator, mo, tip_index=121, name="FingerController"):
|
|
10
|
+
super().__init__(root, name=name)
|
|
11
|
+
|
|
12
|
+
self.mo = mo
|
|
13
|
+
self.actuator = actuator
|
|
14
|
+
self.tip_index = tip_index
|
|
15
|
+
self.dt = root.dt.value
|
|
16
|
+
|
|
17
|
+
# Inputs & outputs dictionaries
|
|
18
|
+
self.inputs = { "cable": None }
|
|
19
|
+
self.outputs = { "tip": None, "measure": None }
|
|
20
|
+
|
|
21
|
+
def prepare_scene(self):
|
|
22
|
+
if self.step_index == 10:
|
|
23
|
+
self.IS_READY = True
|
|
24
|
+
|
|
25
|
+
def get_outputs(self):
|
|
26
|
+
tip = self.mo.position[self.tip_index].copy()
|
|
27
|
+
self.outputs["tip"] = np.asarray(tip).reshape(-1, 1)
|
|
28
|
+
self.outputs["measure"] = np.asarray(tip[1]).reshape(-1, 1)
|
|
29
|
+
|
|
30
|
+
def set_inputs(self):
|
|
31
|
+
# 1. READ INPUT -------------------------------------
|
|
32
|
+
val = self.inputs["cable"]
|
|
33
|
+
|
|
34
|
+
# Safe default for first initialization call
|
|
35
|
+
if val is None:
|
|
36
|
+
# ↓↓↓ Valeur par défaut pour l’initialisation
|
|
37
|
+
val = 0.0
|
|
38
|
+
|
|
39
|
+
# Convert input to Sofa format
|
|
40
|
+
if isinstance(val, np.ndarray):
|
|
41
|
+
processed = val.flatten().tolist()
|
|
42
|
+
elif isinstance(val, (list, tuple)):
|
|
43
|
+
processed = val
|
|
44
|
+
else:
|
|
45
|
+
processed = [float(val)]
|
|
46
|
+
|
|
47
|
+
# Apply to actuator
|
|
48
|
+
self.actuator.value = processed
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from pySimBlocks import Model, Simulator, SimulationConfig, PlotConfig
|
|
4
|
+
from pySimBlocks.blocks.systems.sofa import SofaPlant
|
|
5
|
+
from pySimBlocks.blocks.sources import Step
|
|
6
|
+
from pySimBlocks.blocks.operators import Sum
|
|
7
|
+
from pySimBlocks.blocks.controllers import Pid
|
|
8
|
+
from pySimBlocks.project import plot_from_config
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def main():
|
|
12
|
+
|
|
13
|
+
path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'Finger.py')
|
|
14
|
+
|
|
15
|
+
# --- Create Blocks ---
|
|
16
|
+
step = Step(name="step", value_before=[[0.0]], value_after=[[8.0]], start_time=2.)
|
|
17
|
+
error = Sum(name="error", signs="+-")
|
|
18
|
+
pid = Pid("pid", controller="PI", Kp=0.3, Ki=0.8)
|
|
19
|
+
|
|
20
|
+
sofa_block = SofaPlant(
|
|
21
|
+
name="sofa_finger",
|
|
22
|
+
scene_file=path,
|
|
23
|
+
input_keys=["cable"],
|
|
24
|
+
output_keys=["tip", "measure"],
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
# --- Build the model ---
|
|
28
|
+
model = Model(name="sofa_finger_model")
|
|
29
|
+
for block in [step, error, pid, sofa_block]:
|
|
30
|
+
model.add_block(block)
|
|
31
|
+
|
|
32
|
+
model.connect("step", "out", "error", "in1")
|
|
33
|
+
model.connect("sofa_finger", "measure", "error", "in2")
|
|
34
|
+
model.connect("error", "out", "pid", "e")
|
|
35
|
+
model.connect("pid", "u", "sofa_finger", "cable")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# --- Create the simulator ---
|
|
39
|
+
dt = 0.01
|
|
40
|
+
T = 5.0
|
|
41
|
+
sim_cfg = SimulationConfig(dt, T)
|
|
42
|
+
sim = Simulator(model, sim_cfg, verbose=False)
|
|
43
|
+
|
|
44
|
+
# --- Run simulation ---
|
|
45
|
+
logs = sim.run(logging=[
|
|
46
|
+
"step.outputs.out",
|
|
47
|
+
"sofa_finger.outputs.measure",
|
|
48
|
+
"pid.outputs.u"
|
|
49
|
+
],
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
# --- Inspect / print some results ---
|
|
53
|
+
plot_cfg = PlotConfig([
|
|
54
|
+
{'title': 'Ref vs Output',
|
|
55
|
+
'signals': ['step.outputs.out', 'sofa_finger.outputs.measure']},
|
|
56
|
+
{'title': 'Command',
|
|
57
|
+
'signals': ['pid.outputs.u']}
|
|
58
|
+
])
|
|
59
|
+
plot_from_config(logs, plot_cfg)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
if __name__ == '__main__':
|
|
64
|
+
main()
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from pySimBlocks import Model, Simulator, SimulationConfig, PlotConfig
|
|
4
|
+
from pySimBlocks.blocks.systems.sofa import SofaPlant
|
|
5
|
+
from pySimBlocks.blocks.sources import Step
|
|
6
|
+
from pySimBlocks.project.plot_from_config import plot_from_config
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def main():
|
|
10
|
+
|
|
11
|
+
path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'Finger.py')
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# --- Create Blocks ---
|
|
15
|
+
step = Step(
|
|
16
|
+
name="step",
|
|
17
|
+
value_before=0.0,
|
|
18
|
+
value_after=1.0,
|
|
19
|
+
start_time=0.5,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
sofa_block = SofaPlant(
|
|
23
|
+
name="sofa_finger",
|
|
24
|
+
scene_file=path,
|
|
25
|
+
input_keys=["cable"],
|
|
26
|
+
output_keys=["tip"],
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
# --- Build the model ---
|
|
30
|
+
model = Model(name="sofa_finger_model")
|
|
31
|
+
model.add_block(step)
|
|
32
|
+
model.add_block(sofa_block)
|
|
33
|
+
model.connect("step", "out", "sofa_finger", "cable")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# --- Create the simulator ---
|
|
37
|
+
dt = 0.01
|
|
38
|
+
T = 5.0
|
|
39
|
+
sim_cfg = SimulationConfig(dt, T)
|
|
40
|
+
sim = Simulator(model, sim_cfg)
|
|
41
|
+
|
|
42
|
+
# --- Run simulation ---
|
|
43
|
+
logs = sim.run(logging=[
|
|
44
|
+
"step.outputs.out",
|
|
45
|
+
"sofa_finger.outputs.tip",
|
|
46
|
+
],
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
# --- Plotting ---
|
|
50
|
+
plot_cfg = PlotConfig([
|
|
51
|
+
{'title': 'Finger Tip Position',
|
|
52
|
+
'signals': ['step.outputs.out', 'sofa_finger.outputs.tip']}
|
|
53
|
+
])
|
|
54
|
+
plot_from_config(logs, plot_cfg)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
if __name__ == '__main__':
|
|
58
|
+
main()
|
|
@@ -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,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,91 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import matplotlib.pyplot as plt
|
|
3
|
+
|
|
4
|
+
from pySimBlocks import Model, Simulator, SimulationConfig
|
|
5
|
+
from pySimBlocks.blocks.sources import Step
|
|
6
|
+
from pySimBlocks.blocks.systems import LinearStateSpace
|
|
7
|
+
from pySimBlocks.blocks.operators import Gain, Sum, DiscreteIntegrator
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def main():
|
|
11
|
+
# DC Motor parameters
|
|
12
|
+
R = 0.1
|
|
13
|
+
L = 0.5
|
|
14
|
+
J = 0.01
|
|
15
|
+
K = 0.1
|
|
16
|
+
a = 0.001
|
|
17
|
+
|
|
18
|
+
Kp = 0.001
|
|
19
|
+
Ki = 0.02
|
|
20
|
+
|
|
21
|
+
# Simulation parameters
|
|
22
|
+
dt = 0.01
|
|
23
|
+
T = 100.
|
|
24
|
+
|
|
25
|
+
# State-space matrices
|
|
26
|
+
A = np.array([[1-dt*R/L, -dt*K/L], [dt*K/J, 1-dt*a/J]])
|
|
27
|
+
B = np.array([[dt/L], [0]])
|
|
28
|
+
C = np.array([[0, 1]])
|
|
29
|
+
|
|
30
|
+
# Model
|
|
31
|
+
test_order = [
|
|
32
|
+
[0, 6, 2, 1, 3, 4, 5],
|
|
33
|
+
[0, 3, 2, 1, 6, 4, 5],
|
|
34
|
+
[0, 1, 2, 3, 4, 5, 6],
|
|
35
|
+
[0, 1, 2, 6, 4, 5, 3],
|
|
36
|
+
[3, 2, 0, 1, 6, 4, 5],
|
|
37
|
+
[4, 2, 6, 0, 1, 3, 5]
|
|
38
|
+
]
|
|
39
|
+
for order in test_order:
|
|
40
|
+
ref = Step("ref", start_time=1., value_before=0., value_after=1.)
|
|
41
|
+
motor = LinearStateSpace("motor", A, B, C)
|
|
42
|
+
error = Sum("error", signs="+-")
|
|
43
|
+
kp = Gain("Kp", Kp)
|
|
44
|
+
ki = Gain("Ki", Ki)
|
|
45
|
+
integrator = DiscreteIntegrator("integrator")
|
|
46
|
+
sum = Sum("sum", signs="++")
|
|
47
|
+
|
|
48
|
+
blocks = [ref, error, kp, integrator, ki, sum, motor]
|
|
49
|
+
|
|
50
|
+
model = Model("DC Motor Control")
|
|
51
|
+
for i in order:
|
|
52
|
+
model.add_block(blocks[i])
|
|
53
|
+
model.connect("ref", "out", "error", "in1")
|
|
54
|
+
model.connect("motor", "y", "error", "in2")
|
|
55
|
+
model.connect("error", "out", "Kp", "in")
|
|
56
|
+
model.connect("error", "out", "Ki", "in")
|
|
57
|
+
model.connect("Ki", "out", "integrator", "in")
|
|
58
|
+
model.connect("integrator", "out", "sum", "in2")
|
|
59
|
+
model.connect("Kp", "out", "sum", "in1")
|
|
60
|
+
model.connect("sum", "out", "motor", "u")
|
|
61
|
+
|
|
62
|
+
# Simulator
|
|
63
|
+
sim_cfg = SimulationConfig(dt, T)
|
|
64
|
+
sim = Simulator(model, sim_cfg, verbose=False)
|
|
65
|
+
|
|
66
|
+
logs = sim.run(logging=[
|
|
67
|
+
"ref.outputs.out",
|
|
68
|
+
"motor.outputs.y",
|
|
69
|
+
"sum.outputs.out"
|
|
70
|
+
])
|
|
71
|
+
|
|
72
|
+
time = sim.get_data("time")
|
|
73
|
+
r = sim.get_data("ref.outputs.out").squeeze()
|
|
74
|
+
w = sim.get_data("motor.outputs.y").squeeze()
|
|
75
|
+
u = sim.get_data("sum.outputs.out").squeeze()
|
|
76
|
+
|
|
77
|
+
print(w[-5:].flatten())
|
|
78
|
+
|
|
79
|
+
plt.figure()
|
|
80
|
+
plt.step(time, r, '--r', label="Reference (rad/s)", where='post')
|
|
81
|
+
plt.step(time, w, '--b', label="Motor Speed (rad/s)", where='post')
|
|
82
|
+
plt.step(time, u, '--g', label="Control Input (V)", where='post')
|
|
83
|
+
plt.xlabel("Time (s)")
|
|
84
|
+
plt.ylabel("Speed (rad/s)")
|
|
85
|
+
plt.title("DC Motor Speed Response")
|
|
86
|
+
plt.legend()
|
|
87
|
+
plt.grid()
|
|
88
|
+
plt.show()
|
|
89
|
+
|
|
90
|
+
if __name__ == "__main__":
|
|
91
|
+
main()
|
|
@@ -0,0 +1,62 @@
|
|
|
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
|
+
|
|
7
|
+
|
|
8
|
+
def main():
|
|
9
|
+
# DC Motor parameters
|
|
10
|
+
R = 0.1
|
|
11
|
+
L = 0.5
|
|
12
|
+
J = 0.01
|
|
13
|
+
K = 0.1
|
|
14
|
+
a = 0.001
|
|
15
|
+
|
|
16
|
+
# Simulation parameters
|
|
17
|
+
dt = 0.01
|
|
18
|
+
T = 100.
|
|
19
|
+
|
|
20
|
+
# State-space matrices
|
|
21
|
+
A = np.array([[1-dt*R/L, -dt*K/L], [dt*K/J, 1-dt*a/J]])
|
|
22
|
+
B = np.array([[dt/L], [0]])
|
|
23
|
+
C = np.array([[0, 1]])
|
|
24
|
+
|
|
25
|
+
# Blocks
|
|
26
|
+
ref = Step("command", start_time=1., value_before=0., value_after=1.)
|
|
27
|
+
motor = LinearStateSpace("motor", A, B, C)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# Model
|
|
31
|
+
model = Model("DC Motor Control")
|
|
32
|
+
for block in [ref, motor]:
|
|
33
|
+
model.add_block(block)
|
|
34
|
+
model.connect("command", "out", "motor", "u")
|
|
35
|
+
|
|
36
|
+
# Simulator
|
|
37
|
+
sim_cfg = SimulationConfig(dt, T, logging=[
|
|
38
|
+
"command.outputs.out",
|
|
39
|
+
"motor.outputs.y"
|
|
40
|
+
])
|
|
41
|
+
sim = Simulator(model, sim_cfg)
|
|
42
|
+
logs = sim.run()
|
|
43
|
+
|
|
44
|
+
time = sim.get_data("time")
|
|
45
|
+
u = sim.get_data("command.outputs.out").squeeze()
|
|
46
|
+
w = sim.get_data("motor.outputs.y").squeeze()
|
|
47
|
+
|
|
48
|
+
print("Open-Loop DC Motor Response:")
|
|
49
|
+
print(w[-5:].flatten())
|
|
50
|
+
|
|
51
|
+
plt.figure()
|
|
52
|
+
plt.step(time, w, '--b', label="Motor Speed (rad/s)")
|
|
53
|
+
plt.step(time, u, '--g', label="Control Input (V)")
|
|
54
|
+
plt.xlabel("Time (s)")
|
|
55
|
+
plt.ylabel("Speed (rad/s)")
|
|
56
|
+
plt.legend()
|
|
57
|
+
plt.grid()
|
|
58
|
+
plt.title("Open-Loop DC Motor Response")
|
|
59
|
+
plt.show()
|
|
60
|
+
|
|
61
|
+
if __name__ == "__main__":
|
|
62
|
+
main()
|