Opentrons / opentrons

Software for writing protocols and running them on the Opentrons Flex and Opentrons OT-2
https://opentrons.com
Apache License 2.0
414 stars 177 forks source link

bug: opentrons_execute and set_offsets not setting the correct offsets. #11931

Open fcalzi opened 1 year ago

fcalzi commented 1 year ago

Overview

Hello there!

We are currently facing an issue (similar to 11484) when executing a protocol from the command line with opentrons_execute. We are calibrating the robot with the opentrons app and then we are setting the offsets to the values displayed in the protocol using the set_offset function. The set_offset function is being called, however it seems that the values identified by the opentrons app (which work fine when running the protocol from the opentrons app) do not work when running the protocol from the command line. We need to further adjust these offsets using the set_offset function to properly execute the protocol.

Steps to reproduce

Step 1. Calibrate labware offsets with Opentrons App Step 2. Execute a protocol from command line by setting the offsets with the set_offset function

Current behavior

The robot is not moving to the location as the one calibrated with the OT App

Expected behavior

The robot shall be moving to the labware location originally calibrated with the OT App

Operating system

Linux

System and robot setup or anything else?

App Version: 6.2 Connection: Wi-Fi

fcalzi commented 1 year ago

LabwareCalibration

mcous commented 1 year ago

@fcalzi how are invoking ssh / opentrons_execute?

fcalzi commented 1 year ago

@fcalzi how are invoking ssh / opentrons_execute?

@mcous we are following the guidelines from https://docs.opentrons.com/v2/new_advanced_running.html (Command Line section)

Hence, we are connecting to the opentrons host (the raspberry pi) by creating a ssh connection from a linux shell. Once connected to the host, and once copied the protocols using scp, we are calling $ opentrons_execute protocol.py The protocol runs, but the labware offsets provided by the opentrons app, which we are setting in the protocol by using the set_offsets function are not correct and we have to further adjust these values.

mcous commented 1 year ago

Thanks, that's helpful context! By any chance were you running this flow successfully on a prior 6.x release, before noticing the issue in 6.2?

fcalzi commented 1 year ago

@mcous short answer is no. In fact we only started running protocols from the command line on 6.1 / 6.2 releases and in the past 2 ~ 3 weeks. Prior raising this bug, we had first to figure out that the issue highlighted in 11484 (Missing deck_calibration.json and robot_settings.json) wasn't really an issue for us and then we had to figure out that we needed to invoke the set_offsets function from the protocols. I can confirm that set_offsets effectively changes the offset of the labwares, however the correct values to apply are different from the one provided by the opentrons app highlighted above.

mcous commented 1 year ago

@fcalzi I was unable to reproduce any errors with this system on my end; values from the app and values in set_offset result in movements to the same location in my testing. Perhaps something more exotic is going on with your particular setup?

It looks like you are using non-Opentrons labware in your protocol. Are you able to provide me with copies of those definitions and the setup phase of your protocol so I can try to reproduce here?

Additionally, how are you providing the labware definitions to the robot in your SSH flow? Is it possible that the definitions loaded into the Opentrons App (which will be the definitions used during LPC) are different than whatever definitions are present on the OT-2 (which will be used during opentrons_execute)?

fcalzi commented 1 year ago

@mcous in our protocols we have a list of labware used, we iterate through them and we load them one by one from files stored in a folder on the Raspberry Pi. We do also store the calibrated offsets in a separate file (labware_offsets.json), I can confirm that the offset values are loaded correctly and applied to the labware (I even print the values once loaded). Given this workflow it does not appear that the definitions used during calibration are different from the ones used during opentrons_execute

labware_offsets.json

[
  {
    "Labware": "cardea_bio_gr1",
    "Dx": 1.20,
    "Dy": 0.1,
    "Dz": -0.6
  },
  {
    "Labware": "cardea_bio_gr2",
    "Dx": 0.9,
    "Dy": 0.5,
    "Dz": 0
  }
]

LabwarePath='C:\cardea\labware'
LabwareOffsetFile='C:\cardea\calibrations\labware_offsets.json'

labware_list = [
  {
    "name": "cardea_bio_gr2",
    "block": 1,
    "type": "reader related"
  },
  {
    "name": "opentrons_24_tuberack_generic_2ml_screwcap1",
    "block": 3,
    "type": "tube rack"
  },
  {
    "name": "cardea_bio_gr1",
    "block": 7,
    "type": "reader related"
  },
  {
    "name": "opentrons_96_filtertiprack_200ul",
    "block": 8,
    "type": "tip rack"
  },
  {
    "name": "opentrons_10_tuberack_falcon_4x50ml_6x15ml_conical",
    "block": 11,
    "type": "tube rack"
  }
]

     def __init__(self, protocol: protocol_api.ProtocolContext):
        for labware in labware_list:
            lw = load_labware(protocol, labware["name"], labware["block"])

def load_labware(protocol: protocol_api.ProtocolContext, labwarename, block):
    labwarefilepath=os.path.join(LabwarePath, labwarename+".json")
    if os.path.exists(labwarefilepath):
        with open(labwarefilepath) as labware_file:
            labware_def = json.load(labware_file)
        lw = protocol.load_labware_from_definition(labware_def, block)
        offsets=get_offsets(labwarename)
        if offsets!=None:
            **print(offsets)**
            lw.set_offset(x=offsets["Dx"], y=offsets["Dy"], z=offsets["Dz"])
        return lw
    else:
        return protocol.load_labware(labwarename, block)

def get_offsets(labware_name):
    f = open(LabwareOffsetFile)
    offsets = json.load(f)
    for offset in offsets:
        if offset['Labware']==labware_name:
           f.close()
           return offset
    f.close()
    return None
mcous commented 1 year ago

Are you certain the labware definitions stored on your local machine (it looks like in C:\cardea\labware) are the same definitions as are stored on the Raspberry Pi? Is there any way one could have been updated while the other was not? I ask mostly to try to rule out the simple explanations.

Something you could do to test this would be to use hashlib and print or write the md5 hash (or similar) of every labware definition to compare what happens on your local machine vs what happens on your robot.

While debugging, you could also use an internal Opentrons function to produce a labware definition hash. Do not rely on this function during any actual workflow; it's internal and may be changed by a software update:

from opentrons.calibration_storage.helpers import hash_labware_def

...

with open(labwarefilepath) as labware_file:
    labware_def = json.load(labware_file)

labware_hash = hash_labware_def(labware_def)

...