Skip to content

feat(api): save gripper jaw width to robot fs #18532

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: edge
Choose a base branch
from

Conversation

caila-marashaj
Copy link
Contributor

@caila-marashaj caila-marashaj commented Jun 4, 2025

Overview

Gripper speedup alert 💀 🚨 🚧 ⚠️

Due to mechanical tolerances in the 'grip' axis on the robot, each individual gripper needs to run a calibration routine in order to determine its max width and be able to establish position tracking. This routine has the gripper jaw open all the way until the limit switch is triggered, and then close all the way. The software assumes that the distance between the two gripper paddles is 60mm when closed. It then uses the encoder value at the position the gripper is found to be closed to calculate the max jaw width of the specific gripper that's on the robot.

The problem is that this value lives in the hardware controller, and therefore is wiped from existence any time OT3API is reset. We can reduce the number of times this routine happens by storing the gripper jaw calibration result to the robot file system and looking it up, if the gripper doesn't already have it.

Changelog

  • create a new file that will live adjacent to gripper z-calibration called gripper_jaw_width_data. This will store the encoder value at the gripper's closed position
  • add handlers to store and access this the same way that regular gripper calibration data is handled
  • in Gripper::has_jaw_width_calibration, look for data in this file, and return true + update the Gripper instance at hand if it's there

@caila-marashaj caila-marashaj force-pushed the EXEC-1491-gripper-speedups branch from 7d73c67 to cd7a2c9 Compare June 9, 2025 18:23
Copy link

codecov bot commented Jun 9, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 25.48%. Comparing base (831b80e) to head (f5cbd50).
Report is 8 commits behind head on edge.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             edge   #18532      +/-   ##
==========================================
+ Coverage   24.83%   25.48%   +0.65%     
==========================================
  Files        3274     3275       +1     
  Lines      281044   280870     -174     
  Branches    33831    33744      -87     
==========================================
+ Hits        69786    71580    +1794     
+ Misses     211237   209268    -1969     
- Partials       21       22       +1     
Flag Coverage Δ
step-generation 4.38% <ø> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

see 32 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@caila-marashaj caila-marashaj marked this pull request as ready for review June 9, 2025 18:47
@caila-marashaj caila-marashaj requested review from a team as code owners June 9, 2025 18:47
@@ -56,6 +56,7 @@ def reset_gripper(self) -> None:
self._gripper = new_gripper

async def reset(self) -> None:
# TODO(cm): do we want to reset the gripper jaw width here as well?
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Request

do we want to reset the gripper jaw width where we do the calibration data?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes but that's not here - it's in reset_instrument_offset. this reset is part of the instrument load mechanics

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To clarify, Are you asking if we should clear what's on the filesystem, or just what's in-memory? What are the implications of clearing what's in memory?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd say maybe we clean out old gripper jaw width files during instrument calibration. Would we ever want to keep more than one of these files at a time anyways? It would kind of be beneficial to keep some old ones so that if a previously used gripper gets re-installed it's old jaw width data gets reloaded, but maybe that should "always be remeasured" anyways when doing calibration?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just what's on the filesystem

Copy link
Member

@sfoster1 sfoster1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good and like a nice speedup. Let's make sure that it all works during gripper calibration, both when the calibration is the first thing you're doing after adding a new gripper (i.e. you just unboxed the gripper and have never used it on this machine before) and during a rerun.

@@ -56,6 +56,7 @@ def reset_gripper(self) -> None:
self._gripper = new_gripper

async def reset(self) -> None:
# TODO(cm): do we want to reset the gripper jaw width here as well?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes but that's not here - it's in reset_instrument_offset. this reset is part of the instrument load mechanics

@@ -191,6 +195,10 @@ def update_jaw_open_position_from_closed_position(
is closed, and then altering the logical open position so that it is whatever it needs
to be for the logical closed position to be the same as the config.
"""
if jaw_at_closed is None:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is not None?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, calling this with None is intended to allow you to reset those values

Copy link
Contributor

@SyntaxColoring SyntaxColoring left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Neat idea!

Changes requested for the combination of a couple of things:

  1. We're reusing jaw width across grippers?
  2. After jaw width is saved once, it's reused forever and never cleared or overwritten?

I just skimmed through this, so let me know if I'm misunderstanding.

@@ -56,6 +56,7 @@ def reset_gripper(self) -> None:
self._gripper = new_gripper

async def reset(self) -> None:
# TODO(cm): do we want to reset the gripper jaw width here as well?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To clarify, Are you asking if we should clear what's on the filesystem, or just what's in-memory? What are the implications of clearing what's in memory?

# Save Gripper Jaw Width Data


def save_gripper_jaw_width_data(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I can tell, because we're putting this in a new directory (gripper_jaw_width_data) instead of the existing one (gripper), we need to manually wire this up to the HTTP endpoints that clear calibration. See e.g. clear_gripper_calibration_offsets().

It seems fine to reuse the existing gripper offset reset setting for that, even though jaw width is technically a different thing from offset.

Copy link
Contributor

@vegano1 vegano1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looking good, left small comment

@SyntaxColoring SyntaxColoring dismissed their stale review June 9, 2025 19:20

Mistaken about model needing a serial number field

Copy link
Contributor

@CaseyBatten CaseyBatten left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really cool! Left a few comments regarding file management and stuff.

source=local_types.SourceType.user,
status=cal_status_model,
)
io.save_to_file(gripper_jaw_width_dir, gripper_id, gripper_jaw_width)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since gripper_id is the file name here, will we start creating many copies of this file if there are multiple grippers swapped on a robot? Might want a clean up step that removes stale or irrelevant gripper files, just for cleanliness.

@@ -56,6 +56,7 @@ def reset_gripper(self) -> None:
self._gripper = new_gripper

async def reset(self) -> None:
# TODO(cm): do we want to reset the gripper jaw width here as well?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd say maybe we clean out old gripper jaw width files during instrument calibration. Would we ever want to keep more than one of these files at a time anyways? It would kind of be beneficial to keep some old ones so that if a previously used gripper gets re-installed it's old jaw width data gets reloaded, but maybe that should "always be remeasured" anyways when doing calibration?

@SyntaxColoring

This comment was marked as resolved.

@caila-marashaj caila-marashaj changed the title refactor(api): save gripper jaw width to robot fs feat(api): save gripper jaw width to robot fs Jun 9, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants