virtualmodelcontrol 1.2.2__tar.gz → 1.3.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 (383) hide show
  1. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/CHANGELOG.md +79 -4
  2. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/CITATION.cff +2 -2
  3. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/PKG-INFO +1 -1
  4. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/concepts/conventions.md +1 -1
  5. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/concepts/library.md +1 -1
  6. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/examples/hanging-arm.md +2 -1
  7. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/examples/soft-arm.md +9 -0
  8. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/installation.md +2 -2
  9. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/tutorials/extend.md +6 -2
  10. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/tutorials/fit.md +1 -1
  11. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/tutorials/mpc.md +13 -6
  12. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/tutorials/optimize.md +7 -4
  13. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/tutorials/soft-and-assemblies.md +9 -0
  14. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/tutorials/tuning.md +2 -1
  15. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/tutorials/underactuated.md +163 -16
  16. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/__init__.py +2 -0
  17. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/_version.py +2 -2
  18. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/adaptation/seeking.py +2 -2
  19. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/control/__init__.py +2 -0
  20. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/control/blending.py +2 -0
  21. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/control/controller.py +13 -5
  22. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/control/output.py +62 -0
  23. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/control/schedule.py +2 -0
  24. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/control/state.py +1 -1
  25. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/control/tank.py +2 -0
  26. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/control/underactuated.py +23 -10
  27. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/core/params.py +44 -14
  28. virtualmodelcontrol-1.3.0/src/virtualmodelcontrol/core/registry.py +89 -0
  29. virtualmodelcontrol-1.3.0/src/virtualmodelcontrol/identification/identifiability.py +29 -0
  30. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/identification/params.py +10 -20
  31. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/identification/stiffness.py +19 -1
  32. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/mechanisms/__init__.py +2 -0
  33. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/mechanisms/components/gated.py +5 -1
  34. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/mechanisms/coordinates/__init__.py +2 -1
  35. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/mechanisms/coordinates/geometry.py +3 -3
  36. virtualmodelcontrol-1.3.0/src/virtualmodelcontrol/mechanisms/coordinates/joints.py +99 -0
  37. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/mechanisms/mechanism.py +16 -3
  38. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/models/assembly.py +5 -3
  39. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/optimization/asktell.py +33 -10
  40. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/optimization/builder.py +26 -9
  41. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/optimization/horizon.py +33 -1
  42. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/optimization/mpc.py +53 -22
  43. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/optimization/problem.py +37 -5
  44. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/optimization/solver.py +1 -1
  45. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/optimization/terms.py +3 -1
  46. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/robots/adapt.py +2 -2
  47. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/robots/helyx.py +32 -2
  48. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/robots/planar.py +20 -3
  49. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/sim/rollout.py +7 -7
  50. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/viz/style.py +2 -2
  51. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/control/test_blending.py +10 -0
  52. virtualmodelcontrol-1.3.0/tests/control/test_inputs.py +76 -0
  53. virtualmodelcontrol-1.3.0/tests/control/test_output.py +131 -0
  54. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/control/test_schedule.py +14 -0
  55. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/control/test_state_layout.py +1 -1
  56. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/control/test_tank.py +10 -0
  57. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/control/test_underactuated.py +53 -0
  58. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/core/test_params.py +29 -0
  59. virtualmodelcontrol-1.3.0/tests/core/test_registry.py +82 -0
  60. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/identification/test_fit_params.py +27 -7
  61. virtualmodelcontrol-1.3.0/tests/identification/test_stiffness_fit.py +68 -0
  62. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/mechanisms/test_coordinates.py +7 -0
  63. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/mechanisms/test_gated.py +10 -0
  64. virtualmodelcontrol-1.3.0/tests/mechanisms/test_mechanism.py +79 -0
  65. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/models/test_actuation.py +11 -1
  66. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/models/test_assembly.py +57 -1
  67. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/models/test_pcc.py +17 -1
  68. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/models/test_poe.py +16 -1
  69. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/optimization/test_asktell.py +37 -0
  70. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/optimization/test_equilibrium.py +3 -3
  71. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/optimization/test_horizon.py +36 -0
  72. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/optimization/test_mpc.py +81 -0
  73. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/optimization/test_plant.py +70 -0
  74. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/optimization/test_problem.py +20 -1
  75. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/optimization/test_solvers.py +2 -1
  76. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/optimization/test_structure.py +2 -1
  77. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/robots/test_planar.py +13 -0
  78. virtualmodelcontrol-1.2.2/src/virtualmodelcontrol/core/registry.py +0 -54
  79. virtualmodelcontrol-1.2.2/src/virtualmodelcontrol/mechanisms/coordinates/joints.py +0 -51
  80. virtualmodelcontrol-1.2.2/tests/control/test_inputs.py +0 -32
  81. virtualmodelcontrol-1.2.2/tests/control/test_output.py +0 -63
  82. virtualmodelcontrol-1.2.2/tests/core/test_registry.py +0 -43
  83. virtualmodelcontrol-1.2.2/tests/identification/test_stiffness_fit.py +0 -27
  84. virtualmodelcontrol-1.2.2/tests/mechanisms/test_mechanism.py +0 -39
  85. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/.gitignore +0 -0
  86. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/LICENSE +0 -0
  87. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/README.md +0 -0
  88. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/benchmarks/tick.py +0 -0
  89. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/_ext/video.py +0 -0
  90. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/_static/create-lab.png +0 -0
  91. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/_static/custom.css +0 -0
  92. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/_static/favicon.svg +0 -0
  93. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/_static/logo-light.svg +0 -0
  94. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/_static/logo.svg +0 -0
  95. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/_static/readme-hero.svg +0 -0
  96. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/_templates/autosummary/module.rst +0 -0
  97. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/_templates/partials/toc.html +0 -0
  98. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/api/index.md +0 -0
  99. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/concepts/efficiency.md +0 -0
  100. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/concepts/finger.md +0 -0
  101. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/concepts/passivity.md +0 -0
  102. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/concepts/pcc.md +0 -0
  103. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/concepts/vmc.md +0 -0
  104. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/conf.py +0 -0
  105. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/development/changelog.md +0 -0
  106. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/development/contributing.md +0 -0
  107. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/development/roadmap.md +0 -0
  108. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/development/support.md +0 -0
  109. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/docs_setup.py +0 -0
  110. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/examples/finger.md +0 -0
  111. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/examples/hand.md +0 -0
  112. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/examples/turtle.md +0 -0
  113. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/examples/two-arms.md +0 -0
  114. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/index.md +0 -0
  115. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/schematics/__init__.py +0 -0
  116. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/schematics/_draw.py +0 -0
  117. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/schematics/bimanual.py +0 -0
  118. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/schematics/control_loop.py +0 -0
  119. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/schematics/energy_flow.py +0 -0
  120. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/schematics/finger.py +0 -0
  121. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/schematics/four_steps.py +0 -0
  122. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/schematics/hand.py +0 -0
  123. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/schematics/helyx.py +0 -0
  124. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/schematics/library_map.py +0 -0
  125. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/schematics/mechanism.py +0 -0
  126. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/schematics/params.py +0 -0
  127. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/schematics/pcc.py +0 -0
  128. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/schematics/turtle.py +0 -0
  129. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/schematics/ur5.py +0 -0
  130. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/scripts/readme_figure.py +0 -0
  131. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/troubleshooting.md +0 -0
  132. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/tutorials/build-a-robot.md +0 -0
  133. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/tutorials/components.md +0 -0
  134. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/tutorials/configurations.md +0 -0
  135. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/tutorials/contact.md +0 -0
  136. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/tutorials/coordinates-and-components.md +0 -0
  137. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/tutorials/coordinates.md +0 -0
  138. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/tutorials/crawl.md +0 -0
  139. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/tutorials/energy.md +0 -0
  140. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/tutorials/estimation.md +0 -0
  141. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/tutorials/first-controller.md +0 -0
  142. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/tutorials/force-estimates.md +0 -0
  143. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/tutorials/force.md +0 -0
  144. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/tutorials/friction.md +0 -0
  145. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/tutorials/introduction.md +0 -0
  146. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/tutorials/joints.md +0 -0
  147. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/tutorials/kinematics.md +0 -0
  148. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/tutorials/optimize-obstacles.md +0 -0
  149. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/tutorials/optimize-periodic.md +0 -0
  150. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/tutorials/outside.md +0 -0
  151. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/tutorials/parameters.md +0 -0
  152. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/tutorials/reach.yaml +0 -0
  153. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/tutorials/real-time.md +0 -0
  154. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/tutorials/run-logs.md +0 -0
  155. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/tutorials/run-settings.yaml +0 -0
  156. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/tutorials/stiffness.md +0 -0
  157. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/docs/tutorials/swaps.md +0 -0
  158. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/pyproject.toml +0 -0
  159. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/adaptation/__init__.py +0 -0
  160. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/adaptation/force.py +0 -0
  161. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/adaptation/limits.py +0 -0
  162. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/adaptation/position.py +0 -0
  163. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/adaptation/scalar.py +0 -0
  164. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/adaptation/stiffness.py +0 -0
  165. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/compiler.py +0 -0
  166. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/config/__init__.py +0 -0
  167. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/config/controllers.py +0 -0
  168. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/config/coordinates.py +0 -0
  169. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/config/experiment.py +0 -0
  170. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/config/files.py +0 -0
  171. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/config/spec.py +0 -0
  172. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/control/projection.py +0 -0
  173. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/core/__init__.py +0 -0
  174. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/core/backends.py +0 -0
  175. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/core/signals.py +0 -0
  176. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/core/space.py +0 -0
  177. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/core/symbolic.py +0 -0
  178. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/core/units.py +0 -0
  179. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/dynamics.py +0 -0
  180. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/estimation/__init__.py +0 -0
  181. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/estimation/balance.py +0 -0
  182. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/estimation/compliance.py +0 -0
  183. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/estimation/contact.py +0 -0
  184. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/estimation/imu.py +0 -0
  185. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/estimation/inversion.py +0 -0
  186. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/estimation/kalman.py +0 -0
  187. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/estimation/measurement.py +0 -0
  188. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/estimation/momentum.py +0 -0
  189. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/estimation/sensors.py +0 -0
  190. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/estimation/stiffness.py +0 -0
  191. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/estimation/velocity.py +0 -0
  192. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/hardware/__init__.py +0 -0
  193. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/hardware/profile.py +0 -0
  194. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/identification/__init__.py +0 -0
  195. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/identification/efficiency.py +0 -0
  196. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/identification/plateaus.py +0 -0
  197. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/identification/steps.py +0 -0
  198. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/identification/transmission.py +0 -0
  199. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/math.py +0 -0
  200. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/mechanisms/components/__init__.py +0 -0
  201. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/mechanisms/components/base.py +0 -0
  202. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/mechanisms/components/constrained.py +0 -0
  203. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/mechanisms/components/dissipation.py +0 -0
  204. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/mechanisms/components/inertance.py +0 -0
  205. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/mechanisms/components/locomotion.py +0 -0
  206. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/mechanisms/components/sources.py +0 -0
  207. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/mechanisms/components/storage.py +0 -0
  208. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/mechanisms/coordinates/base.py +0 -0
  209. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/mechanisms/coordinates/frames.py +0 -0
  210. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/mechanisms/coordinates/ops.py +0 -0
  211. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/mechanisms/coordinates/references.py +0 -0
  212. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/models/__init__.py +0 -0
  213. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/models/actuation.py +0 -0
  214. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/models/continuum/__init__.py +0 -0
  215. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/models/continuum/pcc.py +0 -0
  216. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/models/efficiency.py +0 -0
  217. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/models/equations.py +0 -0
  218. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/models/friction.py +0 -0
  219. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/models/function.py +0 -0
  220. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/models/joint_space.py +0 -0
  221. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/models/kinematic.py +0 -0
  222. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/models/kinematics.py +0 -0
  223. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/models/rigid/__init__.py +0 -0
  224. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/models/rigid/couplings.py +0 -0
  225. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/models/rigid/poe.py +0 -0
  226. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/models/rigid/spline.py +0 -0
  227. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/optimization/__init__.py +0 -0
  228. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/optimization/collocation.py +0 -0
  229. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/optimization/equilibrium.py +0 -0
  230. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/optimization/nlp.py +0 -0
  231. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/optimization/result.py +0 -0
  232. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/optimization/search.py +0 -0
  233. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/optimization/shooting.py +0 -0
  234. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/optimization/tank.py +0 -0
  235. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/optimization/trajectory.py +0 -0
  236. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/optimization/variables.py +0 -0
  237. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/optimization/virtual.py +0 -0
  238. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/py.typed +0 -0
  239. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/robots/__init__.py +0 -0
  240. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/robots/bimanual.py +0 -0
  241. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/robots/turtle.py +0 -0
  242. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/robots/ur5.py +0 -0
  243. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/sim/__init__.py +0 -0
  244. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/sim/balance.py +0 -0
  245. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/sim/model_plant.py +0 -0
  246. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/sim/plant.py +0 -0
  247. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/sim/replay.py +0 -0
  248. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/sim/run.py +0 -0
  249. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/sim/runlog.py +0 -0
  250. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/system.py +0 -0
  251. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/testing.py +0 -0
  252. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/viz/__init__.py +0 -0
  253. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/viz/animation.py +0 -0
  254. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/src/virtualmodelcontrol/viz/draw.py +0 -0
  255. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/adaptation/test_fingertip.py +0 -0
  256. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/adaptation/test_force.py +0 -0
  257. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/adaptation/test_grasp.py +0 -0
  258. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/adaptation/test_holding.py +0 -0
  259. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/adaptation/test_loop.py +0 -0
  260. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/adaptation/test_position.py +0 -0
  261. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/adaptation/test_scalar.py +0 -0
  262. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/adaptation/test_seeking.py +0 -0
  263. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/adaptation/test_stiffness_tracking.py +0 -0
  264. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/adaptation/test_virtual_state.py +0 -0
  265. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/backend_helpers.py +0 -0
  266. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/config/test_config.py +0 -0
  267. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/conftest.py +0 -0
  268. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/control/test_honesty.py +0 -0
  269. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/control/test_projection.py +0 -0
  270. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/control/test_underactuated_golden.py +0 -0
  271. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/control/test_vmc_golden.py +0 -0
  272. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/core/test_backends.py +0 -0
  273. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/core/test_backends_torch.py +0 -0
  274. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/core/test_signals.py +0 -0
  275. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/core/test_space.py +0 -0
  276. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/core/test_symbolic.py +0 -0
  277. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/data/adaptation.npz +0 -0
  278. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/data/compliance.npz +0 -0
  279. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/data/components.npz +0 -0
  280. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/data/configs/finger.yaml +0 -0
  281. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/data/configs/hand.yaml +0 -0
  282. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/data/configs/soft-arm.yaml +0 -0
  283. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/data/configs/turtle.yaml +0 -0
  284. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/data/configs/two-arms.yaml +0 -0
  285. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/data/configs/ur5-hand.yaml +0 -0
  286. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/data/derivatives.npz +0 -0
  287. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/data/dynamics.npz +0 -0
  288. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/data/efficiency.npz +0 -0
  289. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/data/estimation.npz +0 -0
  290. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/data/finger.npz +0 -0
  291. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/data/fingertip.npz +0 -0
  292. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/data/hand.npz +0 -0
  293. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/data/heuristics.npz +0 -0
  294. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/data/imu.npz +0 -0
  295. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/data/kalman.npz +0 -0
  296. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/data/mocap.npz +0 -0
  297. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/data/optimization.json +0 -0
  298. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/data/optimization.npz +0 -0
  299. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/data/pcc.npz +0 -0
  300. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/data/transmission.npz +0 -0
  301. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/data/turtle.npz +0 -0
  302. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/data/underactuated.npz +0 -0
  303. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/data/vmc.npz +0 -0
  304. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/estimation/test_compliance_golden.py +0 -0
  305. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/estimation/test_contact.py +0 -0
  306. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/estimation/test_imu.py +0 -0
  307. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/estimation/test_inversion.py +0 -0
  308. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/estimation/test_kalman.py +0 -0
  309. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/estimation/test_kalman_golden.py +0 -0
  310. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/estimation/test_kalman_unscented.py +0 -0
  311. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/estimation/test_mocap_golden.py +0 -0
  312. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/estimation/test_momentum.py +0 -0
  313. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/estimation/test_sensors.py +0 -0
  314. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/estimation/test_unmeasured.py +0 -0
  315. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/estimation/test_velocity.py +0 -0
  316. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/hardware/test_profile.py +0 -0
  317. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/hardware/test_templates.py +0 -0
  318. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/helpers.py +0 -0
  319. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/identification/test_efficiency_fit.py +0 -0
  320. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/identification/test_fit_friction.py +0 -0
  321. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/identification/test_friction.py +0 -0
  322. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/identification/test_steps.py +0 -0
  323. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/identification/test_transmission.py +0 -0
  324. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/lab_optimization.py +0 -0
  325. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/mechanisms/test_components.py +0 -0
  326. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/mechanisms/test_components_golden.py +0 -0
  327. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/mechanisms/test_contact.py +0 -0
  328. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/mechanisms/test_distances.py +0 -0
  329. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/mechanisms/test_frames.py +0 -0
  330. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/mechanisms/test_friction.py +0 -0
  331. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/mechanisms/test_more_components.py +0 -0
  332. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/mechanisms/test_pair_contact.py +0 -0
  333. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/mechanisms/test_phase_spring.py +0 -0
  334. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/mechanisms/test_virtual_model.py +0 -0
  335. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/models/test_contact_map.py +0 -0
  336. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/models/test_efficiency.py +0 -0
  337. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/models/test_equations.py +0 -0
  338. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/models/test_floating.py +0 -0
  339. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/models/test_floating_base.py +0 -0
  340. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/models/test_function.py +0 -0
  341. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/models/test_kinematics.py +0 -0
  342. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/models/test_rail.py +0 -0
  343. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/models/test_static_friction.py +0 -0
  344. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/models/test_underactuated.py +0 -0
  345. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/optimization/test_collocation.py +0 -0
  346. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/optimization/test_lab.py +0 -0
  347. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/optimization/test_periodic.py +0 -0
  348. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/optimization/test_search.py +0 -0
  349. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/optimization/test_shooting.py +0 -0
  350. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/optimization/test_swap.py +0 -0
  351. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/optimization/test_tank.py +0 -0
  352. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/optimization/test_terms.py +0 -0
  353. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/optimization/test_variables.py +0 -0
  354. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/optimization/test_virtual.py +0 -0
  355. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/robots/test_adapt.py +0 -0
  356. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/robots/test_bimanual.py +0 -0
  357. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/robots/test_crawler.py +0 -0
  358. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/robots/test_hand.py +0 -0
  359. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/robots/test_helyx.py +0 -0
  360. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/robots/test_parametric.py +0 -0
  361. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/robots/test_turtle.py +0 -0
  362. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/robots/test_ur5.py +0 -0
  363. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/sim/test_balance.py +0 -0
  364. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/sim/test_record.py +0 -0
  365. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/sim/test_replay.py +0 -0
  366. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/sim/test_rollout.py +0 -0
  367. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/sim/test_run.py +0 -0
  368. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/sim/test_runlog.py +0 -0
  369. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/sim/test_wallclock.py +0 -0
  370. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/small_plan.py +0 -0
  371. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/soft_arm_plan.py +0 -0
  372. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/test_benchmark.py +0 -0
  373. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/test_compiler.py +0 -0
  374. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/test_docs.py +0 -0
  375. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/test_dynamics.py +0 -0
  376. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/test_import.py +0 -0
  377. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/test_math.py +0 -0
  378. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/test_python_versions.py +0 -0
  379. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/test_readme.py +0 -0
  380. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/test_testing.py +0 -0
  381. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/underactuated_lab.py +0 -0
  382. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/viz/test_animate.py +0 -0
  383. {virtualmodelcontrol-1.2.2 → virtualmodelcontrol-1.3.0}/tests/viz/test_viz.py +0 -0
@@ -4,6 +4,80 @@ All notable changes to this project are documented here. The format follows
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and versions follow
5
5
  [Semantic Versioning](https://semver.org/) (0.x: the API may change between minor versions).
6
6
 
7
+ ## [1.3.0] - 2026-10-30
8
+
9
+ A minor release. A plan of a robot with fewer motors than coordinates now reads the motors, as the controller that
10
+ runs does (see Changed), and the result of a review of the whole library: the fixes below, most of them for
11
+ cases that gave a wrong or a silent answer, and the tension floor for tendons that only pull. Nothing else changes
12
+ for a robot with a motor on every coordinate.
13
+
14
+ ### Added
15
+
16
+ - `control.TensionFloor`: an output stage for tendons that only pull. It lifts the torques of each
17
+ group of tendons (a segment's three) together until the lowest is at a floor, so no tendon is
18
+ told to push. Equal torques on evenly spaced tendons only squeeze the segment, so the bend
19
+ that the command makes is the same. Its corner is rounded, and `symbolic` is the same map for
20
+ a plan (`Problem(output=)`).
21
+ - `Mechanism.motor` and the `MotorAngle` coordinate: entries of the robot's motor angles, as its
22
+ actuation gives them. A `LimitSpring` on it is a limit on how far a motor may turn.
23
+ - `helyx.tension_stage`, `helyx.unwind_limit` and their numbers `TENSION_FLOOR`,
24
+ `UNWIND_LIMIT` and `UNWIND_STIFFNESS`: the tension floor, and a limit on how far a motor may
25
+ unwind (pulling is free). Both are optional, and `helyx.output_stage` is unchanged.
26
+ - `control.underactuated.Frozen(at=)` and `controller(gravity=<motor angles>)`: the rest of the
27
+ passive joints balanced once at a pose. The frozen torque stays the gradient of a potential,
28
+ which `gravity=True` (the balance found again at every reading, as before) does not: its torque
29
+ has a curl of a few percent in the test. `Frozen.rest` can be set as the `q_rest` of the
30
+ actuation.
31
+ - A `Param` can be shared by several sections or joints (`PCC([0.2, 0.2], radius)`, one axis for two
32
+ joints): it keeps one value, `ParamSet.by_name()` lists every name it has, and `select` finds
33
+ it by any of them (under its own name).
34
+ - `MovingHorizon.encoder` of a robot with fewer motors than coordinates measures the motors, as the
35
+ Kalman filter's does (it pinned the passive joint at its rest angle: an error of 0.37 rad in the
36
+ test, 0.001 now). `MovingHorizon.P` is defined from `reset` on.
37
+ - A term of a plan can use a site of a `plant` that is another robot.
38
+
39
+ ### Changed
40
+
41
+ - A plan of a robot with fewer motors than coordinates now reads the motors, as `VMCController`
42
+ does (the frozen controller of `control.underactuated`). It used to read the whole state, so
43
+ that `Problem(system)` and the simulation of `VMCController(compile(system))` were different
44
+ controllers (a gap of 0.36 rad in the test). `Problem(reads="state")` plans the whole-state
45
+ controller (`StateController`, `"naive"`), and `reads="motors"` asks for the default outright.
46
+ For a robot with a motor on every coordinate nothing changes.
47
+ - `MPC` applies a plan only if its solve converged (a real-time iteration is applied as it is):
48
+ a failed solve used to jump the controller's Params. `MPC.applied` says whether the last plan
49
+ was, and the next solve after a failed one starts from the measured state. `rti=True` is undone
50
+ by a later `MPC` of the same problem without it, `reset` drops a plan that is being made, and
51
+ `start` waits until the last plan was polled.
52
+ - `Result.params` and `Result.steps` are held inside the Params' bounds (the solver relaxes a
53
+ bound by about 1e-8).
54
+ - `fit_params` and `fit_stiffness_damping` refuse an even `smoothing` (it shifted the derivative by
55
+ half a sample: a damping of 1.5 came out as 1.88), and a run whose `train` mask is off everywhere.
56
+ `fit_stiffness_damping` returns NaN, with a warning that names it, for a stiffness or damping
57
+ that the runs do not determine (it returned 0). `fit_params` takes one name as a string, and
58
+ a run with nothing to train is skipped when another has samples (an error when none has).
59
+ - `VMCController.set` refuses a value whose size is not the Param's (a scalar was broadcast into a
60
+ matrix), and `live_params()` returns copies. `balance()` and `elements()` use the Params as
61
+ `set` left them.
62
+ - `register` replaces a class defined again (a script or a notebook cell run twice) and still
63
+ refuses another object under a taken name. A plugin that fails to import no longer keeps the
64
+ others from loading: its error is raised by `get` of a name that is missing (and said in the
65
+ `KeyError`), and is a warning in `names`.
66
+ - `robot.joint` counts from the end (`slice(0, -1)`) and takes numpy integers; an index out of
67
+ range is a `ValueError` that says how many coordinates there are.
68
+ - `TensionFloor` needs a positive `width` (zero gave NaN).
69
+ - In `ask` and `tell`, a cost that is not finite (an episode that failed) counts as the worst cost
70
+ so far (`inf` until one is known) and is never the best; `Bayes` used to fail for good after one.
71
+ - `planar.arm(rest=)` takes one angle per passive joint as well as one per joint.
72
+ - The messages that named `Collocation` and `Equilibrium` name `Shooting` too.
73
+
74
+ ### Fixed
75
+
76
+ - `Tank`, `SwapController` and `ScheduledController` can be copied and pickled.
77
+ - `Gated` forwards what the wrapped component reads.
78
+ - The docstring of `rollout` says that its `z` rows are the state the law used, one step behind
79
+ `run`'s.
80
+
7
81
  ## [1.2.2] - 2026-10-11
8
82
 
9
83
  A patch: a fit says when its data do not determine a Param, and gives the same values on every machine; two
@@ -369,7 +443,7 @@ tank, tuning and the calibrations), which was never released on its own.
369
443
  geometric terms out and is not provided.
370
444
  - `KalmanFilter(system, dt, Q=None, P0=None, gate=40.0, robot=None)` and `Measurement(q, v, Rq,
371
445
  Rv, observed, name)`: a Kalman filter of the robot's state (q, v). Its process model is the
372
- robot's own dynamics, linearised at the estimate at rest and held over the step (the lab's
446
+ robot's own dynamics, linearized at the estimate at rest and held over the step (the lab's
373
447
  frozen mass matrix, in general form), with the command going through the system's
374
448
  transmission. It fuses encoders (`kf.encoder`) and any sensor that sees part of the state,
375
449
  gates each measurement on its innovation, and reports the sensors it rejected (`rejected`,
@@ -460,9 +534,9 @@ tank, tuning and the calibrations), which was never released on its own.
460
534
  covariance, on a mass, spring and damper. The tutorial "Fit Params to a run" uses it.
461
535
  - `KalmanFilter(..., unscented=True, substeps=10)`: the prediction sends sigma points of the
462
536
  estimate (the scaled unscented transform, a root of three standard deviations out) through the
463
- robot's own integrator, in `substeps` steps, instead of linearising at the estimate; the sensors
537
+ robot's own integrator, in `substeps` steps, instead of linearizing at the estimate; the sensors
464
538
  stay linear, so the update is the same. On a double pendulum it follows the mean of a Monte
465
- Carlo to 0.003 where the linearised prediction is off by 2; on a linear robot the two agree.
539
+ Carlo to 0.003 where the linearized prediction is off by 2; on a linear robot the two agree.
466
540
  - `vmc.estimation.MomentumObserver(system, dt, gain, robot=None)`: the external force on a robot
467
541
  from its motion, without accelerations. From the momentum and what the model says would change
468
542
  it, it gives the generalized force the model does not explain, as a low-pass filter of bandwidth
@@ -854,7 +928,8 @@ First public release.
854
928
  - `robots.adapt`: the ADAPT finger (two motors, coupled distal joints), joint-angle coordinate
855
929
  and joint-limit spring.
856
930
 
857
- [Unreleased]: https://github.com/vigno0405/VirtualModelControl/compare/v1.2.2...HEAD
931
+ [Unreleased]: https://github.com/vigno0405/VirtualModelControl/compare/v1.3.0...HEAD
932
+ [1.3.0]: https://github.com/vigno0405/VirtualModelControl/compare/v1.2.2...v1.3.0
858
933
  [1.2.2]: https://github.com/vigno0405/VirtualModelControl/compare/v1.2.1...v1.2.2
859
934
  [1.2.1]: https://github.com/vigno0405/VirtualModelControl/compare/v1.2.0...v1.2.1
860
935
  [1.2.0]: https://github.com/vigno0405/VirtualModelControl/compare/v1.1.0...v1.2.0
@@ -1,8 +1,8 @@
1
1
  cff-version: 1.2.0
2
2
  message: "If you use this software, please cite it as below."
3
3
  type: software
4
- version: 1.2.2
5
- date-released: "2026-10-11"
4
+ version: 1.3.0
5
+ date-released: "2026-10-30"
6
6
  title: "virtualmodelcontrol: Virtual Model Control for robots"
7
7
  authors:
8
8
  - family-names: Vignoli
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: virtualmodelcontrol
3
- Version: 1.2.2
3
+ Version: 1.3.0
4
4
  Summary: Virtual Model Control: robots and controllers as mechanisms of springs, dampers and inertances, compiled with CasADi.
5
5
  Project-URL: Repository, https://github.com/vigno0405/VirtualModelControl
6
6
  Project-URL: Documentation, https://vigno0405.github.io/VirtualModelControl/
@@ -94,7 +94,7 @@ tank
94
94
 
95
95
  output stage
96
96
  An optional correction of the motor commands for real hardware: friction compensation,
97
- pretension, an offset or a torque limit.
97
+ pretension, a tension floor, an offset or a torque limit.
98
98
 
99
99
  template
100
100
  A function that builds a ready-made robot, such as `helyx.arm`, with every geometric number
@@ -39,7 +39,7 @@ a robot middleware such as ROS: signals come in and go out as arrays.
39
39
  | describe a new robot | `vmc.models`: `PCC`, `SerialChain`, `JointSpace`, `LinearCoupling`, `Assembly`, `TendonTransmission` |
40
40
  | place virtual elements | `vmc.Mechanism`, coordinates and components such as `vmc.LinearSpring` |
41
41
  | compile and run a controller | `vmc.VirtualMechanismSystem`, `vmc.compile`, `vmc.VMCController` |
42
- | correct commands for real hardware | `vmc.control.output`: friction compensation, pretension, limits |
42
+ | correct commands for real hardware | `vmc.control.output`: friction compensation, pretension, a tension floor, limits |
43
43
  | get positions, Jacobians, Hessians | `vmc.Kinematics` |
44
44
  | get the robot's equations of motion | `vmc.compile_dynamics` (from the components, or from a function with `vmc.models.Equations`) |
45
45
  | simulate a closed loop | `vmc.sim`: `ModelPlant`, `run`, `SimClock` |
@@ -76,7 +76,8 @@ size, are held out: the fit never sees them.
76
76
  `Steps` is this experiment as a controller. It runs on any plant, here the simulated arm, and
77
77
  its log goes straight to `identification.fit_stiffness_damping`. The fit knows the arm's
78
78
  masses and gravity, and fits its diagonal stiffness and damping by least squares, relative to
79
- the resting baseline. The masses are never fitted. Here the simulator plays the arm, so the
79
+ the resting baseline. A stiffness or damping that the run does not determine, say of a motor that
80
+ never pulls, comes back as NaN with a warning, not as a zero. The masses are never fitted. Here the simulator plays the arm, so the
80
81
  result can be checked against the values it was given.
81
82
 
82
83
  ```{code-cell} python
@@ -78,6 +78,15 @@ The nine motors and their bus are in `helyx.hardware("145-145-145")`. On the rea
78
78
  `helyx.output_stage()` adds a small pretension to every motor command
79
79
  ({glue:text}`pretension:.3f` N·m per radian of motor angle). The simulations below leave it out.
80
80
 
81
+ A tendon only pulls, and a motor that is told to push unwinds without end. A virtual spring on
82
+ the tip asks the tendons on the far side of a bend to do just that. `helyx.tension_stage()` is an
83
+ output stage that lifts the three tendons of each segment together until the lowest is at
84
+ `helyx.TENSION_FLOOR`: equal torques on three evenly spaced tendons only squeeze the segment, so
85
+ the bend is the same, and every tendon stays taut. `helyx.unwind_limit(arm)` is a spring on the
86
+ motor angles that pushes back a motor that has unwound more than `helyx.UNWIND_LIMIT` from its
87
+ start angle. Both are optional and tunable, and both have a smooth form, so a plan can include
88
+ them with `Problem(output=helyx.tension_stage())`.
89
+
81
90
  ## Reach past an obstacle
82
91
 
83
92
  A spring pulls the tip towards a target beyond an obstacle, while two repulsive Gaussian
@@ -128,10 +128,10 @@ pip install -e .
128
128
  An experiment should always run the same code. Pin the version, and move the pin on purpose:
129
129
 
130
130
  ```bash
131
- pip install "virtualmodelcontrol==1.2.2"
131
+ pip install "virtualmodelcontrol==1.3.0"
132
132
  ```
133
133
 
134
- In a `requirements.txt` file, write `virtualmodelcontrol==1.2.2`. In a conda `environment.yml`,
134
+ In a `requirements.txt` file, write `virtualmodelcontrol==1.3.0`. In a conda `environment.yml`,
135
135
  list it under `pip:`. `pip show virtualmodelcontrol` tells you which version you have, and the
136
136
  [changelog](development/changelog.md) what changed between versions.
137
137
 
@@ -57,7 +57,9 @@ class QuarticSpring(Component):
57
57
  return 0.25 * ctx.param(self.stiffness) * ca.sumsqr(y) ** 2
58
58
  ```
59
59
 
60
- It acts on any coordinate, and `compile` treats it like the built-in springs:
60
+ Run the cell again, in a notebook or after an edit, and the new class replaces the old one
61
+ under the name. Another class under a taken name is refused. It acts on any coordinate, and
62
+ `compile` treats it like the built-in springs:
61
63
 
62
64
  ```{code-cell} python
63
65
  from virtualmodelcontrol.robots import helyx
@@ -370,7 +372,9 @@ my_project = "my_project.vmc_plugins"
370
372
  ```
371
373
 
372
374
  Importing `my_project.vmc_plugins` must run its `register(...)` decorators. The library loads
373
- the plugins the first time a name is looked up and not found. So configuration files can use
375
+ the plugins the first time a name is looked up and not found. A plugin that fails to import does
376
+ not keep the others from loading: its error is raised, and a name that is not there says that a
377
+ plugin is broken. So configuration files can use
374
378
  the plugins' names ([Experiments in files](configurations.md)). Register a robot template as
375
379
  `"robot"`, a simulation's dynamics as `"dynamics"`, an output stage as `"output"`, a controller
376
380
  template as `"controller"` and a component as `"component"`. Register a coordinate kind as
@@ -118,7 +118,7 @@ assert abs(float(tanh_fit.values["damper.damping"]) - 1.5) < 0.1
118
118
 
119
119
  The true values are 2 kg, 40 N/m and 1.5 N·s/m. The log holds no accelerations, so the fit
120
120
  gets them by smoothing the velocity and differentiating it, which is where its error comes from
121
- (`smoothing` is the number of samples of the smoothing). A run that has an `a` of its own, as a
121
+ (`smoothing` is the number of samples of the smoothing: an odd number, at least 5). A run that has an `a` of its own, as a
122
122
  simulation can give, is used as it is. After the fit, {glue:text}`fit_rms:.4f` N is left over
123
123
  per sample. The numbers after ± are the standard errors: how far the values may be off if what is
124
124
  left over is noise. Here it is not noise, it is the smoothing, so the errors are much larger than
@@ -122,8 +122,10 @@ bottom.set_ylabel("stiffness [N/m]");
122
122
 
123
123
  `MPC` wraps the problem. It builds the program once, and every `step` solves it from the
124
124
  measured state, starting from the previous plan moved one interval on. It sets the Params of the
125
- plan on the controller through `controller.set`, and returns the jump this gave the controller's
126
- energy, as the [adaptation laws](stiffness.md) do:
125
+ plan on the controller through `controller.set`, if the solve converged (a failed one leaves the
126
+ Params as they are, `mpc.applied` and `mpc.result.converged` say, and the next solve starts from
127
+ the measured state), and returns the jump this gave the
128
+ controller's energy, as the [adaptation laws](stiffness.md) do:
127
129
 
128
130
  ```{code-cell} python
129
131
  mpc = opt.MPC(problem) # the start and the Params that step become inputs
@@ -211,8 +213,9 @@ while running:
211
213
  ```
212
214
 
213
215
  A solve lets the other threads run, so the loop keeps its rate. `start` does nothing while a
214
- plan is being made, and an error in the thread is raised by the next `poll`. Create the `MPC`
215
- after setting the problem's solver and options, since it creates the solver then.
216
+ plan is being made or has not been polled yet, `reset` drops a plan that is being made, and an
217
+ error in the thread is raised by the next `poll`. Create the `MPC` after setting the problem's
218
+ solver and options, since it creates the solver then.
216
219
 
217
220
  ## A tank pays for the changes
218
221
 
@@ -282,7 +285,9 @@ glue("rti", 1e3 * float(abs(fast[-1, 1] - 1.0)), display=False)
282
285
  ```
283
286
 
284
287
  Every plan is one iteration, and the mass ends {glue:text}`rti:.1f` mm from the goal. A plan
285
- then is not an optimum, only a step towards it; `plan.converged` is false for it by design.
288
+ then is not an optimum, only a step towards it; `plan.converged` is false for it by design, and
289
+ it is applied all the same. `rti=True` sets the problem's solver to `"rti"`, and an `MPC` of the
290
+ same problem without it puts the previous solver back.
286
291
 
287
292
  ## Solvers
288
293
 
@@ -326,7 +331,9 @@ glue("it_exact", int(rows["ipopt-exact"][0]), display=False)
326
331
 
327
332
  The default `"ipopt"` takes {glue:text}`it_ipopt` iterations against {glue:text}`it_exact` for
328
333
  `"ipopt-exact"`, as its Hessian is approximate, and ends at an acceptable level; `"rti"` is one
329
- step from a cold start, so its plan is not the optimum.
334
+ step from a cold start, so its plan is not the optimum. On a harder shooting the quasi-Newton
335
+ Hessian can use all of its 500 iterations without converging (it does on another problem with
336
+ CasADi 3.8.2): take `"ipopt-exact"` for a shooting, and read `plan.converged`.
330
337
 
331
338
  Which QP solver the SQP presets use is the `qpsol` option, and not every one of CasADi's
332
339
  suits a shooting. Tried on this problem with CasADi 3.7 and 3.8: `qrqp` (the default), `qpoases`
@@ -256,8 +256,9 @@ glue("gain", float(100 * (1 - found.result.cost / anchor)), display=False)
256
256
  sideways = [c["cost"] for c in found.candidates
257
257
  if c["cost"] is not None
258
258
  and np.abs(np.array(c["references"][key]) - target)[:2].max() > 1e-6]
259
- assert len(sideways) >= 2 and min(sideways) > 5 * anchor
260
- glue("sideways", float(min(sideways) / anchor), display=False)
259
+ assert len(sideways) >= 2 and min(sideways) > anchor # the target is the better reference
260
+ glue("sideways_min", float(min(sideways) / anchor), display=False)
261
+ glue("sideways_max", float(max(sideways) / anchor), display=False)
261
262
  glue("k_search", found.result.params["new.pull.stiffness"].item(), display=False)
262
263
  ```
263
264
 
@@ -266,8 +267,10 @@ neighbors. The status says how each solve ended: an acceptable level counts as c
266
267
  point that did not converge has no cost. The best reference lies {glue:text}`offset:.0f` mm from
267
268
  the target along z, and it lowers the cost by {glue:text}`gain:.1f` %, with a stiffness of
268
269
  {glue:text}`k_search:.0f` N/m: for this task the target is already a good reference. Moving it
269
- sideways costs at least {glue:text}`sideways:.0f` times more. `found.result` is the plan, `found.references` the
270
- point that gave it, and `found.candidates` every point tried.
270
+ sideways costs {glue:text}`sideways_min:.1f` to {glue:text}`sideways_max:.0f` times more (a solve of
271
+ this non-convex plan can end at a different optimum from another point's, and the CasADi version
272
+ changes where: read the statuses). `found.result` is the plan, `found.references` the point that gave it, and
273
+ `found.candidates` every point tried.
271
274
 
272
275
  ## Where the arm rests
273
276
 
@@ -37,6 +37,15 @@ soft = vmc.Mechanism(
37
37
  soft.actuation.motor_sizes(soft.space) # motor angles, motor rates
38
38
  ```
39
39
 
40
+ A number is a `Param`, and one `Param` can stand for several: give the same one to both sections
41
+ and they share its value, so that `Problem.free` and `set` change it once.
42
+
43
+ ```{code-cell} python
44
+ radius = vmc.Param("radius", 0.03, unit="m", scope="design")
45
+ shared = PCC([0.2, 0.2], radius) # one radius for both sections
46
+ shared.params["seg1.d"] is shared.params["seg2.d"]
47
+ ```
48
+
40
49
  The arm's mass is not lumped by itself. `soft.add_mass_along(name, mass, s0, s1, n)` spreads a mass
41
50
  evenly between two arc parameters as $n$ point masses at the nodes of Gauss-Legendre quadrature,
42
51
  which is exact for any polynomial of degree $2n - 1$ in the arc length. Here the height of the
@@ -125,7 +125,8 @@ A searcher is made from the bounds of the Params: `Grid(lower, upper, points)`,
125
125
  `Bayes(lower, upper, initial=...)` and `Random(lower, upper, size=...)`. `CMAES(start, step, lower,
126
126
  upper, size=...)` also takes a starting point and the size of its first steps, and
127
127
  `ExtremumSeeking(start, amplitude, gain, ...)` the size of its test moves and a gain. The CMA-ES
128
- loop below is the same as `tune`, written out:
128
+ loop below is the same as `tune`, written out. An episode that failed, with a cost that is `inf`
129
+ or NaN, counts as the worst cost so far and is never the best:
129
130
 
130
131
  ```{code-cell} python
131
132
  from virtualmodelcontrol import optimization as opt
@@ -108,7 +108,9 @@ builds it, and `base` chooses how its torques are rendered:
108
108
  - `"frozen"` evaluates the same law with the passive joint held at its rest angle, whatever it
109
109
  does. It needs the motors only, and its torque is the exact gradient of a potential. With
110
110
  `gravity=True` it holds the passive joint where its spring balances the arm's own weight
111
- instead of at the rest angle.
111
+ instead of at the rest angle, found again at every reading ([a passive joint under
112
+ weight](#a-passive-joint-under-weight) says what that costs); with `gravity=` the motor
113
+ angles of a pose it finds the balance once, there.
112
114
 
113
115
  ```{code-cell} python
114
116
  def reach(robot, compensate=True, stiffness=150.0):
@@ -120,8 +122,9 @@ def reach(robot, compensate=True, stiffness=150.0):
120
122
  ctrl.add("gravity", vmc.GravityCompensation(robot))
121
123
  return vmc.compile(vmc.VirtualMechanismSystem(robot, ctrl))
122
124
 
123
- def run(compiled, base, correction=None, gravity=False, T=4.0):
124
- controller = ua.controller(compiled, base, correction, gravity=gravity)
125
+ def run(compiled, base, correction=None, gravity=False, T=4.0, **stage):
126
+ controller = ua.controller(compiled, base, correction, gravity=gravity,
127
+ **stage)
125
128
  plant = vmc.sim.ModelPlant(arm, q0=[0.3, 0.3, 0.3], max_step=1e-4)
126
129
  log = vmc.sim.run(plant, controller, vmc.sim.SimClock(5e-4), T=T)
127
130
  return log.arrays()
@@ -174,6 +177,58 @@ goal (naive, and frozen with `gravity=True`) and {glue:text}`bare_frozen:.0f` mm
174
177
  frozen). Where the passive joint is at rest and carries nothing, naive and frozen send the
175
178
  same torques.
176
179
 
180
+ ## A passive joint under weight
181
+
182
+ The frozen controller keeps the arm passive because its torque is the gradient of a potential:
183
+ the field of motor angles $\theta \mapsto u(\theta)$ has no curl, so the motors cannot gain
184
+ energy by going around a loop. That holds while the rest of the passive joint is a constant. With
185
+ `gravity=True` the rest is the joint's balance at the current motor angles, and it moves as they
186
+ do, which puts a small curl into the field. We measure the curl as the asymmetry of the Jacobian
187
+ of $u$ with respect to $\theta$, relative to its size, for the plain frozen controller, for
188
+ `gravity=True`, and for `gravity=` the motor angles of a pose, where the balance is solved once:
189
+
190
+ ```{code-cell} python
191
+ def curl(gravity, theta, h=1e-6):
192
+ controller = ua.controller(compiled, "frozen", gravity=gravity)
193
+
194
+ def torque(theta):
195
+ reading = vmc.Signals(0.0, motor_position=theta, q=[0.0] * 3,
196
+ motor_velocity=[0.0, 0.0], v=[0.0] * 3)
197
+ controller.reset(0.0, reading)
198
+ return np.asarray(controller.step(0.0, reading)["law_torque"])
199
+
200
+ jac = np.column_stack([(torque(theta + h * e) - torque(theta - h * e))
201
+ / (2 * h) for e in np.eye(2)])
202
+ return abs(jac - jac.T).max() / abs(jac).max()
203
+
204
+ pose = B.T @ runs["naive"]["q"][-1] # the motor angles where the reach ends
205
+ curls = {"plain": curl(False, pose), "moving": curl(True, pose),
206
+ "fixed": curl(pose, pose)}
207
+ ```
208
+
209
+ ```{code-cell} python
210
+ :tags: [remove-cell]
211
+ assert curls["plain"] < 1e-8 and curls["fixed"] < 1e-8
212
+ assert 1e-3 < curls["moving"] < 0.2
213
+ settled = run(compiled, "frozen", gravity=pose)
214
+ final_fixed = 1000 * float(tip_error(settled)[-1])
215
+ assert final_fixed < 1.0, final_fixed
216
+ glue("curl_plain", float(curls["plain"]), display=False)
217
+ glue("curl_fixed", float(curls["fixed"]), display=False)
218
+ glue("curl_moving", float(curls["moving"]), display=False)
219
+ glue("final_fixed", final_fixed, display=False)
220
+ ```
221
+
222
+ The plain frozen torque has no curl ({glue:text}`curl_plain:.0e`), and neither has the one whose
223
+ balance is solved once ({glue:text}`curl_fixed:.0e`). The torque that follows the balance has a
224
+ curl of {glue:text}`curl_moving:.1%` here: a few percent of the field, but not a gradient, so the
225
+ guarantee does not cover it. Give `gravity=` the motor angles of the pose the arm works around, such as where the
226
+ reach ends. With the balance solved there, the frozen controller ends
227
+ {glue:text}`final_fixed:.1e` mm from the goal, as it does with `gravity=True`, and its torque is
228
+ a gradient again. The rest it found is `ua.Frozen(arm.actuation, dynamics, at=pose).rest`, which
229
+ can be set as the `q_rest` of the actuation, the way [a plan](#planning-with-these-controllers)
230
+ reads it.
231
+
177
232
  ## Keeping the arm passive
178
233
 
179
234
  The naive torque is not the gradient of anything, so a controller built on it can put energy
@@ -227,8 +282,57 @@ Without a correction the arm gains up to {glue:text}`peak_none:.2f` J from the c
227
282
  the passive correction it gains {glue:text}`peak_passive:.3f` J at most, the rounding of the
228
283
  control period (the guarantee holds in continuous time and the gain falls with the period). With
229
284
  the tank it gains {glue:text}`peak_tank:.2f` J, the budget it started with (the default
230
- `tank=1.0` J of `ua.controller`). The frozen controller needs no correction for its own
231
- torque, which is a gradient.
285
+ `tank=1.0` J of `ua.controller`).
286
+
287
+ The `width` of a correction, in W, smooths the point where it starts. Its default, 1 W, is as
288
+ large as the power of this arm, so the correction also slows the reach when little needs
289
+ correcting: after 4 s the passive correction is still {glue:text}`slow_default:.0f` mm from the
290
+ goal, and with `width=1e-2` it is {glue:text}`slow_narrow:.1f` mm. Take the smallest power that
291
+ matters in your loop.
292
+
293
+ ```{code-cell} python
294
+ :tags: [remove-cell]
295
+ narrow = run(compiled, "naive", "passive", width=1e-2)
296
+ slow_default = 1000 * float(tip_error(corrected["passive"])[-1])
297
+ slow_narrow = 1000 * float(tip_error(narrow)[-1])
298
+ assert slow_default > 10.0 and slow_narrow < 1.0, (slow_default, slow_narrow)
299
+ glue("slow_default", slow_default, display=False)
300
+ glue("slow_narrow", slow_narrow, display=False)
301
+ ```
302
+
303
+ The frozen controller needs no correction to keep the energy of its own virtual spring from
304
+ growing, since its torque is a gradient (with `gravity=True`, only up to the small curl above).
305
+ Do not stack them. A correction compares the power the motors inject with what the arm's own
306
+ dampers dissipate, and a virtual spring that is releasing its energy injects power, so it also
307
+ cuts the torque of the frozen controller:
308
+
309
+ ```{code-cell} python
310
+ def removed(base, T=4.0, dt=5e-4):
311
+ """The torque [N·m s] that the passive correction takes off the law."""
312
+ controller = ua.controller(compiled, base, "passive", width=1e-4)
313
+ plant = vmc.sim.ModelPlant(arm, q0=[0.3, 0.3, 0.3], max_step=1e-4)
314
+ controller.reset(0.0, plant.read())
315
+ total = 0.0
316
+ for _ in range(round(T / dt)):
317
+ command = controller.step(plant.t, plant.read())
318
+ total += dt * float(np.abs(command["law_torque"]
319
+ - command["motor_torque"]).sum())
320
+ plant.write(command)
321
+ plant.advance(dt)
322
+ return total
323
+
324
+ cut = {base: removed(base) for base in ("naive", "frozen")}
325
+ ```
326
+
327
+ ```{code-cell} python
328
+ :tags: [remove-cell]
329
+ assert cut["frozen"] > 0.1 and cut["naive"] > 0.1, cut
330
+ glue("cut_frozen", float(cut["frozen"]), display=False)
331
+ glue("cut_naive", float(cut["naive"]), display=False)
332
+ ```
333
+
334
+ Over this reach the correction takes {glue:text}`cut_frozen:.1f` N·m·s off the frozen torque and
335
+ {glue:text}`cut_naive:.1f` N·m·s off the naive one. Use the corrections on the naive controller.
232
336
 
233
337
  ## A force along a direction
234
338
 
@@ -308,6 +412,44 @@ for ax, (name, robot) in zip(axes, robots.items()):
308
412
  axes[0].set_ylabel("height [m]");
309
413
  ```
310
414
 
415
+ ## Planning with these controllers
416
+
417
+ A plan of the [optimization layer](optimize.md) is the closed loop, so it has to read what the
418
+ controller on the robot reads. For a robot with fewer motors than joints, `Problem(system)`
419
+ plans the controller that reads the motors, as `VMCController` does: the frozen one, with the
420
+ passive joints at their rest angles. `Problem(system, reads="state")` plans the whole-state
421
+ controller of `"naive"` instead, and `reads="motors"` asks for the first one outright. Here is
422
+ where each plan puts the arm at rest, started near where the matching simulation of the reach
423
+ ended:
424
+
425
+ ```{code-cell} python
426
+ from virtualmodelcontrol import optimization as opt
427
+
428
+ def planned_end(reads, base):
429
+ start = runs[base]["q"][-1] + 0.05 # a guess near where it ends
430
+ problem = opt.Problem(compiled.system, reads=reads)
431
+ problem.add(opt.Equilibrium(start))
432
+ q = problem.solve().q[0]
433
+ return 1000 * np.linalg.norm(kin.position(q, "tip")[:2] - [0.45, 0.15])
434
+
435
+ planned = {"naive": planned_end("state", "naive"),
436
+ "frozen": planned_end("motors", "frozen")}
437
+ ```
438
+
439
+ ```{code-cell} python
440
+ :tags: [remove-cell]
441
+ assert abs(planned["naive"] - 1000 * final["naive"]) < 0.1
442
+ assert abs(planned["frozen"] - 1000 * final["frozen"]) < 0.1
443
+ glue("planned_naive", float(planned["naive"]), display=False)
444
+ glue("planned_frozen", float(planned["frozen"]), display=False)
445
+ ```
446
+
447
+ The plans end {glue:text}`planned_naive:.1f` mm and {glue:text}`planned_frozen:.1f` mm from the
448
+ goal, where the simulations above end. The frozen controller's rest is the `q_rest` Param of the
449
+ actuation, so a plan can optimize it (`problem.free("arm.q_rest")`), and set it to a balanced
450
+ rest as in the section above. The corrections have no symbolic form yet: `Problem(output=...)`
451
+ refuses them. Plan the controller and add the correction when you run it.
452
+
311
453
  ## When the passive joint is not measured
312
454
 
313
455
  The naive controller needs the angle and the rate of every joint, and the encoders give the
@@ -317,7 +459,7 @@ joint moves the other two through the coupling, and the filter reads it from the
317
459
  see, $\theta = B^\top q$, as combinations of $q$ (the measurement's `matrix`), not as a reading
318
460
  of $q$, so the passive joint is left to the model. Each period the loop updates the filter with
319
461
  the encoders, hands the estimate to the controller as the signals `q` and `v`, and predicts
320
- the next period from the torques it sent.
462
+ the next period from the torques it sent. A `MovingHorizon` estimator takes the same `encoder`.
321
463
 
322
464
  ```{code-cell} python
323
465
  from virtualmodelcontrol.estimation import Encoders, KalmanFilter
@@ -440,21 +582,26 @@ assert torque.shape == (2,) and np.isfinite(torque).all()
440
582
 
441
583
  ## Choosing
442
584
 
443
- - Start with `"frozen"`, with `gravity=True` if the arm carries weight: it needs only the
444
- motors, its torque is a gradient, and it agrees with the naive one where the passive joints
445
- rest.
585
+ - Start with `"frozen"`, with `gravity=` the motor angles of the pose it works around if the
586
+ arm carries weight: it needs only the motors, its torque is a gradient, and it agrees with
587
+ the naive one where the passive joints rest. `gravity=True` follows the balance at every
588
+ reading instead, which is closer to the naive controller far from one pose, but its torque
589
+ is not a gradient.
446
590
  - Take `"naive"` when the whole state is measured or estimated well and you want the force the
447
591
  spring asks for even away from the passive joints' rest.
448
- - Add `"passive"` when the arm must never gain energy, and `"tank"` to allow short bursts up
449
- to a budget. A correction removes torque, so it can slow the reach; set `tank` to what the
450
- task needs.
592
+ - Add `"passive"` to the naive controller when the arm must never gain energy, and `"tank"` to
593
+ allow short bursts up to a budget. A correction removes torque, so it can slow the reach: set
594
+ `tank` to what the task needs and `width` to the smallest power that matters. They also cut
595
+ the frozen torque, which needs no correction.
451
596
  - The laws and estimators that read a controller's motors (`ForceTracking`, `StiffnessTracking`,
452
597
  `PositionRegulation`, `HoldingGoals`, `ContactForce`, `TaskStiffness`) refuse a
453
- `StateController`, which is what `"naive"` and `"frozen"` with `gravity=True` give, with a
598
+ `StateController`, which is what `"naive"` and `"frozen"` with `gravity` set give, with a
454
599
  `ValueError`. Plain `"frozen"` is a `VMCController` and works with them; for a force on
455
600
  an underactuated arm use `DirectionalForce`.
456
- - `gravity=True` holds the passive joints where their springs balance the arm's weight, not at
601
+ - `gravity=` holds the passive joints where their springs balance the arm's weight, not at
457
602
  the rest angle: the right reference for an arm that hangs or reaches out. The weight itself
458
603
  is compensated by a `GravityCompensation` in the controller, as for any robot.
459
- - `planar.arm(rest=...)` sets the passive joints' rest angles; the frozen controller and the
460
- passive spring read them from the same place.
604
+ - `planar.arm(rest=...)` sets the passive joints' rest angles, one for all, one per passive
605
+ joint or one per joint. The frozen controller reads them as the `q_rest` of the actuation,
606
+ and `add_dynamics` copies them into the passive springs' rest: the two do not follow each
607
+ other after that.
@@ -37,6 +37,7 @@ from .mechanisms import (
37
37
  LinearDamper,
38
38
  LinearSpring,
39
39
  Mechanism,
40
+ MotorAngle,
40
41
  Norm,
41
42
  OrientationError,
42
43
  PhaseSpring,
@@ -110,6 +111,7 @@ __all__ = [
110
111
  "LinearDamper",
111
112
  "LinearSpring",
112
113
  "Mechanism",
114
+ "MotorAngle",
113
115
  "Norm",
114
116
  "OrientationError",
115
117
  "Param",
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '1.2.2'
22
- __version_tuple__ = version_tuple = (1, 2, 2)
21
+ __version__ = version = '1.3.0'
22
+ __version_tuple__ = version_tuple = (1, 3, 0)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -74,9 +74,9 @@ class DitherSeeking:
74
74
  if len(self._t) > 2 and self._t[-1] - self._t[0] > 0.9 * self.window:
75
75
  times, costs = np.array(self._t), np.array(self._cost)
76
76
  weights = np.gradient(times)
77
- centred = costs - np.sum(costs * weights) / np.sum(weights)
77
+ centered = costs - np.sum(costs * weights) / np.sum(weights)
78
78
  probes = np.sin(np.outer(self.frequency, times))
79
- self.slope = 2.0 / self.amplitude * (probes @ (centred * weights)) / np.sum(weights)
79
+ self.slope = 2.0 / self.amplitude * (probes @ (centered * weights)) / np.sum(weights)
80
80
  dt = times[-1] - times[-2]
81
81
  rate = np.clip(self.gain * self.slope, -self.max_rate, self.max_rate)
82
82
  for i, name in enumerate(self.names):
@@ -6,6 +6,7 @@ from .output import (
6
6
  FrictionCompensation,
7
7
  Pretension,
8
8
  StaticFrictionCompensation,
9
+ TensionFloor,
9
10
  TorqueLimit,
10
11
  TorqueOffset,
11
12
  )
@@ -23,6 +24,7 @@ __all__ = [
23
24
  "StaticFrictionCompensation",
24
25
  "SwapController",
25
26
  "Tank",
27
+ "TensionFloor",
26
28
  "TorqueLimit",
27
29
  "TorqueOffset",
28
30
  "VMCController",
@@ -78,4 +78,6 @@ class SwapController:
78
78
  return Signals(t, **mixed)
79
79
 
80
80
  def __getattr__(self, name: str) -> Any:
81
+ if name.startswith("__") or "controller" not in self.__dict__: # copy and pickle ask early
82
+ raise AttributeError(name)
81
83
  return getattr(self.controller, name)