virtualmodelcontrol 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. virtualmodelcontrol-0.1.0/.gitignore +24 -0
  2. virtualmodelcontrol-0.1.0/CHANGELOG.md +40 -0
  3. virtualmodelcontrol-0.1.0/CITATION.cff +17 -0
  4. virtualmodelcontrol-0.1.0/LICENSE +21 -0
  5. virtualmodelcontrol-0.1.0/PKG-INFO +82 -0
  6. virtualmodelcontrol-0.1.0/README.md +47 -0
  7. virtualmodelcontrol-0.1.0/benchmarks/tick.py +58 -0
  8. virtualmodelcontrol-0.1.0/docs/_static/custom.css +4 -0
  9. virtualmodelcontrol-0.1.0/docs/_templates/autosummary/module.rst +60 -0
  10. virtualmodelcontrol-0.1.0/docs/conf.py +51 -0
  11. virtualmodelcontrol-0.1.0/docs/development/architecture.md +44 -0
  12. virtualmodelcontrol-0.1.0/docs/development/contributing.md +49 -0
  13. virtualmodelcontrol-0.1.0/docs/getting-started/first-controller.md +79 -0
  14. virtualmodelcontrol-0.1.0/docs/getting-started/install.md +50 -0
  15. virtualmodelcontrol-0.1.0/docs/getting-started/simulate.md +67 -0
  16. virtualmodelcontrol-0.1.0/docs/how-to/add-a-model.md +92 -0
  17. virtualmodelcontrol-0.1.0/docs/how-to/extend.md +111 -0
  18. virtualmodelcontrol-0.1.0/docs/index.md +57 -0
  19. virtualmodelcontrol-0.1.0/docs/reference/api.md +9 -0
  20. virtualmodelcontrol-0.1.0/docs/reference/changelog.md +2 -0
  21. virtualmodelcontrol-0.1.0/docs/reference/conventions.md +75 -0
  22. virtualmodelcontrol-0.1.0/docs/robots/helyx.md +42 -0
  23. virtualmodelcontrol-0.1.0/docs/theory/mechanisms.md +61 -0
  24. virtualmodelcontrol-0.1.0/docs/theory/pcc.md +64 -0
  25. virtualmodelcontrol-0.1.0/pyproject.toml +141 -0
  26. virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/__init__.py +75 -0
  27. virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/_version.py +24 -0
  28. virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/compiler.py +158 -0
  29. virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/control/__init__.py +5 -0
  30. virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/control/controller.py +76 -0
  31. virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/core/__init__.py +24 -0
  32. virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/core/params.py +244 -0
  33. virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/core/registry.py +54 -0
  34. virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/core/signals.py +46 -0
  35. virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/core/space.py +162 -0
  36. virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/core/symbolic.py +67 -0
  37. virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/core/units.py +34 -0
  38. virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/dynamics.py +149 -0
  39. virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/mechanisms/__init__.py +67 -0
  40. virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/mechanisms/components/__init__.py +33 -0
  41. virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/mechanisms/components/base.py +71 -0
  42. virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/mechanisms/components/dissipation.py +50 -0
  43. virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/mechanisms/components/inertance.py +56 -0
  44. virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/mechanisms/components/sources.py +63 -0
  45. virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/mechanisms/components/storage.py +272 -0
  46. virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/mechanisms/coordinates/__init__.py +24 -0
  47. virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/mechanisms/coordinates/base.py +117 -0
  48. virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/mechanisms/coordinates/frames.py +62 -0
  49. virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/mechanisms/coordinates/joints.py +51 -0
  50. virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/mechanisms/coordinates/ops.py +146 -0
  51. virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/mechanisms/coordinates/references.py +43 -0
  52. virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/mechanisms/mechanism.py +88 -0
  53. virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/models/__init__.py +22 -0
  54. virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/models/actuation.py +196 -0
  55. virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/models/assembly.py +154 -0
  56. virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/models/continuum/__init__.py +5 -0
  57. virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/models/continuum/pcc.py +120 -0
  58. virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/models/kinematic.py +41 -0
  59. virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/models/rigid/__init__.py +6 -0
  60. virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/models/rigid/couplings.py +64 -0
  61. virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/models/rigid/poe.py +95 -0
  62. virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/py.typed +0 -0
  63. virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/robots/__init__.py +5 -0
  64. virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/robots/adapt.py +98 -0
  65. virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/robots/helyx.py +79 -0
  66. virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/sim/__init__.py +7 -0
  67. virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/sim/model_plant.py +79 -0
  68. virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/sim/plant.py +40 -0
  69. virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/sim/run.py +73 -0
  70. virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/system.py +55 -0
  71. virtualmodelcontrol-0.1.0/tests/conftest.py +5 -0
  72. virtualmodelcontrol-0.1.0/tests/control/test_honesty.py +130 -0
  73. virtualmodelcontrol-0.1.0/tests/control/test_vmc_golden.py +78 -0
  74. virtualmodelcontrol-0.1.0/tests/core/test_params.py +88 -0
  75. virtualmodelcontrol-0.1.0/tests/core/test_registry.py +43 -0
  76. virtualmodelcontrol-0.1.0/tests/core/test_signals.py +15 -0
  77. virtualmodelcontrol-0.1.0/tests/core/test_space.py +52 -0
  78. virtualmodelcontrol-0.1.0/tests/core/test_symbolic.py +32 -0
  79. virtualmodelcontrol-0.1.0/tests/data/components.npz +0 -0
  80. virtualmodelcontrol-0.1.0/tests/data/dynamics.npz +0 -0
  81. virtualmodelcontrol-0.1.0/tests/data/finger.npz +0 -0
  82. virtualmodelcontrol-0.1.0/tests/data/pcc.npz +0 -0
  83. virtualmodelcontrol-0.1.0/tests/data/vmc.npz +0 -0
  84. virtualmodelcontrol-0.1.0/tests/helpers.py +55 -0
  85. virtualmodelcontrol-0.1.0/tests/mechanisms/test_components.py +135 -0
  86. virtualmodelcontrol-0.1.0/tests/mechanisms/test_components_golden.py +85 -0
  87. virtualmodelcontrol-0.1.0/tests/mechanisms/test_coordinates.py +93 -0
  88. virtualmodelcontrol-0.1.0/tests/mechanisms/test_mechanism.py +39 -0
  89. virtualmodelcontrol-0.1.0/tests/models/test_actuation.py +52 -0
  90. virtualmodelcontrol-0.1.0/tests/models/test_assembly.py +95 -0
  91. virtualmodelcontrol-0.1.0/tests/models/test_pcc.py +163 -0
  92. virtualmodelcontrol-0.1.0/tests/models/test_poe.py +75 -0
  93. virtualmodelcontrol-0.1.0/tests/robots/test_adapt.py +49 -0
  94. virtualmodelcontrol-0.1.0/tests/robots/test_helyx.py +26 -0
  95. virtualmodelcontrol-0.1.0/tests/sim/test_run.py +59 -0
  96. virtualmodelcontrol-0.1.0/tests/test_benchmark.py +27 -0
  97. virtualmodelcontrol-0.1.0/tests/test_compiler.py +116 -0
  98. virtualmodelcontrol-0.1.0/tests/test_dynamics.py +94 -0
  99. virtualmodelcontrol-0.1.0/tests/test_import.py +13 -0
@@ -0,0 +1,24 @@
1
+ # Environments
2
+ .venv/
3
+
4
+ # Python
5
+ __pycache__/
6
+ *.py[cod]
7
+ *.egg-info/
8
+ build/
9
+ dist/
10
+
11
+ # Generated
12
+ src/virtualmodelcontrol/_version.py
13
+ docs/_build/
14
+ docs/jupyter_execute/
15
+ docs/reference/generated/
16
+
17
+ # Tool caches
18
+ .pytest_cache/
19
+ .mypy_cache/
20
+ .ruff_cache/
21
+ .ipynb_checkpoints/
22
+
23
+ # Papers stay out of the repository
24
+ *.pdf
@@ -0,0 +1,40 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and versions follow
5
+ [Semantic Versioning](https://semver.org/) (0.x: the API may still change between minor
6
+ versions, with one minor version of deprecation before a removal).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.1.0] - 2026-10-04
11
+
12
+ First public release.
13
+
14
+ ### Added
15
+
16
+ - Package scaffold: `pyproject.toml` (hatchling + hatch-vcs), tests, docs, CI workflows.
17
+ - `core`: `Param` and `ParamSet` with scopes (fixed, design, episode, stage) and a `Binding` that
18
+ keeps live Params symbolic and folds the rest in; spaces `Euclidean`, `SO2`, `Product`;
19
+ symbolic helpers; `Signals`; a registry with plugin entry points; unit strings.
20
+ - `mechanisms`: coordinates (`FramePoint`, `Joint`, `State`, `Ref`, `Difference`, `Slice`,
21
+ `Stack`, `Projection`, `Norm`, `Custom`), components (linear, tanh, Gaussian, sigmoid,
22
+ polynomial and limit springs; linear and tanh dampers; `PointMass`, `Inertance`; `ForceSource`,
23
+ `GravityCompensation`) and `Mechanism`. A tanh spring on a projection saturates at its maximum
24
+ force in every direction.
25
+ - `models`: parametric `PCC` with n segments, `TendonTransmission` (θ > 0 pulls), `Direct`
26
+ drive, and `SerialChain` (product of exponentials).
27
+ - `robots.helyx`: the three Helyx arm geometries with their default parameters.
28
+ - `VirtualMechanismSystem`, `compile()` and `VMCController`: about 20 µs per control step for
29
+ three springs and three dampers on a three-segment arm (`benchmarks/tick.py`).
30
+ - `compile_dynamics()`: robot dynamics assembled from the robot's components (canonical
31
+ residual, mass matrix, energy, power, linearly implicit Euler step); `Gravity` component.
32
+ - `sim`: `Plant` and `SimPlant` protocols, `ModelPlant`, `run()` with a `Guard` (zero torque on
33
+ missing or non-finite readings) and a `RunLog`.
34
+ - `models.Actuation` protocol; `helyx.add_dynamics` with the simulated arm's stiffness and
35
+ damping.
36
+ - Docs: closed-loop simulation tutorial and an "Extend the library" how-to.
37
+ - `models.LinearCoupling` (one motor driving several joints), `models.Assembly` (several models
38
+ mounted on one base) with `StackedActuation`; `FramePoint` accepts a part and an arc parameter.
39
+ - `robots.adapt`: the ADAPT finger (two motors, coupled distal joints), joint-angle coordinate
40
+ and joint-limit spring.
@@ -0,0 +1,17 @@
1
+ cff-version: 1.2.0
2
+ message: "If you use this software, please cite it as below."
3
+ type: software
4
+ version: 0.1.0
5
+ date-released: "2026-10-04"
6
+ title: "virtualmodelcontrol: Virtual Model Control for robots"
7
+ authors:
8
+ - family-names: Vignoli
9
+ given-names: Lorenzo
10
+ affiliation: "EPFL"
11
+ repository-code: "https://github.com/vigno0405/VirtualModelControl"
12
+ license: MIT
13
+ keywords:
14
+ - robotics
15
+ - virtual model control
16
+ - passivity
17
+ - soft robots
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Lorenzo Vignoli
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,82 @@
1
+ Metadata-Version: 2.5
2
+ Name: virtualmodelcontrol
3
+ Version: 0.1.0
4
+ Summary: Virtual Model Control: robots and controllers as mechanisms of springs, dampers and inertances, compiled with CasADi.
5
+ Project-URL: Repository, https://github.com/vigno0405/VirtualModelControl
6
+ Project-URL: Documentation, https://vigno0405.github.io/VirtualModelControl/
7
+ Author: Lorenzo Vignoli
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Keywords: casadi,control,passivity,robotics,soft robots,virtual model control
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Topic :: Scientific/Engineering
16
+ Requires-Python: >=3.10
17
+ Requires-Dist: casadi>=3.6
18
+ Requires-Dist: numpy>=1.26
19
+ Requires-Dist: scipy>=1.11
20
+ Provides-Extra: dev
21
+ Requires-Dist: import-linter>=2.0; extra == 'dev'
22
+ Requires-Dist: mypy>=1.10; extra == 'dev'
23
+ Requires-Dist: pre-commit>=3.5; extra == 'dev'
24
+ Requires-Dist: pytest>=8; extra == 'dev'
25
+ Requires-Dist: ruff>=0.6; extra == 'dev'
26
+ Provides-Extra: docs
27
+ Requires-Dist: myst-nb>=1.1; extra == 'docs'
28
+ Requires-Dist: pydata-sphinx-theme>=0.15; extra == 'docs'
29
+ Requires-Dist: sphinx-copybutton>=0.5; extra == 'docs'
30
+ Requires-Dist: sphinx-design>=0.5; extra == 'docs'
31
+ Requires-Dist: sphinx>=7.2; extra == 'docs'
32
+ Provides-Extra: viz
33
+ Requires-Dist: matplotlib>=3.8; extra == 'viz'
34
+ Description-Content-Type: text/markdown
35
+
36
+ # VirtualModelControl
37
+
38
+ `virtualmodelcontrol` is a Python library for Virtual Model Control (VMC). Robots and
39
+ controllers are described as mechanisms: coordinates plus springs, dampers, inertances and
40
+ sources. One CasADi model serves the real-time controller, simulation and optimization.
41
+
42
+ A collaboration between EPFL (Prof. Josie Hughes) and the University of Cambridge
43
+ (Prof. Fulvio Forni).
44
+
45
+ ## Install
46
+
47
+ Python ≥ 3.10 on Linux, macOS or Windows:
48
+
49
+ ```bash
50
+ pip install virtualmodelcontrol
51
+ ```
52
+
53
+ Then `import virtualmodelcontrol as vmc`. The latest development version installs with
54
+ `pip install "git+https://github.com/vigno0405/VirtualModelControl.git"`.
55
+
56
+ ## Develop
57
+
58
+ On Linux or macOS (Windows: see the install page in the docs):
59
+
60
+ ```bash
61
+ git clone https://github.com/vigno0405/VirtualModelControl.git
62
+ cd VirtualModelControl
63
+ python3 -m venv .venv
64
+ .venv/bin/pip install -e ".[dev,docs]"
65
+ .venv/bin/python -m pytest
66
+ ```
67
+
68
+ On Debian/Ubuntu, `python3 -m venv` needs the `python3-venv` package
69
+ (`sudo apt install python3-venv`); a conda environment works too. If ROS 2 is sourced in your
70
+ shell, prefix these commands with `env -u PYTHONPATH` so ROS's Python packages stay out of the
71
+ environment.
72
+
73
+ ## Documentation
74
+
75
+ https://vigno0405.github.io/VirtualModelControl/
76
+
77
+ To build it locally: `sphinx-build docs docs/_build/html`, then open
78
+ `docs/_build/html/index.html`.
79
+
80
+ ## Citing
81
+
82
+ See `CITATION.cff`.
@@ -0,0 +1,47 @@
1
+ # VirtualModelControl
2
+
3
+ `virtualmodelcontrol` is a Python library for Virtual Model Control (VMC). Robots and
4
+ controllers are described as mechanisms: coordinates plus springs, dampers, inertances and
5
+ sources. One CasADi model serves the real-time controller, simulation and optimization.
6
+
7
+ A collaboration between EPFL (Prof. Josie Hughes) and the University of Cambridge
8
+ (Prof. Fulvio Forni).
9
+
10
+ ## Install
11
+
12
+ Python ≥ 3.10 on Linux, macOS or Windows:
13
+
14
+ ```bash
15
+ pip install virtualmodelcontrol
16
+ ```
17
+
18
+ Then `import virtualmodelcontrol as vmc`. The latest development version installs with
19
+ `pip install "git+https://github.com/vigno0405/VirtualModelControl.git"`.
20
+
21
+ ## Develop
22
+
23
+ On Linux or macOS (Windows: see the install page in the docs):
24
+
25
+ ```bash
26
+ git clone https://github.com/vigno0405/VirtualModelControl.git
27
+ cd VirtualModelControl
28
+ python3 -m venv .venv
29
+ .venv/bin/pip install -e ".[dev,docs]"
30
+ .venv/bin/python -m pytest
31
+ ```
32
+
33
+ On Debian/Ubuntu, `python3 -m venv` needs the `python3-venv` package
34
+ (`sudo apt install python3-venv`); a conda environment works too. If ROS 2 is sourced in your
35
+ shell, prefix these commands with `env -u PYTHONPATH` so ROS's Python packages stay out of the
36
+ environment.
37
+
38
+ ## Documentation
39
+
40
+ https://vigno0405.github.io/VirtualModelControl/
41
+
42
+ To build it locally: `sphinx-build docs docs/_build/html`, then open
43
+ `docs/_build/html/index.html`.
44
+
45
+ ## Citing
46
+
47
+ See `CITATION.cff`.
@@ -0,0 +1,58 @@
1
+ """Per-tick cost of a compiled soft-arm controller: 3 springs + 3 dampers on the 145/290/290 arm.
2
+
3
+ Run from the repository root: ``python benchmarks/tick.py``. Target: ≤ 30 µs per tick.
4
+ """
5
+
6
+ import time
7
+
8
+ import numpy as np
9
+
10
+ import virtualmodelcontrol as vmc
11
+ from virtualmodelcontrol.robots import helyx
12
+
13
+
14
+ def build() -> vmc.Compiled:
15
+ """The benchmark controller, compiled."""
16
+ arm = helyx.arm("145-290-290")
17
+ ctrl = vmc.Mechanism("ctrl")
18
+ for i, s in enumerate((0.2, 0.6, 1.0)):
19
+ point = arm.point(s=s)
20
+ goal = vmc.Ref("goal", 3, value=[0.02, 0.0, 0.7 * s])
21
+ ctrl.add(f"k{i}", vmc.LinearSpring(point - goal, 50.0))
22
+ ctrl.add(f"c{i}", vmc.LinearDamper(point, 2.0))
23
+ return vmc.compile(vmc.VirtualMechanismSystem(arm, ctrl))
24
+
25
+
26
+ def per_tick(step, inputs, n: int = 5000) -> float:
27
+ """Mean wall time of ``step(input)`` [µs] after a warm-up."""
28
+ for k in range(300):
29
+ step(inputs[k % len(inputs)])
30
+ t0 = time.perf_counter()
31
+ for k in range(n):
32
+ step(inputs[k % len(inputs)])
33
+ return (time.perf_counter() - t0) / n * 1e6
34
+
35
+
36
+ def main() -> None:
37
+ """Print compile time and per-tick costs."""
38
+ t0 = time.perf_counter()
39
+ law = build()
40
+ build_ms = (time.perf_counter() - t0) * 1e3
41
+ rng = np.random.default_rng(0)
42
+ theta, theta_dot = rng.uniform(-0.3, 0.3, (200, 9)), rng.uniform(-1.0, 1.0, (200, 9))
43
+ x = [
44
+ np.concatenate([a, b, law.live_values(), [0.0]])
45
+ for a, b in zip(theta, theta_dot, strict=True)
46
+ ]
47
+ meas = [
48
+ vmc.Signals(0.0, motor_position=a, motor_velocity=b)
49
+ for a, b in zip(theta, theta_dot, strict=True)
50
+ ]
51
+ controller = vmc.VMCController(law)
52
+ print(f"compile : {build_ms:6.1f} ms")
53
+ print(f"fast function : {per_tick(lambda v: np.asarray(law.fast(v)), x):6.1f} µs/tick")
54
+ print(f"controller.step : {per_tick(lambda m: controller.step(0.0, m), meas):6.1f} µs/tick")
55
+
56
+
57
+ if __name__ == "__main__":
58
+ main()
@@ -0,0 +1,4 @@
1
+ /* Long names (modules, classes) wrap instead of running off the page. */
2
+ h1, h2, h3, .prev-next-title, .bd-sidenav a, .bd-breadcrumbs a, .autosummary td {
3
+ overflow-wrap: anywhere;
4
+ }
@@ -0,0 +1,60 @@
1
+ {{ name | escape | underline}}
2
+
3
+ .. automodule:: {{ fullname }}
4
+
5
+ {% block attributes %}
6
+ {%- if attributes %}
7
+ .. rubric:: {{ _('Module Attributes') }}
8
+
9
+ .. autosummary::
10
+ {% for item in attributes %}
11
+ {{ item }}
12
+ {%- endfor %}
13
+ {% endif %}
14
+ {%- endblock %}
15
+
16
+ {%- block functions %}
17
+ {%- if functions %}
18
+ .. rubric:: {{ _('Functions') }}
19
+
20
+ .. autosummary::
21
+ {% for item in functions %}
22
+ {{ item }}
23
+ {%- endfor %}
24
+ {% endif %}
25
+ {%- endblock %}
26
+
27
+ {%- block classes %}
28
+ {%- if classes %}
29
+ .. rubric:: {{ _('Classes') }}
30
+
31
+ .. autosummary::
32
+ {% for item in classes %}
33
+ {{ item }}
34
+ {%- endfor %}
35
+ {% endif %}
36
+ {%- endblock %}
37
+
38
+ {%- block exceptions %}
39
+ {%- if exceptions %}
40
+ .. rubric:: {{ _('Exceptions') }}
41
+
42
+ .. autosummary::
43
+ {% for item in exceptions %}
44
+ {{ item }}
45
+ {%- endfor %}
46
+ {% endif %}
47
+ {%- endblock %}
48
+
49
+ {%- block modules %}
50
+ {%- if modules %}
51
+ .. rubric:: Modules
52
+
53
+ .. autosummary::
54
+ :toctree:
55
+ :recursive:
56
+ {% for item in modules %}
57
+ {{ item }}
58
+ {%- endfor %}
59
+ {% endif %}
60
+ {%- endblock %}
@@ -0,0 +1,51 @@
1
+ """Sphinx configuration."""
2
+
3
+ from importlib.metadata import version as _version
4
+
5
+ project = "virtualmodelcontrol"
6
+ author = "Lorenzo Vignoli"
7
+ copyright = "2026, Lorenzo Vignoli"
8
+ release = _version("virtualmodelcontrol")
9
+ version = ".".join(release.split(".")[:2])
10
+
11
+ extensions = [
12
+ "myst_nb",
13
+ "sphinx.ext.autodoc",
14
+ "sphinx.ext.autosummary",
15
+ "sphinx.ext.napoleon",
16
+ "sphinx.ext.intersphinx",
17
+ "sphinx.ext.mathjax",
18
+ "sphinx_copybutton",
19
+ "sphinx_design",
20
+ ]
21
+
22
+ myst_enable_extensions = ["amsmath", "colon_fence", "deflist", "dollarmath"]
23
+ myst_heading_anchors = 3
24
+
25
+ # Every code cell in the docs runs at each build, and an error fails the build.
26
+ nb_execution_mode = "force"
27
+ nb_execution_raise_on_error = True
28
+ nb_execution_timeout = 300
29
+
30
+ autosummary_generate = True
31
+ autodoc_typehints = "description"
32
+ autodoc_member_order = "bysource"
33
+ napoleon_google_docstring = False
34
+ napoleon_numpy_docstring = True
35
+
36
+ intersphinx_mapping = {
37
+ "python": ("https://docs.python.org/3", None),
38
+ "numpy": ("https://numpy.org/doc/stable", None),
39
+ }
40
+
41
+ html_theme = "pydata_sphinx_theme"
42
+ html_title = "virtualmodelcontrol"
43
+ exclude_patterns = ["_build", "jupyter_execute"]
44
+
45
+ html_theme_options = {"github_url": "https://github.com/vigno0405/VirtualModelControl"}
46
+ html_copy_source = False
47
+ html_show_sourcelink = False
48
+ templates_path = ["_templates"]
49
+ html_static_path = ["_static"]
50
+ html_css_files = ["custom.css"]
51
+ add_module_names = False
@@ -0,0 +1,44 @@
1
+ # Architecture
2
+
3
+ ## Vocabulary
4
+
5
+ A **mechanism** is a set of coordinates plus components acting on them. Every component is one
6
+ of four kinds:
7
+
8
+ - **storage:** has an energy `V(y)`; springs, gravity, elastic structure
9
+ - **dissipation:** dampers and friction
10
+ - **inertance:** masses, inertias, inerters
11
+ - **source:** metered forces and motions
12
+
13
+ This holds for the physical robot and for the virtual mechanism that controls it. A closed loop
14
+ is the union of both mechanisms, so the same objects simulate, optimize and control.
15
+
16
+ ## Layers
17
+
18
+ A module imports only from layers below it. `import-linter` checks this in CI
19
+ (`lint-imports`), using the contracts in `pyproject.toml`.
20
+
21
+ | Layer | Packages | Role |
22
+ |---|---|---|
23
+ | L7 research | `codesign`, `learning` | optional extras |
24
+ | L6 decide | `optim`, `adaptation` | optimization, online tuning |
25
+ | L5 analyse | `estimation`, `identification`, `passivity` | |
26
+ | L4 run | `sim` | plants, model simulator, run loop |
27
+ | L3 control | `system`, `compiler`, `dynamics`, `control` | compiled controllers and robot dynamics |
28
+ | L2 describe | `models` | kinematics, continuum, rigid, actuation |
29
+ | L1 vocabulary | `mechanisms` | coordinates and components |
30
+ | L0 core | `core`, `io` | parameters, spaces, symbolic helpers, signals, registry |
31
+ | edges | `viz`, `hardware`, `ros`, `robots` | never imported by L0–L7 |
32
+
33
+ Packages appear as their release lands; the table is the target.
34
+
35
+ ## Seams
36
+
37
+ Protocols exist only where implementations are swapped: model ↔ compiler, runtime ↔ hardware,
38
+ problem ↔ solver. Everything else is concrete classes.
39
+
40
+ ## Symbolic core
41
+
42
+ All models are written once with CasADi symbols. The dynamics have one canonical form, the
43
+ implicit residual `M(q) a + h(q, v) − B(q) u − Σ Jᵀ f = 0`, so simulation, collocation,
44
+ estimation and identification share it.
@@ -0,0 +1,49 @@
1
+ # Contributing
2
+
3
+ ## Set up
4
+
5
+ Linux or macOS, from a clone of the repository:
6
+
7
+ ```bash
8
+ python3 -m venv .venv
9
+ .venv/bin/pip install -e ".[dev,docs]"
10
+ ```
11
+
12
+ On Debian/Ubuntu, `python3 -m venv` needs the `python3-venv` package
13
+ (`sudo apt install python3-venv`); a conda environment works too. If ROS 2 is sourced in your
14
+ shell, prefix every command on this page with `env -u PYTHONPATH`.
15
+
16
+ ## Check your change
17
+
18
+ Run these from the repository root before opening a pull request:
19
+
20
+ ```bash
21
+ .venv/bin/python -m pytest
22
+ .venv/bin/ruff check .
23
+ .venv/bin/ruff format --check .
24
+ .venv/bin/mypy
25
+ .venv/bin/lint-imports
26
+ .venv/bin/sphinx-build -W -b html docs docs/_build/html
27
+ ```
28
+
29
+ `pre-commit run --all-files` runs the formatters and file checks in one go. It is not
30
+ installed as a git hook.
31
+
32
+ ## Rules
33
+
34
+ - **SI units** inside the library. Unit conversions belong in the hardware layers.
35
+ - **No numbers in model code.** Geometry, gains, masses and calibration are `Param`s; their
36
+ defaults live in robot data.
37
+ - **CasADi is the only symbolic source.** No second copy of a model in numpy or sympy.
38
+ - **Short docstrings:** numpydoc, a 1–3 line summary, units on physical parameters. Theory goes
39
+ in the docs.
40
+ - **Ported code** comes with a golden regression test: a small `.npz` fixture in `tests/data/`
41
+ generated from the original implementation, plus the property tests that apply.
42
+ - **Behaviour changes are opt-in.** Projects pin library versions; deprecate for one minor
43
+ version before removing anything.
44
+ - Add a line to `CHANGELOG.md` under "Unreleased".
45
+
46
+ ## Release
47
+
48
+ Releases follow semantic versioning (0.x for now). The version comes from git tags: pushing a
49
+ tag `vX.Y.Z` builds the package and creates a GitHub release.
@@ -0,0 +1,79 @@
1
+ ---
2
+ file_format: mystnb
3
+ kernelspec:
4
+ name: python3
5
+ ---
6
+
7
+ # Your first controller
8
+
9
+ This page builds a controller for the three-segment soft arm, evaluates its torques and changes
10
+ a gain while it runs. Every cell runs when the documentation is built.
11
+
12
+ ## The robot
13
+
14
+ A robot is a `Mechanism` with a kinematic model. The `helyx` module builds the soft arm: a
15
+ piecewise-constant-curvature (PCC) model with three segments, three tendons per segment, a lumped
16
+ mass per segment and the gravity vector of its mounting.
17
+
18
+ ```{code-cell} python
19
+ import numpy as np
20
+ import virtualmodelcontrol as vmc
21
+ from virtualmodelcontrol.robots import helyx
22
+
23
+ arm = helyx.arm("145-290-290")
24
+ print(arm)
25
+ print(list(arm.params)[:6], "...")
26
+ ```
27
+
28
+ Every number is a `Param` with a unit and a scope, geometry included:
29
+
30
+ ```{code-cell} python
31
+ arm.params["seg1.delta"]
32
+ ```
33
+
34
+ ## The controller
35
+
36
+ The controller is a second mechanism. Its components act on coordinates of the robot: here a
37
+ spring pulls the tip towards a goal, and a damper slows the middle of the arm.
38
+
39
+ ```{code-cell} python
40
+ ctrl = vmc.Mechanism("ctrl")
41
+ tip = arm.point(s=1.0) # arc parameter s: 0 at the base, 1 at the tip
42
+ middle = arm.point(s=0.5)
43
+ ctrl.add("drag", vmc.LinearSpring(tip - vmc.Ref("goal", 3, value=[0.1, 0.0, 0.6]), 30.0))
44
+ ctrl.add("damp", vmc.LinearDamper(middle, 1.5))
45
+ ctrl.add("gravity", vmc.GravityCompensation(arm))
46
+ ```
47
+
48
+ ## Compile and run
49
+
50
+ `compile` turns the robot and the controller into CasADi functions. Gains and goals (scope
51
+ `stage`) stay live inputs; geometry and attachment points are folded in.
52
+
53
+ ```{code-cell} python
54
+ law = vmc.compile(vmc.VirtualMechanismSystem(arm, ctrl))
55
+ print(law.live)
56
+ ```
57
+
58
+ The controller reads motor angles and rates and returns motor torques. Motor angles follow the
59
+ library convention: θ > 0 pulls a tendon.
60
+
61
+ ```{code-cell} python
62
+ controller = vmc.VMCController(law)
63
+ meas = vmc.Signals(0.0, motor_position=np.zeros(9), motor_velocity=np.zeros(9))
64
+ controller.step(0.0, meas)["motor_torque"].round(4)
65
+ ```
66
+
67
+ ## Change a gain while running
68
+
69
+ `set` changes live Params at once and returns the jump of the controller's stored energy, which
70
+ energy-based safety layers need.
71
+
72
+ ```{code-cell} python
73
+ jump = controller.set({"ctrl.drag.stiffness": 60.0})
74
+ print(f"energy jump: {jump:.3e} J")
75
+ controller.step(0.003, meas)["motor_torque"].round(4)
76
+ ```
77
+
78
+ Attachment points are frozen by default. To move one while running, compile with
79
+ `runtime=["ctrl.drag.s"]` (glob patterns work, for example `"*.s"`).
@@ -0,0 +1,50 @@
1
+ # Install
2
+
3
+ The library needs Python ≥ 3.10 and runs on Linux, macOS and Windows. Its core depends only
4
+ on numpy, scipy and CasADi.
5
+
6
+ ## Into an existing environment
7
+
8
+ ```bash
9
+ pip install virtualmodelcontrol
10
+ ```
11
+
12
+ The latest development version, straight from GitHub:
13
+
14
+ ```bash
15
+ pip install "git+https://github.com/vigno0405/VirtualModelControl.git"
16
+ ```
17
+
18
+ ## Into a new virtual environment
19
+
20
+ Linux and macOS:
21
+
22
+ ```bash
23
+ python3 -m venv ~/venvs/vmc
24
+ ~/venvs/vmc/bin/pip install virtualmodelcontrol
25
+ ~/venvs/vmc/bin/python -c "import virtualmodelcontrol as vmc; print(vmc.__version__)"
26
+ ```
27
+
28
+ Windows (PowerShell):
29
+
30
+ ```powershell
31
+ py -m venv $HOME\venvs\vmc
32
+ & $HOME\venvs\vmc\Scripts\pip install virtualmodelcontrol
33
+ & $HOME\venvs\vmc\Scripts\python -c "import virtualmodelcontrol as vmc; print(vmc.__version__)"
34
+ ```
35
+
36
+ On Debian/Ubuntu, `python3 -m venv` needs the `python3-venv` package
37
+ (`sudo apt install python3-venv`). A conda environment works the same way: create one with any
38
+ Python ≥ 3.10, activate it and run `pip install virtualmodelcontrol`.
39
+
40
+ ## ROS 2
41
+
42
+ The library never needs ROS. Its optional ROS 2 parts use only `rclpy` and `std_msgs`, so any
43
+ ROS 2 distribution works. ROS comes from the system's ROS install, not from pip.
44
+
45
+ When ROS is sourced in your shell, `PYTHONPATH` points at ROS's own Python packages. Keep them
46
+ out of the library's environment for installs and tests:
47
+
48
+ ```bash
49
+ env -u PYTHONPATH ~/venvs/vmc/bin/python -c "import virtualmodelcontrol"
50
+ ```