virtualmodelcontrol 0.1.0__tar.gz → 0.2.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 → virtualmodelcontrol-0.2.0}/.gitignore +8 -1
- virtualmodelcontrol-0.2.0/CHANGELOG.md +148 -0
- virtualmodelcontrol-0.2.0/PKG-INFO +162 -0
- virtualmodelcontrol-0.2.0/README.md +120 -0
- virtualmodelcontrol-0.2.0/docs/_ext/video.py +49 -0
- virtualmodelcontrol-0.2.0/docs/_static/create-lab.png +0 -0
- virtualmodelcontrol-0.2.0/docs/_static/custom.css +171 -0
- virtualmodelcontrol-0.2.0/docs/_static/favicon.svg +1 -0
- virtualmodelcontrol-0.2.0/docs/_static/logo-light.svg +1 -0
- virtualmodelcontrol-0.2.0/docs/_static/logo.svg +1 -0
- virtualmodelcontrol-0.2.0/docs/_static/readme-hero.svg +1910 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/docs/_templates/autosummary/module.rst +1 -1
- virtualmodelcontrol-0.2.0/docs/_templates/partials/toc.html +25 -0
- virtualmodelcontrol-0.2.0/docs/api/index.md +22 -0
- virtualmodelcontrol-0.2.0/docs/concepts/conventions.md +70 -0
- virtualmodelcontrol-0.2.0/docs/concepts/library.md +59 -0
- virtualmodelcontrol-0.2.0/docs/concepts/pcc.md +68 -0
- virtualmodelcontrol-0.2.0/docs/conf.py +110 -0
- virtualmodelcontrol-0.2.0/docs/development/contributing.md +81 -0
- virtualmodelcontrol-0.2.0/docs/development/roadmap.md +2 -0
- virtualmodelcontrol-0.2.0/docs/docs_setup.py +10 -0
- virtualmodelcontrol-0.2.0/docs/examples/finger.md +188 -0
- virtualmodelcontrol-0.2.0/docs/examples/hand.md +190 -0
- virtualmodelcontrol-0.2.0/docs/examples/soft-arm.md +253 -0
- virtualmodelcontrol-0.2.0/docs/examples/turtle.md +162 -0
- virtualmodelcontrol-0.2.0/docs/examples/two-arms.md +179 -0
- virtualmodelcontrol-0.2.0/docs/index.md +134 -0
- virtualmodelcontrol-0.2.0/docs/installation.md +114 -0
- virtualmodelcontrol-0.2.0/docs/schematics/__init__.py +1 -0
- virtualmodelcontrol-0.2.0/docs/schematics/_draw.py +107 -0
- virtualmodelcontrol-0.2.0/docs/schematics/bimanual.py +56 -0
- virtualmodelcontrol-0.2.0/docs/schematics/control_loop.py +29 -0
- virtualmodelcontrol-0.2.0/docs/schematics/energy_flow.py +42 -0
- virtualmodelcontrol-0.2.0/docs/schematics/finger.py +70 -0
- virtualmodelcontrol-0.2.0/docs/schematics/four_steps.py +52 -0
- virtualmodelcontrol-0.2.0/docs/schematics/hand.py +49 -0
- virtualmodelcontrol-0.2.0/docs/schematics/helyx.py +79 -0
- virtualmodelcontrol-0.2.0/docs/schematics/library_map.py +62 -0
- virtualmodelcontrol-0.2.0/docs/schematics/mechanism.py +132 -0
- virtualmodelcontrol-0.2.0/docs/schematics/params.py +37 -0
- virtualmodelcontrol-0.2.0/docs/schematics/pcc.py +40 -0
- virtualmodelcontrol-0.2.0/docs/schematics/turtle.py +84 -0
- virtualmodelcontrol-0.2.0/docs/schematics/ur5.py +105 -0
- virtualmodelcontrol-0.2.0/docs/scripts/readme_figure.py +52 -0
- virtualmodelcontrol-0.2.0/docs/troubleshooting.md +66 -0
- virtualmodelcontrol-0.2.0/docs/tutorials/build-a-robot.md +181 -0
- virtualmodelcontrol-0.2.0/docs/tutorials/contact.md +198 -0
- virtualmodelcontrol-0.2.0/docs/tutorials/coordinates-and-components.md +160 -0
- virtualmodelcontrol-0.2.0/docs/tutorials/energy.md +202 -0
- virtualmodelcontrol-0.2.0/docs/tutorials/extend.md +191 -0
- virtualmodelcontrol-0.2.0/docs/tutorials/first-controller.md +162 -0
- virtualmodelcontrol-0.2.0/docs/tutorials/introduction.md +80 -0
- virtualmodelcontrol-0.2.0/docs/tutorials/kinematics.md +144 -0
- virtualmodelcontrol-0.2.0/docs/tutorials/parameters.md +185 -0
- virtualmodelcontrol-0.2.0/docs/tutorials/tuning.md +202 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/pyproject.toml +43 -9
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/src/virtualmodelcontrol/__init__.py +28 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/src/virtualmodelcontrol/_version.py +2 -2
- virtualmodelcontrol-0.2.0/src/virtualmodelcontrol/control/__init__.py +12 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/src/virtualmodelcontrol/control/controller.py +15 -5
- virtualmodelcontrol-0.2.0/src/virtualmodelcontrol/control/output.py +81 -0
- virtualmodelcontrol-0.2.0/src/virtualmodelcontrol/hardware/__init__.py +22 -0
- virtualmodelcontrol-0.2.0/src/virtualmodelcontrol/hardware/bus.py +202 -0
- virtualmodelcontrol-0.2.0/src/virtualmodelcontrol/hardware/check.py +40 -0
- virtualmodelcontrol-0.2.0/src/virtualmodelcontrol/hardware/dynamixel.py +161 -0
- virtualmodelcontrol-0.2.0/src/virtualmodelcontrol/hardware/homing.py +70 -0
- virtualmodelcontrol-0.2.0/src/virtualmodelcontrol/hardware/profile.py +194 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/src/virtualmodelcontrol/mechanisms/__init__.py +10 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/src/virtualmodelcontrol/mechanisms/components/__init__.py +6 -2
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/src/virtualmodelcontrol/mechanisms/components/dissipation.py +22 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/src/virtualmodelcontrol/mechanisms/components/sources.py +25 -1
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/src/virtualmodelcontrol/mechanisms/components/storage.py +32 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/src/virtualmodelcontrol/mechanisms/coordinates/__init__.py +3 -0
- virtualmodelcontrol-0.2.0/src/virtualmodelcontrol/mechanisms/coordinates/geometry.py +74 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/src/virtualmodelcontrol/models/__init__.py +4 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/src/virtualmodelcontrol/models/actuation.py +27 -11
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/src/virtualmodelcontrol/models/assembly.py +26 -9
- virtualmodelcontrol-0.2.0/src/virtualmodelcontrol/models/joint_space.py +33 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/src/virtualmodelcontrol/models/kinematic.py +14 -3
- virtualmodelcontrol-0.2.0/src/virtualmodelcontrol/models/kinematics.py +93 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/src/virtualmodelcontrol/models/rigid/poe.py +71 -8
- virtualmodelcontrol-0.2.0/src/virtualmodelcontrol/robots/__init__.py +5 -0
- virtualmodelcontrol-0.2.0/src/virtualmodelcontrol/robots/adapt.py +612 -0
- virtualmodelcontrol-0.2.0/src/virtualmodelcontrol/robots/bimanual.py +170 -0
- virtualmodelcontrol-0.2.0/src/virtualmodelcontrol/robots/helyx.py +148 -0
- virtualmodelcontrol-0.2.0/src/virtualmodelcontrol/robots/turtle.py +94 -0
- virtualmodelcontrol-0.2.0/src/virtualmodelcontrol/robots/ur5.py +72 -0
- virtualmodelcontrol-0.2.0/src/virtualmodelcontrol/ros/__init__.py +23 -0
- virtualmodelcontrol-0.2.0/src/virtualmodelcontrol/ros/joint_io.py +51 -0
- virtualmodelcontrol-0.2.0/src/virtualmodelcontrol/ros/params.py +43 -0
- virtualmodelcontrol-0.2.0/src/virtualmodelcontrol/ros/plant.py +121 -0
- virtualmodelcontrol-0.2.0/src/virtualmodelcontrol/ros/twin.py +82 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/src/virtualmodelcontrol/sim/__init__.py +2 -2
- virtualmodelcontrol-0.2.0/src/virtualmodelcontrol/sim/run.py +158 -0
- virtualmodelcontrol-0.2.0/src/virtualmodelcontrol/viz/__init__.py +35 -0
- virtualmodelcontrol-0.2.0/src/virtualmodelcontrol/viz/animation.py +141 -0
- virtualmodelcontrol-0.2.0/src/virtualmodelcontrol/viz/draw.py +269 -0
- virtualmodelcontrol-0.2.0/src/virtualmodelcontrol/viz/style.py +91 -0
- virtualmodelcontrol-0.2.0/tests/control/test_output.py +63 -0
- virtualmodelcontrol-0.2.0/tests/data/derivatives.npz +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/tests/data/dynamics.npz +0 -0
- virtualmodelcontrol-0.2.0/tests/data/hand.npz +0 -0
- virtualmodelcontrol-0.2.0/tests/data/turtle.npz +0 -0
- virtualmodelcontrol-0.2.0/tests/hardware/test_dynamixel.py +169 -0
- virtualmodelcontrol-0.2.0/tests/hardware/test_profile.py +85 -0
- virtualmodelcontrol-0.2.0/tests/hardware/test_templates.py +47 -0
- virtualmodelcontrol-0.2.0/tests/mechanisms/test_contact.py +69 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/tests/models/test_actuation.py +13 -0
- virtualmodelcontrol-0.2.0/tests/models/test_kinematics.py +57 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/tests/robots/test_adapt.py +27 -0
- virtualmodelcontrol-0.2.0/tests/robots/test_bimanual.py +53 -0
- virtualmodelcontrol-0.2.0/tests/robots/test_hand.py +84 -0
- virtualmodelcontrol-0.2.0/tests/robots/test_parametric.py +136 -0
- virtualmodelcontrol-0.2.0/tests/robots/test_turtle.py +50 -0
- virtualmodelcontrol-0.2.0/tests/robots/test_ur5.py +78 -0
- virtualmodelcontrol-0.2.0/tests/ros/conftest.py +9 -0
- virtualmodelcontrol-0.2.0/tests/ros/test_joint_io.py +29 -0
- virtualmodelcontrol-0.2.0/tests/ros/test_ros.py +98 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/tests/sim/test_run.py +37 -1
- virtualmodelcontrol-0.2.0/tests/sim/test_wallclock.py +99 -0
- virtualmodelcontrol-0.2.0/tests/test_docs.py +43 -0
- virtualmodelcontrol-0.2.0/tests/test_import.py +30 -0
- virtualmodelcontrol-0.2.0/tests/test_readme.py +18 -0
- virtualmodelcontrol-0.2.0/tests/viz/test_animate.py +88 -0
- virtualmodelcontrol-0.2.0/tests/viz/test_viz.py +66 -0
- virtualmodelcontrol-0.1.0/CHANGELOG.md +0 -40
- virtualmodelcontrol-0.1.0/PKG-INFO +0 -82
- virtualmodelcontrol-0.1.0/README.md +0 -47
- virtualmodelcontrol-0.1.0/docs/_static/custom.css +0 -4
- virtualmodelcontrol-0.1.0/docs/conf.py +0 -51
- virtualmodelcontrol-0.1.0/docs/development/architecture.md +0 -44
- virtualmodelcontrol-0.1.0/docs/development/contributing.md +0 -49
- virtualmodelcontrol-0.1.0/docs/getting-started/first-controller.md +0 -79
- virtualmodelcontrol-0.1.0/docs/getting-started/install.md +0 -50
- virtualmodelcontrol-0.1.0/docs/getting-started/simulate.md +0 -67
- virtualmodelcontrol-0.1.0/docs/how-to/add-a-model.md +0 -92
- virtualmodelcontrol-0.1.0/docs/how-to/extend.md +0 -111
- virtualmodelcontrol-0.1.0/docs/index.md +0 -57
- virtualmodelcontrol-0.1.0/docs/reference/api.md +0 -9
- virtualmodelcontrol-0.1.0/docs/reference/conventions.md +0 -75
- virtualmodelcontrol-0.1.0/docs/robots/helyx.md +0 -42
- virtualmodelcontrol-0.1.0/docs/theory/mechanisms.md +0 -61
- virtualmodelcontrol-0.1.0/docs/theory/pcc.md +0 -64
- virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/control/__init__.py +0 -5
- virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/robots/__init__.py +0 -5
- virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/robots/adapt.py +0 -98
- virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/robots/helyx.py +0 -79
- virtualmodelcontrol-0.1.0/src/virtualmodelcontrol/sim/run.py +0 -73
- virtualmodelcontrol-0.1.0/tests/test_import.py +0 -13
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/CITATION.cff +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/LICENSE +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/benchmarks/tick.py +0 -0
- {virtualmodelcontrol-0.1.0/docs/reference → virtualmodelcontrol-0.2.0/docs/development}/changelog.md +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/src/virtualmodelcontrol/compiler.py +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/src/virtualmodelcontrol/core/__init__.py +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/src/virtualmodelcontrol/core/params.py +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/src/virtualmodelcontrol/core/registry.py +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/src/virtualmodelcontrol/core/signals.py +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/src/virtualmodelcontrol/core/space.py +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/src/virtualmodelcontrol/core/symbolic.py +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/src/virtualmodelcontrol/core/units.py +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/src/virtualmodelcontrol/dynamics.py +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/src/virtualmodelcontrol/mechanisms/components/base.py +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/src/virtualmodelcontrol/mechanisms/components/inertance.py +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/src/virtualmodelcontrol/mechanisms/coordinates/base.py +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/src/virtualmodelcontrol/mechanisms/coordinates/frames.py +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/src/virtualmodelcontrol/mechanisms/coordinates/joints.py +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/src/virtualmodelcontrol/mechanisms/coordinates/ops.py +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/src/virtualmodelcontrol/mechanisms/coordinates/references.py +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/src/virtualmodelcontrol/mechanisms/mechanism.py +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/src/virtualmodelcontrol/models/continuum/__init__.py +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/src/virtualmodelcontrol/models/continuum/pcc.py +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/src/virtualmodelcontrol/models/rigid/__init__.py +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/src/virtualmodelcontrol/models/rigid/couplings.py +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/src/virtualmodelcontrol/py.typed +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/src/virtualmodelcontrol/sim/model_plant.py +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/src/virtualmodelcontrol/sim/plant.py +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/src/virtualmodelcontrol/system.py +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/tests/conftest.py +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/tests/control/test_honesty.py +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/tests/control/test_vmc_golden.py +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/tests/core/test_params.py +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/tests/core/test_registry.py +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/tests/core/test_signals.py +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/tests/core/test_space.py +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/tests/core/test_symbolic.py +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/tests/data/components.npz +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/tests/data/finger.npz +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/tests/data/pcc.npz +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/tests/data/vmc.npz +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/tests/helpers.py +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/tests/mechanisms/test_components.py +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/tests/mechanisms/test_components_golden.py +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/tests/mechanisms/test_coordinates.py +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/tests/mechanisms/test_mechanism.py +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/tests/models/test_assembly.py +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/tests/models/test_pcc.py +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/tests/models/test_poe.py +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/tests/robots/test_helyx.py +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/tests/test_benchmark.py +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/tests/test_compiler.py +0 -0
- {virtualmodelcontrol-0.1.0 → virtualmodelcontrol-0.2.0}/tests/test_dynamics.py +0 -0
|
@@ -12,7 +12,6 @@ dist/
|
|
|
12
12
|
src/virtualmodelcontrol/_version.py
|
|
13
13
|
docs/_build/
|
|
14
14
|
docs/jupyter_execute/
|
|
15
|
-
docs/reference/generated/
|
|
16
15
|
|
|
17
16
|
# Tool caches
|
|
18
17
|
.pytest_cache/
|
|
@@ -22,3 +21,11 @@ docs/reference/generated/
|
|
|
22
21
|
|
|
23
22
|
# Papers stay out of the repository
|
|
24
23
|
*.pdf
|
|
24
|
+
docs/api/generated/
|
|
25
|
+
# Animations written by the docs build
|
|
26
|
+
docs/**/*.mp4
|
|
27
|
+
docs/**/*.gif
|
|
28
|
+
docs/**/*.webp
|
|
29
|
+
|
|
30
|
+
# Lock file written by editor tooling; the project pins nothing
|
|
31
|
+
uv.lock
|
|
@@ -0,0 +1,148 @@
|
|
|
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.2.0] - 2026-10-04
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Every dependency installs with the package: numpy, SciPy, CasADi, matplotlib, PyYAML, the
|
|
15
|
+
Dynamixel SDK and pyserial. CI runs on Ubuntu 22.04 and 24.04, with a clean-install job.
|
|
16
|
+
- Robot templates: `robots.bimanual` (two Helyx arms on one frame, with their identified
|
|
17
|
+
stiffness and damping and the transmission efficiency 0.12), `robots.adapt.hand` (five digits,
|
|
18
|
+
13 motors, spread coupling, masses and joint limits), `robots.turtle` (two cranks and the
|
|
19
|
+
virtual-flywheel controller), `robots.ur5` (from DH parameters) and `ur5.with_hand()`.
|
|
20
|
+
- Every template is parametric in its geometry, with today's values as defaults:
|
|
21
|
+
`helyx.arm(lengths=..., section_radius=..., spool_radius=..., tendon_angles=..., masses=...)`
|
|
22
|
+
for any number of segments; `bimanual.arms(lengths=..., tendon_angles=...,
|
|
23
|
+
base_positions=..., efficiency=...)`, each one for both arms or by arm;
|
|
24
|
+
`adapt.finger(link_lengths=..., link_masses=..., link_cogs=..., joint_axes=...,
|
|
25
|
+
motor_radius=..., pulley_radius=..., pip_transmission=...)` and `adapt.finger_coupling`;
|
|
26
|
+
`adapt.hand(...)` and `ur5.with_hand(...)`, which replace any entry of the hand's geometry
|
|
27
|
+
tables by key (`tip_offsets={"thumb": ...}`), with the hand's mounting position on the
|
|
28
|
+
flange; `ur5.arm(d=..., a=..., alpha=...)`; joint-limit springs with custom ranges; output
|
|
29
|
+
stages and `add_dynamics` sized to the arm.
|
|
30
|
+
- `vmc.Kinematics`: positions, rotations, Jacobians, angular Jacobians and Hessians of any site,
|
|
31
|
+
from configuration or from motor angles.
|
|
32
|
+
- `models.JointSpace` for robots described by their joints only; `SerialChain.from_dh` and site
|
|
33
|
+
rotations; `Assembly` parts mounted on another part's frame.
|
|
34
|
+
- `TendonTransmission(efficiency=...)` and `Direct(efficiency=...)` (one value or one per
|
|
35
|
+
motor): the robot receives the commanded torque times the efficiency. `adapt.finger`,
|
|
36
|
+
`adapt.hand` and `ur5.with_hand` take `efficiency=...` and stay lossless by default: the
|
|
37
|
+
measured `adapt.MOTOR_EFFICIENCY` and `adapt.HAND_MOTOR_EFFICIENCY` are static calibrations for
|
|
38
|
+
force estimates, and unequal efficiencies on coupled motors make virtual elements
|
|
39
|
+
non-conservative in simulation.
|
|
40
|
+
- `SpeedRegulator`: drives a virtual state at a commanded speed, with a ramp.
|
|
41
|
+
- `control.output`, opt-in output stages for real hardware: `FrictionCompensation`,
|
|
42
|
+
`Pretension`, `TorqueOffset`, `TorqueLimit`; each template's own stage
|
|
43
|
+
(`helyx.output_stage()`, `bimanual.output_stage()`, `adapt.output_stage()`,
|
|
44
|
+
`adapt.hand_output_stage()`).
|
|
45
|
+
- `vmc.viz`: the lab figure style (`use_style`, `save` as PDF and SVG) and drawing helpers
|
|
46
|
+
(`draw_robot`, `draw_spring`, `draw_damper`, `draw_frame`, `draw_force`, `draw_goal`,
|
|
47
|
+
`label_axes`). `vmc.viz` is available after `import virtualmodelcontrol as vmc` and loads
|
|
48
|
+
matplotlib only when first used.
|
|
49
|
+
- `vmc.viz.animate`: an animation of a run (the robot, springs to fixed points, the path of a
|
|
50
|
+
site, anything else through a callback), saved as MP4, GIF or animated WebP. `imageio-ffmpeg`
|
|
51
|
+
is now a dependency, so MP4 works on every system.
|
|
52
|
+
- `vmc.sim.run(..., z0=...)` starts the controller from a given virtual state, and the run log
|
|
53
|
+
records the virtual state `z` when the controller has one.
|
|
54
|
+
- Contact as stiff one-sided springs: signed distances `PlaneDistance` and `SphereDistance`,
|
|
55
|
+
`ContactSpring` (optional smoothing for optimization) and `ContactDamper` (damps only in
|
|
56
|
+
contact); `adapt.add_dynamics` gives the finger and the hand their gravity for simulation.
|
|
57
|
+
- Documentation rebuilt for students: the Material layout (sections as tabs, the pages of each
|
|
58
|
+
section in the left sidebar), large type, the library's logo and navy colours;
|
|
59
|
+
installation and troubleshooting; tutorials (how it works, a first controller, coordinates
|
|
60
|
+
and components, parameters, energy, kinematics, tuning, contact, building a robot, extending
|
|
61
|
+
the library); an example per robot (soft arm, two arms, finger, hand and the UR5, turtle), each
|
|
62
|
+
opening with its robot's schematic and parameters; concepts (the library's structure, soft-arm
|
|
63
|
+
kinematics, conventions and glossary). Every figure, schematic, table value and animation is
|
|
64
|
+
computed when the documentation builds.
|
|
65
|
+
- `vmc.hardware.HardwareProfile`: the motors of a robot (bus ID, Dynamixel model, sign, mode,
|
|
66
|
+
motor constant, optional torque limit) in the library's motor order, with the bus settings;
|
|
67
|
+
conversions both ways between encoder ticks, raw velocities, goal currents and the published
|
|
68
|
+
degrees of a ROS driver and SI units, with commands clamped before the integer conversion so
|
|
69
|
+
they saturate instead of overflowing; saved and loaded as YAML. Each template has its
|
|
70
|
+
profile: `helyx.hardware()`, `bimanual.hardware()`, `adapt.finger_hardware()`,
|
|
71
|
+
`adapt.hand_hardware()`, `turtle.hardware()`.
|
|
72
|
+
- `vmc.hardware.DynamixelPlant`: a robot's Dynamixel motors as a plant through the Dynamixel
|
|
73
|
+
SDK, without ROS. It sets a safe goal before each motor's torque goes on, takes the start
|
|
74
|
+
positions as zero, reads angles and smoothed rates, writes torques as clamped goal currents,
|
|
75
|
+
sends zero torque when commands stop (a watchdog), and on leaving (exceptions and Ctrl-C
|
|
76
|
+
included) sends zero torque and switches the torque motors off; held motors keep their
|
|
77
|
+
position. `hardware.home` drives the motors slowly to recorded home positions and refuses a
|
|
78
|
+
motor that lost a turn; `hardware.scan` finds the motors and their baud rate;
|
|
79
|
+
`hardware.FakeBus` stands in for the bus in tests and dry runs.
|
|
80
|
+
- `vmc.sim.WallClock`: `vmc.sim.run` on real time, the loop of simulation unchanged. Each step
|
|
81
|
+
runs on the computer's clock with its measured time; a reading older than `stale` sends
|
|
82
|
+
zero torque; a late step starts the next one from now instead of catching up; the log
|
|
83
|
+
records each step's `dt`, and `log.info` the rate, the slowest step, the overruns and the
|
|
84
|
+
stale readings, with a warning when steps overran.
|
|
85
|
+
- `virtualmodelcontrol.ros` (ROS 2, any distribution; rclpy and std_msgs, loaded only when
|
|
86
|
+
used): `RosPlant`, the robot behind the lab driver's topics (degrees, the bus order, raw
|
|
87
|
+
signs, torques kept within the range of the driver's goal current); `serve`, a digital twin
|
|
88
|
+
that publishes a simulated robot on the same topics, with a lockstep mode in which a run over
|
|
89
|
+
ROS repeats the simulator's; `LiveParams`, a controller's live Params as ROS parameters,
|
|
90
|
+
applied between steps.
|
|
91
|
+
|
|
92
|
+
### Changed
|
|
93
|
+
|
|
94
|
+
- The soft arm models the efficiency of its tendons: it receives η = 0.12 times the commanded
|
|
95
|
+
torque (`helyx.EFFICIENCY`), and `helyx.SIM_STIFFNESS` and `helyx.SIM_DAMPING` are the
|
|
96
|
+
identified, physical values. 0.1.0 divided them by 0.12 and delivered the full torque: the
|
|
97
|
+
same equilibria without gravity, but natural frequencies about 2.9 times too high and a sag
|
|
98
|
+
under gravity about 8 times too small. Controllers are unchanged and never divide their
|
|
99
|
+
torques by η, so the simulated arm receives η of every command, gravity compensation
|
|
100
|
+
included.
|
|
101
|
+
- The figure style without LaTeX uses matplotlib's own Computer Modern font (`cmr10`) instead
|
|
102
|
+
of DejaVu Serif, with tick labels set as math. Figures made with LaTeX are unchanged.
|
|
103
|
+
|
|
104
|
+
### Fixed
|
|
105
|
+
|
|
106
|
+
- `RunLog` copies each row, so a signal updated in place (a controller's virtual state) is
|
|
107
|
+
recorded correctly at every step.
|
|
108
|
+
- `bimanual.add_dynamics` takes the stiffness and damping of one arm (a dict by arm, arrays
|
|
109
|
+
included) and keeps the other arm's identified values.
|
|
110
|
+
- `viz.draw_robot`: no joint markers along continuum arms mounted in an assembly; robots without
|
|
111
|
+
geometry (joint-space models) draw nothing instead of failing; serial chains end at their
|
|
112
|
+
`tool` site (the UR5 flange) as well as at a `tip`.
|
|
113
|
+
|
|
114
|
+
## [0.1.0] - 2026-10-04
|
|
115
|
+
|
|
116
|
+
First public release.
|
|
117
|
+
|
|
118
|
+
### Added
|
|
119
|
+
|
|
120
|
+
- Package scaffold: `pyproject.toml` (hatchling + hatch-vcs), tests, docs, CI workflows.
|
|
121
|
+
- `core`: `Param` and `ParamSet` with scopes (fixed, design, episode, stage) and a `Binding` that
|
|
122
|
+
keeps live Params symbolic and folds the rest in; spaces `Euclidean`, `SO2`, `Product`;
|
|
123
|
+
symbolic helpers; `Signals`; a registry with plugin entry points; unit strings.
|
|
124
|
+
- `mechanisms`: coordinates (`FramePoint`, `Joint`, `State`, `Ref`, `Difference`, `Slice`,
|
|
125
|
+
`Stack`, `Projection`, `Norm`, `Custom`), components (linear, tanh, Gaussian, sigmoid,
|
|
126
|
+
polynomial and limit springs; linear and tanh dampers; `PointMass`, `Inertance`; `ForceSource`,
|
|
127
|
+
`GravityCompensation`) and `Mechanism`. A tanh spring on a projection saturates at its maximum
|
|
128
|
+
force in every direction.
|
|
129
|
+
- `models`: parametric `PCC` with n segments, `TendonTransmission` (θ > 0 pulls), `Direct`
|
|
130
|
+
drive, and `SerialChain` (product of exponentials).
|
|
131
|
+
- `robots.helyx`: the three Helyx arm geometries with their default parameters.
|
|
132
|
+
- `VirtualMechanismSystem`, `compile()` and `VMCController`: about 20 µs per control step for
|
|
133
|
+
three springs and three dampers on a three-segment arm (`benchmarks/tick.py`).
|
|
134
|
+
- `compile_dynamics()`: robot dynamics assembled from the robot's components (canonical
|
|
135
|
+
residual, mass matrix, energy, power, linearly implicit Euler step); `Gravity` component.
|
|
136
|
+
- `sim`: `Plant` and `SimPlant` protocols, `ModelPlant`, `run()` with a `Guard` (zero torque on
|
|
137
|
+
missing or non-finite readings) and a `RunLog`.
|
|
138
|
+
- `models.Actuation` protocol; `helyx.add_dynamics` with the simulated arm's stiffness and
|
|
139
|
+
damping.
|
|
140
|
+
- Docs: closed-loop simulation tutorial and an "Extend the library" how-to.
|
|
141
|
+
- `models.LinearCoupling` (one motor driving several joints), `models.Assembly` (several models
|
|
142
|
+
mounted on one base) with `StackedActuation`; `FramePoint` accepts a part and an arc parameter.
|
|
143
|
+
- `robots.adapt`: the ADAPT finger (two motors, coupled distal joints), joint-angle coordinate
|
|
144
|
+
and joint-limit spring.
|
|
145
|
+
|
|
146
|
+
[Unreleased]: https://github.com/vigno0405/VirtualModelControl/compare/v0.2.0...HEAD
|
|
147
|
+
[0.2.0]: https://github.com/vigno0405/VirtualModelControl/compare/v0.1.0...v0.2.0
|
|
148
|
+
[0.1.0]: https://github.com/vigno0405/VirtualModelControl/releases/tag/v0.1.0
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: virtualmodelcontrol
|
|
3
|
+
Version: 0.2.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: Operating System :: POSIX :: Linux
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Requires-Dist: casadi>=3.6
|
|
24
|
+
Requires-Dist: dynamixel-sdk>=3.7
|
|
25
|
+
Requires-Dist: imageio-ffmpeg>=0.5
|
|
26
|
+
Requires-Dist: matplotlib>=3.8
|
|
27
|
+
Requires-Dist: numpy>=1.26
|
|
28
|
+
Requires-Dist: pyserial>=3.5
|
|
29
|
+
Requires-Dist: pyyaml>=6
|
|
30
|
+
Requires-Dist: scipy>=1.11
|
|
31
|
+
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: import-linter>=2.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
34
|
+
Requires-Dist: pre-commit>=3.5; extra == 'dev'
|
|
35
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
36
|
+
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
37
|
+
Provides-Extra: docs
|
|
38
|
+
Requires-Dist: myst-nb>=1.1; extra == 'docs'
|
|
39
|
+
Requires-Dist: sphinx-immaterial>=0.13; extra == 'docs'
|
|
40
|
+
Requires-Dist: sphinx>=7.2; extra == 'docs'
|
|
41
|
+
Description-Content-Type: text/markdown
|
|
42
|
+
|
|
43
|
+
<a href="https://www.epfl.ch/labs/create/"><img align="right" height="72" src="https://raw.githubusercontent.com/vigno0405/VirtualModelControl/main/docs/_static/create-lab.png" alt="CREATE Lab, EPFL"></a>
|
|
44
|
+
|
|
45
|
+
# VirtualModelControl
|
|
46
|
+
|
|
47
|
+
`virtualmodelcontrol` is a Python library for Virtual Model Control. You build a controller by
|
|
48
|
+
attaching virtual springs, dampers and masses to your robot, and the library turns them into
|
|
49
|
+
motor torques at the control rate, in simulation and on the real robot.
|
|
50
|
+
|
|
51
|
+
<p align="center">
|
|
52
|
+
<a href="https://vigno0405.github.io/VirtualModelControl/"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/vigno0405/VirtualModelControl/main/docs/_static/logo-light.svg"><img src="https://raw.githubusercontent.com/vigno0405/VirtualModelControl/main/docs/_static/logo.svg" height="64" align="middle" alt="virtualmodelcontrol logo"></picture></a>
|
|
53
|
+
|
|
54
|
+
<a href="https://vigno0405.github.io/VirtualModelControl/"><img src="https://img.shields.io/badge/Documentation-tutorials%20%C2%B7%20examples%20%C2%B7%20API-3c5488?style=for-the-badge&labelColor=13294b" align="middle" alt="Documentation: tutorials, examples, API"></a>
|
|
55
|
+
</p>
|
|
56
|
+
|
|
57
|
+
<p align="center">
|
|
58
|
+
<img src="https://raw.githubusercontent.com/vigno0405/VirtualModelControl/main/docs/_static/readme-hero.svg" width="820" alt="A soft arm whose tip a virtual spring pulls towards a goal, and the distance from the tip to the goal over time">
|
|
59
|
+
</p>
|
|
60
|
+
|
|
61
|
+
## Install
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
pip install virtualmodelcontrol
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
The newest code, from GitHub, before it is released (the same line updates an existing install):
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
pip install --upgrade "git+https://github.com/vigno0405/VirtualModelControl.git"
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Virtual environments, conda, ROS 2 and pinned versions are covered in [Installation][install].
|
|
74
|
+
|
|
75
|
+
## Requirements
|
|
76
|
+
|
|
77
|
+
Python 3.10 or newer, tested with 3.10 to 3.14 on Ubuntu 22.04 and 24.04. Nothing to compile;
|
|
78
|
+
ROS and LaTeX are not needed.
|
|
79
|
+
|
|
80
|
+
## Dependencies
|
|
81
|
+
|
|
82
|
+
pip installs them with the library:
|
|
83
|
+
|
|
84
|
+
| Package | Version | Used for |
|
|
85
|
+
| --- | --- | --- |
|
|
86
|
+
| [numpy](https://numpy.org) | 1.26 or newer, 2.x included | arrays |
|
|
87
|
+
| [CasADi](https://web.casadi.org) | 3.6 or newer | the symbolic models, their derivatives and the compiled controllers |
|
|
88
|
+
| [SciPy](https://scipy.org) | 1.11 or newer | rotations of chains built from DH tables |
|
|
89
|
+
| [matplotlib](https://matplotlib.org) | 3.8 or newer | figures and animations |
|
|
90
|
+
| [imageio-ffmpeg](https://github.com/imageio/imageio-ffmpeg) | 0.5 or newer | MP4 videos of the animations |
|
|
91
|
+
| [PyYAML](https://pyyaml.org) | 6 or newer | robot and hardware files |
|
|
92
|
+
| [Dynamixel SDK](https://github.com/ROBOTIS-GIT/DynamixelSDK) | 3.7 or newer | Dynamixel motors |
|
|
93
|
+
| [pyserial](https://github.com/pyserial/pyserial) | 3.5 or newer | serial sensors |
|
|
94
|
+
|
|
95
|
+
## The example in the figure
|
|
96
|
+
|
|
97
|
+
The robot and the controller are two separate mechanisms: the robot mechanism describes the
|
|
98
|
+
hardware, and the controller holds the virtual elements placed on it.
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
import numpy as np
|
|
102
|
+
import virtualmodelcontrol as vmc
|
|
103
|
+
from virtualmodelcontrol.robots import helyx
|
|
104
|
+
|
|
105
|
+
arm = helyx.arm("145-290-290") # a soft arm with nine tendon motors
|
|
106
|
+
tip = arm.point(s=1.0)
|
|
107
|
+
|
|
108
|
+
ctrl = vmc.Mechanism("ctrl")
|
|
109
|
+
ctrl.add("reach", vmc.LinearSpring(tip - [0.25, 0.0, 0.55], 600.0)) # [N/m]
|
|
110
|
+
ctrl.add("damp", vmc.LinearDamper(tip, 5.0)) # [N·s/m]
|
|
111
|
+
ctrl.add("gravity", vmc.GravityCompensation(arm))
|
|
112
|
+
controller = vmc.VMCController(vmc.compile(vmc.VirtualMechanismSystem(arm, ctrl)))
|
|
113
|
+
|
|
114
|
+
meas = vmc.Signals(0.0, motor_position=np.zeros(9), motor_velocity=np.zeros(9))
|
|
115
|
+
torques = controller.step(0.0, meas)["motor_torque"] # [N·m], one per motor
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
The same controller runs on a simulation built from the robot's own masses, stiffness and
|
|
119
|
+
damping; [readme_figure.py][figure] draws the figure from this run.
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
arm = helyx.add_dynamics(arm)
|
|
123
|
+
plant = vmc.sim.ModelPlant(arm)
|
|
124
|
+
log = vmc.sim.run(plant, controller, vmc.sim.SimClock(dt=1 / 330), T=3.0)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Examples
|
|
128
|
+
|
|
129
|
+
Each example builds a ready-made robot, controls it in simulation and animates the run:
|
|
130
|
+
|
|
131
|
+
- [Soft arm][soft-arm]: reach a point, avoid an obstacle, shape the whole arm.
|
|
132
|
+
- [Two arms][two-arms]: squeeze an object between the tips of two soft arms.
|
|
133
|
+
- [Finger][finger]: a stiff fingertip and soft joint limits.
|
|
134
|
+
- [Hand][hand]: grasp a ball, alone or mounted on a UR5 arm.
|
|
135
|
+
- [Turtle][turtle]: two cranks that follow a virtual flywheel.
|
|
136
|
+
|
|
137
|
+
## Documentation
|
|
138
|
+
|
|
139
|
+
The [documentation][docs] holds the tutorials, the examples, the concepts and the API. What
|
|
140
|
+
changed is in the [changelog][changelog], what comes next in the [roadmap][roadmap], and how to
|
|
141
|
+
contribute in [Contributing][contributing].
|
|
142
|
+
|
|
143
|
+
## Authors
|
|
144
|
+
|
|
145
|
+
Lorenzo Vignoli, at the [CREATE Lab][lab] of EPFL (Prof. Josie Hughes), in a collaboration
|
|
146
|
+
with the University of Cambridge (Prof. Fulvio Forni). To cite the library, see [CITATION.cff][cite].
|
|
147
|
+
The code is under the [MIT license][license].
|
|
148
|
+
|
|
149
|
+
[lab]: https://www.epfl.ch/labs/create/
|
|
150
|
+
[install]: https://vigno0405.github.io/VirtualModelControl/installation.html
|
|
151
|
+
[figure]: https://github.com/vigno0405/VirtualModelControl/blob/main/docs/scripts/readme_figure.py
|
|
152
|
+
[soft-arm]: https://vigno0405.github.io/VirtualModelControl/examples/soft-arm.html
|
|
153
|
+
[two-arms]: https://vigno0405.github.io/VirtualModelControl/examples/two-arms.html
|
|
154
|
+
[finger]: https://vigno0405.github.io/VirtualModelControl/examples/finger.html
|
|
155
|
+
[hand]: https://vigno0405.github.io/VirtualModelControl/examples/hand.html
|
|
156
|
+
[turtle]: https://vigno0405.github.io/VirtualModelControl/examples/turtle.html
|
|
157
|
+
[docs]: https://vigno0405.github.io/VirtualModelControl/
|
|
158
|
+
[changelog]: https://vigno0405.github.io/VirtualModelControl/development/changelog.html
|
|
159
|
+
[roadmap]: https://vigno0405.github.io/VirtualModelControl/development/roadmap.html
|
|
160
|
+
[contributing]: https://vigno0405.github.io/VirtualModelControl/development/contributing.html
|
|
161
|
+
[cite]: https://github.com/vigno0405/VirtualModelControl/blob/main/CITATION.cff
|
|
162
|
+
[license]: https://github.com/vigno0405/VirtualModelControl/blob/main/LICENSE
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
<a href="https://www.epfl.ch/labs/create/"><img align="right" height="72" src="https://raw.githubusercontent.com/vigno0405/VirtualModelControl/main/docs/_static/create-lab.png" alt="CREATE Lab, EPFL"></a>
|
|
2
|
+
|
|
3
|
+
# VirtualModelControl
|
|
4
|
+
|
|
5
|
+
`virtualmodelcontrol` is a Python library for Virtual Model Control. You build a controller by
|
|
6
|
+
attaching virtual springs, dampers and masses to your robot, and the library turns them into
|
|
7
|
+
motor torques at the control rate, in simulation and on the real robot.
|
|
8
|
+
|
|
9
|
+
<p align="center">
|
|
10
|
+
<a href="https://vigno0405.github.io/VirtualModelControl/"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/vigno0405/VirtualModelControl/main/docs/_static/logo-light.svg"><img src="https://raw.githubusercontent.com/vigno0405/VirtualModelControl/main/docs/_static/logo.svg" height="64" align="middle" alt="virtualmodelcontrol logo"></picture></a>
|
|
11
|
+
|
|
12
|
+
<a href="https://vigno0405.github.io/VirtualModelControl/"><img src="https://img.shields.io/badge/Documentation-tutorials%20%C2%B7%20examples%20%C2%B7%20API-3c5488?style=for-the-badge&labelColor=13294b" align="middle" alt="Documentation: tutorials, examples, API"></a>
|
|
13
|
+
</p>
|
|
14
|
+
|
|
15
|
+
<p align="center">
|
|
16
|
+
<img src="https://raw.githubusercontent.com/vigno0405/VirtualModelControl/main/docs/_static/readme-hero.svg" width="820" alt="A soft arm whose tip a virtual spring pulls towards a goal, and the distance from the tip to the goal over time">
|
|
17
|
+
</p>
|
|
18
|
+
|
|
19
|
+
## Install
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pip install virtualmodelcontrol
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The newest code, from GitHub, before it is released (the same line updates an existing install):
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install --upgrade "git+https://github.com/vigno0405/VirtualModelControl.git"
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Virtual environments, conda, ROS 2 and pinned versions are covered in [Installation][install].
|
|
32
|
+
|
|
33
|
+
## Requirements
|
|
34
|
+
|
|
35
|
+
Python 3.10 or newer, tested with 3.10 to 3.14 on Ubuntu 22.04 and 24.04. Nothing to compile;
|
|
36
|
+
ROS and LaTeX are not needed.
|
|
37
|
+
|
|
38
|
+
## Dependencies
|
|
39
|
+
|
|
40
|
+
pip installs them with the library:
|
|
41
|
+
|
|
42
|
+
| Package | Version | Used for |
|
|
43
|
+
| --- | --- | --- |
|
|
44
|
+
| [numpy](https://numpy.org) | 1.26 or newer, 2.x included | arrays |
|
|
45
|
+
| [CasADi](https://web.casadi.org) | 3.6 or newer | the symbolic models, their derivatives and the compiled controllers |
|
|
46
|
+
| [SciPy](https://scipy.org) | 1.11 or newer | rotations of chains built from DH tables |
|
|
47
|
+
| [matplotlib](https://matplotlib.org) | 3.8 or newer | figures and animations |
|
|
48
|
+
| [imageio-ffmpeg](https://github.com/imageio/imageio-ffmpeg) | 0.5 or newer | MP4 videos of the animations |
|
|
49
|
+
| [PyYAML](https://pyyaml.org) | 6 or newer | robot and hardware files |
|
|
50
|
+
| [Dynamixel SDK](https://github.com/ROBOTIS-GIT/DynamixelSDK) | 3.7 or newer | Dynamixel motors |
|
|
51
|
+
| [pyserial](https://github.com/pyserial/pyserial) | 3.5 or newer | serial sensors |
|
|
52
|
+
|
|
53
|
+
## The example in the figure
|
|
54
|
+
|
|
55
|
+
The robot and the controller are two separate mechanisms: the robot mechanism describes the
|
|
56
|
+
hardware, and the controller holds the virtual elements placed on it.
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
import numpy as np
|
|
60
|
+
import virtualmodelcontrol as vmc
|
|
61
|
+
from virtualmodelcontrol.robots import helyx
|
|
62
|
+
|
|
63
|
+
arm = helyx.arm("145-290-290") # a soft arm with nine tendon motors
|
|
64
|
+
tip = arm.point(s=1.0)
|
|
65
|
+
|
|
66
|
+
ctrl = vmc.Mechanism("ctrl")
|
|
67
|
+
ctrl.add("reach", vmc.LinearSpring(tip - [0.25, 0.0, 0.55], 600.0)) # [N/m]
|
|
68
|
+
ctrl.add("damp", vmc.LinearDamper(tip, 5.0)) # [N·s/m]
|
|
69
|
+
ctrl.add("gravity", vmc.GravityCompensation(arm))
|
|
70
|
+
controller = vmc.VMCController(vmc.compile(vmc.VirtualMechanismSystem(arm, ctrl)))
|
|
71
|
+
|
|
72
|
+
meas = vmc.Signals(0.0, motor_position=np.zeros(9), motor_velocity=np.zeros(9))
|
|
73
|
+
torques = controller.step(0.0, meas)["motor_torque"] # [N·m], one per motor
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The same controller runs on a simulation built from the robot's own masses, stiffness and
|
|
77
|
+
damping; [readme_figure.py][figure] draws the figure from this run.
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
arm = helyx.add_dynamics(arm)
|
|
81
|
+
plant = vmc.sim.ModelPlant(arm)
|
|
82
|
+
log = vmc.sim.run(plant, controller, vmc.sim.SimClock(dt=1 / 330), T=3.0)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Examples
|
|
86
|
+
|
|
87
|
+
Each example builds a ready-made robot, controls it in simulation and animates the run:
|
|
88
|
+
|
|
89
|
+
- [Soft arm][soft-arm]: reach a point, avoid an obstacle, shape the whole arm.
|
|
90
|
+
- [Two arms][two-arms]: squeeze an object between the tips of two soft arms.
|
|
91
|
+
- [Finger][finger]: a stiff fingertip and soft joint limits.
|
|
92
|
+
- [Hand][hand]: grasp a ball, alone or mounted on a UR5 arm.
|
|
93
|
+
- [Turtle][turtle]: two cranks that follow a virtual flywheel.
|
|
94
|
+
|
|
95
|
+
## Documentation
|
|
96
|
+
|
|
97
|
+
The [documentation][docs] holds the tutorials, the examples, the concepts and the API. What
|
|
98
|
+
changed is in the [changelog][changelog], what comes next in the [roadmap][roadmap], and how to
|
|
99
|
+
contribute in [Contributing][contributing].
|
|
100
|
+
|
|
101
|
+
## Authors
|
|
102
|
+
|
|
103
|
+
Lorenzo Vignoli, at the [CREATE Lab][lab] of EPFL (Prof. Josie Hughes), in a collaboration
|
|
104
|
+
with the University of Cambridge (Prof. Fulvio Forni). To cite the library, see [CITATION.cff][cite].
|
|
105
|
+
The code is under the [MIT license][license].
|
|
106
|
+
|
|
107
|
+
[lab]: https://www.epfl.ch/labs/create/
|
|
108
|
+
[install]: https://vigno0405.github.io/VirtualModelControl/installation.html
|
|
109
|
+
[figure]: https://github.com/vigno0405/VirtualModelControl/blob/main/docs/scripts/readme_figure.py
|
|
110
|
+
[soft-arm]: https://vigno0405.github.io/VirtualModelControl/examples/soft-arm.html
|
|
111
|
+
[two-arms]: https://vigno0405.github.io/VirtualModelControl/examples/two-arms.html
|
|
112
|
+
[finger]: https://vigno0405.github.io/VirtualModelControl/examples/finger.html
|
|
113
|
+
[hand]: https://vigno0405.github.io/VirtualModelControl/examples/hand.html
|
|
114
|
+
[turtle]: https://vigno0405.github.io/VirtualModelControl/examples/turtle.html
|
|
115
|
+
[docs]: https://vigno0405.github.io/VirtualModelControl/
|
|
116
|
+
[changelog]: https://vigno0405.github.io/VirtualModelControl/development/changelog.html
|
|
117
|
+
[roadmap]: https://vigno0405.github.io/VirtualModelControl/development/roadmap.html
|
|
118
|
+
[contributing]: https://vigno0405.github.io/VirtualModelControl/development/contributing.html
|
|
119
|
+
[cite]: https://github.com/vigno0405/VirtualModelControl/blob/main/CITATION.cff
|
|
120
|
+
[license]: https://github.com/vigno0405/VirtualModelControl/blob/main/LICENSE
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""A ``video`` directive: embeds an MP4 that the page's own code wrote, looping and muted."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import html
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import ClassVar
|
|
8
|
+
|
|
9
|
+
from docutils import nodes
|
|
10
|
+
from docutils.parsers.rst import directives
|
|
11
|
+
from sphinx.application import Sphinx
|
|
12
|
+
from sphinx.util import logging
|
|
13
|
+
from sphinx.util.docutils import SphinxDirective
|
|
14
|
+
|
|
15
|
+
LIMIT = 2_000_000 # [bytes] per video, so pages stay light on a phone
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Video(SphinxDirective):
|
|
20
|
+
"""``video <path>`` (relative to the page) with an optional ``:caption:``."""
|
|
21
|
+
|
|
22
|
+
required_arguments = 1
|
|
23
|
+
option_spec: ClassVar = {"caption": directives.unchanged}
|
|
24
|
+
|
|
25
|
+
def run(self) -> list[nodes.Node]:
|
|
26
|
+
rel, path = self.env.relfn2path(self.arguments[0], self.env.docname)
|
|
27
|
+
if not Path(path).is_file():
|
|
28
|
+
logger.warning(f"video not found: {path}", location=self.get_location())
|
|
29
|
+
return []
|
|
30
|
+
size = Path(path).stat().st_size
|
|
31
|
+
if size > LIMIT:
|
|
32
|
+
logger.warning(f"video over 2 MB ({size} bytes): {path}", location=self.get_location())
|
|
33
|
+
self.env.note_dependency(rel)
|
|
34
|
+
dest = self.env.dlfiles.add_file(self.env.docname, rel)
|
|
35
|
+
src = "../" * self.env.docname.count("/") + "_downloads/" + Path(dest).as_posix()
|
|
36
|
+
caption = self.options.get("caption")
|
|
37
|
+
figcaption = f"<figcaption>{html.escape(caption)}</figcaption>" if caption else ""
|
|
38
|
+
markup = (
|
|
39
|
+
'<figure class="video">'
|
|
40
|
+
f'<video src="{src}" controls autoplay muted loop playsinline preload="metadata">'
|
|
41
|
+
"</video>"
|
|
42
|
+
f"{figcaption}</figure>"
|
|
43
|
+
)
|
|
44
|
+
return [nodes.raw("", markup, format="html")]
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def setup(app: Sphinx) -> dict[str, bool]:
|
|
48
|
+
app.add_directive("video", Video)
|
|
49
|
+
return {"parallel_read_safe": True, "parallel_write_safe": True}
|
|
Binary file
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/* Large type: about 17 px text, 19 px on wide screens (the theme's root size grows with the
|
|
2
|
+
window). Bold titles. */
|
|
3
|
+
.md-typeset {
|
|
4
|
+
font-size: 0.85rem;
|
|
5
|
+
}
|
|
6
|
+
.md-typeset h1,
|
|
7
|
+
.md-typeset h2 {
|
|
8
|
+
color: var(--md-default-fg-color);
|
|
9
|
+
font-weight: 700;
|
|
10
|
+
}
|
|
11
|
+
.md-typeset h3 {
|
|
12
|
+
font-weight: 600;
|
|
13
|
+
}
|
|
14
|
+
/* The page title links to the page already; its hidden permalink would wrap on phones. */
|
|
15
|
+
.md-typeset h1 .headerlink {
|
|
16
|
+
display: none;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/* The library's logo in the header, large enough to read. */
|
|
20
|
+
.md-header__button.md-logo img {
|
|
21
|
+
height: 2rem;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/* The lab's logo, on the right above the page outline, on every page (not in the phone menu,
|
|
25
|
+
which reuses the outline). */
|
|
26
|
+
.md-sidebar--secondary .lab-logo {
|
|
27
|
+
display: inline-block;
|
|
28
|
+
margin: 0 0 1.2rem 0.6rem;
|
|
29
|
+
}
|
|
30
|
+
.md-sidebar--secondary .lab-logo img {
|
|
31
|
+
display: block;
|
|
32
|
+
height: 4rem;
|
|
33
|
+
width: auto;
|
|
34
|
+
}
|
|
35
|
+
.md-nav--primary .lab-logo {
|
|
36
|
+
display: none;
|
|
37
|
+
}
|
|
38
|
+
/* Narrow windows and phones have no right column: the logo sits at the right end of the
|
|
39
|
+
header instead, so it is always on screen. */
|
|
40
|
+
@media screen and (max-width: 59.9375em) {
|
|
41
|
+
.md-header__inner::after {
|
|
42
|
+
content: "";
|
|
43
|
+
flex-shrink: 0;
|
|
44
|
+
width: 2rem;
|
|
45
|
+
height: 2rem;
|
|
46
|
+
margin: 0 0.2rem 0 0.4rem;
|
|
47
|
+
background: url("create-lab.png") center / contain no-repeat;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/* The layout fills wide windows. */
|
|
52
|
+
.md-grid {
|
|
53
|
+
max-width: 68rem;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/* The home page belongs to no section: its sidebar lists all of them. */
|
|
57
|
+
@media screen and (min-width: 76.25em) {
|
|
58
|
+
.md-nav--lifted > .md-nav__list:not(:has(> .md-nav__item--active)) > .md-nav__item {
|
|
59
|
+
display: block;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/* Code cells like the theme's code blocks: no coloured margin. */
|
|
64
|
+
:root {
|
|
65
|
+
--mystnb-source-border-color: transparent;
|
|
66
|
+
--mystnb-source-margin-color: transparent;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/* Colours: a deep navy header on a cool, very light page; in dark mode, deep blues throughout.
|
|
70
|
+
Links in blue, the figures' red as the accent. */
|
|
71
|
+
[data-md-color-primary="custom"] {
|
|
72
|
+
--md-primary-fg-color: #13294b;
|
|
73
|
+
--md-primary-fg-color--light: #1f3d6b;
|
|
74
|
+
--md-primary-fg-color--dark: #0b1b33;
|
|
75
|
+
--md-primary-bg-color: #ffffff;
|
|
76
|
+
--md-primary-bg-color--light: rgba(255, 255, 255, 0.72);
|
|
77
|
+
}
|
|
78
|
+
[data-md-color-accent="custom"] {
|
|
79
|
+
--md-accent-fg-color: #e64b35;
|
|
80
|
+
--md-accent-fg-color--transparent: rgba(230, 75, 53, 0.1);
|
|
81
|
+
--md-accent-bg-color: #ffffff;
|
|
82
|
+
--md-accent-bg-color--light: rgba(255, 255, 255, 0.72);
|
|
83
|
+
}
|
|
84
|
+
[data-md-color-scheme="default"] {
|
|
85
|
+
--md-default-bg-color: #f5f8fc;
|
|
86
|
+
--md-code-bg-color: #e9eff7;
|
|
87
|
+
--md-typeset-a-color: #2b5797;
|
|
88
|
+
--md-footer-bg-color: #13294b;
|
|
89
|
+
--md-footer-bg-color--dark: #0b1b33;
|
|
90
|
+
}
|
|
91
|
+
[data-md-color-scheme="slate"] {
|
|
92
|
+
--mystnb-source-border-color: transparent;
|
|
93
|
+
--md-hue: 216;
|
|
94
|
+
--md-default-bg-color: #0d1a2d;
|
|
95
|
+
--md-default-bg-color--light: rgba(13, 26, 45, 0.54);
|
|
96
|
+
--md-default-bg-color--lighter: rgba(13, 26, 45, 0.26);
|
|
97
|
+
--md-default-bg-color--lightest: rgba(13, 26, 45, 0.07);
|
|
98
|
+
--md-code-bg-color: #13243d;
|
|
99
|
+
--md-typeset-a-color: #8ab4f8;
|
|
100
|
+
--md-footer-bg-color: #08121f;
|
|
101
|
+
--md-footer-bg-color--dark: #050c16;
|
|
102
|
+
}
|
|
103
|
+
[data-md-color-scheme="slate"][data-md-color-primary="custom"] {
|
|
104
|
+
--md-primary-fg-color: #08121f;
|
|
105
|
+
--md-primary-fg-color--light: #12223a;
|
|
106
|
+
--md-primary-fg-color--dark: #050c16;
|
|
107
|
+
}
|
|
108
|
+
[data-md-color-scheme="slate"][data-md-color-accent="custom"] {
|
|
109
|
+
--md-accent-fg-color: #ff7b66;
|
|
110
|
+
--md-accent-fg-color--transparent: rgba(255, 123, 102, 0.12);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/* Animations written by the pages (the video directive), shown at their own size or smaller. */
|
|
114
|
+
figure.video {
|
|
115
|
+
margin: 1.5em 0;
|
|
116
|
+
text-align: center;
|
|
117
|
+
}
|
|
118
|
+
figure.video video {
|
|
119
|
+
display: inline-block;
|
|
120
|
+
width: auto;
|
|
121
|
+
height: auto;
|
|
122
|
+
max-width: 100%;
|
|
123
|
+
border: 1px solid var(--md-default-fg-color--lightest);
|
|
124
|
+
border-radius: 0.2rem;
|
|
125
|
+
}
|
|
126
|
+
figure.video figcaption {
|
|
127
|
+
font-size: 0.85em;
|
|
128
|
+
color: var(--md-default-fg-color--light);
|
|
129
|
+
margin-top: 0.5em;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/* Figures: white background in dark mode too, never wider than the text. */
|
|
133
|
+
.cell_output img {
|
|
134
|
+
max-width: 100%;
|
|
135
|
+
height: auto;
|
|
136
|
+
background: #fff;
|
|
137
|
+
border-radius: 0.2rem;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/* Task lists (the roadmap): the checkbox replaces the bullet. */
|
|
141
|
+
ul.contains-task-list {
|
|
142
|
+
list-style: none;
|
|
143
|
+
padding-left: 0.25rem;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/* Reference tables: names on one line and a readable last column; on a phone a wide table
|
|
147
|
+
scrolls sideways inside itself instead of squeezing its columns. */
|
|
148
|
+
.md-typeset table.docutils td code {
|
|
149
|
+
white-space: nowrap;
|
|
150
|
+
word-break: normal;
|
|
151
|
+
overflow-wrap: normal;
|
|
152
|
+
}
|
|
153
|
+
.md-typeset table.docutils td:last-child {
|
|
154
|
+
min-width: 14em;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/* Display equations: a wide one scrolls sideways inside itself on a phone, not the page. */
|
|
158
|
+
.md-typeset mjx-container[display="true"] {
|
|
159
|
+
overflow-x: auto;
|
|
160
|
+
overflow-y: hidden;
|
|
161
|
+
max-width: 100%;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/* The general index: its two columns stack on a phone, and long module names wrap. */
|
|
165
|
+
@media screen and (max-width: 44.9375em) {
|
|
166
|
+
table.genindextable td {
|
|
167
|
+
display: block;
|
|
168
|
+
width: auto !important;
|
|
169
|
+
overflow-wrap: anywhere;
|
|
170
|
+
}
|
|
171
|
+
}
|