virtualmodelcontrol 0.3.0__tar.gz → 1.0.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 (419) hide show
  1. virtualmodelcontrol-1.0.0/CHANGELOG.md +777 -0
  2. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/PKG-INFO +7 -10
  3. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/README.md +3 -4
  4. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/_static/custom.css +14 -0
  5. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/api/index.md +4 -1
  6. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/concepts/conventions.md +15 -8
  7. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/concepts/efficiency.md +10 -10
  8. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/concepts/finger.md +11 -9
  9. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/concepts/library.md +17 -10
  10. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/concepts/passivity.md +15 -9
  11. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/concepts/pcc.md +8 -7
  12. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/concepts/vmc.md +15 -11
  13. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/development/contributing.md +15 -10
  14. virtualmodelcontrol-1.0.0/docs/development/support.md +48 -0
  15. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/docs_setup.py +1 -0
  16. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/examples/finger.md +131 -8
  17. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/examples/hand.md +21 -20
  18. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/examples/hanging-arm.md +110 -58
  19. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/examples/soft-arm.md +32 -30
  20. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/examples/turtle.md +12 -8
  21. virtualmodelcontrol-1.0.0/docs/examples/two-arms.md +307 -0
  22. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/index.md +39 -8
  23. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/installation.md +16 -16
  24. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/schematics/library_map.py +13 -3
  25. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/troubleshooting.md +4 -4
  26. virtualmodelcontrol-1.0.0/docs/tutorials/build-a-robot.md +468 -0
  27. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/tutorials/configurations.md +30 -27
  28. virtualmodelcontrol-1.0.0/docs/tutorials/contact.md +435 -0
  29. virtualmodelcontrol-1.0.0/docs/tutorials/coordinates-and-components.md +292 -0
  30. virtualmodelcontrol-1.0.0/docs/tutorials/crawl.md +451 -0
  31. virtualmodelcontrol-1.0.0/docs/tutorials/energy.md +310 -0
  32. virtualmodelcontrol-1.0.0/docs/tutorials/estimation.md +368 -0
  33. virtualmodelcontrol-1.0.0/docs/tutorials/extend.md +340 -0
  34. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/tutorials/first-controller.md +14 -7
  35. virtualmodelcontrol-1.0.0/docs/tutorials/fit.md +92 -0
  36. virtualmodelcontrol-1.0.0/docs/tutorials/force.md +543 -0
  37. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/tutorials/introduction.md +8 -9
  38. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/tutorials/kinematics.md +97 -7
  39. virtualmodelcontrol-1.0.0/docs/tutorials/mpc.md +350 -0
  40. virtualmodelcontrol-1.0.0/docs/tutorials/optimize.md +823 -0
  41. virtualmodelcontrol-1.0.0/docs/tutorials/outside.md +203 -0
  42. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/tutorials/parameters.md +9 -7
  43. virtualmodelcontrol-1.0.0/docs/tutorials/real-time.md +362 -0
  44. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/tutorials/run-logs.md +27 -24
  45. virtualmodelcontrol-1.0.0/docs/tutorials/stiffness.md +294 -0
  46. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/tutorials/swaps.md +35 -21
  47. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/tutorials/tuning.md +80 -18
  48. virtualmodelcontrol-1.0.0/docs/tutorials/underactuated.md +439 -0
  49. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/pyproject.toml +14 -25
  50. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/__init__.py +40 -9
  51. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/_version.py +2 -2
  52. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/adaptation/__init__.py +17 -0
  53. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/adaptation/force.py +109 -0
  54. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/adaptation/limits.py +39 -0
  55. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/adaptation/position.py +97 -0
  56. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/adaptation/scalar.py +94 -0
  57. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/adaptation/seeking.py +90 -0
  58. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/adaptation/stiffness.py +73 -0
  59. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/compiler.py +12 -0
  60. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/config/coordinates.py +110 -0
  61. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/config/files.py +2 -2
  62. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/control/__init__.py +6 -0
  63. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/control/controller.py +43 -14
  64. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/control/projection.py +14 -0
  65. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/control/state.py +75 -0
  66. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/control/tank.py +70 -0
  67. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/control/underactuated.py +278 -0
  68. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/core/__init__.py +2 -1
  69. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/core/backends.py +274 -0
  70. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/core/space.py +106 -0
  71. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/core/symbolic.py +1 -25
  72. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/dynamics.py +229 -0
  73. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/estimation/__init__.py +28 -0
  74. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/estimation/balance.py +34 -0
  75. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/estimation/compliance.py +29 -0
  76. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/estimation/contact.py +37 -0
  77. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/estimation/imu.py +131 -0
  78. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/estimation/inversion.py +99 -0
  79. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/estimation/kalman.py +243 -0
  80. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/estimation/measurement.py +102 -0
  81. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/estimation/momentum.py +74 -0
  82. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/estimation/sensors.py +122 -0
  83. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/estimation/stiffness.py +47 -0
  84. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/estimation/velocity.py +37 -0
  85. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/hardware/__init__.py +5 -0
  86. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/hardware/profile.py +114 -0
  87. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/identification/__init__.py +19 -0
  88. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/identification/params.py +120 -0
  89. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/identification/steps.py +55 -0
  90. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/identification/stiffness.py +126 -0
  91. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/identification/transmission.py +18 -0
  92. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/math.py +207 -0
  93. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/mechanisms/__init__.py +28 -0
  94. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/mechanisms/components/__init__.py +9 -2
  95. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/mechanisms/components/base.py +6 -2
  96. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/mechanisms/components/dissipation.py +152 -0
  97. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/mechanisms/components/gated.py +40 -0
  98. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/mechanisms/components/inertance.py +29 -1
  99. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/mechanisms/components/locomotion.py +59 -0
  100. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/mechanisms/components/sources.py +27 -4
  101. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/mechanisms/coordinates/__init__.py +21 -4
  102. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/mechanisms/coordinates/base.py +10 -0
  103. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/mechanisms/coordinates/frames.py +189 -0
  104. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/mechanisms/coordinates/geometry.py +187 -0
  105. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/mechanisms/coordinates/ops.py +18 -0
  106. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/mechanisms/coordinates/references.py +13 -0
  107. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/mechanisms/mechanism.py +20 -3
  108. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/models/__init__.py +7 -1
  109. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/models/actuation.py +149 -0
  110. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/models/assembly.py +2 -2
  111. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/models/equations.py +30 -0
  112. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/models/function.py +74 -0
  113. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/models/kinematics.py +28 -3
  114. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/models/rigid/poe.py +238 -0
  115. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/models/rigid/spline.py +41 -0
  116. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/optimization/__init__.py +49 -0
  117. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/optimization/asktell.py +258 -0
  118. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/optimization/builder.py +226 -0
  119. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/optimization/collocation.py +267 -0
  120. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/optimization/equilibrium.py +89 -0
  121. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/optimization/horizon.py +224 -0
  122. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/optimization/mpc.py +180 -0
  123. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/optimization/nlp.py +71 -0
  124. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/optimization/problem.py +305 -0
  125. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/optimization/result.py +71 -0
  126. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/optimization/search.py +125 -0
  127. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/optimization/shooting.py +264 -0
  128. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/optimization/solver.py +140 -0
  129. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/optimization/tank.py +57 -0
  130. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/optimization/terms.py +217 -0
  131. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/optimization/trajectory.py +56 -0
  132. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/optimization/variables.py +67 -0
  133. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/optimization/virtual.py +56 -0
  134. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/robots/__init__.py +5 -0
  135. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/robots/adapt.py +12 -19
  136. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/robots/bimanual.py +5 -14
  137. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/robots/helyx.py +4 -10
  138. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/robots/planar.py +217 -0
  139. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/robots/turtle.py +213 -0
  140. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/sim/__init__.py +5 -0
  141. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/sim/balance.py +50 -0
  142. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/sim/model_plant.py +15 -2
  143. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/sim/rollout.py +158 -0
  144. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/sim/run.py +35 -51
  145. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/system.py +4 -0
  146. virtualmodelcontrol-1.0.0/src/virtualmodelcontrol/testing.py +234 -0
  147. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/viz/draw.py +1 -1
  148. virtualmodelcontrol-1.0.0/tests/adaptation/test_fingertip.py +139 -0
  149. virtualmodelcontrol-1.0.0/tests/adaptation/test_force.py +219 -0
  150. virtualmodelcontrol-1.0.0/tests/adaptation/test_grasp.py +99 -0
  151. virtualmodelcontrol-1.0.0/tests/adaptation/test_holding.py +154 -0
  152. virtualmodelcontrol-1.0.0/tests/adaptation/test_loop.py +58 -0
  153. virtualmodelcontrol-1.0.0/tests/adaptation/test_position.py +71 -0
  154. virtualmodelcontrol-1.0.0/tests/adaptation/test_scalar.py +155 -0
  155. virtualmodelcontrol-1.0.0/tests/adaptation/test_seeking.py +139 -0
  156. virtualmodelcontrol-1.0.0/tests/adaptation/test_stiffness_tracking.py +136 -0
  157. virtualmodelcontrol-1.0.0/tests/adaptation/test_virtual_state.py +67 -0
  158. virtualmodelcontrol-1.0.0/tests/backend_helpers.py +71 -0
  159. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/config/test_config.py +82 -4
  160. virtualmodelcontrol-1.0.0/tests/control/test_inputs.py +32 -0
  161. virtualmodelcontrol-1.0.0/tests/control/test_projection.py +45 -0
  162. virtualmodelcontrol-1.0.0/tests/control/test_state_layout.py +48 -0
  163. virtualmodelcontrol-1.0.0/tests/control/test_tank.py +152 -0
  164. virtualmodelcontrol-1.0.0/tests/control/test_underactuated.py +341 -0
  165. virtualmodelcontrol-1.0.0/tests/control/test_underactuated_golden.py +130 -0
  166. virtualmodelcontrol-1.0.0/tests/core/test_backends.py +166 -0
  167. virtualmodelcontrol-1.0.0/tests/core/test_backends_torch.py +78 -0
  168. virtualmodelcontrol-1.0.0/tests/core/test_space.py +129 -0
  169. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/core/test_symbolic.py +1 -10
  170. virtualmodelcontrol-1.0.0/tests/data/adaptation.npz +0 -0
  171. virtualmodelcontrol-1.0.0/tests/data/compliance.npz +0 -0
  172. virtualmodelcontrol-1.0.0/tests/data/estimation.npz +0 -0
  173. virtualmodelcontrol-1.0.0/tests/data/fingertip.npz +0 -0
  174. virtualmodelcontrol-1.0.0/tests/data/heuristics.npz +0 -0
  175. virtualmodelcontrol-1.0.0/tests/data/imu.npz +0 -0
  176. virtualmodelcontrol-1.0.0/tests/data/kalman.npz +0 -0
  177. virtualmodelcontrol-1.0.0/tests/data/mocap.npz +0 -0
  178. virtualmodelcontrol-1.0.0/tests/data/optimization.json +326 -0
  179. virtualmodelcontrol-1.0.0/tests/data/optimization.npz +0 -0
  180. virtualmodelcontrol-1.0.0/tests/data/transmission.npz +0 -0
  181. virtualmodelcontrol-1.0.0/tests/data/underactuated.npz +0 -0
  182. virtualmodelcontrol-1.0.0/tests/estimation/test_compliance_golden.py +63 -0
  183. virtualmodelcontrol-1.0.0/tests/estimation/test_contact.py +201 -0
  184. virtualmodelcontrol-1.0.0/tests/estimation/test_imu.py +236 -0
  185. virtualmodelcontrol-1.0.0/tests/estimation/test_inversion.py +264 -0
  186. virtualmodelcontrol-1.0.0/tests/estimation/test_kalman.py +261 -0
  187. virtualmodelcontrol-1.0.0/tests/estimation/test_kalman_golden.py +149 -0
  188. virtualmodelcontrol-1.0.0/tests/estimation/test_kalman_unscented.py +184 -0
  189. virtualmodelcontrol-1.0.0/tests/estimation/test_mocap_golden.py +109 -0
  190. virtualmodelcontrol-1.0.0/tests/estimation/test_momentum.py +176 -0
  191. virtualmodelcontrol-1.0.0/tests/estimation/test_sensors.py +115 -0
  192. virtualmodelcontrol-1.0.0/tests/estimation/test_unmeasured.py +159 -0
  193. virtualmodelcontrol-1.0.0/tests/estimation/test_velocity.py +67 -0
  194. virtualmodelcontrol-1.0.0/tests/hardware/test_profile.py +37 -0
  195. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/helpers.py +23 -3
  196. virtualmodelcontrol-1.0.0/tests/identification/test_fit_params.py +168 -0
  197. virtualmodelcontrol-1.0.0/tests/identification/test_friction.py +98 -0
  198. virtualmodelcontrol-1.0.0/tests/identification/test_steps.py +174 -0
  199. virtualmodelcontrol-1.0.0/tests/identification/test_transmission.py +59 -0
  200. virtualmodelcontrol-1.0.0/tests/lab_optimization.py +137 -0
  201. virtualmodelcontrol-1.0.0/tests/mechanisms/test_distances.py +140 -0
  202. virtualmodelcontrol-1.0.0/tests/mechanisms/test_frames.py +182 -0
  203. virtualmodelcontrol-1.0.0/tests/mechanisms/test_friction.py +163 -0
  204. virtualmodelcontrol-1.0.0/tests/mechanisms/test_gated.py +68 -0
  205. virtualmodelcontrol-1.0.0/tests/mechanisms/test_more_components.py +208 -0
  206. virtualmodelcontrol-1.0.0/tests/mechanisms/test_pair_contact.py +124 -0
  207. virtualmodelcontrol-1.0.0/tests/mechanisms/test_phase_spring.py +346 -0
  208. virtualmodelcontrol-1.0.0/tests/mechanisms/test_virtual_model.py +175 -0
  209. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/models/test_actuation.py +67 -4
  210. virtualmodelcontrol-1.0.0/tests/models/test_contact_map.py +38 -0
  211. virtualmodelcontrol-1.0.0/tests/models/test_equations.py +274 -0
  212. virtualmodelcontrol-1.0.0/tests/models/test_floating.py +165 -0
  213. virtualmodelcontrol-1.0.0/tests/models/test_floating_base.py +46 -0
  214. virtualmodelcontrol-1.0.0/tests/models/test_function.py +86 -0
  215. virtualmodelcontrol-1.0.0/tests/models/test_poe.py +165 -0
  216. virtualmodelcontrol-1.0.0/tests/models/test_rail.py +196 -0
  217. virtualmodelcontrol-1.0.0/tests/models/test_underactuated.py +80 -0
  218. virtualmodelcontrol-1.0.0/tests/optimization/test_asktell.py +259 -0
  219. virtualmodelcontrol-1.0.0/tests/optimization/test_collocation.py +206 -0
  220. virtualmodelcontrol-1.0.0/tests/optimization/test_equilibrium.py +151 -0
  221. virtualmodelcontrol-1.0.0/tests/optimization/test_horizon.py +263 -0
  222. virtualmodelcontrol-1.0.0/tests/optimization/test_lab.py +128 -0
  223. virtualmodelcontrol-1.0.0/tests/optimization/test_mpc.py +316 -0
  224. virtualmodelcontrol-1.0.0/tests/optimization/test_periodic.py +586 -0
  225. virtualmodelcontrol-1.0.0/tests/optimization/test_plant.py +252 -0
  226. virtualmodelcontrol-1.0.0/tests/optimization/test_problem.py +424 -0
  227. virtualmodelcontrol-1.0.0/tests/optimization/test_search.py +146 -0
  228. virtualmodelcontrol-1.0.0/tests/optimization/test_shooting.py +339 -0
  229. virtualmodelcontrol-1.0.0/tests/optimization/test_solvers.py +123 -0
  230. virtualmodelcontrol-1.0.0/tests/optimization/test_structure.py +120 -0
  231. virtualmodelcontrol-1.0.0/tests/optimization/test_swap.py +51 -0
  232. virtualmodelcontrol-1.0.0/tests/optimization/test_tank.py +117 -0
  233. virtualmodelcontrol-1.0.0/tests/optimization/test_terms.py +226 -0
  234. virtualmodelcontrol-1.0.0/tests/optimization/test_variables.py +43 -0
  235. virtualmodelcontrol-1.0.0/tests/optimization/test_virtual.py +416 -0
  236. virtualmodelcontrol-1.0.0/tests/robots/test_crawler.py +333 -0
  237. virtualmodelcontrol-1.0.0/tests/robots/test_planar.py +94 -0
  238. virtualmodelcontrol-1.0.0/tests/robots/test_turtle.py +113 -0
  239. virtualmodelcontrol-1.0.0/tests/sim/test_balance.py +110 -0
  240. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/sim/test_record.py +22 -0
  241. virtualmodelcontrol-1.0.0/tests/sim/test_rollout.py +193 -0
  242. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/sim/test_wallclock.py +14 -10
  243. virtualmodelcontrol-1.0.0/tests/small_plan.py +62 -0
  244. virtualmodelcontrol-1.0.0/tests/soft_arm_plan.py +30 -0
  245. virtualmodelcontrol-1.0.0/tests/test_benchmark.py +54 -0
  246. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/test_docs.py +6 -4
  247. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/test_dynamics.py +18 -0
  248. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/test_import.py +1 -10
  249. virtualmodelcontrol-1.0.0/tests/test_math.py +152 -0
  250. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/test_readme.py +1 -1
  251. virtualmodelcontrol-1.0.0/tests/test_testing.py +191 -0
  252. virtualmodelcontrol-1.0.0/tests/underactuated_lab.py +50 -0
  253. virtualmodelcontrol-0.3.0/CHANGELOG.md +0 -291
  254. virtualmodelcontrol-0.3.0/docs/examples/two-arms.md +0 -180
  255. virtualmodelcontrol-0.3.0/docs/tutorials/build-a-robot.md +0 -181
  256. virtualmodelcontrol-0.3.0/docs/tutorials/contact.md +0 -199
  257. virtualmodelcontrol-0.3.0/docs/tutorials/coordinates-and-components.md +0 -161
  258. virtualmodelcontrol-0.3.0/docs/tutorials/energy.md +0 -193
  259. virtualmodelcontrol-0.3.0/docs/tutorials/extend.md +0 -193
  260. virtualmodelcontrol-0.3.0/docs/tutorials/interactive.md +0 -221
  261. virtualmodelcontrol-0.3.0/docs/tutorials/real-time.md +0 -232
  262. virtualmodelcontrol-0.3.0/src/virtualmodelcontrol/dynamics.py +0 -151
  263. virtualmodelcontrol-0.3.0/src/virtualmodelcontrol/hardware/__init__.py +0 -38
  264. virtualmodelcontrol-0.3.0/src/virtualmodelcontrol/hardware/bus.py +0 -203
  265. virtualmodelcontrol-0.3.0/src/virtualmodelcontrol/hardware/check.py +0 -41
  266. virtualmodelcontrol-0.3.0/src/virtualmodelcontrol/hardware/dynamixel.py +0 -162
  267. virtualmodelcontrol-0.3.0/src/virtualmodelcontrol/hardware/homing.py +0 -70
  268. virtualmodelcontrol-0.3.0/src/virtualmodelcontrol/hardware/profile.py +0 -194
  269. virtualmodelcontrol-0.3.0/src/virtualmodelcontrol/identification/__init__.py +0 -7
  270. virtualmodelcontrol-0.3.0/src/virtualmodelcontrol/identification/stiffness.py +0 -70
  271. virtualmodelcontrol-0.3.0/src/virtualmodelcontrol/interactive/__init__.py +0 -25
  272. virtualmodelcontrol-0.3.0/src/virtualmodelcontrol/interactive/controls.py +0 -82
  273. virtualmodelcontrol-0.3.0/src/virtualmodelcontrol/interactive/interactive.py +0 -111
  274. virtualmodelcontrol-0.3.0/src/virtualmodelcontrol/interactive/joystick.py +0 -101
  275. virtualmodelcontrol-0.3.0/src/virtualmodelcontrol/interactive/keyboard.py +0 -64
  276. virtualmodelcontrol-0.3.0/src/virtualmodelcontrol/interactive/recorder.py +0 -87
  277. virtualmodelcontrol-0.3.0/src/virtualmodelcontrol/interactive/session.py +0 -96
  278. virtualmodelcontrol-0.3.0/src/virtualmodelcontrol/interactive/window.py +0 -205
  279. virtualmodelcontrol-0.3.0/src/virtualmodelcontrol/mechanisms/components/dissipation.py +0 -72
  280. virtualmodelcontrol-0.3.0/src/virtualmodelcontrol/mechanisms/coordinates/frames.py +0 -62
  281. virtualmodelcontrol-0.3.0/src/virtualmodelcontrol/mechanisms/coordinates/geometry.py +0 -74
  282. virtualmodelcontrol-0.3.0/src/virtualmodelcontrol/models/rigid/poe.py +0 -158
  283. virtualmodelcontrol-0.3.0/src/virtualmodelcontrol/robots/__init__.py +0 -5
  284. virtualmodelcontrol-0.3.0/src/virtualmodelcontrol/robots/turtle.py +0 -99
  285. virtualmodelcontrol-0.3.0/src/virtualmodelcontrol/ros/__init__.py +0 -41
  286. virtualmodelcontrol-0.3.0/src/virtualmodelcontrol/ros/joint_io.py +0 -52
  287. virtualmodelcontrol-0.3.0/src/virtualmodelcontrol/ros/node.py +0 -53
  288. virtualmodelcontrol-0.3.0/src/virtualmodelcontrol/ros/params.py +0 -43
  289. virtualmodelcontrol-0.3.0/src/virtualmodelcontrol/ros/plant.py +0 -121
  290. virtualmodelcontrol-0.3.0/src/virtualmodelcontrol/ros/twin.py +0 -82
  291. virtualmodelcontrol-0.3.0/tests/core/test_space.py +0 -52
  292. virtualmodelcontrol-0.3.0/tests/hardware/test_dynamixel.py +0 -164
  293. virtualmodelcontrol-0.3.0/tests/hardware/test_profile.py +0 -85
  294. virtualmodelcontrol-0.3.0/tests/interactive/conftest.py +0 -58
  295. virtualmodelcontrol-0.3.0/tests/interactive/test_controls.py +0 -83
  296. virtualmodelcontrol-0.3.0/tests/interactive/test_interactive.py +0 -261
  297. virtualmodelcontrol-0.3.0/tests/interactive/test_joystick.py +0 -74
  298. virtualmodelcontrol-0.3.0/tests/interactive/test_keyboard.py +0 -62
  299. virtualmodelcontrol-0.3.0/tests/interactive/test_session.py +0 -127
  300. virtualmodelcontrol-0.3.0/tests/interactive/test_window.py +0 -191
  301. virtualmodelcontrol-0.3.0/tests/models/test_poe.py +0 -75
  302. virtualmodelcontrol-0.3.0/tests/robots/test_turtle.py +0 -50
  303. virtualmodelcontrol-0.3.0/tests/ros/conftest.py +0 -9
  304. virtualmodelcontrol-0.3.0/tests/ros/test_joint_io.py +0 -29
  305. virtualmodelcontrol-0.3.0/tests/ros/test_ros.py +0 -136
  306. virtualmodelcontrol-0.3.0/tests/sim/test_paced.py +0 -108
  307. virtualmodelcontrol-0.3.0/tests/test_benchmark.py +0 -27
  308. virtualmodelcontrol-0.3.0/tests/test_deprecations.py +0 -106
  309. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/.gitignore +0 -0
  310. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/CITATION.cff +0 -0
  311. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/LICENSE +0 -0
  312. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/benchmarks/tick.py +0 -0
  313. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/_ext/video.py +0 -0
  314. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/_static/create-lab.png +0 -0
  315. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/_static/favicon.svg +0 -0
  316. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/_static/logo-light.svg +0 -0
  317. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/_static/logo.svg +0 -0
  318. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/_static/readme-hero.svg +0 -0
  319. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/_templates/autosummary/module.rst +0 -0
  320. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/_templates/partials/toc.html +0 -0
  321. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/conf.py +0 -0
  322. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/development/changelog.md +0 -0
  323. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/development/roadmap.md +0 -0
  324. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/schematics/__init__.py +0 -0
  325. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/schematics/_draw.py +0 -0
  326. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/schematics/bimanual.py +0 -0
  327. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/schematics/control_loop.py +0 -0
  328. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/schematics/energy_flow.py +0 -0
  329. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/schematics/finger.py +0 -0
  330. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/schematics/four_steps.py +0 -0
  331. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/schematics/hand.py +0 -0
  332. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/schematics/helyx.py +0 -0
  333. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/schematics/mechanism.py +0 -0
  334. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/schematics/params.py +0 -0
  335. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/schematics/pcc.py +0 -0
  336. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/schematics/turtle.py +0 -0
  337. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/schematics/ur5.py +0 -0
  338. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/scripts/readme_figure.py +0 -0
  339. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/tutorials/reach.yaml +0 -0
  340. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/docs/tutorials/run-settings.yaml +0 -0
  341. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/config/__init__.py +0 -0
  342. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/config/controllers.py +0 -0
  343. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/config/experiment.py +0 -0
  344. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/config/spec.py +0 -0
  345. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/control/blending.py +0 -0
  346. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/control/output.py +0 -0
  347. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/control/schedule.py +0 -0
  348. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/core/params.py +0 -0
  349. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/core/registry.py +0 -0
  350. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/core/signals.py +0 -0
  351. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/core/units.py +0 -0
  352. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/identification/efficiency.py +0 -0
  353. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/identification/plateaus.py +0 -0
  354. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/mechanisms/components/constrained.py +0 -0
  355. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/mechanisms/components/storage.py +0 -0
  356. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/mechanisms/coordinates/joints.py +0 -0
  357. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/models/continuum/__init__.py +0 -0
  358. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/models/continuum/pcc.py +0 -0
  359. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/models/efficiency.py +0 -0
  360. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/models/joint_space.py +0 -0
  361. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/models/kinematic.py +0 -0
  362. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/models/rigid/__init__.py +0 -0
  363. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/models/rigid/couplings.py +0 -0
  364. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/py.typed +0 -0
  365. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/robots/ur5.py +0 -0
  366. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/sim/plant.py +0 -0
  367. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/sim/replay.py +0 -0
  368. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/sim/runlog.py +0 -0
  369. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/viz/__init__.py +0 -0
  370. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/viz/animation.py +0 -0
  371. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/src/virtualmodelcontrol/viz/style.py +0 -0
  372. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/conftest.py +0 -0
  373. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/control/test_blending.py +0 -0
  374. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/control/test_honesty.py +0 -0
  375. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/control/test_output.py +0 -0
  376. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/control/test_schedule.py +0 -0
  377. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/control/test_vmc_golden.py +0 -0
  378. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/core/test_params.py +0 -0
  379. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/core/test_registry.py +0 -0
  380. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/core/test_signals.py +0 -0
  381. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/data/components.npz +0 -0
  382. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/data/configs/finger.yaml +0 -0
  383. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/data/configs/hand.yaml +0 -0
  384. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/data/configs/soft-arm.yaml +0 -0
  385. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/data/configs/turtle.yaml +0 -0
  386. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/data/configs/two-arms.yaml +0 -0
  387. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/data/configs/ur5-hand.yaml +0 -0
  388. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/data/derivatives.npz +0 -0
  389. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/data/dynamics.npz +0 -0
  390. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/data/efficiency.npz +0 -0
  391. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/data/finger.npz +0 -0
  392. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/data/hand.npz +0 -0
  393. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/data/pcc.npz +0 -0
  394. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/data/turtle.npz +0 -0
  395. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/data/vmc.npz +0 -0
  396. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/hardware/test_templates.py +0 -0
  397. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/identification/test_efficiency_fit.py +0 -0
  398. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/identification/test_stiffness_fit.py +0 -0
  399. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/mechanisms/test_components.py +0 -0
  400. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/mechanisms/test_components_golden.py +0 -0
  401. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/mechanisms/test_contact.py +0 -0
  402. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/mechanisms/test_coordinates.py +0 -0
  403. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/mechanisms/test_mechanism.py +0 -0
  404. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/models/test_assembly.py +0 -0
  405. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/models/test_efficiency.py +0 -0
  406. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/models/test_kinematics.py +0 -0
  407. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/models/test_pcc.py +0 -0
  408. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/robots/test_adapt.py +0 -0
  409. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/robots/test_bimanual.py +0 -0
  410. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/robots/test_hand.py +0 -0
  411. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/robots/test_helyx.py +0 -0
  412. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/robots/test_parametric.py +0 -0
  413. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/robots/test_ur5.py +0 -0
  414. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/sim/test_replay.py +0 -0
  415. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/sim/test_run.py +0 -0
  416. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/sim/test_runlog.py +0 -0
  417. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/test_compiler.py +0 -0
  418. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/viz/test_animate.py +0 -0
  419. {virtualmodelcontrol-0.3.0 → virtualmodelcontrol-1.0.0}/tests/viz/test_viz.py +0 -0
@@ -0,0 +1,777 @@
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 change between minor versions).
6
+
7
+ ## [1.0.0] - 2026-10-07
8
+
9
+ The first stable release. It also carries the work meant for 0.6.0 (planning with virtual states,
10
+ unmeasured coordinates, custom dynamics, the planner on a larger plant, MPC and underactuation) and
11
+ for 0.7.0 (models as numpy and PyTorch code), which were never released on their own, and it adds
12
+ the checking: the whole suite on the oldest and the newest supported Python and numpy, on Linux,
13
+ macOS and Windows, every page of the documentation run from a clean checkout, and a review of the
14
+ public API (what `__all__` lists in each module is the API) and of every page of the docs at desktop
15
+ and phone width. The rules for what changes between versions are on the page "Support policy".
16
+
17
+ ### Added
18
+
19
+ - `optimization.Shooting(q0, horizon, nodes, v0=, steps=, initial=, transition=, scales=,
20
+ integrator=, substeps=)`: multiple shooting, a second transcription next to `Collocation`
21
+ with the same terms. The unknowns are q and v at the nodes; over each interval the closed
22
+ loop is simulated as `vmc.sim.rollout` does (the command held over each of `substeps` control
23
+ steps, the robot advanced by the linearly implicit step or by `"rk4"`), and constraints join
24
+ the end of an interval to the next node. A plan is the closed loop of a simulation at that
25
+ control step (a test reproduces `rollout` to rounding error), and agrees with `Collocation` as
26
+ the control step shrinks (first order). The start is the pair of Params `shooting.q0` and
27
+ `shooting.v0`, which a problem can declare as parameters. `steps` names live Params that take
28
+ one value in every interval, between their bounds: trajectories of stiffness and references,
29
+ returned in `Result.steps` (`apply(target, interval=k)` sets the values of interval k). The
30
+ model does not carry the force that balances the robot at q0 under `initial`, as `Collocation`
31
+ does.
32
+ - `optimization.TankBudget(level, refill=True)`: the planned changes of the Params that step stay
33
+ within the energy of a `vmc.control.Tank`. The plan keeps the tank's level from the exact jump
34
+ of the controller's energy at each change, and from the energy the controller's dampers take
35
+ back, and keeps it at least zero; a test runs the plan through a real `Tank` and finds its level
36
+ to rounding error. The tank's capacity is not planned.
37
+ - `optimization.MPC(problem, shift=1, rti=False)`: the plan as a receding-horizon controller. The
38
+ program is built once, with the measured state, the controller's Params now, the tank's level
39
+ and the references as parameters of each solve. `step(controller, q, v, references, latency=)`
40
+ solves from the measured state, starting from the previous plan moved up by `shift` intervals,
41
+ and applies the plan's Params through `controller.set` (so a `Tank` bounds them), returning
42
+ the energy jump as the adaptation laws do. A plan that took `latency` seconds (the measured
43
+ solve time by default) is applied as of that time. With `rti`, one SQP iteration per step.
44
+ `start` and `poll` plan in a thread and apply the plan once it is ready, as of the time since
45
+ the start (CasADi lets other threads run during a solve; the solver is created with the `MPC`,
46
+ as it is not safe to create one in a second thread). `Problem.warm_up()` builds the program and
47
+ the solver now, and `Problem.solve` attaches the iteration callback only when it is given a
48
+ `progress`.
49
+ - Planning with virtual states: `Collocation`, `Shooting` and `Equilibrium` accept controllers
50
+ with virtual states (a flywheel, a mass the robot is tied to). The states are unknowns of the
51
+ plan at every node, with the controller's own dynamics: collocated with the same scheme as the
52
+ robot (`Collocation`), or advanced as `VMCController.step` and `vmc.sim.rollout` advance them
53
+ (`Shooting`, which then reproduces `rollout` to rounding error, the states included). They
54
+ start from the state the controller is compiled with, or from `z0` (positions, then
55
+ velocities); a periodic motion repeats them; `Equilibrium` rests them. `Result.z` holds them,
56
+ `warm_start` takes them, `TankBudget` reads the energy at them, and the controller in place
57
+ (`initial`) may have states too, as a system (it starts with the plan) or as a running
58
+ controller (its state is where it is now). `MPC` plans a running controller from
59
+ `controller.z` when the `Shooting` has a `z0`, which is then a parameter of the program
60
+ (`shooting.z0`). The docstring of `Shooting` now says what the code does: the controller is
61
+ called at every one of an interval's `substeps` control steps.
62
+ - Sensors of combinations of the coordinates: `Measurement(..., matrix=)` takes an (m, n) matrix
63
+ and reads `matrix @ q` and `matrix @ v` (instead of `observed`, a selection of coordinates).
64
+ `KalmanFilter.encoder` of a robot with fewer motors than coordinates (`Underactuated`) no longer
65
+ reads the motors as if they gave `q`, with the passive coordinates frozen: it returns the
66
+ measurement of the motors themselves, with the Jacobian of the motor angles at the estimate as
67
+ the matrix, so the passive coordinates are left to the model (`Rq` and `Rv` are then the
68
+ covariances of the motors' readings). A robot with a motor on every coordinate keeps its encoder
69
+ as it was. The underactuated tutorial runs the naive controller on such an estimate, and shows
70
+ what it takes: a process noise that admits the model's error (with the default one the gate
71
+ throws out every reading after the first swing and the filter breaks) and a controller that
72
+ does not outrun the filter.
73
+ - Equations of motion from a function: `models.Equations(residual, energy=None)`, given to
74
+ `FunctionModel(..., equations=)`, for a black-box, learned or external model whose dynamics
75
+ are not built from parts. The residual `r(q, v, a, tau, f, p) = M(q) a + h(q, v) - tau - f` is
76
+ written with CasADi operations and affine in `a`; `tau` is the generalized force of the motors
77
+ and `f` that of the robot's own components, which still add springs, dampers and contact (the
78
+ masses and the weight are in the equations, so there is no `Inertance` or `Gravity`).
79
+ Everything that runs on a robot runs on it: the simulator, `rollout`, the planners, the
80
+ Kalman filter, the momentum observer. With an `energy(q, v, p)` returning (T, V) it has the
81
+ robot's energy and power; without one `Dynamics.energy` and `power` are `None` and what needs
82
+ them (`ModelPlant.energy`, the `Passivation` stage) raises a `ValueError` that says so
83
+ (`dynamics.needs_energy`). `testing.check_model` checks such a robot's mass matrix
84
+ (symmetric, positive) and, with an energy, that a simulation never gains any. A test builds
85
+ the same two-link arm from parts and from textbook equations and finds the same dynamics,
86
+ energy, simulation, Kalman prediction and plan.
87
+ - Plan a controller on some coordinates of a larger robot: `Problem(system, plant=robot)` takes
88
+ the dynamics from `plant` (every term and `q0` in its coordinates) while the controller stays
89
+ written for its own robot, and reads the plant's motors as `VMCController` does in a
90
+ simulation, through its transmission. The law, the energy of a `TankBudget` and the dampers'
91
+ power of a shooting all go through it. A shooting of the turtle's flywheel controller on
92
+ `turtle.crawler()` equals the simulation step for step, the flywheel's states included (a
93
+ test), and the tutorial "Crawl with a flywheel" plans a stride with it. `Shooting(running=)`
94
+ says whether a controller started from `z0` is already running (`z0` alone still means so)
95
+ or starts with the plan, as one reset there does. `Space.on_manifold(q)` gives the equations
96
+ that a configuration obeys (the unit norm of a quaternion; none for a flat space), and
97
+ `Shooting` and `Equilibrium` constrain their nodes with them: before, a plan of a floating body
98
+ could drift off unit quaternions and plan another body's motion.
99
+ - The models as numpy and PyTorch code: `core.backends.source`, `translate` and `export` write
100
+ a CasADi function as straight-line Python, one statement per operation of its expression
101
+ graph, for numpy (the text imports numpy only, so it runs without CasADi) or PyTorch
102
+ (differentiable, on any dtype and device). Arguments and results have CasADi's shapes, with any
103
+ leading batch dimensions. `Kinematics.functions(at, backend="numpy")`, `Dynamics.export` and
104
+ `Compiled.export` give a model's functions that way, each with its `source`. numpy gives
105
+ CasADi's numbers to rounding error and PyTorch to 1e-12 on the kinematics with their Jacobians
106
+ and Hessians, including the derivatives by autograd; a model with a poorly conditioned mass
107
+ matrix (the soft arm's dynamics) differs by a few parts in 1e11, as PyTorch's `sin` and `cos`
108
+ differ in the last place. The extra `virtualmodelcontrol[torch]` and the tutorial "Use a model
109
+ outside CasADi".
110
+ - `ForceTracking`, `StiffnessTracking`, `PositionRegulation`, `HoldingGoals`, `ContactForce` and
111
+ `TaskStiffness` refuse a `StateController` (also inside a `Tank`) with a `ValueError` that says
112
+ so, instead of failing on the shapes of its inputs: they read a controller in the layout of the
113
+ motors. A robot with fewer motors than coordinates uses the plain frozen controller (it reads the
114
+ motors), or `control.underactuated.DirectionalForce` for a force.
115
+ - Solver presets: `Problem(system, solver=...)` takes `"ipopt"` (the default), `"ipopt-exact"`
116
+ (exact Hessians: 40 iterations instead of 165 on the tutorial's shooting), `"sqp"` (CasADi's
117
+ `sqpmethod` with `qrqp`, the Hessian's negative eigenvalues clipped; other QP solvers by the
118
+ `qpsol` option), `"rti"` and `"fatrop"` (as a general problem, without stages).
119
+ `optimization.PRESETS` lists them and `optimization.solver.available(name)` tells whether the
120
+ CasADi build has the plugin. `Result.status` words FATROP's endings as IPOPT's.
121
+ - Docs: tutorial "Model predictive control".
122
+ - Docs: the page "Support policy": which versions of Python and numpy are supported, what
123
+ semantic versioning means here (a deprecation lasts at least one minor version), what the public
124
+ API is, the schema of the run logs, where to report an issue, and the safety checks before the
125
+ first run on a robot (also in "Real time and the robot").
126
+
127
+ ### Changed
128
+
129
+ - `__all__` in `math`, `testing`, `compiler`, `dynamics` and `system`: what they list is the public
130
+ API, as in every other module; `from virtualmodelcontrol.x import *` gives only that.
131
+ - The configuration and hardware profile files are read and written as UTF-8 on every platform,
132
+ so that a file written on Linux reads on Windows.
133
+ - The docs use American spelling, the figure legends are 18 pt (readable on a phone), and wide
134
+ tables wrap their code on a narrow screen.
135
+
136
+ ## [0.5.0] - 2026-10-06
137
+
138
+ This release also carries the work meant for 0.4.0 (optimization of virtual mechanisms, the energy
139
+ tank, tuning and the calibrations), which was never released on its own.
140
+
141
+ ### Added
142
+
143
+ - Underactuated Virtual Model Control, for robots with fewer motors than coordinates.
144
+ `models.Underactuated(B)` is the actuation with an input matrix B (`Underactuated.joints` for
145
+ motors on some joints): motor angles Bᵀq, torques B u, the allocation B⁺ τ, the projector E and
146
+ the `defect` E τ it cannot realize. Its motors give the frozen configuration (the motors' part
147
+ of q, the rest at `q_rest`), so a `VMCController` on it is the frozen controller.
148
+ `control.StateController` reads (q, v) from the measurement (`meas["q"]`, `meas["v"]`, or its
149
+ `read`) instead of the motors, which is the naive controller and also how a controller acts
150
+ through an estimate of unmeasured coordinates. `control.underactuated` has `controller(compiled,
151
+ base="naive"|"frozen", correction=None|"passive"|"tank", gravity=)`, the `Passivation` output
152
+ stage (the passive and the tank correction: the motors never inject more power than the
153
+ robot's own dampers take, or more than the tank holds), `Frozen` (the frozen configuration,
154
+ also balancing the robot's gravity), `DirectionalForce` (a force along a direction by one
155
+ scalar of the stiffness, closed form, positive definite), `projector`, `defect` and `feasible`
156
+ (the wrenches the motors realize in full). An output stage with a `reset` is reset with its
157
+ controller. They agree with the lab's own code on its three robots (every flag, the frozen
158
+ point, the force law, and the closed loops).
159
+ - `robots.planar`: `arm("three-link")` with one passive joint, `arm("five-link")` with three, and
160
+ `continuum()`, a three-section continuum arm driven by two tendons; `add_dynamics` and
161
+ `add_continuum_dynamics` give them gravity and their springs and dampers.
162
+ - Docs: tutorial "Robots with fewer motors than joints".
163
+ - `vmc.optimization`: plan the motion of a controller on a robot and optimize its Params.
164
+ `Problem(system)` is built once and solved many times. `free` chooses the Params to optimize
165
+ and `parameter` the ones set at every solve (references, say), by name or glob; bounds and
166
+ scale come from the Param. `Collocation(q0, horizon, nodes, initial=, transition=, scales=)` is
167
+ trapezoidal collocation of the closed loop on the robot's full dynamics, without inverting the
168
+ mass matrix. With an `initial` controller, its torques fade out as the new ones fade in with the
169
+ blend of `SwapController`, so the plan is what the swap executes, and the model starts at rest
170
+ where the robot is whatever it gets wrong there. `Effort`, `Cost` (a library coordinate brought
171
+ to zero from a time on) and `Bound` (a coordinate kept within limits) are the terms, `Term`
172
+ the base to write your own. `Result` holds the motion, the Params found, the cost by term and
173
+ the solver's status, and `apply` puts the values into a running controller (returning the exact
174
+ energy jump) or into a system. `search_references` searches a reference on a grid, one at a
175
+ time, keeping the best converged plan. `Problem.build` returns the `NLP` for any other solver.
176
+ `Collocation(scheme="hermite-simpson")` integrates to fourth order instead of second: it adds the
177
+ acceleration at the middle of every interval to the unknowns, and needs far fewer nodes for the
178
+ same agreement with a simulation.
179
+ The program agrees with the lab's own optimization code to rounding error, and its solved plans
180
+ to solver tolerance. Controllers with virtual states and non-flat configuration spaces are not
181
+ supported yet.
182
+ - `optimization.Equilibrium(q0)`: the closed loop at rest, as a static problem. It takes the place of
183
+ `Collocation`: the unknown is a configuration where the controller's torques balance the
184
+ robot's own forces, found with the free Params, and the terms see it as their one node.
185
+ `Cost` and `Effort` count it once, and a `Bound` holds there. The tutorial finds where the
186
+ soft arm rests, in milliseconds.
187
+ - `Collocation(periodic=True)` plans an orbit that repeats: the last node equals the first (q and
188
+ v), the first node is free (`q0` and `v0` are only the solver's starting guess), and `Bound`
189
+ reaches it. `Collocation(free_time=(lower, upper))` makes the horizon an unknown within these
190
+ bounds, starting at `horizon`: the node spacing, the controller's time and the coordinates of
191
+ `vmc.Time()` are expressions of it, and `Effort` and `Cost` integrate with the horizon found.
192
+ The windows of the terms still read the nodes' times at the starting horizon. `Result.horizon`
193
+ is the horizon found (the given one for a fixed horizon, 0 for an `Equilibrium`) and
194
+ `Result.t` uses it; `warm_start` takes a `horizon`. `Trajectory.dt` is a number or an
195
+ expression of the free horizon, and `Trajectory.times` and `Trajectory.horizon` are the same,
196
+ for terms of your own. `optimization.Period(param)` holds the horizon equal to a Param, so that
197
+ a controller whose reference is a function of `vmc.Time()` and that Param (a period, free or a
198
+ parameter) repeats with the orbit. Neither option goes with an `initial` controller. Both
199
+ schemes work.
200
+ - Docs: tutorial "Optimizing a virtual mechanism".
201
+ - Docs: the optimization tutorial finds the period of a mass on a spring, and the least-effort
202
+ period of a pulled one with a controller that keeps time, against their closed forms.
203
+ - Docs: the optimization tutorial plans the soft arm around a sphere (`Bound` on `SphereDistance`),
204
+ checks the clearance in simulation, and shows that two sets of scales give the same plan.
205
+ - `vmc.sim.rollout(system, q0, T, dt)`: the closed loop simulated in one compiled call
206
+ (`mapaccum`), differentiable with respect to the controller's live Params, with the robot
207
+ integrated by `"implicit"` (the default, as `ModelPlant`), `"rk4"` or `"cvodes"`;
208
+ it reproduces `vmc.sim.run` to rounding error. `vmc.sim.ode(system)` is the closed loop in
209
+ continuous time, f(t, x) for SciPy's `solve_ivp`.
210
+ - `vmc.control.Tank(controller, level, capacity)`: an energy budget for changing a running
211
+ controller. `tank.set(values)` applies a change of live Params as far as the tank can pay for
212
+ the exact jump it gives the controller's energy (the largest fraction of the step that fits);
213
+ a step that releases energy is applied whole, even with an empty tank, and refills it.
214
+ `result.apply(tank)` passes an optimizer's result through it. `VMCController.jump(values)`
215
+ gives the jump `set` would give, without applying it.
216
+ Run in place of the controller (`vmc.sim.run(plant, tank, ...)`), the tank also refills itself
217
+ with what the controller's own dampers take at every step.
218
+ - `identification.fit_stiffness_damping` takes a run log (`RunLog`) as it is, or a mapping whose motor
219
+ torques are `motor_torque` or `u`.
220
+ - `identification.Steps(baseline, pulls, held_out, hold, rest)`: the step experiment as a controller
221
+ that runs on any plant. Every motor gets the baseline torque, and each pull adds a torque to some
222
+ motors for `hold` seconds, with `rest` seconds at the baseline between them. Its log marks the
223
+ training steps (`train`), which the fit uses as they are; the held-out ones are for validation.
224
+ - `fit_stiffness_damping(..., friction=True)` adds a static friction torque per motor, F sign(θ̇),
225
+ to the fit and returns it after K and D (`K, D, F`). Friction that is not in the fit passes for
226
+ damping. A motor that stands still has none in the model, so a run that stops at the end of every
227
+ step hardly shows it: use one that keeps the motors moving.
228
+ - `identification.validate(model, run)`: simulates a robot with its stiffness and damping under the
229
+ logged torques of the held-out steps of a run, from the state the run is in at the first of them,
230
+ and returns the root-mean-square error and the variance accounted for of each coordinate.
231
+ `vmc.sim.rollout` takes `u`, motor torques with a row per step, to simulate open loop.
232
+ - Docs: the hanging arm's page identifies its stiffness and damping with `Steps`, checks them on
233
+ held-out steps and shows the friction option.
234
+ - `identification.fit_transmission(motor, joint)`: the joint angle per motor angle of a cable drive,
235
+ from a sweep of the joint, by least squares through the origin. It reproduces the finger's and
236
+ the hand's transmission constants from their recorded sweeps. The finger's page shows it.
237
+ - `vmc.adaptation`: laws that change a running controller's live Params, a step at a time. Every
238
+ `step` returns the jump of the controller's energy [J] that was applied, and a `Tank` passed in
239
+ place of the controller applies only the part it pays for. A new value stays within the Param's
240
+ bounds (a stiffness stops at zero), and a square matrix stays symmetric and positive
241
+ semidefinite (`control.project_psd`).
242
+ - `ForceTracking(controller, site, params, normal=None, max_force_step=0.01, max_step=None,
243
+ rate=None)` descends the error of a contact force: `params` are glob patterns of live Params
244
+ (a spring's goal or its stiffness), `normal` keeps the force along it. The gradient is exact,
245
+ by automatic differentiation of the delivered torque, and agrees with the lab's hand-derived
246
+ one to 1e-8 for the goal and the stiffness, at the tip and mid-arm, along a normal and in 3D,
247
+ also where the point's Jacobian has rank below 3 (the two-motor finger). A step is the largest
248
+ that changes the force by `max_force_step` [N] (the lab finds it by a finite-difference probe
249
+ and agrees to 1e-5), or `rate`, a fixed learning rate as in the finger's experiments, where
250
+ it agrees with the lab's joint-space stiffness and reference descent at the lab's rates.
251
+ `direction` gives the descent direction without applying it.
252
+ - `ForceRatio(controller, params, max_change=0.05)` and `Stiffening(controller, params, low,
253
+ high, alpha)`: the laws of the lab's hand and finger that need no model. The first scales
254
+ stiffnesses by 1 plus or minus `max_change` times the relative error of the force (the hand's
255
+ multiplicative law); the second sets k(F) = low + (high - low)(1 - exp(-alpha F)) from the
256
+ measured force (the finger's law). Both equal the lab's. K(d) is the `SigmoidSpring` and the
257
+ `PolynomialSpring`.
258
+ - `StiffnessTracking(controller, site, params, normal=None, robot=None, fraction=1.0)`: direct
259
+ stiffness tracking. The stiffness at the site is affine in the springs' stiffness, so `target`
260
+ solves a linear system for the Params that give a wanted matrix, the solution of smallest
261
+ norm as in the lab's inversion, and `step` goes `fraction` of the way. Where no spring pulls
262
+ it agrees with the lab's inversion to 1e-6; where they do, it counts the geometric terms
263
+ that the lab's leaves out.
264
+ - `PositionRegulation(controller, sites, gain=0.05)`: integral pose regulation. A step adds
265
+ `gain` times the error of a point of the robot to the goal of the spring that pulls it.
266
+ - `HoldingGoals(controller, sites, robot=None)`: the open-loop counterpart, the lab's position
267
+ feedforward. It puts the goals at wanted positions (default: where the points are) plus the
268
+ smallest offset that lets the springs carry the robot's own stiffness and weight at the pose
269
+ now, one Newton step on the static balance. It agrees with the lab's to 1e-9; with as many
270
+ springs as motors the arm then stays where it is.
271
+ - `vmc.estimation`: quantities of a running robot that no sensor measures. Both take the
272
+ controller, a site (a name, an arc parameter or `(part, s)`) and a `normal`, and a `robot`:
273
+ the model that holds the arm at rest, without the surroundings the plant may have. They use the
274
+ controller's law before the output stages, the system's transmission and efficiency, and leave
275
+ velocities out.
276
+ - `ContactForce(controller, site, normal=None, robot=None)`: the force a robot exerts at a site
277
+ at rest. What the delivered motor torques and the robot's own stiffness and weight do not
278
+ balance goes through the site's Jacobian by the pseudo-inverse. It agrees with the lab's
279
+ contact force from the virtual springs and the structural stiffness (the efficiency on the
280
+ springs' term only) to 1e-9, with its torque-based variant, and with the finger's. A fingertip
281
+ at rest on a simulated table reads the table's force to 2 %.
282
+ - `TaskStiffness(controller, site, normal=None, robot=None)`: the stiffness [N/m] at the site, a
283
+ 3 by 3 matrix. It is the stiffness of the static balance, by automatic differentiation, with
284
+ the geometric terms (the Hessians of the site and of the springs) counted, and agrees with
285
+ the lab's exact apparent stiffness to 1e-8. The lab's first-order version leaves the
286
+ geometric terms out and is not provided.
287
+ - `KalmanFilter(system, dt, Q=None, P0=None, gate=40.0, robot=None)` and `Measurement(q, v, Rq,
288
+ Rv, observed, name)`: a Kalman filter of the robot's state (q, v). Its process model is the
289
+ robot's own dynamics, linearised at the estimate at rest and held over the step (the lab's
290
+ frozen mass matrix, in general form), with the command going through the system's
291
+ transmission. It fuses encoders (`kf.encoder`) and any sensor that sees part of the state,
292
+ gates each measurement on its innovation, and reports the sensors it rejected (`rejected`,
293
+ `rejected_total`) and the expected ones that offered nothing (`missing`). Over a run with
294
+ dropouts and an outlier of each sensor it agrees with the lab's filter in its state to 1e-9,
295
+ its covariance, its innovation distances and the sensors it rejects.
296
+ - `Inversion(robot, at, damping, tol, max_iter)`: the configuration that puts the robot's points
297
+ `at` where a position sensor saw them (motion-capture markers), by damped Gauss-Newton from the
298
+ last result, for any robot. It agrees with the lab's closed form for the three-section arm
299
+ to 1e-8, and finds the soft arm from its neutral configuration up to 0.6 rad of bend in each
300
+ section (give `q0` beyond). `VelocityFilter(dt, alpha=0.3)`: the low-passed difference of
301
+ consecutive samples, the lab's velocity of the markers, with `dt` the sensor's own period.
302
+ - `ImuFilter(arm, mounts, kp, acc_tol, gravity)`: the curvature (Dx, Dy) of each section of a
303
+ soft arm and its rate from an IMU on its base and on each section end, by a complementary
304
+ filter of each section's relative rotation (the gyros turn it, the gravity both accelerometers
305
+ see corrects it, and it is kept on the rotations the arm can make). `calibrate` takes the
306
+ gyro bias and the starting pose from the arm held still. Its `observed` coordinates go to
307
+ `Measurement`. It agrees with the lab's filter to 1e-10 over a run with accelerometers that
308
+ cannot be trusted.
309
+ - `VMCController.inputs()`: the vector the law reads, with the live Params as they are now.
310
+ A Param that acts only through a virtual state does not change the force at once, and the
311
+ laws leave it.
312
+ - Docs: the tutorials "Track a contact force" (the laws, the estimate without a sensor, the tank)
313
+ and "Shape the stiffness of the tip"; the finger's page presses between two force levels; the
314
+ two arms' page holds an object with a chosen force, with estimates alone, with a sensor on the
315
+ object, and through a tank (the object feels the wanted force to 0.2 % from the estimates).
316
+ - `vmc.Gated(component, gate)`: an element whose force and energy are multiplied by a live Param
317
+ `gate` between 0 and 1. `optimization.Sparsity(weight, *patterns)` adds the sum of the free Params
318
+ that match (each at least 0) to the cost. With the gates free, the optimizer keeps the elements
319
+ that earn their place: structure optimization, tested on a mass with three springs and on the
320
+ soft arm with five repulsive fields, and shown in the optimization tutorial ("Which fields to
321
+ keep").
322
+ - `vmc.models.FunctionModel(frame, space, params, sites, q_unit)`: a kinematic model from a function
323
+ `frame(q, at, p)` that returns the rotation and the position of a site, as CasADi values or as
324
+ lists and arrays. It works in `Kinematics`, mechanisms and `check_model`; a function cannot be
325
+ written to a file, so it has no `to_dict`.
326
+ - `vmc.testing.check_model(model)`: the model contract in one call, for a kinematic model or a
327
+ robot's mechanism: everything finite (also with each Param a tenth of its default and ten times
328
+ it), orthonormal rotations, Jacobians, angular Jacobians and Hessians against finite
329
+ differences, no jump along the arc parameter `s`, `to_dict` and `from_dict` giving the model
330
+ back, and, for a robot with masses and no dampers, an energy drift that falls with the step. It
331
+ raises an `AssertionError` that lists the failed checks and returns the worst error of each. The
332
+ tutorials "Build your own robot" and "Extend the library" use it.
333
+ - `vmc.BoxDistance`, `CapsuleDistance` and `CylinderDistance`: signed distances from a point to a
334
+ box with its sides along the axes, a capsule and a cylinder, for contacts and obstacles, with the
335
+ same Params as `SphereDistance` (and the kinds `box_distance`, `capsule_distance` and
336
+ `cylinder_distance` in configuration files). The contact tutorial uses the cylinder.
337
+ - `vmc.ContactFriction(distance, stiffness, friction)`: Coulomb friction at a contact, smoothed. It
338
+ pushes a point against its sliding speed along the surface, with the size of the friction
339
+ coefficient times the contact spring's force (give it the spring's stiffness `Param`), and falls
340
+ linearly below the small speed `speed`, so a point at rest creeps. It works on every signed
341
+ distance, which now share the base class `SurfaceDistance` with a `unit_normal`; the type is
342
+ `contact_friction` in configuration files. The contact tutorial drags a fingertip along a table.
343
+ - `vmc.sim.run(..., record="robot")` logs what a simulated robot feels, component by component:
344
+ `robot/<name>/y`, `ydot`, `force` and `torque` of each spring, damper and contact, from
345
+ `ModelPlant.elements()` (and `Dynamics.elements`). A real robot reports no forces, so recording
346
+ it raises an error.
347
+ - `vmc.estimation.Encoders`, `Markers`, `Imus` and `LoadCell`: simulated sensors, the true state in
348
+ and a noisy reading out, as the estimators take it. The encoders read the motors through the
349
+ transmission, with noise and a constant slack per motor; the markers, the positions of points of
350
+ the arm; the IMUs, the angular velocity and gravity in each frame's own axes, with a constant gyro
351
+ bias; the load cell, a force with a bias and noise. Every one has a seed. The estimation tutorial
352
+ uses them in place of the lines it wrote by hand.
353
+ - `vmc.adaptation.DitherSeeking(controller, params, amplitude, frequency, gain, window)`: extremum
354
+ seeking from the cost alone, the two-tone law of the turtle paper. Each scalar live Param is held
355
+ at an estimate plus a sinusoidal dither at its own frequency (2π n / `window`, a whole n each, so
356
+ that the tones are orthogonal); the cost's correlation with a tone over the window is the slope
357
+ with respect to that Param, and the estimate moves downhill at `gain` times it, at most
358
+ `max_rate`, within the Param's bounds. On a `Tank`, the changes are paid from its energy, which is
359
+ the paper's passivity cap. The crawl tutorial lets the crawler find its best `peak` this way.
360
+ - `turtle.controller(robot, depth=, peak=, steer=, limit=)`: with any of them the cranks' springs
361
+ are `PhaseSpring`s (the paper's phase-modulated, saturating potential); without, they are the
362
+ plain ones of the lab's controller, as before.
363
+ - `vmc.optimization.MovingHorizon(system, dt, window=10, Q=, P=)`: moving-horizon estimation of a
364
+ robot's state (q, v). Every step finds the states of the last `window` steps that best agree
365
+ with the sensors' readings (the same `Measurement`s as the Kalman filter's), with the robot's own
366
+ dynamics up to the process noise `Q`, and with the state before the window, which goes one step of
367
+ an extended Kalman filter whenever the window slides. It is solved by Gauss-Newton with a trust
368
+ region. With a linear robot it is the Kalman filter, to rounding error, however short the window;
369
+ it also gives `cost` and the covariance `P`. The estimation tutorial runs it on the soft arm.
370
+ - `vmc.identification.fit_params(robot, names, runs)`: any Params of a robot, by name or glob,
371
+ fitted to logged runs by least squares on the residual of its own dynamics, within the Params'
372
+ bounds. A residual linear in the Params (masses, stiffnesses, dampings, efficiencies) is solved
373
+ in a step, and any other, such as the saturating damper's, by Gauss-Newton. It returns the values
374
+ in each Param's shape, their standard errors and the residual's root mean square. A run with
375
+ an `a` uses it, and otherwise the acceleration is the smoothed velocity's difference against the
376
+ torques averaged over the step they were held. It agrees with ordinary least squares, values and
377
+ covariance, on a mass, spring and damper. The tutorial "Fit Params to a run" uses it.
378
+ - `KalmanFilter(..., unscented=True, substeps=10)`: the prediction sends sigma points of the
379
+ estimate (the scaled unscented transform, a root of three standard deviations out) through the
380
+ robot's own integrator, in `substeps` steps, instead of linearising at the estimate; the sensors
381
+ stay linear, so the update is the same. On a double pendulum it follows the mean of a Monte
382
+ Carlo to 0.003 where the linearised prediction is off by 2; on a linear robot the two agree.
383
+ - `vmc.estimation.MomentumObserver(system, dt, gain, robot=None)`: the external force on a robot
384
+ from its motion, without accelerations. From the momentum and what the model says would change
385
+ it, it gives the generalized force the model does not explain, as a low-pass filter of bandwidth
386
+ `gain` of the true one, and `force(site, normal)` carries it to a site, as `ContactForce` does.
387
+ It works while the robot moves, where `ContactForce` assumes rest. The force tutorial shows a
388
+ finger landing on a table. Its velocity must be the rate of its configuration (no floating
389
+ bodies yet).
390
+ - `vmc.estimation.object_compliance(position, force, baseline_position, baseline_force)`: the
391
+ compliance in m/N of an object a tip presses, the distance over the force that stiffer settings
392
+ of the controller add to a gentle one, from the median of each setting's samples. It is the
393
+ lab's probe of the hand, and it agrees with the lab's code to rounding error. The force tutorial
394
+ shows it on three objects, with the force from `ContactForce`.
395
+ - Docs: the contact tutorial also shows two points of a robot touching (a self-contact, the two
396
+ arms), friction between two points and a soft object between two tips, from the pieces above.
397
+ - `vmc.control.project_psd(K)`: the symmetric positive semidefinite matrix nearest to a stiffness,
398
+ for an update that an adaptation law proposes, before it goes through `set` or a tank.
399
+ - `vmc.sim.energy_balance(log)`: the controller's energy over a run recorded with
400
+ `record=["energy"]`: its energy, the work given through its port, what its dampers took and its
401
+ sources gave, `injected` (what changes of live Params put in, plus the error of the steps) and
402
+ the `margin` it can still give, which stays above 0 while the controller is passive.
403
+ - `vmc.optimization.Grid`, `Random`, `CMAES`, `ExtremumSeeking` and `Bayes`: gradient-free tuning by ask and tell
404
+ (`ask()` gives candidates, `tell(candidates, costs)` takes their costs), `tune` to run it, and
405
+ `bounds_of` for the bounds and values of named Params. `Bayes` fits a Gaussian process to the
406
+ costs seen so far and asks for the candidate with the highest expected improvement: it suits
407
+ slow episodes, tens of them. The episodes are yours, in simulation or on a robot in your own loop.
408
+ - `compile_dynamics(robot, runtime, actuation)` takes the actuation of a robot that has none, so a
409
+ system's efficiency Params (`robot.efficiency.c1`) are the ones the dynamics read.
410
+ - `vmc.math`: rotations and rigid transforms that run on numpy arrays and CasADi symbols alike
411
+ (`rot_x`, `rot_y`, `rot_z`, `rot`, `skew`, `vee`, `exp_so3`, `log_so3`, `transform`, `invert`,
412
+ `exp_se3`, `log_se3`, `adjoint`), for writing the `frame` of a `FunctionModel`. Every function
413
+ is smooth at the identity, also in its second derivatives (the exact forms lose them there).
414
+ They replace the three rotation helpers of `core.symbolic`.
415
+ - `SerialChain` joints: `"helical"` (a turn that slides by a pitch, given as `("helical", pitch)`),
416
+ `"spherical"` (three coordinates, a rotation vector about the joint's point) and `"free"` (six:
417
+ a translation and a rotation vector, a floating base). A chain on a free base, a brick of point
418
+ masses thrown with a spin and its momenta and energy are tested; a branch of a tree is a part
419
+ mounted on a site of another part by `Assembly`.
420
+ - `SerialChain` joint `("rail", waypoints)`: a slide along the natural cubic spline through the
421
+ waypoints, which are a `design` Param (an array of rows of three) in the frame of the joint
422
+ before it. Its coordinate is the spline parameter, 0 at the first waypoint and 1 at the last. It
423
+ agrees with SciPy's natural spline, and a bead on a circular wire swings as a pendulum.
424
+ - `SerialChain` joint `"floating"`: a floating base whose rotation is a unit quaternion, with
425
+ seven coordinates and six velocities (the translation's and the body's angular velocity in its
426
+ own axes). `vmc.Quaternion` is its space, and `Space.coadjoint` gives the term that
427
+ velocities that do not commute add to the dynamics (the Euler-Poincare term), which
428
+ `compile_dynamics` now includes. A body tumbles about its intermediate axis through several
429
+ full turns by Euler's equations, and a base with an arm keeps its momenta and energy.
430
+ - `vmc.FramePoint(model, site, q=coordinate)`: a point of a virtual model, at the configuration
431
+ that `coordinate` gives: a virtual state (a cart on a rail with a mass of its own), a
432
+ reference, or a function of time. `vmc.Time()` is the time of the run, to build functions of
433
+ time with `Custom`; a damper on the difference to such a goal feels its velocity.
434
+ - More coordinates: `vmc.Sum` (and the plus sign: `tip + [0, 0, 0.05]`), `vmc.FrameRotation` (a
435
+ frame's rotation matrix, row by row), `vmc.OrientationError(model, site, goal=)` (the rotation
436
+ vector from a goal orientation to a frame's, in the goal's axes, with `goal` a live Param; a
437
+ spring on it is a rotational spring whose torque is exact at any error, where the one from the
438
+ angular Jacobian is 8 % off at 0.4 rad), `vmc.InFrame` and `vmc.FromFrame` (a vector along the
439
+ axes of a frame, and back: a stiffness along the tool's axes turns with the tool). They take a
440
+ site or an arc parameter, and a virtual model through `q=`. Configuration files build them as
441
+ `sum`, `rotation`, `orientation_error`, `in_frame` and `from_frame`.
442
+ - More components: `vmc.RotationalInertia(rotation, inertia)`, the inertia of a rigid body about
443
+ its frame (a matrix, or three principal moments) on the frame's rotation matrix, next to a
444
+ `PointMass` for its mass: it follows Euler's equations, moves like the same body made of point
445
+ masses to rounding error, and a rotational spring on it oscillates at the square root of K over
446
+ I. `vmc.DiodeDamper(y, D, sign, smoothing)` damps one direction of motion only.
447
+ `ForceSource(y, f, max_force=, max_power=)` bounds the force smoothly, and the power it
448
+ delivers. `Mechanism.add_mass_along(name, mass, s0, s1, n)` spreads a mass along a model as
449
+ point masses at the nodes of Gauss-Legendre quadrature. An `Inertance` on a difference is an
450
+ inerter, tested.
451
+ - Docs: "A spring on the tool's orientation" and "A spring along the tool's axes" in "Kinematics on
452
+ the UR5"; "A damper that works one way" in "Coordinates and components"; a rigid body and the
453
+ mass along an arm in "Build your own robot".
454
+ - Docs: the tutorial "Estimate the state of a soft arm" (encoders with slack, markers and IMUs
455
+ fused by the filter, an outlier and lost frames); "A rail along a path" and the floating joint
456
+ in "Build your own robot"; "A cart on a rail" in "Coordinates and components".
457
+ - `vmc.PhaseSpring(coord, stiffness, depth, peak, steer, side, limit)`: a spring on a deflection e
458
+ whose stiffness follows a phase, on the coordinate `Stack(e, phase)`: K (1 + side steer) (1 +
459
+ depth cos(phase - peak)). Its force is minus the gradient of its energy, so the phase feels the
460
+ reaction that keeps a flywheel loop passive. With `limit` the potential saturates, and the torque
461
+ never goes above K (1 + |steer|) times the limit. With no depth, no steer and no limit it is a
462
+ `LinearSpring`. The type is `phase_spring` in configuration files.
463
+ - `vmc.models.Passive(nv)`: an actuation with no motors, for a part that nothing drives, such as a
464
+ floating body. Stacked with the other parts' motors (`Assembly.stacked_actuation`), the motor
465
+ vector holds only theirs, so a simulated robot can have more coordinates than motors. The motors
466
+ do not give a passive part's configuration, so a controller is compiled against a robot without
467
+ it, and the simulator runs the one with it.
468
+ - `robots.turtle.crawler(**CRAWLER)`: a simple crawler for the simulator, with placeholder
469
+ constants, not the lab's turtle: a floating body on the ground with gravity, two cranks about its
470
+ lateral axis with a foot each, and a contact spring, damper and friction at each foot and at the
471
+ four corners of the underside. Its motors are the two cranks, in the gait convention of
472
+ `turtle.robot()`, which is what the controller is compiled against. Under the flywheel controller
473
+ it crawls forward, further the faster the flywheel is driven, and does not advance without
474
+ friction; a positive steer of `PhaseSpring` turns it left. Docs: the tutorial "Crawl with a
475
+ flywheel".
476
+
477
+ ### Removed
478
+
479
+ - `vmc.ros` (`RosPlant`, `control`, `serve`, `LiveParams`, `JointIO`, `TOPICS`) and the direct
480
+ Dynamixel path (`hardware.DynamixelPlant`, `home`, `present_ticks`, `scan`, `latency_timer`,
481
+ `Bus`, `SdkBus`, `FakeBus`), deprecated in 0.3.0. The library is communication-agnostic: it
482
+ turns measurements into torques, and how they reach the robot stays outside it. The Dynamixel
483
+ SDK and pyserial are no longer dependencies, and CI no longer runs the ROS tests. Hardware
484
+ profiles (`HardwareProfile`, `Motor`, `KT`, `MODES` and each robot's `hardware` template) stay.
485
+ A project that still uses a removed name keeps `virtualmodelcontrol==0.3.0`.
486
+ - `vmc.interactive` (the window, the keyboard, the joystick, the session, the mailbox, the wrapper
487
+ and the recorder), the `joystick` extra and `SimClock`'s `speed`, `now` and `sleep`: the library
488
+ is for control, not for interfaces. A running controller still changes through
489
+ `controller.set`, schedules and swaps. A project that uses them keeps `virtualmodelcontrol==0.3.0`.
490
+ - From the hardware profiles, the raw Dynamixel units (encoder ticks, velocity units, goal
491
+ currents, motor constants `kt` and the `KT` table, baud rate, serial port, velocity filter, home
492
+ ticks, per-motor torque limits and operating modes). A `Motor` is now `Motor(id, sign, hold)`
493
+ and a `HardwareProfile` `(motors, rate, bus_order)`, with the conversions between a driver's
494
+ degrees, bus order and signs and the library's SI units.
495
+
496
+ ## [0.3.0] - 2026-10-05
497
+
498
+ ### Added
499
+
500
+ - `vmc.config`: experiments in YAML files. `vmc.config.load` reads the robot (a template and its
501
+ arguments), named coordinates, the controller's elements (a registered component `type`, the
502
+ `coordinate` it acts on, its gains), controllers to swap to, and how to run them: a simulation
503
+ (with the robot's dynamics and simulated elements such as contacts), the rate, the duration,
504
+ output stages, schedules and the initial virtual state.
505
+ `Experiment.run` runs it, `save` writes it back with the Params' current values. Robot
506
+ templates, output stages and coordinate kinds are registry entries, so a project adds its own.
507
+ An experiment from a file gives the same run as the same experiment written in Python.
508
+ - `vmc.control.Schedule` and `ScheduledController`: live Params that follow values over time
509
+ (in straight lines or steps) and swaps at given times; a new run starts afresh. The lab's
510
+ linear ramps are two-point schedules.
511
+ - `vmc.sim.run(..., z0=...)` also takes a function of the first reading, such as
512
+ `turtle.initial_state`.
513
+ - Docs: tutorial "Experiments in files".
514
+ - Run logs. `vmc.sim.RunLog` saves a run whole or not at all (`save`: a compressed `.npz` with a
515
+ schema version and what the run was, in `meta`: the library's version, the start time, the
516
+ Params at the start, the plant's hardware profile and, from a configuration, the
517
+ configuration; written through a temporary file, and a name already taken is refused unless
518
+ `overwrite=True`), loads it back (`load`) and exports CSV (`to_csv`, one column per entry).
519
+ `step` adds one step and keeps every signal at one row per step, with NaN for what a step
520
+ did not compute.
521
+ - `vmc.sim.replay` sends a log's motor torques again to a simulated robot, each held for as long
522
+ as it was in the run, from the log's `q` and `v` or, for a real robot's log, from its first
523
+ motor reading (`ModelPlant.state_from_motors`); `vmc.sim.compare` gives the largest and the
524
+ root-mean-square difference of each signal of two runs. A replay on the true model gives the
525
+ run back.
526
+ - A run's log holds `law_torque`, the controller's torque before the output stages.
527
+ `vmc.sim.run(..., record=...)` adds the live Params (`"params"`), each element's coordinate,
528
+ rate, force and share of the motor torques (`"elements"`), and the controller's energies and
529
+ powers (`"energy"`), from `VMCController.elements()` and `balance()`.
530
+ - Configurations: `experiment.run` settings (`name`, `folder`, `overwrite`, `record`) save each
531
+ run beside the file and refuse a name already taken before the run starts.
532
+ - Docs: tutorial "Run logs".
533
+ - `vmc.interactive`: change a running controller by hand. `Controls` is a thread-safe mailbox
534
+ for new values of live Params and for swaps. `Interactive` wraps any controller and applies
535
+ what the mailbox holds just before each step, in a simulation or inside a robot's own node;
536
+ the changes act exactly as `controller.set` before the step, and it counts the energy they give
537
+ the running controller (`injected`). `Recorder` turns what was applied into `schedule` entries
538
+ with `step` interpolation and saves them into a configuration, which repeats the session
539
+ exactly. `Window` (matplotlib, no new dependency) draws the robot live, with goals to drag,
540
+ sliders, buttons to swap controllers, record and pause; `Keyboard` and `Joystick` (pygame, the
541
+ extra `virtualmodelcontrol[joystick]`) move a goal; `Session` runs a simulation in real time
542
+ in a worker thread with its window.
543
+ - `vmc.sim.SimClock(dt, speed=...)` paces a simulation to the computer's clock: with `speed`,
544
+ `run(T=None)` runs until stopped and Ctrl-C ends it with the log so far. Other simulations are
545
+ unchanged. `VMCController.live_params()` and `Experiment.z0()`.
546
+ - Docs: tutorial "Interactive control".
547
+ - Docs: tutorials "Real-time runs" (the loop on the computer's clock: measured steps, late steps
548
+ never caught up in a burst, old and missing readings, Ctrl-C, and a checklist for the first run
549
+ on a robot) and "Swaps and schedules" (the quintic blend and its continuity, a swap on the soft
550
+ arm, the schedules' interpolation).
551
+ - `vmc.ros.control`: the controller node. It runs a controller at the profile's rate on the
552
+ robot behind the driver's topics, with its live Params as ROS parameters, for a duration or
553
+ until Ctrl-C, and ends with zero torque.
554
+ - On real time (`WallClock`), `vmc.sim.run(..., T=None)` runs until Ctrl-C, and Ctrl-C ends
555
+ a real-time run with the log so far.
556
+ - CI runs the ROS tests in ROS 2 Humble and Jazzy containers, on a private domain.
557
+ - `vmc.control.SwapController` and `blend_weight`: one set of virtual elements replaces another
558
+ with a quintic blend of the two controllers' torques (continuous torque and first two
559
+ derivatives), the blend the lab's controller uses; a swap asked for during a blend waits for
560
+ it. `vmc.ros.control(..., swaps={name: controller})` swaps on a `String` message.
561
+ - `vmc.Efficiency`: the motor torque a transmission delivers, as a polynomial of the commanded
562
+ one, τ = c₁u + c₂u² + … + cₙuⁿ, each coefficient one value for every motor or one per motor.
563
+ The coefficients are `design` Params, to change, tune, keep live in a simulator or identify.
564
+ `Direct` and `TendonTransmission` take one, or a number for its linear coefficient.
565
+ - `vmc.identification`: `plateaus`, the settled end of every hold of a command in a log, and
566
+ `fit_efficiency`, a least-squares fit through the origin, motor by motor or shared, from
567
+ delivered torques or from one measured quantity with a weight per motor (a fingertip force,
568
+ say). Both agree with the lab's calibration code, and the fit gives the finger's
569
+ `adapt.MOTOR_EFFICIENCY` back from its recorded data.
570
+ - Docs: concepts pages "Transmission efficiency", "Virtual Model Control" (coordinates, forces,
571
+ the torques that realize them, virtual states), "Passivity" (the energy balances, stability,
572
+ what weakens it) and "Finger and hand kinematics" (motors to joints, joints to the
573
+ fingertip, the hand's couplings).
574
+ - The constrained elements as components of their own: `ConstrainedLinearSpring`,
575
+ `ConstrainedTanhSpring`, `ConstrainedGaussianSpring`, `ConstrainedLinearDamper` and
576
+ `ConstrainedTanhDamper` act along one direction (`normal`) only, leaving the plane normal to
577
+ it free. They give the forces of the lab's constrained elements, checked against them; the
578
+ tanh ones saturate along the normal rather than axis by axis.
579
+ - `vmc.identification.fit_stiffness_damping`: a robot's diagonal stiffness and damping from
580
+ logged runs under known motor torques, by least squares with K, D ≥ 0 in motor torques,
581
+ relative to a resting baseline, with the robot's masses and gravity known; each run can carry
582
+ its own gravity. It recovers the stiffness and damping of a simulated arm within a few percent.
583
+ - Docs: two single-arm examples, each about its own arm and its own experiments. The soft arm on
584
+ its side (`145-145-145`) reaches past an obstacle with two repulsive fields, over a sweep of
585
+ their strength, and a tanh spring limits the force on a string tied to the tip. The hanging
586
+ arm (`145-290-290`) is identified from simulated step responses with
587
+ `fit_stiffness_damping` and reaches around an obstacle with a tanh spring and two fields.
588
+ Both use the same stiffness and damping. The tutorials, the home page and the README use the
589
+ arm on its side; the Helyx geometries and the parametric template are on the soft-arm
590
+ kinematics page.
591
+
592
+ ### Changed
593
+
594
+ - Every robot's efficiency is 1 by default: models identified from the commanded torques
595
+ already include their transmission. The soft arm and the two arms take each torque as the
596
+ controller sends it again, with `helyx.SIM_STIFFNESS`, `helyx.SIM_DAMPING`,
597
+ `bimanual.STIFFNESS` and `bimanual.DAMPING` referred to the commanded torque (the identified
598
+ values divided by the 0.12 they were fitted with), as in 0.1.0. 0.2.0 applied 0.12 to every
599
+ torque with physical stiffness and damping: the same equilibria without gravity, but natural
600
+ frequencies about 2.9 times lower and a gravity compensation that cancelled only 12 % of the
601
+ weight. `helyx.EFFICIENCY`, `bimanual.EFFICIENCY`, `adapt.MOTOR_EFFICIENCY` and
602
+ `adapt.HAND_MOTOR_EFFICIENCY` stay as calibrations, never as defaults.
603
+ - A transmission's efficiency Param is now `efficiency.c1` (and `c2`, … for a polynomial);
604
+ transmissions saved by 0.2.0 still load.
605
+ - `import virtualmodelcontrol` no longer loads SciPy, which took about three quarters of its
606
+ import time; `identification.fit_stiffness_damping` loads it when called.
607
+ - Docs: the opening page has no left index (the pages of the five sections keep theirs) and
608
+ leads to the concepts and the reference. Installation: the conda recipe takes Python from
609
+ conda-forge, because conda's default channels stop a new installation until Anaconda's terms of
610
+ service are accepted, and shows how to keep ROS's `PYTHONPATH` out of a conda environment, where
611
+ pip otherwise ends with an error about ROS's packages and skips the ones ROS also ships.
612
+
613
+ ### Deprecated
614
+
615
+ - `vmc.ros` (`RosPlant`, `control`, `serve`, `LiveParams`, `JointIO`, `TOPICS`) and the direct
616
+ Dynamixel path (`hardware.DynamixelPlant`, `home`, `present_ticks`, `scan`, `latency_timer`,
617
+ `Bus`, `SdkBus`, `FakeBus`) warn when used, and leave in 0.4.0 together with the Dynamixel SDK
618
+ and pyserial dependencies and the ROS CI job. The library is communication-agnostic: it turns
619
+ measurements into torques, and how they reach the robot stays outside it. Hardware profiles
620
+ (`HardwareProfile`, `Motor`, `KT`, `MODES` and each robot's `hardware` template) stay. Python
621
+ shows a `DeprecationWarning` only for code run as a script or in a notebook; run a node with
622
+ `python -W default` to see them there.
623
+
624
+ ### Fixed
625
+
626
+ - `vmc.sim.run` needs only the plant's documented methods: when its guard trips, it sends zero
627
+ torque without reading the plant's last command `u`, which a plant of one's own may not have.
628
+ The zero command has one torque per motor rate, also on real time.
629
+ - `vmc.ros` is available after `import virtualmodelcontrol as vmc`, like `vmc.viz` and
630
+ `vmc.hardware`; it imports ROS only when a ROS class is first used.
631
+ - The hand example grasps the ball with the pads of the thumb, index and middle fingers: the
632
+ ball sits where the thumb opposes the fingers, and the grasp takes the thumb's tip at the end
633
+ of its last phalanx. Before, the thumb pressed on the ball with its side. The template's
634
+ kinematics are unchanged.
635
+ - Figure labels written as math where the figure font has no glyph (N·m on the efficiency
636
+ page); a test keeps figure text to characters the font draws.
637
+
638
+ ## [0.2.0] - 2026-10-04
639
+
640
+ ### Added
641
+
642
+ - Every dependency installs with the package: numpy, SciPy, CasADi, matplotlib, PyYAML, the
643
+ Dynamixel SDK and pyserial. CI runs on Ubuntu 22.04 and 24.04, with a clean-install job.
644
+ - Robot templates: `robots.bimanual` (two Helyx arms on one frame, with their identified
645
+ stiffness and damping and the transmission efficiency 0.12), `robots.adapt.hand` (five digits,
646
+ 13 motors, spread coupling, masses and joint limits), `robots.turtle` (two cranks and the
647
+ virtual-flywheel controller), `robots.ur5` (from DH parameters) and `ur5.with_hand()`.
648
+ - Every template is parametric in its geometry, with today's values as defaults:
649
+ `helyx.arm(lengths=..., section_radius=..., spool_radius=..., tendon_angles=..., masses=...)`
650
+ for any number of segments; `bimanual.arms(lengths=..., tendon_angles=...,
651
+ base_positions=..., efficiency=...)`, each one for both arms or by arm;
652
+ `adapt.finger(link_lengths=..., link_masses=..., link_cogs=..., joint_axes=...,
653
+ motor_radius=..., pulley_radius=..., pip_transmission=...)` and `adapt.finger_coupling`;
654
+ `adapt.hand(...)` and `ur5.with_hand(...)`, which replace any entry of the hand's geometry
655
+ tables by key (`tip_offsets={"thumb": ...}`), with the hand's mounting position on the
656
+ flange; `ur5.arm(d=..., a=..., alpha=...)`; joint-limit springs with custom ranges; output
657
+ stages and `add_dynamics` sized to the arm.
658
+ - `vmc.Kinematics`: positions, rotations, Jacobians, angular Jacobians and Hessians of any site,
659
+ from configuration or from motor angles.
660
+ - `models.JointSpace` for robots described by their joints only; `SerialChain.from_dh` and site
661
+ rotations; `Assembly` parts mounted on another part's frame.
662
+ - `TendonTransmission(efficiency=...)` and `Direct(efficiency=...)` (one value or one per
663
+ motor): the robot receives the commanded torque times the efficiency. `adapt.finger`,
664
+ `adapt.hand` and `ur5.with_hand` take `efficiency=...` and stay lossless by default: the
665
+ measured `adapt.MOTOR_EFFICIENCY` and `adapt.HAND_MOTOR_EFFICIENCY` are static calibrations for
666
+ force estimates, and unequal efficiencies on coupled motors make virtual elements
667
+ non-conservative in simulation.
668
+ - `SpeedRegulator`: drives a virtual state at a commanded speed, with a ramp.
669
+ - `control.output`, opt-in output stages for real hardware: `FrictionCompensation`,
670
+ `Pretension`, `TorqueOffset`, `TorqueLimit`; each template's own stage
671
+ (`helyx.output_stage()`, `bimanual.output_stage()`, `adapt.output_stage()`,
672
+ `adapt.hand_output_stage()`).
673
+ - `vmc.viz`: the lab figure style (`use_style`, `save` as PDF and SVG) and drawing helpers
674
+ (`draw_robot`, `draw_spring`, `draw_damper`, `draw_frame`, `draw_force`, `draw_goal`,
675
+ `label_axes`). `vmc.viz` is available after `import virtualmodelcontrol as vmc` and loads
676
+ matplotlib only when first used.
677
+ - `vmc.viz.animate`: an animation of a run (the robot, springs to fixed points, the path of a
678
+ site, anything else through a callback), saved as MP4, GIF or animated WebP. `imageio-ffmpeg`
679
+ is now a dependency, so MP4 works on every system.
680
+ - `vmc.sim.run(..., z0=...)` starts the controller from a given virtual state, and the run log
681
+ records the virtual state `z` when the controller has one.
682
+ - Contact as stiff one-sided springs: signed distances `PlaneDistance` and `SphereDistance`,
683
+ `ContactSpring` (optional smoothing for optimization) and `ContactDamper` (damps only in
684
+ contact); `adapt.add_dynamics` gives the finger and the hand their gravity for simulation.
685
+ - Documentation rebuilt for students: the Material layout (sections as tabs, the pages of each
686
+ section in the left sidebar), large type, the library's logo and navy colours;
687
+ installation and troubleshooting; tutorials (how it works, a first controller, coordinates
688
+ and components, parameters, energy, kinematics, tuning, contact, building a robot, extending
689
+ the library); an example per robot (soft arm, two arms, finger, hand and the UR5, turtle), each
690
+ opening with its robot's schematic and parameters; concepts (the library's structure, soft-arm
691
+ kinematics, conventions and glossary). Every figure, schematic, table value and animation is
692
+ computed when the documentation builds.
693
+ - `vmc.hardware.HardwareProfile`: the motors of a robot (bus ID, Dynamixel model, sign, mode,
694
+ motor constant, optional torque limit) in the library's motor order, with the bus settings;
695
+ conversions both ways between encoder ticks, raw velocities, goal currents and the published
696
+ degrees of a ROS driver and SI units, with commands clamped before the integer conversion so
697
+ they saturate instead of overflowing; saved and loaded as YAML. Each template has its
698
+ profile: `helyx.hardware()`, `bimanual.hardware()`, `adapt.finger_hardware()`,
699
+ `adapt.hand_hardware()`, `turtle.hardware()`.
700
+ - `vmc.hardware.DynamixelPlant`: a robot's Dynamixel motors as a plant through the Dynamixel
701
+ SDK, without ROS. It sets a safe goal before each motor's torque goes on, takes the start
702
+ positions as zero, reads angles and smoothed rates, writes torques as clamped goal currents,
703
+ sends zero torque when commands stop (a watchdog), and on leaving (exceptions and Ctrl-C
704
+ included) sends zero torque and switches the torque motors off; held motors keep their
705
+ position. `hardware.home` drives the motors slowly to recorded home positions and refuses a
706
+ motor that lost a turn; `hardware.scan` finds the motors and their baud rate;
707
+ `hardware.FakeBus` stands in for the bus in tests and dry runs.
708
+ - `vmc.sim.WallClock`: `vmc.sim.run` on real time, the loop of simulation unchanged. Each step
709
+ runs on the computer's clock with its measured time; a reading older than `stale` sends
710
+ zero torque; a late step starts the next one from now instead of catching up; the log
711
+ records each step's `dt`, and `log.info` the rate, the slowest step, the overruns and the
712
+ stale readings, with a warning when steps overran.
713
+ - `virtualmodelcontrol.ros` (ROS 2, any distribution; rclpy and std_msgs, loaded only when
714
+ used): `RosPlant`, the robot behind the lab driver's topics (degrees, the bus order, raw
715
+ signs, torques kept within the range of the driver's goal current); `serve`, a digital twin
716
+ that publishes a simulated robot on the same topics, with a lockstep mode in which a run over
717
+ ROS repeats the simulator's; `LiveParams`, a controller's live Params as ROS parameters,
718
+ applied between steps.
719
+
720
+ ### Changed
721
+
722
+ - The soft arm models the efficiency of its tendons: it receives η = 0.12 times the commanded
723
+ torque (`helyx.EFFICIENCY`), and `helyx.SIM_STIFFNESS` and `helyx.SIM_DAMPING` are the
724
+ identified, physical values. 0.1.0 divided them by 0.12 and delivered the full torque: the
725
+ same equilibria without gravity, but natural frequencies about 2.9 times too high and a sag
726
+ under gravity about 8 times too small. Controllers are unchanged and never divide their
727
+ torques by η, so the simulated arm receives η of every command, gravity compensation
728
+ included.
729
+ - The figure style without LaTeX uses matplotlib's own Computer Modern font (`cmr10`) instead
730
+ of DejaVu Serif, with tick labels set as math. Figures made with LaTeX are unchanged.
731
+
732
+ ### Fixed
733
+
734
+ - `RunLog` copies each row, so a signal updated in place (a controller's virtual state) is
735
+ recorded correctly at every step.
736
+ - `bimanual.add_dynamics` takes the stiffness and damping of one arm (a dict by arm, arrays
737
+ included) and keeps the other arm's identified values.
738
+ - `viz.draw_robot`: no joint markers along continuum arms mounted in an assembly; robots without
739
+ geometry (joint-space models) draw nothing instead of failing; serial chains end at their
740
+ `tool` site (the UR5 flange) as well as at a `tip`.
741
+
742
+ ## [0.1.0] - 2026-10-04
743
+
744
+ First public release.
745
+
746
+ ### Added
747
+
748
+ - Package scaffold: `pyproject.toml` (hatchling + hatch-vcs), tests, docs, CI workflows.
749
+ - `core`: `Param` and `ParamSet` with scopes (fixed, design, episode, stage) and a `Binding` that
750
+ keeps live Params symbolic and folds the rest in; spaces `Euclidean`, `SO2`, `Product`;
751
+ symbolic helpers; `Signals`; a registry with plugin entry points; unit strings.
752
+ - `mechanisms`: coordinates (`FramePoint`, `Joint`, `State`, `Ref`, `Difference`, `Slice`,
753
+ `Stack`, `Projection`, `Norm`, `Custom`), components (linear, tanh, Gaussian, sigmoid,
754
+ polynomial and limit springs; linear and tanh dampers; `PointMass`, `Inertance`; `ForceSource`,
755
+ `GravityCompensation`) and `Mechanism`. A tanh spring on a projection saturates at its maximum
756
+ force in every direction.
757
+ - `models`: parametric `PCC` with n segments, `TendonTransmission` (θ > 0 pulls), `Direct`
758
+ drive, and `SerialChain` (product of exponentials).
759
+ - `robots.helyx`: the three Helyx arm geometries with their default parameters.
760
+ - `VirtualMechanismSystem`, `compile()` and `VMCController`: about 20 µs per control step for
761
+ three springs and three dampers on a three-segment arm (`benchmarks/tick.py`).
762
+ - `compile_dynamics()`: robot dynamics assembled from the robot's components (canonical
763
+ residual, mass matrix, energy, power, linearly implicit Euler step); `Gravity` component.
764
+ - `sim`: `Plant` and `SimPlant` protocols, `ModelPlant`, `run()` with a `Guard` (zero torque on
765
+ missing or non-finite readings) and a `RunLog`.
766
+ - `models.Actuation` protocol; `helyx.add_dynamics` with the simulated arm's stiffness and
767
+ damping.
768
+ - Docs: closed-loop simulation tutorial and an "Extend the library" how-to.
769
+ - `models.LinearCoupling` (one motor driving several joints), `models.Assembly` (several models
770
+ mounted on one base) with `StackedActuation`; `FramePoint` accepts a part and an arc parameter.
771
+ - `robots.adapt`: the ADAPT finger (two motors, coupled distal joints), joint-angle coordinate
772
+ and joint-limit spring.
773
+
774
+ [Unreleased]: https://github.com/vigno0405/VirtualModelControl/compare/v0.3.0...HEAD
775
+ [0.3.0]: https://github.com/vigno0405/VirtualModelControl/compare/v0.2.0...v0.3.0
776
+ [0.2.0]: https://github.com/vigno0405/VirtualModelControl/compare/v0.1.0...v0.2.0
777
+ [0.1.0]: https://github.com/vigno0405/VirtualModelControl/releases/tag/v0.1.0