virtualmodelcontrol 1.1.0__tar.gz → 1.2.1__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 (369) hide show
  1. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/CHANGELOG.md +25 -0
  2. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/PKG-INFO +1 -1
  3. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/installation.md +2 -2
  4. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/troubleshooting.md +1 -1
  5. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/tutorials/real-time.md +27 -2
  6. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/_version.py +2 -2
  7. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/sim/run.py +75 -29
  8. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/sim/runlog.py +11 -0
  9. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/sim/test_runlog.py +28 -0
  10. virtualmodelcontrol-1.2.1/tests/sim/test_wallclock.py +247 -0
  11. virtualmodelcontrol-1.2.1/tests/test_python_versions.py +58 -0
  12. virtualmodelcontrol-1.1.0/tests/sim/test_wallclock.py +0 -125
  13. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/.gitignore +0 -0
  14. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/CITATION.cff +0 -0
  15. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/LICENSE +0 -0
  16. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/README.md +0 -0
  17. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/benchmarks/tick.py +0 -0
  18. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/_ext/video.py +0 -0
  19. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/_static/create-lab.png +0 -0
  20. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/_static/custom.css +0 -0
  21. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/_static/favicon.svg +0 -0
  22. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/_static/logo-light.svg +0 -0
  23. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/_static/logo.svg +0 -0
  24. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/_static/readme-hero.svg +0 -0
  25. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/_templates/autosummary/module.rst +0 -0
  26. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/_templates/partials/toc.html +0 -0
  27. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/api/index.md +0 -0
  28. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/concepts/conventions.md +0 -0
  29. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/concepts/efficiency.md +0 -0
  30. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/concepts/finger.md +0 -0
  31. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/concepts/library.md +0 -0
  32. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/concepts/passivity.md +0 -0
  33. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/concepts/pcc.md +0 -0
  34. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/concepts/vmc.md +0 -0
  35. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/conf.py +0 -0
  36. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/development/changelog.md +0 -0
  37. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/development/contributing.md +0 -0
  38. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/development/roadmap.md +0 -0
  39. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/development/support.md +0 -0
  40. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/docs_setup.py +0 -0
  41. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/examples/finger.md +0 -0
  42. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/examples/hand.md +0 -0
  43. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/examples/hanging-arm.md +0 -0
  44. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/examples/soft-arm.md +0 -0
  45. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/examples/turtle.md +0 -0
  46. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/examples/two-arms.md +0 -0
  47. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/index.md +0 -0
  48. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/schematics/__init__.py +0 -0
  49. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/schematics/_draw.py +0 -0
  50. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/schematics/bimanual.py +0 -0
  51. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/schematics/control_loop.py +0 -0
  52. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/schematics/energy_flow.py +0 -0
  53. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/schematics/finger.py +0 -0
  54. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/schematics/four_steps.py +0 -0
  55. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/schematics/hand.py +0 -0
  56. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/schematics/helyx.py +0 -0
  57. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/schematics/library_map.py +0 -0
  58. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/schematics/mechanism.py +0 -0
  59. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/schematics/params.py +0 -0
  60. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/schematics/pcc.py +0 -0
  61. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/schematics/turtle.py +0 -0
  62. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/schematics/ur5.py +0 -0
  63. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/scripts/readme_figure.py +0 -0
  64. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/tutorials/build-a-robot.md +0 -0
  65. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/tutorials/configurations.md +0 -0
  66. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/tutorials/contact.md +0 -0
  67. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/tutorials/coordinates-and-components.md +0 -0
  68. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/tutorials/crawl.md +0 -0
  69. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/tutorials/energy.md +0 -0
  70. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/tutorials/estimation.md +0 -0
  71. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/tutorials/extend.md +0 -0
  72. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/tutorials/first-controller.md +0 -0
  73. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/tutorials/fit.md +0 -0
  74. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/tutorials/force.md +0 -0
  75. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/tutorials/friction.md +0 -0
  76. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/tutorials/introduction.md +0 -0
  77. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/tutorials/kinematics.md +0 -0
  78. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/tutorials/mpc.md +0 -0
  79. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/tutorials/optimize.md +0 -0
  80. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/tutorials/outside.md +0 -0
  81. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/tutorials/parameters.md +0 -0
  82. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/tutorials/reach.yaml +0 -0
  83. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/tutorials/run-logs.md +0 -0
  84. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/tutorials/run-settings.yaml +0 -0
  85. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/tutorials/stiffness.md +0 -0
  86. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/tutorials/swaps.md +0 -0
  87. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/tutorials/tuning.md +0 -0
  88. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/docs/tutorials/underactuated.md +0 -0
  89. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/pyproject.toml +0 -0
  90. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/__init__.py +0 -0
  91. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/adaptation/__init__.py +0 -0
  92. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/adaptation/force.py +0 -0
  93. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/adaptation/limits.py +0 -0
  94. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/adaptation/position.py +0 -0
  95. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/adaptation/scalar.py +0 -0
  96. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/adaptation/seeking.py +0 -0
  97. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/adaptation/stiffness.py +0 -0
  98. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/compiler.py +0 -0
  99. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/config/__init__.py +0 -0
  100. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/config/controllers.py +0 -0
  101. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/config/coordinates.py +0 -0
  102. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/config/experiment.py +0 -0
  103. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/config/files.py +0 -0
  104. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/config/spec.py +0 -0
  105. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/control/__init__.py +0 -0
  106. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/control/blending.py +0 -0
  107. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/control/controller.py +0 -0
  108. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/control/output.py +0 -0
  109. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/control/projection.py +0 -0
  110. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/control/schedule.py +0 -0
  111. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/control/state.py +0 -0
  112. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/control/tank.py +0 -0
  113. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/control/underactuated.py +0 -0
  114. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/core/__init__.py +0 -0
  115. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/core/backends.py +0 -0
  116. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/core/params.py +0 -0
  117. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/core/registry.py +0 -0
  118. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/core/signals.py +0 -0
  119. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/core/space.py +0 -0
  120. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/core/symbolic.py +0 -0
  121. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/core/units.py +0 -0
  122. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/dynamics.py +0 -0
  123. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/estimation/__init__.py +0 -0
  124. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/estimation/balance.py +0 -0
  125. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/estimation/compliance.py +0 -0
  126. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/estimation/contact.py +0 -0
  127. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/estimation/imu.py +0 -0
  128. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/estimation/inversion.py +0 -0
  129. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/estimation/kalman.py +0 -0
  130. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/estimation/measurement.py +0 -0
  131. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/estimation/momentum.py +0 -0
  132. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/estimation/sensors.py +0 -0
  133. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/estimation/stiffness.py +0 -0
  134. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/estimation/velocity.py +0 -0
  135. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/hardware/__init__.py +0 -0
  136. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/hardware/profile.py +0 -0
  137. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/identification/__init__.py +0 -0
  138. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/identification/efficiency.py +0 -0
  139. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/identification/params.py +0 -0
  140. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/identification/plateaus.py +0 -0
  141. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/identification/steps.py +0 -0
  142. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/identification/stiffness.py +0 -0
  143. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/identification/transmission.py +0 -0
  144. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/math.py +0 -0
  145. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/mechanisms/__init__.py +0 -0
  146. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/mechanisms/components/__init__.py +0 -0
  147. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/mechanisms/components/base.py +0 -0
  148. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/mechanisms/components/constrained.py +0 -0
  149. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/mechanisms/components/dissipation.py +0 -0
  150. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/mechanisms/components/gated.py +0 -0
  151. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/mechanisms/components/inertance.py +0 -0
  152. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/mechanisms/components/locomotion.py +0 -0
  153. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/mechanisms/components/sources.py +0 -0
  154. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/mechanisms/components/storage.py +0 -0
  155. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/mechanisms/coordinates/__init__.py +0 -0
  156. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/mechanisms/coordinates/base.py +0 -0
  157. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/mechanisms/coordinates/frames.py +0 -0
  158. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/mechanisms/coordinates/geometry.py +0 -0
  159. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/mechanisms/coordinates/joints.py +0 -0
  160. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/mechanisms/coordinates/ops.py +0 -0
  161. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/mechanisms/coordinates/references.py +0 -0
  162. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/mechanisms/mechanism.py +0 -0
  163. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/models/__init__.py +0 -0
  164. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/models/actuation.py +0 -0
  165. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/models/assembly.py +0 -0
  166. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/models/continuum/__init__.py +0 -0
  167. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/models/continuum/pcc.py +0 -0
  168. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/models/efficiency.py +0 -0
  169. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/models/equations.py +0 -0
  170. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/models/friction.py +0 -0
  171. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/models/function.py +0 -0
  172. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/models/joint_space.py +0 -0
  173. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/models/kinematic.py +0 -0
  174. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/models/kinematics.py +0 -0
  175. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/models/rigid/__init__.py +0 -0
  176. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/models/rigid/couplings.py +0 -0
  177. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/models/rigid/poe.py +0 -0
  178. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/models/rigid/spline.py +0 -0
  179. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/optimization/__init__.py +0 -0
  180. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/optimization/asktell.py +0 -0
  181. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/optimization/builder.py +0 -0
  182. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/optimization/collocation.py +0 -0
  183. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/optimization/equilibrium.py +0 -0
  184. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/optimization/horizon.py +0 -0
  185. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/optimization/mpc.py +0 -0
  186. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/optimization/nlp.py +0 -0
  187. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/optimization/problem.py +0 -0
  188. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/optimization/result.py +0 -0
  189. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/optimization/search.py +0 -0
  190. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/optimization/shooting.py +0 -0
  191. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/optimization/solver.py +0 -0
  192. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/optimization/tank.py +0 -0
  193. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/optimization/terms.py +0 -0
  194. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/optimization/trajectory.py +0 -0
  195. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/optimization/variables.py +0 -0
  196. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/optimization/virtual.py +0 -0
  197. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/py.typed +0 -0
  198. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/robots/__init__.py +0 -0
  199. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/robots/adapt.py +0 -0
  200. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/robots/bimanual.py +0 -0
  201. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/robots/helyx.py +0 -0
  202. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/robots/planar.py +0 -0
  203. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/robots/turtle.py +0 -0
  204. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/robots/ur5.py +0 -0
  205. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/sim/__init__.py +0 -0
  206. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/sim/balance.py +0 -0
  207. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/sim/model_plant.py +0 -0
  208. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/sim/plant.py +0 -0
  209. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/sim/replay.py +0 -0
  210. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/sim/rollout.py +0 -0
  211. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/system.py +0 -0
  212. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/testing.py +0 -0
  213. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/viz/__init__.py +0 -0
  214. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/viz/animation.py +0 -0
  215. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/viz/draw.py +0 -0
  216. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/src/virtualmodelcontrol/viz/style.py +0 -0
  217. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/adaptation/test_fingertip.py +0 -0
  218. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/adaptation/test_force.py +0 -0
  219. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/adaptation/test_grasp.py +0 -0
  220. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/adaptation/test_holding.py +0 -0
  221. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/adaptation/test_loop.py +0 -0
  222. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/adaptation/test_position.py +0 -0
  223. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/adaptation/test_scalar.py +0 -0
  224. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/adaptation/test_seeking.py +0 -0
  225. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/adaptation/test_stiffness_tracking.py +0 -0
  226. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/adaptation/test_virtual_state.py +0 -0
  227. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/backend_helpers.py +0 -0
  228. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/config/test_config.py +0 -0
  229. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/conftest.py +0 -0
  230. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/control/test_blending.py +0 -0
  231. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/control/test_honesty.py +0 -0
  232. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/control/test_inputs.py +0 -0
  233. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/control/test_output.py +0 -0
  234. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/control/test_projection.py +0 -0
  235. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/control/test_schedule.py +0 -0
  236. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/control/test_state_layout.py +0 -0
  237. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/control/test_tank.py +0 -0
  238. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/control/test_underactuated.py +0 -0
  239. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/control/test_underactuated_golden.py +0 -0
  240. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/control/test_vmc_golden.py +0 -0
  241. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/core/test_backends.py +0 -0
  242. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/core/test_backends_torch.py +0 -0
  243. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/core/test_params.py +0 -0
  244. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/core/test_registry.py +0 -0
  245. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/core/test_signals.py +0 -0
  246. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/core/test_space.py +0 -0
  247. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/core/test_symbolic.py +0 -0
  248. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/data/adaptation.npz +0 -0
  249. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/data/compliance.npz +0 -0
  250. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/data/components.npz +0 -0
  251. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/data/configs/finger.yaml +0 -0
  252. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/data/configs/hand.yaml +0 -0
  253. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/data/configs/soft-arm.yaml +0 -0
  254. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/data/configs/turtle.yaml +0 -0
  255. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/data/configs/two-arms.yaml +0 -0
  256. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/data/configs/ur5-hand.yaml +0 -0
  257. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/data/derivatives.npz +0 -0
  258. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/data/dynamics.npz +0 -0
  259. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/data/efficiency.npz +0 -0
  260. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/data/estimation.npz +0 -0
  261. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/data/finger.npz +0 -0
  262. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/data/fingertip.npz +0 -0
  263. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/data/hand.npz +0 -0
  264. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/data/heuristics.npz +0 -0
  265. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/data/imu.npz +0 -0
  266. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/data/kalman.npz +0 -0
  267. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/data/mocap.npz +0 -0
  268. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/data/optimization.json +0 -0
  269. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/data/optimization.npz +0 -0
  270. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/data/pcc.npz +0 -0
  271. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/data/transmission.npz +0 -0
  272. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/data/turtle.npz +0 -0
  273. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/data/underactuated.npz +0 -0
  274. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/data/vmc.npz +0 -0
  275. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/estimation/test_compliance_golden.py +0 -0
  276. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/estimation/test_contact.py +0 -0
  277. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/estimation/test_imu.py +0 -0
  278. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/estimation/test_inversion.py +0 -0
  279. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/estimation/test_kalman.py +0 -0
  280. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/estimation/test_kalman_golden.py +0 -0
  281. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/estimation/test_kalman_unscented.py +0 -0
  282. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/estimation/test_mocap_golden.py +0 -0
  283. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/estimation/test_momentum.py +0 -0
  284. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/estimation/test_sensors.py +0 -0
  285. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/estimation/test_unmeasured.py +0 -0
  286. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/estimation/test_velocity.py +0 -0
  287. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/hardware/test_profile.py +0 -0
  288. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/hardware/test_templates.py +0 -0
  289. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/helpers.py +0 -0
  290. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/identification/test_efficiency_fit.py +0 -0
  291. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/identification/test_fit_friction.py +0 -0
  292. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/identification/test_fit_params.py +0 -0
  293. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/identification/test_friction.py +0 -0
  294. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/identification/test_steps.py +0 -0
  295. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/identification/test_stiffness_fit.py +0 -0
  296. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/identification/test_transmission.py +0 -0
  297. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/lab_optimization.py +0 -0
  298. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/mechanisms/test_components.py +0 -0
  299. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/mechanisms/test_components_golden.py +0 -0
  300. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/mechanisms/test_contact.py +0 -0
  301. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/mechanisms/test_coordinates.py +0 -0
  302. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/mechanisms/test_distances.py +0 -0
  303. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/mechanisms/test_frames.py +0 -0
  304. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/mechanisms/test_friction.py +0 -0
  305. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/mechanisms/test_gated.py +0 -0
  306. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/mechanisms/test_mechanism.py +0 -0
  307. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/mechanisms/test_more_components.py +0 -0
  308. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/mechanisms/test_pair_contact.py +0 -0
  309. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/mechanisms/test_phase_spring.py +0 -0
  310. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/mechanisms/test_virtual_model.py +0 -0
  311. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/models/test_actuation.py +0 -0
  312. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/models/test_assembly.py +0 -0
  313. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/models/test_contact_map.py +0 -0
  314. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/models/test_efficiency.py +0 -0
  315. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/models/test_equations.py +0 -0
  316. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/models/test_floating.py +0 -0
  317. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/models/test_floating_base.py +0 -0
  318. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/models/test_function.py +0 -0
  319. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/models/test_kinematics.py +0 -0
  320. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/models/test_pcc.py +0 -0
  321. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/models/test_poe.py +0 -0
  322. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/models/test_rail.py +0 -0
  323. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/models/test_static_friction.py +0 -0
  324. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/models/test_underactuated.py +0 -0
  325. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/optimization/test_asktell.py +0 -0
  326. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/optimization/test_collocation.py +0 -0
  327. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/optimization/test_equilibrium.py +0 -0
  328. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/optimization/test_horizon.py +0 -0
  329. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/optimization/test_lab.py +0 -0
  330. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/optimization/test_mpc.py +0 -0
  331. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/optimization/test_periodic.py +0 -0
  332. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/optimization/test_plant.py +0 -0
  333. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/optimization/test_problem.py +0 -0
  334. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/optimization/test_search.py +0 -0
  335. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/optimization/test_shooting.py +0 -0
  336. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/optimization/test_solvers.py +0 -0
  337. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/optimization/test_structure.py +0 -0
  338. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/optimization/test_swap.py +0 -0
  339. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/optimization/test_tank.py +0 -0
  340. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/optimization/test_terms.py +0 -0
  341. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/optimization/test_variables.py +0 -0
  342. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/optimization/test_virtual.py +0 -0
  343. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/robots/test_adapt.py +0 -0
  344. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/robots/test_bimanual.py +0 -0
  345. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/robots/test_crawler.py +0 -0
  346. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/robots/test_hand.py +0 -0
  347. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/robots/test_helyx.py +0 -0
  348. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/robots/test_parametric.py +0 -0
  349. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/robots/test_planar.py +0 -0
  350. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/robots/test_turtle.py +0 -0
  351. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/robots/test_ur5.py +0 -0
  352. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/sim/test_balance.py +0 -0
  353. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/sim/test_record.py +0 -0
  354. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/sim/test_replay.py +0 -0
  355. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/sim/test_rollout.py +0 -0
  356. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/sim/test_run.py +0 -0
  357. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/small_plan.py +0 -0
  358. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/soft_arm_plan.py +0 -0
  359. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/test_benchmark.py +0 -0
  360. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/test_compiler.py +0 -0
  361. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/test_docs.py +0 -0
  362. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/test_dynamics.py +0 -0
  363. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/test_import.py +0 -0
  364. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/test_math.py +0 -0
  365. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/test_readme.py +0 -0
  366. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/test_testing.py +0 -0
  367. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/underactuated_lab.py +0 -0
  368. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/viz/test_animate.py +0 -0
  369. {virtualmodelcontrol-1.1.0 → virtualmodelcontrol-1.2.1}/tests/viz/test_viz.py +0 -0
@@ -4,6 +4,31 @@ All notable changes to this project are documented here. The format follows
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and versions follow
5
5
  [Semantic Versioning](https://semver.org/) (0.x: the API may change between minor versions).
6
6
 
7
+ ## [1.2.1] - 2026-10-10
8
+
9
+ A patch: a Ctrl-C can no longer lose the log of a real-time run. Nothing else changes.
10
+
11
+ ### Fixed
12
+
13
+ - A Ctrl-C (or `_thread.interrupt_main()`) that arrived in the middle of a real-time step could land
14
+ inside a call into CasADi or numpy, come out as a `SystemError` and lose the log. On the main thread,
15
+ with Python's own handler on SIGINT, `sim.run` now only raises a flag, reads it between two steps and
16
+ returns the log; a second Ctrl-C raises at once, for a run that is stuck. A handler of your own, and
17
+ any other thread, are left alone.
18
+
19
+ ## [1.2.0] - 2026-10-09
20
+
21
+ A robot that runs for hours: the log of a real-time run can keep only its last seconds. Nothing that
22
+ worked before changes.
23
+
24
+ ### Added
25
+
26
+ - `sim.run(..., window=)`: on real time, a run that goes on for hours keeps only the last `window`
27
+ seconds of its log (up to a quarter more, until the next trim) instead of every step in memory;
28
+ `info["steps"]` still counts them all, and a Ctrl-C keeps only whole steps.
29
+ - `RunLog.crop(t_min, t_max)`: the steps of a time window as a new log, with the same `info` and
30
+ `meta`; an end left out is open.
31
+
7
32
  ## [1.1.0] - 2026-10-09
8
33
 
9
34
  Static friction of the motors: a model of it, a compensation for it, its identification and a
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: virtualmodelcontrol
3
- Version: 1.1.0
3
+ Version: 1.2.1
4
4
  Summary: Virtual Model Control: robots and controllers as mechanisms of springs, dampers and inertances, compiled with CasADi.
5
5
  Project-URL: Repository, https://github.com/vigno0405/VirtualModelControl
6
6
  Project-URL: Documentation, https://vigno0405.github.io/VirtualModelControl/
@@ -120,10 +120,10 @@ pip install -e .
120
120
  An experiment should always run the same code. Pin the version, and move the pin on purpose:
121
121
 
122
122
  ```bash
123
- pip install "virtualmodelcontrol==1.1.0"
123
+ pip install "virtualmodelcontrol==1.2.1"
124
124
  ```
125
125
 
126
- In a `requirements.txt` file, write `virtualmodelcontrol==1.1.0`. In a conda `environment.yml`,
126
+ In a `requirements.txt` file, write `virtualmodelcontrol==1.2.1`. In a conda `environment.yml`,
127
127
  list it under `pip:`. `pip show virtualmodelcontrol` tells you which version you have, and the
128
128
  [changelog](development/changelog.md) what changed between versions.
129
129
 
@@ -32,7 +32,7 @@ sudo apt install python3-venv
32
32
  pip installs CasADi from a prebuilt wheel. If pip starts building CasADi from source, or
33
33
  `import casadi` fails, there is no wheel for your Python version or platform. Create the
34
34
  environment with a Python version listed among the files on CasADi's
35
- [PyPI page](https://pypi.org/project/casadi/#files).
35
+ [PyPI page](https://pypi.org/project/casadi/).
36
36
 
37
37
  ## Compiling takes long
38
38
 
@@ -132,6 +132,29 @@ length of each step. `log.info` counts the `overruns` (steps longer than the per
132
132
  `stale` readings the loop refused as too old and the `guard_trips` (readings missing or not
133
133
  numbers). A refused reading means zero torque for that step.
134
134
 
135
+ A run that goes on for hours would keep every step in memory. With `window` [s], `run` keeps
136
+ only the last seconds of the log (up to a quarter more, until it trims), and `log.info["steps"]`
137
+ still counts every step. `log.crop(t_min, t_max)` takes a part of any log:
138
+
139
+ ```{code-cell} python
140
+ driver = Driver()
141
+ clock = vmc.sim.WallClock(1 / 330, 0.05, now=driver.time,
142
+ sleep=driver.sim.advance)
143
+ long = vmc.sim.run(Plant(driver), controller, clock, T=3.0, window=1.0)
144
+ kept = long.arrays()["t"].ravel()
145
+ last = long.crop(t_min=kept[-1] - 0.5) # the last half second
146
+ len(kept), len(last.arrays()["t"])
147
+ ```
148
+
149
+ ```{code-cell} python
150
+ :tags: [remove-cell]
151
+ assert long.info["steps"] == 990 and 330 <= len(kept) <= 413
152
+ assert abs(kept[-1] - 989 / 330) < 1e-9 and 160 <= len(last.arrays()["t"]) <= 167
153
+ glue("kept", len(kept), display=False)
154
+ ```
155
+
156
+ The log holds {glue:text}`kept` of the 990 steps, the last second or so, and nothing older.
157
+
135
158
  `run` does not close the plant: do it yourself, with `try` and `finally`:
136
159
 
137
160
  ```python
@@ -346,8 +369,10 @@ assert old.info["stale"] == len(old.arrays()["t"])
346
369
  assert not old.arrays()["motor_torque"].any() and blind.info["guard_trips"] > 20
347
370
  ```
348
371
 
349
- **Ctrl-C.** It ends a real-time run with the log so far. Here the controller raises it at its
350
- 60th step:
372
+ **Ctrl-C.** It ends a real-time run with the log so far. The step that is running finishes first
373
+ (an interrupt in the middle of a step could land inside a call into CasADi or numpy and lose the log),
374
+ and a second Ctrl-C stops the run at once, for one that is stuck. A signal handler of your own is
375
+ left alone. Here the controller raises it at its 60th step:
351
376
 
352
377
  ```{code-cell} python
353
378
  class Stop(Slow):
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '1.1.0'
22
- __version_tuple__ = version_tuple = (1, 1, 0)
21
+ __version__ = version = '1.2.1'
22
+ __version_tuple__ = version_tuple = (1, 2, 1)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -2,9 +2,12 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import contextlib
6
+ import signal
7
+ import threading
5
8
  import time
6
9
  import warnings
7
- from collections.abc import Callable, Iterable
10
+ from collections.abc import Callable, Iterable, Iterator
8
11
  from dataclasses import dataclass
9
12
  from datetime import datetime
10
13
  from typing import Any
@@ -62,18 +65,21 @@ def run(
62
65
  guard: Guard | None = None,
63
66
  z0: Any = None,
64
67
  record: Iterable[str] | str = (),
68
+ window: float | None = None,
65
69
  ) -> RunLog:
66
70
  """Run ``controller`` on ``plant`` for ``T`` [s]; returns the recorded run.
67
71
 
68
72
  Each step reads the plant, asks the controller for a command (zero torque if the guard
69
73
  trips), writes it, then advances a simulated plant by ``clock.dt`` or, with a ``WallClock``,
70
74
  waits for the next step. On real time, ``T=None`` runs until Ctrl-C, and Ctrl-C ends the run
71
- with the log so far. ``z0`` sets the controller's initial
75
+ with the log so far, after the step that is running. ``z0`` sets the controller's initial
72
76
  virtual state, or is a function of the first reading that returns it. The log holds, at every
73
77
  step, the measurements, the command (and the law's torque before any output stage), the
74
78
  virtual state ``z`` when the controller has one, and what ``record`` asks for (see
75
79
  ``RECORDS``); a value the step did not compute is NaN. Its ``meta`` holds the Params at the
76
- start and the plant's hardware profile.
80
+ start and the plant's hardware profile. On real time, ``window`` [s] keeps only the last
81
+ seconds of a run that goes on for hours (up to a quarter more, until the next trim);
82
+ ``info["steps"]`` still counts them all.
77
83
  """
78
84
  guard = Guard() if guard is None else guard
79
85
  extras = _extras(record)
@@ -83,8 +89,10 @@ def run(
83
89
  raise ValueError(
84
90
  "record='robot' needs a simulated plant: a real robot does not report forces"
85
91
  )
92
+ if window is not None and not isinstance(clock, WallClock):
93
+ raise ValueError("window is for a real-time run: a simulated one has its duration")
86
94
  if isinstance(clock, WallClock):
87
- return _run_wall(plant, controller, clock, T, guard, z0, extras)
95
+ return _run_wall(plant, controller, clock, T, guard, z0, extras, window)
88
96
  if T is None:
89
97
  raise ValueError("a simulated run needs its duration T")
90
98
  log = RunLog()
@@ -181,6 +189,36 @@ def _reset(controller: Any, t: float, meas: Signals, z0: Any) -> None:
181
189
  controller.reset(t, meas, z0=z0)
182
190
 
183
191
 
192
+ @contextlib.contextmanager
193
+ def _ctrl_c_waits_for_the_step() -> Iterator[Callable[[], bool]]:
194
+ """Ctrl-C waits for the step that is running.
195
+
196
+ On the main thread, with Python's own handler on SIGINT, a first Ctrl-C only raises a flag
197
+ that the loop reads between two steps. An interrupt raised in the middle of a step can land
198
+ inside a call into C code (CasADi, numpy), which turns it into a SystemError and loses the log.
199
+ A second Ctrl-C raises KeyboardInterrupt at once, for a run that is stuck. Any other handler,
200
+ and any other thread, is left alone.
201
+ """
202
+ flag = threading.Event()
203
+ ours = (
204
+ threading.current_thread() is threading.main_thread()
205
+ and signal.getsignal(signal.SIGINT) is signal.default_int_handler
206
+ )
207
+
208
+ def on_sigint(signum: int, frame: object) -> None:
209
+ if flag.is_set():
210
+ raise KeyboardInterrupt
211
+ flag.set()
212
+
213
+ if ours:
214
+ signal.signal(signal.SIGINT, on_sigint)
215
+ try:
216
+ yield flag.is_set
217
+ finally:
218
+ if ours:
219
+ signal.signal(signal.SIGINT, signal.default_int_handler)
220
+
221
+
184
222
  def _run_wall(
185
223
  plant: Any,
186
224
  controller: Any,
@@ -189,6 +227,7 @@ def _run_wall(
189
227
  guard: Guard,
190
228
  z0: Any,
191
229
  extras: Callable[[Any, Any], dict[str, Any]],
230
+ window: float | None,
192
231
  ) -> RunLog:
193
232
  """The run loop on real time: measured steps, stale readings refused, rate statistics."""
194
233
  log, limit = RunLog(), None if T is None else round(T / clock.dt)
@@ -197,31 +236,38 @@ def _run_wall(
197
236
  t0 = previous = tick = clock.now()
198
237
  _reset(controller, 0.0, meas, z0)
199
238
  _describe(log, plant, controller)
200
- steps = overruns = stale = 0
201
- try:
202
- while limit is None or steps < limit:
203
- now = clock.now()
204
- t = now - t0
205
- meas = plant.read()
206
- old = clock.stale is not None and plant.t - meas.t > clock.stale
207
- stale += old
208
- good = guard.ok(meas) and not old
209
- if good:
210
- cmd = controller.step(t, meas)
211
- else:
212
- cmd = Signals(t, motor_torque=np.zeros(motors))
213
- plant.write(cmd)
214
- values = _values(t, cmd, meas, controller, plant, extras if good else None)
215
- log.step(dt=now - previous, **values)
216
- steps += 1
217
- previous, tick = now, tick + clock.dt
218
- wait = tick - clock.now()
219
- if wait > 0:
220
- clock.sleep(wait)
221
- else: # late: start again from now rather than catching up in a burst
222
- overruns, tick = overruns + 1, clock.now()
223
- except KeyboardInterrupt: # Ctrl-C ends a real-time run with the log so far
224
- log.rows = {name: rows[:steps] for name, rows in log.rows.items()}
239
+ steps = overruns = stale = dropped = 0
240
+ with _ctrl_c_waits_for_the_step() as interrupted:
241
+ try:
242
+ while (limit is None or steps < limit) and not interrupted():
243
+ now = clock.now()
244
+ t = now - t0
245
+ meas = plant.read()
246
+ old = clock.stale is not None and plant.t - meas.t > clock.stale
247
+ stale += old
248
+ good = guard.ok(meas) and not old
249
+ if good:
250
+ cmd = controller.step(t, meas)
251
+ else:
252
+ cmd = Signals(t, motor_torque=np.zeros(motors))
253
+ plant.write(cmd)
254
+ values = _values(t, cmd, meas, controller, plant, extras if good else None)
255
+ log.step(dt=now - previous, **values)
256
+ steps += 1
257
+ if window is not None and len(log.rows["t"]) > 1.25 * window / clock.dt:
258
+ # a fifth at a time: trimming a list costs its length
259
+ drop = len(log.rows["t"]) // 5
260
+ dropped += drop
261
+ for rows in log.rows.values():
262
+ del rows[:drop]
263
+ previous, tick = now, tick + clock.dt
264
+ wait = tick - clock.now()
265
+ if wait > 0:
266
+ clock.sleep(wait)
267
+ else: # late: start again from now rather than catching up in a burst
268
+ overruns, tick = overruns + 1, clock.now()
269
+ except KeyboardInterrupt: # Ctrl-C ends a real-time run with the log so far
270
+ log.rows = {name: rows[: steps - dropped] for name, rows in log.rows.items()}
225
271
  dt = np.diff(log.arrays()["t"].ravel()) if steps > 1 else np.array([clock.dt])
226
272
  log.info = {
227
273
  "steps": steps,
@@ -50,6 +50,17 @@ class RunLog:
50
50
  """Each signal as an (n_steps, ...) array."""
51
51
  return {name: np.array(rows) for name, rows in self.rows.items()}
52
52
 
53
+ def crop(self, t_min: float | None = None, t_max: float | None = None) -> RunLog:
54
+ """The steps with ``t_min`` ≤ t ≤ ``t_max`` [s] (an end left out is open), as a new log with
55
+ the same ``info`` and ``meta``."""
56
+ t = np.array([row[0] for row in self.rows["t"]])
57
+ keep = np.flatnonzero(
58
+ (t >= (-np.inf if t_min is None else t_min))
59
+ & (t <= (np.inf if t_max is None else t_max))
60
+ )
61
+ rows = {name: [values[i] for i in keep] for name, values in self.rows.items()}
62
+ return RunLog(rows, dict(self.info), dict(self.meta))
63
+
53
64
  def save(self, path: str | os.PathLike[str], *, overwrite: bool = False) -> Path:
54
65
  """Write the log as a compressed ``.npz`` and return its path. The file is written whole or
55
66
  not at all (through a temporary file), and an existing one is kept unless ``overwrite``."""
@@ -167,3 +167,31 @@ def test_the_library_reads_runs_of_its_own_simulator(tmp_path):
167
167
  assert back.meta == log.meta and back.meta["library"] == vmc.__version__
168
168
  for name, values in log.arrays().items():
169
169
  np.testing.assert_array_equal(back.arrays()[name], values, err_msg=name)
170
+
171
+
172
+ def test_crop_keeps_the_steps_of_a_time_window_and_leaves_the_log_as_it_was():
173
+ log = example_log()
174
+ part = log.crop(0.25, 1.0)
175
+ rows = part.arrays()
176
+ np.testing.assert_array_equal(rows["t"].ravel(), [0.5, 1.0])
177
+ np.testing.assert_array_equal(rows["q"], [[3.0, 4.0], [5.0, 6.0]])
178
+ assert rows["tag"].shape == (2, 1) and np.isnan(rows["tag"]).all() # the names stay, aligned
179
+ assert part.info == log.info and part.meta == log.meta
180
+ part.info["steps"] = 0 # a copy: the original is not touched
181
+ assert log.info["steps"] == 3 and len(log.arrays()["t"]) == 3
182
+ np.testing.assert_array_equal(
183
+ log.crop(t_max=0.5).arrays()["t"].ravel(), [0.0, 0.5]
184
+ ) # open ends
185
+ np.testing.assert_array_equal(log.crop(t_min=0.6).arrays()["t"].ravel(), [1.0])
186
+ assert len(log.crop().arrays()["t"]) == 3 and len(log.crop(2.0, 3.0).rows["t"]) == 0
187
+ np.testing.assert_array_equal(
188
+ log.crop(0.5, 0.5).arrays()["t"].ravel(), [0.5]
189
+ ) # both ends count
190
+
191
+
192
+ def test_crop_with_an_open_end_takes_every_time_on_that_side():
193
+ log = RunLog()
194
+ for t in (-1.0, 0.0, 5.0):
195
+ log.step(t=t, q=[t])
196
+ np.testing.assert_array_equal(log.crop(t_max=0.0).arrays()["t"].ravel(), [-1.0, 0.0])
197
+ np.testing.assert_array_equal(log.crop(t_min=0.0).arrays()["t"].ravel(), [0.0, 5.0])
@@ -0,0 +1,247 @@
1
+ """The run loop on real time: steps on schedule, measured time, stale readings, overruns."""
2
+
3
+ import signal
4
+ import threading
5
+ import time
6
+
7
+ import numpy as np
8
+ import pytest
9
+
10
+ import virtualmodelcontrol as vmc
11
+ from virtualmodelcontrol.robots import adapt
12
+
13
+
14
+ class FakeTime:
15
+ """A clock that moves only when the loop sleeps or a step takes time."""
16
+
17
+ def __init__(self):
18
+ self.t = 100.0
19
+
20
+ def now(self):
21
+ return self.t
22
+
23
+ def sleep(self, seconds):
24
+ self.t += seconds
25
+
26
+
27
+ class Computer:
28
+ """The computer's own clock, as the time of a plant."""
29
+
30
+ @property
31
+ def t(self):
32
+ return time.monotonic()
33
+
34
+
35
+ class Plant:
36
+ """Two motors at rest; ``lag`` [s] makes every reading that old."""
37
+
38
+ def __init__(self, time, lag=0.0):
39
+ self.time, self.lag, self.sent = time, lag, []
40
+
41
+ @property
42
+ def t(self):
43
+ return self.time.t
44
+
45
+ def read(self):
46
+ return vmc.Signals(
47
+ self.t - self.lag, motor_position=np.zeros(2), motor_velocity=np.zeros(2)
48
+ )
49
+
50
+ def write(self, cmd):
51
+ self.sent.append(np.array(cmd["motor_torque"]))
52
+
53
+
54
+ class Controller:
55
+ """Constant torque; each step takes ``cost`` [s] of the fake clock."""
56
+
57
+ def __init__(self, time, cost=0.0):
58
+ self.time, self.cost, self.times = time, cost, []
59
+
60
+ def reset(self, t, meas):
61
+ pass
62
+
63
+ def step(self, t, meas):
64
+ self.times.append(t)
65
+ self.time.t += self.cost
66
+ return vmc.Signals(t, motor_torque=np.ones(2))
67
+
68
+
69
+ def test_steps_run_on_schedule_with_the_measured_time():
70
+ time = FakeTime()
71
+ clock = vmc.sim.WallClock(dt=0.01, now=time.now, sleep=time.sleep)
72
+ controller = Controller(time, cost=0.002)
73
+ log = vmc.sim.run(Plant(time), controller, clock, T=0.1)
74
+ np.testing.assert_allclose(controller.times, np.arange(10) * 0.01, atol=1e-12)
75
+ assert log.info["steps"] == 10 and log.info["overruns"] == 0
76
+ assert log.info["rate"] == pytest.approx(100.0)
77
+ np.testing.assert_allclose(log.arrays()["dt"][1:].ravel(), 0.01)
78
+
79
+
80
+ def test_late_steps_are_counted_and_warned_without_a_burst():
81
+ time = FakeTime()
82
+ clock = vmc.sim.WallClock(dt=0.01, now=time.now, sleep=time.sleep)
83
+ controller = Controller(time, cost=0.015)
84
+ with pytest.warns(RuntimeWarning, match="10 of 10 steps took longer than 10.0 ms"):
85
+ log = vmc.sim.run(Plant(time), controller, clock, T=0.1)
86
+ assert log.info["overruns"] == 10
87
+ np.testing.assert_allclose(np.diff(controller.times), 0.015) # never squeezed to catch up
88
+
89
+
90
+ def test_stale_readings_send_zero_torque():
91
+ time = FakeTime()
92
+ clock = vmc.sim.WallClock(dt=0.01, stale=0.05, now=time.now, sleep=time.sleep)
93
+ plant, controller = Plant(time, lag=0.2), Controller(time)
94
+ log = vmc.sim.run(plant, controller, clock, T=0.05)
95
+ assert log.info["stale"] == 5 and controller.times == []
96
+ assert all(np.all(u == 0.0) for u in plant.sent)
97
+
98
+
99
+ def test_a_real_time_run_on_the_computers_clock_is_paced():
100
+ robot, ctrl = adapt.finger(), vmc.Mechanism("ctrl")
101
+ ctrl.add("hold", vmc.LinearSpring(robot.joint(slice(0, 2)), 1.0))
102
+ controller = vmc.VMCController(vmc.compile(vmc.VirtualMechanismSystem(robot, ctrl)))
103
+ clock = vmc.sim.WallClock(dt=0.005, stale=0.05)
104
+ log = vmc.sim.run(Plant(Computer()), controller, clock, T=0.1)
105
+ assert log.info["steps"] == 20 and log.info["stale"] == 0
106
+ assert 20.0 < log.info["rate"] < 300.0 # paced at 200 Hz, as far as a busy computer lets it
107
+
108
+
109
+ def test_ctrl_c_ends_a_real_time_run_with_the_log_so_far():
110
+ time = FakeTime()
111
+
112
+ class Interrupted(Controller):
113
+ def step(self, t, meas):
114
+ if len(self.times) == 5:
115
+ raise KeyboardInterrupt
116
+ return super().step(t, meas)
117
+
118
+ clock = vmc.sim.WallClock(dt=0.01, now=time.now, sleep=time.sleep)
119
+ log = vmc.sim.run(Plant(time), Interrupted(time), clock, T=None)
120
+ rows = log.arrays()
121
+ assert log.info["steps"] == 5
122
+ assert len(rows["t"]) == len(rows["motor_torque"]) == len(rows["motor_position"]) == 5
123
+
124
+
125
+ @pytest.fixture
126
+ def pythons_sigint_handler():
127
+ """SIGINT as a plain Python program has it, and whatever it was put back afterwards."""
128
+ before = signal.signal(signal.SIGINT, signal.default_int_handler)
129
+ yield
130
+ signal.signal(signal.SIGINT, before)
131
+
132
+
133
+ def test_a_ctrl_c_in_the_middle_of_a_step_ends_the_run_after_that_step(pythons_sigint_handler):
134
+ """The step is never interrupted: inside a call into C code (CasADi, numpy) the interrupt would
135
+ come out as a SystemError and the log would be lost."""
136
+
137
+ class Signalled(Controller):
138
+ def step(self, t, meas):
139
+ out = super().step(t, meas)
140
+ if len(self.times) == 5:
141
+ signal.raise_signal(signal.SIGINT) # as it arrives in the middle of the step
142
+ return out
143
+
144
+ time = FakeTime()
145
+ clock = vmc.sim.WallClock(dt=0.01, now=time.now, sleep=time.sleep)
146
+ log = vmc.sim.run(Plant(time), Signalled(time), clock, T=None)
147
+ rows = log.arrays()
148
+ assert log.info["steps"] == 5 and all(
149
+ len(v) == 5 for v in rows.values()
150
+ ) # whole steps, the fifth too
151
+ assert (
152
+ signal.getsignal(signal.SIGINT) is signal.default_int_handler
153
+ ) # and the handler is put back
154
+
155
+
156
+ def test_a_second_ctrl_c_stops_a_run_at_once(pythons_sigint_handler):
157
+ class Stuck(Controller):
158
+ def step(self, t, meas):
159
+ if len(self.times) == 2:
160
+ signal.raise_signal(signal.SIGINT)
161
+ signal.raise_signal(signal.SIGINT) # the first only asked; this one raises
162
+ return super().step(t, meas)
163
+
164
+ time = FakeTime()
165
+ clock = vmc.sim.WallClock(dt=0.01, now=time.now, sleep=time.sleep)
166
+ log = vmc.sim.run(Plant(time), Stuck(time), clock, T=None)
167
+ assert log.info["steps"] == 2 and all(len(v) == 2 for v in log.arrays().values())
168
+ assert signal.getsignal(signal.SIGINT) is signal.default_int_handler
169
+
170
+
171
+ def test_a_handler_of_the_program_and_other_threads_are_left_alone(pythons_sigint_handler):
172
+ seen = []
173
+ mine = lambda *_: seen.append(1) # noqa: E731
174
+ signal.signal(signal.SIGINT, mine)
175
+
176
+ class Looking(Controller):
177
+ def step(self, t, meas):
178
+ seen.append(signal.getsignal(signal.SIGINT) is mine)
179
+ return super().step(t, meas)
180
+
181
+ time = FakeTime()
182
+ clock = vmc.sim.WallClock(dt=0.01, now=time.now, sleep=time.sleep)
183
+ vmc.sim.run(Plant(time), Looking(time), clock, T=0.03)
184
+ assert seen == [True] * 3 and signal.getsignal(signal.SIGINT) is mine
185
+ signal.signal(signal.SIGINT, signal.default_int_handler)
186
+ done = []
187
+
188
+ def in_a_thread(): # signals can only be set on the main thread: no error, the run just goes
189
+ time = FakeTime()
190
+ clock = vmc.sim.WallClock(dt=0.01, now=time.now, sleep=time.sleep)
191
+ done.append(vmc.sim.run(Plant(time), Controller(time), clock, T=0.03).info["steps"])
192
+
193
+ thread = threading.Thread(target=in_a_thread)
194
+ thread.start()
195
+ thread.join()
196
+ assert done == [3]
197
+
198
+
199
+ def test_a_simulated_run_needs_its_duration():
200
+ with pytest.raises(ValueError, match="duration"):
201
+ vmc.sim.run(None, None, vmc.sim.SimClock(0.01), T=None)
202
+
203
+
204
+ def test_a_window_keeps_the_last_seconds_of_a_run_that_goes_on():
205
+ for steps in range(300, 313): # the run ends at every point of the trimming cycle
206
+ time = FakeTime()
207
+ clock = vmc.sim.WallClock(dt=0.01, now=time.now, sleep=time.sleep)
208
+ log = vmc.sim.run(Plant(time), Controller(time), clock, T=steps * 0.01, window=0.5)
209
+ rows = log.arrays()
210
+ t = rows["t"].ravel()
211
+ assert log.info["steps"] == steps # every step ran
212
+ assert 50 <= len(t) <= 62 # the last 0.5 s, and at most a quarter more
213
+ assert t[-1] == pytest.approx((steps - 1) * 0.01) # the newest steps are the kept ones
214
+ np.testing.assert_allclose(np.diff(t), 0.01, atol=1e-9) # no gap: the oldest rows went
215
+ assert all(len(v) == len(t) for v in rows.values()) # every signal was trimmed together
216
+ time = FakeTime()
217
+ clock = vmc.sim.WallClock(dt=0.01, now=time.now, sleep=time.sleep)
218
+ full = vmc.sim.run(Plant(time), Controller(time), clock, T=3.0)
219
+ assert len(full.arrays()["t"]) == 300 # without a window nothing is dropped
220
+
221
+
222
+ def test_ctrl_c_in_the_middle_of_a_step_of_a_windowed_run_keeps_only_whole_steps(monkeypatch):
223
+ original = vmc.sim.runlog.RunLog.step
224
+ calls = []
225
+
226
+ def interrupted(self, **values):
227
+ calls.append(1)
228
+ if len(calls) == 121: # the signal comes after the first signal of the step is logged
229
+ first, *_ = values
230
+ self.rows.setdefault(first, []).append(np.array([0.0]))
231
+ raise KeyboardInterrupt
232
+ original(self, **values)
233
+
234
+ monkeypatch.setattr(vmc.sim.runlog.RunLog, "step", interrupted)
235
+ time = FakeTime()
236
+ clock = vmc.sim.WallClock(dt=0.01, now=time.now, sleep=time.sleep)
237
+ log = vmc.sim.run(Plant(time), Controller(time), clock, T=None, window=0.5)
238
+ rows = log.arrays()
239
+ assert log.info["steps"] == 120 and 50 <= len(rows["t"]) <= 62
240
+ assert len({len(v) for v in rows.values()}) == 1 and rows["t"].ravel()[-1] == pytest.approx(
241
+ 1.19
242
+ )
243
+
244
+
245
+ def test_a_window_is_for_a_run_on_real_time():
246
+ with pytest.raises(ValueError, match="real-time"):
247
+ vmc.sim.run(None, None, vmc.sim.SimClock(0.01), T=1.0, window=0.5)
@@ -0,0 +1,58 @@
1
+ """Everything written in Python compiles on the oldest Python the library supports.
2
+
3
+ A syntax of a newer Python is the usual surprise on a student's machine (an f-string that reuses
4
+ its own quotes is legal on 3.12 and a SyntaxError on 3.10). The tests import only some modules and
5
+ the docs build runs only some snippets, so this reads them all: every ``.py`` file of the
6
+ repository and every Python code block of the Markdown pages. It fails on the 3.10 job of the CI,
7
+ which is where it matters; a newer interpreter at least catches the warnings that are going to be
8
+ errors.
9
+ """
10
+
11
+ import re
12
+ import warnings
13
+ from pathlib import Path
14
+
15
+ ROOT = Path(__file__).resolve().parent.parent
16
+ SKIP = {
17
+ ".git",
18
+ ".venv",
19
+ "venv",
20
+ "_build",
21
+ "build",
22
+ "dist",
23
+ "node_modules",
24
+ ".claude",
25
+ "__pycache__",
26
+ }
27
+ FENCE = re.compile(r"^(`{3,})\s*(\{code-cell\}\s*python|python|py)\s*$(.*?)^\1\s*$", re.S | re.M)
28
+
29
+
30
+ def sources():
31
+ for path in sorted(ROOT.rglob("*")):
32
+ if SKIP & set(path.relative_to(ROOT).parts):
33
+ continue
34
+ if path.suffix == ".py":
35
+ yield str(path.relative_to(ROOT)), path.read_text(encoding="utf-8")
36
+ elif path.suffix == ".md":
37
+ for i, block in enumerate(FENCE.finditer(path.read_text(encoding="utf-8"))):
38
+ body = block.group(3)
39
+ if block.group(2).startswith(
40
+ "{"
41
+ ): # a MyST cell: its options (:tags: ...) are not code
42
+ body = "\n".join(line for line in body.splitlines() if not line.startswith(":"))
43
+ yield f"{path.relative_to(ROOT)} (block {i + 1})", body
44
+
45
+
46
+ def test_every_python_file_and_docs_snippet_compiles_on_the_oldest_supported_python():
47
+ problems, count = [], 0
48
+ with warnings.catch_warnings():
49
+ warnings.simplefilter("error", SyntaxWarning)
50
+ warnings.simplefilter("error", DeprecationWarning)
51
+ for name, text in sources():
52
+ count += 1
53
+ try:
54
+ compile(text, name, "exec")
55
+ except (SyntaxError, SyntaxWarning, DeprecationWarning) as exc:
56
+ problems.append(f"{name}: {type(exc).__name__}: {exc}")
57
+ assert count > 400, "the repository was not found where this test expects it"
58
+ assert not problems, "\n".join(problems)