Environment
- Device: OpenScan Mini (greenshield), camera IMX519
- Firmware: 0.11.4
- Raspberry Pi, software GPIO stepping (gpiozero / RPi.GPIO), open-loop, no endstop
Symptom
During a scan the steppers grind/growl and drop steps. Because the rig is open-loop with no endstop, the lost steps make the firmware's position estimate drift by several degrees, so homing ends up off and the soft limits no longer reliably protect the mechanism (risk of over-travelling the range). The motion is smooth when the device is idle — the roughness only appears under the CPU load of a running scan. That's the tell that it's load-related, not an acceleration/max-speed setting (changing those makes no audible difference at idle).
Root cause
In ScanTask._capture_photos_at_position (openscan_firmware/controllers/services/tasks/core/scan_task.py) each photo is saved fire-and-forget — both the single-photo path and the focus-stacking path:
asyncio.create_task(self._ctx.project_manager.add_photo_async(photo_data))
await asyncio.sleep(0)
The loop never awaits that task, so it advances to the next await motors.move_to_point(...) while the previous photo's save is still running. add_photo_async does the JPEG write + metadata + _recalculate_and_save_scan_size (an os.walk over the scan directory that grows with photo count), hopping into the default ThreadPoolExecutor. The motor move's stepping, meanwhile, is a software time.sleep-timed GPIO bit-bang running in MotorController._executor. The two thread pools contend for the CPU, the step-pulse intervals overrun, and steps are dropped. Open-loop + no endstop ⇒ the position estimate drifts.
Impact
- Audible rough/grinding scan motion
- Dropped steps → open-loop position drift (several degrees)
- Homing ends up off by several degrees
- On endstop-less rigs, risk of driving past the intended range
Suggested fix
Await the save instead of detaching it, so each point is strictly move → capture → save → next move and the motor only ever steps while the CPU is otherwise idle. (pause/cancel are still checked at the top of the loop, so dropping the await asyncio.sleep(0) costs nothing.)
Verification
Patched both capture branches on a Mini (greenshield, IMX519, firmware 0.11.4): the audible growl during scans is gone, scans complete cleanly, and homing holds across repeated runs.
🤖 Filed with Claude Code
Environment
Symptom
During a scan the steppers grind/growl and drop steps. Because the rig is open-loop with no endstop, the lost steps make the firmware's position estimate drift by several degrees, so homing ends up off and the soft limits no longer reliably protect the mechanism (risk of over-travelling the range). The motion is smooth when the device is idle — the roughness only appears under the CPU load of a running scan. That's the tell that it's load-related, not an acceleration/max-speed setting (changing those makes no audible difference at idle).
Root cause
In
ScanTask._capture_photos_at_position(openscan_firmware/controllers/services/tasks/core/scan_task.py) each photo is saved fire-and-forget — both the single-photo path and the focus-stacking path:The loop never awaits that task, so it advances to the next
await motors.move_to_point(...)while the previous photo's save is still running.add_photo_asyncdoes the JPEG write + metadata +_recalculate_and_save_scan_size(anos.walkover the scan directory that grows with photo count), hopping into the defaultThreadPoolExecutor. The motor move's stepping, meanwhile, is a softwaretime.sleep-timed GPIO bit-bang running inMotorController._executor. The two thread pools contend for the CPU, the step-pulse intervals overrun, and steps are dropped. Open-loop + no endstop ⇒ the position estimate drifts.Impact
Suggested fix
Await the save instead of detaching it, so each point is strictly
move → capture → save → next moveand the motor only ever steps while the CPU is otherwise idle. (pause/cancelare still checked at the top of the loop, so dropping theawait asyncio.sleep(0)costs nothing.)Verification
Patched both capture branches on a Mini (greenshield, IMX519, firmware 0.11.4): the audible growl during scans is gone, scans complete cleanly, and homing holds across repeated runs.
🤖 Filed with Claude Code