openbricks 2.6.0__tar.gz → 2.6.2__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 (141) hide show
  1. {openbricks-2.6.0 → openbricks-2.6.2}/CHANGELOG.md +33 -0
  2. {openbricks-2.6.0/openbricks.egg-info → openbricks-2.6.2}/PKG-INFO +1 -1
  3. {openbricks-2.6.0 → openbricks-2.6.2}/native/_shared/drivebase_core.c +45 -6
  4. {openbricks-2.6.0 → openbricks-2.6.2}/native/_shared/drivebase_core.h +21 -2
  5. {openbricks-2.6.0 → openbricks-2.6.2}/native/openbricks_sim_native.c +4 -3
  6. {openbricks-2.6.0 → openbricks-2.6.2/openbricks.egg-info}/PKG-INFO +1 -1
  7. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_dev/__init__.py +1 -1
  8. {openbricks-2.6.0 → openbricks-2.6.2}/MANIFEST.in +0 -0
  9. {openbricks-2.6.0 → openbricks-2.6.2}/README.md +0 -0
  10. {openbricks-2.6.0 → openbricks-2.6.2}/examples/color_drive.py +0 -0
  11. {openbricks-2.6.0 → openbricks-2.6.2}/examples/repeat_mission.py +0 -0
  12. {openbricks-2.6.0 → openbricks-2.6.2}/examples/scored_mission.py +0 -0
  13. {openbricks-2.6.0 → openbricks-2.6.2}/examples/unified_chassis_dims.py +0 -0
  14. {openbricks-2.6.0 → openbricks-2.6.2}/examples/wall_avoid.py +0 -0
  15. {openbricks-2.6.0 → openbricks-2.6.2}/examples/wander.py +0 -0
  16. {openbricks-2.6.0 → openbricks-2.6.2}/examples/wander_hardware_style.py +0 -0
  17. {openbricks-2.6.0 → openbricks-2.6.2}/examples/wander_with_gyro.py +0 -0
  18. {openbricks-2.6.0 → openbricks-2.6.2}/examples/wro_elementary_walkthrough.py +0 -0
  19. {openbricks-2.6.0 → openbricks-2.6.2}/native/_shared/motor_process_core.c +0 -0
  20. {openbricks-2.6.0 → openbricks-2.6.2}/native/_shared/motor_process_core.h +0 -0
  21. {openbricks-2.6.0 → openbricks-2.6.2}/native/_shared/observer_core.c +0 -0
  22. {openbricks-2.6.0 → openbricks-2.6.2}/native/_shared/observer_core.h +0 -0
  23. {openbricks-2.6.0 → openbricks-2.6.2}/native/_shared/servo_core.c +0 -0
  24. {openbricks-2.6.0 → openbricks-2.6.2}/native/_shared/servo_core.h +0 -0
  25. {openbricks-2.6.0 → openbricks-2.6.2}/native/_shared/st_move_core.c +0 -0
  26. {openbricks-2.6.0 → openbricks-2.6.2}/native/_shared/st_move_core.h +0 -0
  27. {openbricks-2.6.0 → openbricks-2.6.2}/native/_shared/trajectory_core.c +0 -0
  28. {openbricks-2.6.0 → openbricks-2.6.2}/native/_shared/trajectory_core.h +0 -0
  29. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks.egg-info/SOURCES.txt +0 -0
  30. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks.egg-info/dependency_links.txt +0 -0
  31. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks.egg-info/entry_points.txt +0 -0
  32. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks.egg-info/requires.txt +0 -0
  33. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks.egg-info/top_level.txt +0 -0
  34. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_dev/__main__.py +0 -0
  35. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_dev/_docs/offline-docs.zip +0 -0
  36. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_dev/_nus.py +0 -0
  37. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_dev/_ports.py +0 -0
  38. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_dev/_signing.py +0 -0
  39. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_dev/cli.py +0 -0
  40. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_dev/docs.py +0 -0
  41. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_dev/flash.py +0 -0
  42. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_dev/log.py +0 -0
  43. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_dev/mpycompile.py +0 -0
  44. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_dev/pasteprobe.py +0 -0
  45. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_dev/run.py +0 -0
  46. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_dev/scan.py +0 -0
  47. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_dev/servo_id.py +0 -0
  48. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_dev/stop.py +0 -0
  49. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_dev/upload.py +0 -0
  50. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/__init__.py +0 -0
  51. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/__main__.py +0 -0
  52. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/chassis.py +0 -0
  53. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/cli.py +0 -0
  54. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/lego_mjcf.py +0 -0
  55. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/randomization.py +0 -0
  56. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/robot.py +0 -0
  57. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/runtime.py +0 -0
  58. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/shim.py +0 -0
  59. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/world.py +0 -0
  60. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/worlds/practice_line/world.xml +0 -0
  61. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/worlds/practice_walls/README.md +0 -0
  62. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/worlds/practice_walls/world.xml +0 -0
  63. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/worlds/practice_zones/README.md +0 -0
  64. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/worlds/practice_zones/world.xml +0 -0
  65. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/worlds/wro_2026_elementary_robot_rockstars/README.md +0 -0
  66. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/worlds/wro_2026_elementary_robot_rockstars/mat.png +0 -0
  67. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/worlds/wro_2026_elementary_robot_rockstars/props/amplifier.ldr +0 -0
  68. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/worlds/wro_2026_elementary_robot_rockstars/props/cable.ldr +0 -0
  69. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/worlds/wro_2026_elementary_robot_rockstars/props/clef.ldr +0 -0
  70. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/worlds/wro_2026_elementary_robot_rockstars/props/congas.ldr +0 -0
  71. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/worlds/wro_2026_elementary_robot_rockstars/props/guitar.ldr +0 -0
  72. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/worlds/wro_2026_elementary_robot_rockstars/props/keyboard.ldr +0 -0
  73. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/worlds/wro_2026_elementary_robot_rockstars/props/microphone.ldr +0 -0
  74. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/worlds/wro_2026_elementary_robot_rockstars/props/note_black.ldr +0 -0
  75. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/worlds/wro_2026_elementary_robot_rockstars/props/note_blue.ldr +0 -0
  76. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/worlds/wro_2026_elementary_robot_rockstars/props/note_green.ldr +0 -0
  77. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/worlds/wro_2026_elementary_robot_rockstars/props/note_red.ldr +0 -0
  78. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/worlds/wro_2026_elementary_robot_rockstars/props/note_white.ldr +0 -0
  79. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/worlds/wro_2026_elementary_robot_rockstars/props/note_yellow.ldr +0 -0
  80. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/worlds/wro_2026_elementary_robot_rockstars/props/speaker.ldr +0 -0
  81. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/worlds/wro_2026_elementary_robot_rockstars/world.xml +0 -0
  82. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/worlds/wro_2026_junior_heritage_heroes/README.md +0 -0
  83. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/worlds/wro_2026_junior_heritage_heroes/mat.png +0 -0
  84. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/worlds/wro_2026_junior_heritage_heroes/props/artefact.ldr +0 -0
  85. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/worlds/wro_2026_junior_heritage_heroes/props/barrier.ldr +0 -0
  86. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/worlds/wro_2026_junior_heritage_heroes/props/dirt.ldr +0 -0
  87. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/worlds/wro_2026_junior_heritage_heroes/props/parrot.ldr +0 -0
  88. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/worlds/wro_2026_junior_heritage_heroes/props/red_tower.ldr +0 -0
  89. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/worlds/wro_2026_junior_heritage_heroes/props/visitor.ldr +0 -0
  90. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/worlds/wro_2026_junior_heritage_heroes/props/yellow_tower.ldr +0 -0
  91. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/worlds/wro_2026_junior_heritage_heroes/world.xml +0 -0
  92. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/worlds/wro_2026_senior_mosaic_masters/README.md +0 -0
  93. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/worlds/wro_2026_senior_mosaic_masters/mat.png +0 -0
  94. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/worlds/wro_2026_senior_mosaic_masters/mosaic_frame.stl +0 -0
  95. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/worlds/wro_2026_senior_mosaic_masters/props/cement_bowl.ldr +0 -0
  96. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/worlds/wro_2026_senior_mosaic_masters/props/cement_element.ldr +0 -0
  97. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/worlds/wro_2026_senior_mosaic_masters/props/masonry_trowel.ldr +0 -0
  98. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/worlds/wro_2026_senior_mosaic_masters/props/mosaic_tile.ldr +0 -0
  99. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/worlds/wro_2026_senior_mosaic_masters/props/rect_trowel.ldr +0 -0
  100. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/worlds/wro_2026_senior_mosaic_masters/props/senior_barrier_black_red.ldr +0 -0
  101. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/worlds/wro_2026_senior_mosaic_masters/props/senior_barrier_red_blue.ldr +0 -0
  102. {openbricks-2.6.0 → openbricks-2.6.2}/openbricks_sim/worlds/wro_2026_senior_mosaic_masters/world.xml +0 -0
  103. {openbricks-2.6.0 → openbricks-2.6.2}/pyproject.toml +0 -0
  104. {openbricks-2.6.0 → openbricks-2.6.2}/setup.cfg +0 -0
  105. {openbricks-2.6.0 → openbricks-2.6.2}/setup.py +0 -0
  106. {openbricks-2.6.0 → openbricks-2.6.2}/tests/test_chassis.py +0 -0
  107. {openbricks-2.6.0 → openbricks-2.6.2}/tests/test_cibuildwheel_config.py +0 -0
  108. {openbricks-2.6.0 → openbricks-2.6.2}/tests/test_cli.py +0 -0
  109. {openbricks-2.6.0 → openbricks-2.6.2}/tests/test_color_sensor.py +0 -0
  110. {openbricks-2.6.0 → openbricks-2.6.2}/tests/test_distance_sensor.py +0 -0
  111. {openbricks-2.6.0 → openbricks-2.6.2}/tests/test_docs_cli.py +0 -0
  112. {openbricks-2.6.0 → openbricks-2.6.2}/tests/test_flash.py +0 -0
  113. {openbricks-2.6.0 → openbricks-2.6.2}/tests/test_imu.py +0 -0
  114. {openbricks-2.6.0 → openbricks-2.6.2}/tests/test_lego_mjcf.py +0 -0
  115. {openbricks-2.6.0 → openbricks-2.6.2}/tests/test_line_follow_sim.py +0 -0
  116. {openbricks-2.6.0 → openbricks-2.6.2}/tests/test_log.py +0 -0
  117. {openbricks-2.6.0 → openbricks-2.6.2}/tests/test_mpycompile.py +0 -0
  118. {openbricks-2.6.0 → openbricks-2.6.2}/tests/test_native_drivebase.py +0 -0
  119. {openbricks-2.6.0 → openbricks-2.6.2}/tests/test_native_motor_process.py +0 -0
  120. {openbricks-2.6.0 → openbricks-2.6.2}/tests/test_native_observer.py +0 -0
  121. {openbricks-2.6.0 → openbricks-2.6.2}/tests/test_native_servo.py +0 -0
  122. {openbricks-2.6.0 → openbricks-2.6.2}/tests/test_native_trajectory.py +0 -0
  123. {openbricks-2.6.0 → openbricks-2.6.2}/tests/test_no_stale_names.py +0 -0
  124. {openbricks-2.6.0 → openbricks-2.6.2}/tests/test_nus.py +0 -0
  125. {openbricks-2.6.0 → openbricks-2.6.2}/tests/test_pasteprobe.py +0 -0
  126. {openbricks-2.6.0 → openbricks-2.6.2}/tests/test_project_urls.py +0 -0
  127. {openbricks-2.6.0 → openbricks-2.6.2}/tests/test_randomization.py +0 -0
  128. {openbricks-2.6.0 → openbricks-2.6.2}/tests/test_release_tags.py +0 -0
  129. {openbricks-2.6.0 → openbricks-2.6.2}/tests/test_robot.py +0 -0
  130. {openbricks-2.6.0 → openbricks-2.6.2}/tests/test_run.py +0 -0
  131. {openbricks-2.6.0 → openbricks-2.6.2}/tests/test_runtime.py +0 -0
  132. {openbricks-2.6.0 → openbricks-2.6.2}/tests/test_scan.py +0 -0
  133. {openbricks-2.6.0 → openbricks-2.6.2}/tests/test_sdist_build.py +0 -0
  134. {openbricks-2.6.0 → openbricks-2.6.2}/tests/test_servo_id.py +0 -0
  135. {openbricks-2.6.0 → openbricks-2.6.2}/tests/test_shim.py +0 -0
  136. {openbricks-2.6.0 → openbricks-2.6.2}/tests/test_signing.py +0 -0
  137. {openbricks-2.6.0 → openbricks-2.6.2}/tests/test_sim_cli.py +0 -0
  138. {openbricks-2.6.0 → openbricks-2.6.2}/tests/test_stop.py +0 -0
  139. {openbricks-2.6.0 → openbricks-2.6.2}/tests/test_upload.py +0 -0
  140. {openbricks-2.6.0 → openbricks-2.6.2}/tests/test_wheel_bundles_worlds.py +0 -0
  141. {openbricks-2.6.0 → openbricks-2.6.2}/tests/test_world.py +0 -0
@@ -3,6 +3,39 @@
3
3
  Versions the unified `openbricks` PyPI package (CLI + MuJoCo sim).
4
4
  Firmware versions are tracked separately on the `v*` tag namespace.
5
5
 
6
+ ## 2.6.2 — done requires standstill; per-axis settle diagnostics
7
+
8
+ Bench (the twitch that survived 2.6.0, now measured): the final
9
+ curve expired with a 94-wheel-deg tracking deficit; the robot
10
+ stalled short, the landing walked it in — and done latched the
11
+ moment POSITION was close, while the landing reference still
12
+ carried speed, so the automatic then="coast" dispatch released the
13
+ wheels mid-motion: stop, lurch, abrupt stop. Two fixes:
14
+
15
+ * **pbio's standstill rule**: done additionally requires the
16
+ correction reference to be slow (<60 dps) — the missing half of
17
+ their on-target condition. Wheels are never released at speed.
18
+ * **Diagnostics**: `db_settle_stats()` now reports per-axis —
19
+ (res_sum, res_diff, landings, integ_sum_dps, integ_diff_dps) at
20
+ profile expiry — and the landing count survives the stop dispatch
21
+ (2.6.1 kept the residual but wiped the count). The next bench
22
+ read says WHICH axis lags and what the integral was supplying.
23
+
24
+ The 94-wheel-deg deficit itself (why the integral didn't absorb it
25
+ during the approach) is the open root cause the new numbers will
26
+ pin down. Both sides move: flash 2.6.2 AND `pipx upgrade
27
+ openbricks`.
28
+
29
+ ## 2.6.1 — settle stats survive the stop dispatch
30
+
31
+ `db_settle_stats()` read (0.0, 0) on every standard-path move:
32
+ `done()` dispatches `stop()` before the user's move call returns,
33
+ and the stop wiped the diagnostics with the rest of the move state
34
+ — the instrument destroyed its own evidence (bench 2026-08-17,
35
+ while chasing the end-of-run twitch). Expiry residual + landing
36
+ count now reset only when the NEXT move arms; integrals and the
37
+ landing budget still die with the move. Firmware-side: flash 2.6.1.
38
+
6
39
  ## 2.6.0 — PID + landing settle: no more end-of-move jerk
7
40
 
8
41
  Bench report: at the end of a mission the robot stopped, abruptly
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openbricks
3
- Version: 2.6.0
3
+ Version: 2.6.2
4
4
  Summary: openbricks — host-side CLI + MuJoCo-backed simulator for the openbricks MicroPython firmware. Flash, list, run, upload, log; ``openbricks sim`` for the physics simulator.
5
5
  Author: openbricks contributors
6
6
  License: MIT
@@ -61,6 +61,10 @@ void ob_drivebase_init(ob_drivebase_t *db,
61
61
  db->landing_active = false;
62
62
  db->landing_best_err = 0.0;
63
63
  db->expiry_residual = 0.0;
64
+ db->expiry_res_sum = 0.0;
65
+ db->expiry_res_diff = 0.0;
66
+ db->expiry_integ_sum_dps = 0.0;
67
+ db->expiry_integ_diff_dps = 0.0;
64
68
  db->expiry_captured = false;
65
69
  }
66
70
 
@@ -76,6 +80,10 @@ static void db_move_state_reset(ob_drivebase_t *db) {
76
80
  db->landing_active = false;
77
81
  db->landing_best_err = 0.0;
78
82
  db->expiry_residual = 0.0;
83
+ db->expiry_res_sum = 0.0;
84
+ db->expiry_res_diff = 0.0;
85
+ db->expiry_integ_sum_dps = 0.0;
86
+ db->expiry_integ_diff_dps = 0.0;
79
87
  db->expiry_captured = false;
80
88
  }
81
89
 
@@ -355,7 +363,20 @@ void ob_drivebase_stop(ob_drivebase_t *db) {
355
363
  db->turn_active = false;
356
364
  db->done = true;
357
365
  db->settling = false;
358
- db_move_state_reset(db);
366
+ // Integrals and the landing budget die with the move — but NOT
367
+ // the settle diagnostics: done() dispatches stop() before the
368
+ // user's move call even returns, so wiping expiry stats here
369
+ // made db_settle_stats() read (0.0, 0) on every standard-path
370
+ // move (bench 2026-08-17 — the diagnostic destroyed its own
371
+ // evidence). Stats reset only when the NEXT move arms.
372
+ db->integ_sum = 0.0;
373
+ db->integ_diff = 0.0;
374
+ db->landing_active = false;
375
+ db->landing_best_err = 0.0;
376
+ // db->landings is DIAGNOSTIC state alongside expiry_residual —
377
+ // it survives the stop dispatch (bench read (94.4, 0): the
378
+ // landing count wiped while the residual survived) and resets
379
+ // at the next arm.
359
380
  }
360
381
 
361
382
 
@@ -510,8 +531,16 @@ void ob_drivebase_tick(ob_drivebase_t *db, long now_ms) {
510
531
  ob_float_t de_now = db->turn_hold - diff_pos;
511
532
  se_now = (se_now < 0) ? -se_now : se_now;
512
533
  de_now = (de_now < 0) ? -de_now : de_now;
534
+ // ...and pbio's STANDSTILL condition: the landing's own
535
+ // feed-forward must be slow before done may latch, or a
536
+ // plant crossing the window at speed gets its wheels
537
+ // released mid-motion by the then= dispatch.
538
+ ob_float_t vf = (fwd_ff_vel < 0) ? -fwd_ff_vel : fwd_ff_vel;
539
+ ob_float_t vt = (turn_ff_vel < 0) ? -turn_ff_vel : turn_ff_vel;
513
540
  if (se_now < (ob_float_t)OB_DRIVEBASE_DONE_TOL_WHEEL_DEG
514
- && de_now < (ob_float_t)OB_DRIVEBASE_DONE_TOL_WHEEL_DEG) {
541
+ && de_now < (ob_float_t)OB_DRIVEBASE_DONE_TOL_WHEEL_DEG
542
+ && vf < (ob_float_t)OB_DRIVEBASE_ARRIVAL_SPEED_TOL_DPS
543
+ && vt < (ob_float_t)OB_DRIVEBASE_ARRIVAL_SPEED_TOL_DPS) {
515
544
  db->fwd_active = false;
516
545
  db->turn_active = false;
517
546
  db->landing_active = false;
@@ -573,6 +602,10 @@ void ob_drivebase_tick(ob_drivebase_t *db, long now_ms) {
573
602
  if (!db->expiry_captured) {
574
603
  db->expiry_captured = true;
575
604
  db->expiry_residual = worst;
605
+ db->expiry_res_sum = se;
606
+ db->expiry_res_diff = de;
607
+ db->expiry_integ_sum_dps = db->ki * db->integ_sum;
608
+ db->expiry_integ_diff_dps = db->ki * db->integ_diff;
576
609
  }
577
610
  // Landing settle (2.6.0): residual above arrival
578
611
  // tolerance gets a SHAPED mini-trajectory back to the
@@ -741,8 +774,14 @@ void ob_drivebase_gyro_frame_reset(ob_drivebase_t *db) {
741
774
  // took. The bench reads this to decide whether the integral gains
742
775
  // need another look — measured numbers, not guesses.
743
776
  void ob_drivebase_settle_stats(const ob_drivebase_t *db,
744
- ob_float_t *expiry_residual,
745
- int *landings) {
746
- *expiry_residual = db->expiry_residual;
747
- *landings = (int)db->landings;
777
+ ob_float_t *res_sum,
778
+ ob_float_t *res_diff,
779
+ int *landings,
780
+ ob_float_t *integ_sum_dps,
781
+ ob_float_t *integ_diff_dps) {
782
+ *res_sum = db->expiry_res_sum;
783
+ *res_diff = db->expiry_res_diff;
784
+ *landings = (int)db->landings;
785
+ *integ_sum_dps = db->expiry_integ_sum_dps;
786
+ *integ_diff_dps = db->expiry_integ_diff_dps;
748
787
  }
@@ -92,6 +92,14 @@
92
92
  // machinery is the backstop for a genuinely stuck robot.
93
93
  #define OB_DRIVEBASE_LANDING_DPS 120.0
94
94
  #define OB_DRIVEBASE_MAX_LANDINGS 3
95
+ // Arrival additionally requires the reference to be SLOW — pbio's
96
+ // standstill condition for zero-end-speed maneuvers. Without it, a
97
+ // plant that crosses the position window mid-landing (still
98
+ // carrying the landing ramp's speed) latched done at speed, and
99
+ // the automatic then="coast" dispatch released the wheels while
100
+ // they were turning: stop, lurch, abrupt stop (bench 2026-08-17,
101
+ // the twitch that survived 2.6.0).
102
+ #define OB_DRIVEBASE_ARRIVAL_SPEED_TOL_DPS 60.0
95
103
 
96
104
 
97
105
  typedef struct {
@@ -164,6 +172,14 @@ typedef struct {
164
172
  bool landing_active; // a landing profile is in flight
165
173
  ob_float_t landing_best_err; // best worst-axis err seen at a gate
166
174
  ob_float_t expiry_residual;
175
+ // Per-axis diagnostics at the same capture point: which axis
176
+ // carries the lag, and how much the integral was supplying (in
177
+ // dps) when the profile expired — the numbers that say whether
178
+ // the integral did its job during the approach.
179
+ ob_float_t expiry_res_sum;
180
+ ob_float_t expiry_res_diff;
181
+ ob_float_t expiry_integ_sum_dps;
182
+ ob_float_t expiry_integ_diff_dps;
167
183
  bool expiry_captured;
168
184
  } ob_drivebase_t;
169
185
 
@@ -255,8 +271,11 @@ ob_float_t ob_drivebase_body_to_wheel_diff(const ob_drivebase_t *db,
255
271
  // Settle diagnostics: residual at the last move's first profile
256
272
  // expiry (wheel-deg, worst axis) and shaped-landing count.
257
273
  void ob_drivebase_settle_stats(const ob_drivebase_t *db,
258
- ob_float_t *expiry_residual,
259
- int *landings);
274
+ ob_float_t *res_sum,
275
+ ob_float_t *res_diff,
276
+ int *landings,
277
+ ob_float_t *integ_sum_dps,
278
+ ob_float_t *integ_diff_dps);
260
279
 
261
280
 
262
281
  // Reset the gyro-mode absolute frame (turn_hold + override slot).
@@ -819,10 +819,11 @@ static PyObject *RawDriveBase_curve(RawDriveBaseObject *self,
819
819
 
820
820
  static PyObject *RawDriveBase_settle_stats(RawDriveBaseObject *self,
821
821
  PyObject *Py_UNUSED(ignored)) {
822
- ob_float_t r;
822
+ ob_float_t rs, rd, is_, id_;
823
823
  int n;
824
- ob_drivebase_settle_stats(&self->core, &r, &n);
825
- return Py_BuildValue("di", (double)r, n);
824
+ ob_drivebase_settle_stats(&self->core, &rs, &rd, &n, &is_, &id_);
825
+ return Py_BuildValue("ddidd", (double)rs, (double)rd, n,
826
+ (double)is_, (double)id_);
826
827
  }
827
828
 
828
829
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openbricks
3
- Version: 2.6.0
3
+ Version: 2.6.2
4
4
  Summary: openbricks — host-side CLI + MuJoCo-backed simulator for the openbricks MicroPython firmware. Flash, list, run, upload, log; ``openbricks sim`` for the physics simulator.
5
5
  Author: openbricks contributors
6
6
  License: MIT
@@ -1,4 +1,4 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  """openbricks — host-side CLI for flashing and running code on hubs."""
3
3
 
4
- __version__ = "2.6.0"
4
+ __version__ = "2.6.2"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes