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.
- virtualmodelcontrol-0.1.0/.gitignore +24 -0
- virtualmodelcontrol-0.1.0/CHANGELOG.md +40 -0
- virtualmodelcontrol-0.1.0/CITATION.cff +17 -0
- virtualmodelcontrol-0.1.0/LICENSE +21 -0
- virtualmodelcontrol-0.1.0/PKG-INFO +82 -0
- virtualmodelcontrol-0.1.0/README.md +47 -0
- virtualmodelcontrol-0.1.0/benchmarks/tick.py +58 -0
- virtualmodelcontrol-0.1.0/docs/_static/custom.css +4 -0
- virtualmodelcontrol-0.1.0/docs/_templates/autosummary/module.rst +60 -0
- virtualmodelcontrol-0.1.0/docs/conf.py +51 -0
- virtualmodelcontrol-0.1.0/docs/development/architecture.md +44 -0
- virtualmodelcontrol-0.1.0/docs/development/contributing.md +49 -0
- virtualmodelcontrol-0.1.0/docs/getting-started/first-controller.md +79 -0
- virtualmodelcontrol-0.1.0/docs/getting-started/install.md +50 -0
- virtualmodelcontrol-0.1.0/docs/getting-started/simulate.md +67 -0
- virtualmodelcontrol-0.1.0/docs/how-to/add-a-model.md +92 -0
- virtualmodelcontrol-0.1.0/docs/how-to/extend.md +111 -0
- virtualmodelcontrol-0.1.0/docs/index.md +57 -0
- virtualmodelcontrol-0.1.0/docs/reference/api.md +9 -0
- virtualmodelcontrol-0.1.0/docs/reference/changelog.md +2 -0
- virtualmodelcontrol-0.1.0/docs/reference/conventions.md +75 -0
- virtualmodelcontrol-0.1.0/docs/robots/helyx.md +42 -0
- virtualmodelcontrol-0.1.0/docs/theory/mechanisms.md +61 -0
- virtualmodelcontrol-0.1.0/docs/theory/pcc.md +64 -0
- virtualmodelcontrol-0.1.0/pyproject.toml +141 -0
- virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/__init__.py +75 -0
- virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/_version.py +24 -0
- virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/compiler.py +158 -0
- virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/control/__init__.py +5 -0
- virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/control/controller.py +76 -0
- virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/core/__init__.py +24 -0
- virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/core/params.py +244 -0
- virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/core/registry.py +54 -0
- virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/core/signals.py +46 -0
- virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/core/space.py +162 -0
- virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/core/symbolic.py +67 -0
- virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/core/units.py +34 -0
- virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/dynamics.py +149 -0
- virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/mechanisms/__init__.py +67 -0
- virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/mechanisms/components/__init__.py +33 -0
- virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/mechanisms/components/base.py +71 -0
- virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/mechanisms/components/dissipation.py +50 -0
- virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/mechanisms/components/inertance.py +56 -0
- virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/mechanisms/components/sources.py +63 -0
- virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/mechanisms/components/storage.py +272 -0
- virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/mechanisms/coordinates/__init__.py +24 -0
- virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/mechanisms/coordinates/base.py +117 -0
- virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/mechanisms/coordinates/frames.py +62 -0
- virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/mechanisms/coordinates/joints.py +51 -0
- virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/mechanisms/coordinates/ops.py +146 -0
- virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/mechanisms/coordinates/references.py +43 -0
- virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/mechanisms/mechanism.py +88 -0
- virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/models/__init__.py +22 -0
- virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/models/actuation.py +196 -0
- virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/models/assembly.py +154 -0
- virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/models/continuum/__init__.py +5 -0
- virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/models/continuum/pcc.py +120 -0
- virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/models/kinematic.py +41 -0
- virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/models/rigid/__init__.py +6 -0
- virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/models/rigid/couplings.py +64 -0
- virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/models/rigid/poe.py +95 -0
- virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/py.typed +0 -0
- virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/robots/__init__.py +5 -0
- virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/robots/adapt.py +98 -0
- virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/robots/helyx.py +79 -0
- virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/sim/__init__.py +7 -0
- virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/sim/model_plant.py +79 -0
- virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/sim/plant.py +40 -0
- virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/sim/run.py +73 -0
- virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/system.py +55 -0
- virtualmodelcontrol-0.1.0/tests/conftest.py +5 -0
- virtualmodelcontrol-0.1.0/tests/control/test_honesty.py +130 -0
- virtualmodelcontrol-0.1.0/tests/control/test_vmc_golden.py +78 -0
- virtualmodelcontrol-0.1.0/tests/core/test_params.py +88 -0
- virtualmodelcontrol-0.1.0/tests/core/test_registry.py +43 -0
- virtualmodelcontrol-0.1.0/tests/core/test_signals.py +15 -0
- virtualmodelcontrol-0.1.0/tests/core/test_space.py +52 -0
- virtualmodelcontrol-0.1.0/tests/core/test_symbolic.py +32 -0
- virtualmodelcontrol-0.1.0/tests/data/components.npz +0 -0
- virtualmodelcontrol-0.1.0/tests/data/dynamics.npz +0 -0
- virtualmodelcontrol-0.1.0/tests/data/finger.npz +0 -0
- virtualmodelcontrol-0.1.0/tests/data/pcc.npz +0 -0
- virtualmodelcontrol-0.1.0/tests/data/vmc.npz +0 -0
- virtualmodelcontrol-0.1.0/tests/helpers.py +55 -0
- virtualmodelcontrol-0.1.0/tests/mechanisms/test_components.py +135 -0
- virtualmodelcontrol-0.1.0/tests/mechanisms/test_components_golden.py +85 -0
- virtualmodelcontrol-0.1.0/tests/mechanisms/test_coordinates.py +93 -0
- virtualmodelcontrol-0.1.0/tests/mechanisms/test_mechanism.py +39 -0
- virtualmodelcontrol-0.1.0/tests/models/test_actuation.py +52 -0
- virtualmodelcontrol-0.1.0/tests/models/test_assembly.py +95 -0
- virtualmodelcontrol-0.1.0/tests/models/test_pcc.py +163 -0
- virtualmodelcontrol-0.1.0/tests/models/test_poe.py +75 -0
- virtualmodelcontrol-0.1.0/tests/robots/test_adapt.py +49 -0
- virtualmodelcontrol-0.1.0/tests/robots/test_helyx.py +26 -0
- virtualmodelcontrol-0.1.0/tests/sim/test_run.py +59 -0
- virtualmodelcontrol-0.1.0/tests/test_benchmark.py +27 -0
- virtualmodelcontrol-0.1.0/tests/test_compiler.py +116 -0
- virtualmodelcontrol-0.1.0/tests/test_dynamics.py +94 -0
- 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,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
|
+
```
|