LH iswap close gripper

Background: I need the swapper to be in the fully extended position to access a plate outside of deck in plate reader. In position 34 below.

Currently I have able to move there using relative movements. As well as release the plate.

await lh.backend.move_iswap_x_relative(-99)
await lh.backend.iswap_open_gripper()

Unable to perform:

await lh.backend.iswap_close_gripper(grip_strength=4, plate_width=820, plate_width_tolerance=20)

Error:

Traceback (most recent call last):
  File "C:\Users\User\Documents\GitHub\lh_opt\test.py", line 270, in <module>
    asyncio.run(main())
    ~~~~~~~~~~~^^^^^^^^
  File "C:\Users\User\AppData\Local\Programs\Python\Python313\Lib\asyncio\runners.py", line 195, in run
    return runner.run(main)
           ~~~~~~~~~~^^^^^^
  File "C:\Users\User\AppData\Local\Programs\Python\Python313\Lib\asyncio\runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
  File "C:\Users\User\AppData\Local\Programs\Python\Python313\Lib\asyncio\base_events.py", line 719, in run_until_complete
    return future.result()
           ~~~~~~~~~~~~~^^
  File "C:\Users\User\Documents\GitHub\lh_opt\test.py", line 186, in main
    await lh.backend.iswap_close_gripper(grip_strength=4, plate_width=900, plate_width_tolerance=20)
  File "C:\Users\User\Documents\GitHub\lh_opt\pylabrobot\pylabrobot\liquid_handling\backends\hamilton\STAR.py", line 6390, in iswap_close_gripper
    return await self.send_command(
           ^^^^^^^^^^^^^^^^^^^^^^^^
    ...<5 lines>...
    )
    ^
  File "C:\Users\User\Documents\GitHub\lh_opt\pylabrobot\pylabrobot\liquid_handling\backends\hamilton\base.py", line 247, in send_command
    resp = await self._write_and_read_command(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    ...<5 lines>...
    )
    ^
  File "C:\Users\User\Documents\GitHub\lh_opt\pylabrobot\pylabrobot\liquid_handling\backends\hamilton\base.py", line 279, in _write_and_read_command
    result = await fut
             ^^^^^^^^^
  File "C:\Users\User\Documents\GitHub\lh_opt\pylabrobot\pylabrobot\liquid_handling\backends\hamilton\base.py", line 360, in _continuously_read   
    self.check_fw_string_error(resp)
    ~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
  File "C:\Users\User\Documents\GitHub\lh_opt\pylabrobot\pylabrobot\liquid_handling\backends\hamilton\STAR.py", line 1337, in check_fw_string_error
    raise he
pylabrobot.liquid_handling.backends.hamilton.STAR.STARFirmwareError: {'Master': CommandSyntaxError('Parameter out of range')}, C0GCid0027er01/32  

The location to place and pickup the plate from position = await lh.backend.request_iswap_position() is x=1078, y=152, z=260.

Interested in any other solutions to achieve this. I believe that the frontend pick_up_resource is unable to pickup in this extended fashion.

Thank you!

you might find the hotel versions have more reach

await lh.pick_up_plate(..., use_unsafe_hotel=True)

await lh.drop_plate(..., use_unsafe_hotel=True)

I would STRONGLY recommend testing this without the device / plate reader nearby so you develop an intuition for how this command works with no risk of breaking your hardware.

This also gives you access to the following parameters:

hotel_depth=160.0,
hotel_clearance_height=7.5,
hotel_high_speed=False,

The commands works by:

going down, forward (towards edge), down.

  1. down to level of the destination + the clearance height (so clearance height can be subtracted)
  2. hotel_depth is forward.
  3. clearance height is second down.
2 Likes

Trying to do:

await lh.pick_up_resource(plate1, offset=Coordinate(0, 0, -1), direction=GripDirection.RIGHT)

await lh.drop_resource(
        destination=mfx_car[1],
        # direction=GripDirection.LEFT, 
        use_unsafe_hotel=True,
        hotel_clearance_height=10,
        hotel_depth=5,        
        )

Same with:

await lh.drop_resource(
        destination=Coordinate(300,150,30),
        # direction=GripDirection.LEFT, 
        use_unsafe_hotel=True,
        hotel_clearance_height=10,
        hotel_depth=5,        
        )

Error:

Traceback (most recent call last):
  File "C:\Users\User\Documents\GitHub\lh_opt\test2.py", line 88, in <module>
    asyncio.run(main())
    ~~~~~~~~~~~^^^^^^^^
  File "C:\Users\User\AppData\Local\Programs\Python\Python313\Lib\asyncio\runners.py", line 195, in run
    return runner.run(main)
           ~~~~~~~~~~^^^^^^
  File "C:\Users\User\AppData\Local\Programs\Python\Python313\Lib\asyncio\runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
  File "C:\Users\User\AppData\Local\Programs\Python\Python313\Lib\asyncio\base_events.py", line 719, in run_until_complete
    return future.result()
           ~~~~~~~~~~~~~^^
  File "C:\Users\User\Documents\GitHub\lh_opt\test2.py", line 76, in main
    await lh.drop_resource(
    ...<5 lines>...
        )
  File "C:\Users\User\Documents\GitHub\lh_opt\pylabrobot\pylabrobot\liquid_handling\liquid_handler.py", line 1942, in drop_resource
    result = await self.backend.drop_resource(drop=drop, **backend_kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\User\Documents\GitHub\lh_opt\pylabrobot\pylabrobot\liquid_handling\backends\hamilton\STAR.py", line 2976, in drop_resource
    await self.unsafe.put_in_hotel(
    ...<14 lines>...
    )
  File "C:\Users\User\Documents\GitHub\lh_opt\pylabrobot\pylabrobot\liquid_handling\backends\hamilton\STAR.py", line 7965, in put_in_hotel
    return await self.star.send_command(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    ...<23 lines>...
    )
    ^
  File "C:\Users\User\Documents\GitHub\lh_opt\pylabrobot\pylabrobot\liquid_handling\backends\hamilton\base.py", line 247, in send_command
    resp = await self._write_and_read_command(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    ...<5 lines>...
    )
    ^
  File "C:\Users\User\Documents\GitHub\lh_opt\pylabrobot\pylabrobot\liquid_handling\backends\hamilton\base.py", line 279, in _write_and_read_command
    result = await fut
             ^^^^^^^^^
  File "C:\Users\User\Documents\GitHub\lh_opt\pylabrobot\pylabrobot\liquid_handling\backends\hamilton\base.py", line 360, in _continuously_read
    self.check_fw_string_error(resp)
    ~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
  File "C:\Users\User\Documents\GitHub\lh_opt\pylabrobot\pylabrobot\liquid_handling\backends\hamilton\STAR.py", line 1337, in check_fw_string_error
    raise he
pylabrobot.liquid_handling.backends.hamilton.STAR.STARFirmwareError: {'ISWAP': PositionNotReachableError('Unknown trace information code 00')}, C0PIid0009er99/00 R027/00

Any ideas on why this is? Also any idea why close gripper is not working?

is it possible that this is actually out of range? What if you change Coordinate(300,150,30) to say Coordinate(200,150,30) (plate will likely be dropped in an inconvenient location, but just to figure out the range.)

It is most likely an error with the reach, since picking up the plate uses the same width for the gripper arm when opening as dropping a plate.

Tried:

destination=Coordinate(200,150,25)
destination=Coordinate(200,150,30)

Still:

Traceback (most recent call last):
  File "C:\Users\User\Documents\GitHub\lh_opt\test2.py", line 96, in <module>
    asyncio.run(main())
    ~~~~~~~~~~~^^^^^^^^
  File "C:\Users\User\AppData\Local\Programs\Python\Python313\Lib\asyncio\runners.py", line 195, in run
    return runner.run(main)
           ~~~~~~~~~~^^^^^^
  File "C:\Users\User\AppData\Local\Programs\Python\Python313\Lib\asyncio\runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
  File "C:\Users\User\AppData\Local\Programs\Python\Python313\Lib\asyncio\base_events.py", line 719, in run_until_complete
    return future.result()
           ~~~~~~~~~~~~~^^
  File "C:\Users\User\Documents\GitHub\lh_opt\test2.py", line 76, in main
    await lh.drop_resource(
    ...<6 lines>...
        )
  File "C:\Users\User\Documents\GitHub\lh_opt\pylabrobot\pylabrobot\liquid_handling\liquid_handler.py", line 1942, in drop_resource
    result = await self.backend.drop_resource(drop=drop, **backend_kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\User\Documents\GitHub\lh_opt\pylabrobot\pylabrobot\liquid_handling\backends\hamilton\STAR.py", line 2976, in drop_resource
    await self.unsafe.put_in_hotel(
    ...<14 lines>...
    )
  File "C:\Users\User\Documents\GitHub\lh_opt\pylabrobot\pylabrobot\liquid_handling\backends\hamilton\STAR.py", line 7965, in put_in_hotel
    return await self.star.send_command(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    ...<23 lines>...
    )
    ^
  File "C:\Users\User\Documents\GitHub\lh_opt\pylabrobot\pylabrobot\liquid_handling\backends\hamilton\base.py", line 247, in send_command
    resp = await self._write_and_read_command(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    ...<5 lines>...
    )
    ^
  File "C:\Users\User\Documents\GitHub\lh_opt\pylabrobot\pylabrobot\liquid_handling\backends\hamilton\base.py", line 279, in _write_and_read_command
    result = await fut
             ^^^^^^^^^
  File "C:\Users\User\Documents\GitHub\lh_opt\pylabrobot\pylabrobot\liquid_handling\backends\hamilton\base.py", line 360, in _continuously_read
    self.check_fw_string_error(resp)
    ~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
  File "C:\Users\User\Documents\GitHub\lh_opt\pylabrobot\pylabrobot\liquid_handling\backends\hamilton\STAR.py", line 1337, in check_fw_string_error
    raise he
pylabrobot.liquid_handling.backends.hamilton.STAR.STARFirmwareError: {'ISWAP': PositionNotReachableError('Unknown trace information code 00')}, C0PIid0008er99/00 R027/00

Thought it could be unit issue so tried:

destination=Coordinate(2000,1500,270)

Got:

Traceback (most recent call last):
  File "C:\Users\User\Documents\GitHub\lh_opt\test2.py", line 96, in <module>
    asyncio.run(main())
    ~~~~~~~~~~~^^^^^^^^
  File "C:\Users\User\AppData\Local\Programs\Python\Python313\Lib\asyncio\runners.py", line 195, in run
    return runner.run(main)
           ~~~~~~~~~~^^^^^^
  File "C:\Users\User\AppData\Local\Programs\Python\Python313\Lib\asyncio\runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
  File "C:\Users\User\AppData\Local\Programs\Python\Python313\Lib\asyncio\base_events.py", line 719, in run_until_complete
    return future.result()
           ~~~~~~~~~~~~~^^
  File "C:\Users\User\Documents\GitHub\lh_opt\test2.py", line 76, in main
    await lh.drop_resource(
    ...<6 lines>...
        )
  File "C:\Users\User\Documents\GitHub\lh_opt\pylabrobot\pylabrobot\liquid_handling\liquid_handler.py", line 1942, in drop_resource
    result = await self.backend.drop_resource(drop=drop, **backend_kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\User\Documents\GitHub\lh_opt\pylabrobot\pylabrobot\liquid_handling\backends\hamilton\STAR.py", line 2976, in drop_resource
    await self.unsafe.put_in_hotel(
    ...<14 lines>...
    )
  File "C:\Users\User\Documents\GitHub\lh_opt\pylabrobot\pylabrobot\liquid_handling\backends\hamilton\STAR.py", line 7957, in put_in_hotel
    assert 0 <= hotel_center_y_coord <= 6_500
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError

Don’t really understand why it would fail this assertion

in the second case: destination=Coordinate(2000,1500,270): PLR units are always in mm, uL, seconds, mg. This causes an assertion error because it is saying 2000mm (=2m) to the right of the robot’s origin, which is obviously out of range even on a STAR.

Apparently, we don’t have the range in the assertion tight enough because in the first case the firmware still throws an error (assuming it is due to destination being of range). We should limit the assertion to the actual bounds, not the maximum value the firmware will accept (that is the current assertion). This is so we can throw an error without having to send a command to the machine.

On closer inspection, the z-height might be too low. When passing a Coordinate, that is a coordinate wrt the deck origin. The deck origin is actually 100mm below the deck. Could you try z=220, or something in that range? x=200, y=150 seems fine to me.

If that doesn’t work, could you try dropping the plate onto its pickup location as a sanity check? Eg lh.drop_plate(plate_carrier[0]).

This does work, doing the extension and all:

    await lh.drop_resource(
        destination=mfx_car2[1],
        direction=GripDirection.LEFT, 
        use_unsafe_hotel=True,
        hotel_clearance_height=10,
        hotel_depth=10,        
        )

Tried with variying depth, 200 will have the arm fully extended as in position 34. 500 give PositionNotReachableError.

After trying different coordinates got this to work:

    await lh.drop_resource(
        # destination=mfx_car2[1],
        destination=Coordinate(400,170,250),
        direction=GripDirection.LEFT, 
        use_unsafe_hotel=True,
        hotel_clearance_height=10,
        hotel_depth=10,        
        )

Position of the dropped plate doesn’t make sense though.

destination=Coordinate(700,200,250)

Doesn’t really align with the coordinate from the summary

Any ideas with this coordinate system? Is it because the plate is rotated 180 degrees?

great!

rotations are a bit tricky: the location always refers to the left front bottom (LFB) of the plate (in its own “plate space”), regardless of rotation. Thinking about the resource when it is freshly instantiated, where is its origin? Imagine a dot fixed to that location. That dot is going to the Coordinate specified in lh.drop_resource.

(background: one might wonder, why not the lfb of the plate in deck space? (meaning: left of the plate as viewed from the deck). The reason is this is the only unambiguous way to specify this functionality. What if you dropped a plate with a 45 degree rotation. the it has a left point but that point is no longer in the front. Second, it is also important because some plates are not symmetrical, certainly when thinking about samples in the wells. If we automatically resolved symmetries, it is not explicit that plates are flipped.)

For resources rotated a multiple of 90 degrees, there is a utility function get_child_location to compute the distance between the LFB of the plate (imaginary dot) and LFB as seen from the deck. For example, for a plate rotated by 180 degrees the LFB in absolute space would be the right-back-bottom (RBB) in deck-space. This function is useful when dropping rotate resources on a destination resource holder, like a tray for a plate holder because it ensures the center of the plate is always in the same spot, utilizing its 180-degree symmetry.

Usage example:

from pylabrobot.resources.resource_holder import get_child_location

lfb_of_destination = Coordinate(...)
rotation_applied_by_move = 180
destination_location = lfb_of_destination + \
  get_child_location(resource.rotated(z=rotation_applied_by_move))

await lh.drop_resource(destination=destination_location)

But really I would suggest just moving it to a destination ResourceHolder/PlateHolder so PLR does this automatically.

await lh.drop_resource(destination=some_plate_holder)
1 Like
    await lh.drop_resource(
        destination=Coordinate(1092,195,248),
        direction=GripDirection.LEFT)

The above also seems to be working now. Quite lost. But it works.

Thanks for all the help.

2 Likes

this is unfortunate, what is confusing?

My own problem. Just needed to understand how the functions work and what they expect.

2 Likes

actually at least partially my problem, since my job is to make a nice api that is not confusing. whether that’s education or detarding the code

2 Likes

I too have trouble intuiting iSWAP commands, even after using PLR for a couple years. Rotations can get very confusing. With your feedback rick can make it easier for you, which will make PLR better for everyone

1 Like

I agree. PLR is already amazing and so much easier to use than the default software.

With regards to iSwap, I think some more documentation around coordinates would be nice. However, I do see that my use case is somewhat unique. The default behaviour of iSwap works great.

1 Like