Opentrons OT2 "protocol.max_speeds" Command Not Working, Known Limitation; Plans for API 2.14 and Up?

Hello,

I recently updated my Opentrons OT2s to Robot Version 6.3.1. Among other things, I was excited to be able to use the new “define_liquids” and “well.load_liquid” in API version 2.14 to make things easier for the operators of the OT2s.

I was disappointed to see that the “protocol_context.max_speeds” command is not working on API version 2.14 at this point and is a known limitation. The suggested workaround is to use “InstrumentContext.default_speed” or “the per method speed argument”. I don’t see any documentation anywhere about the second option, so not sure how to try that.

I use the protocol_context.max_speeds quite heavily in my code, specifically to control the z-Axis speed for my pipettes to ensure that i don’t bring extra liquid out with me as the pipette tips withdraw from the liquids after i dispense, and then resetting it to be faster once i have done a blowout to quickly get the tips to the trash. I also use it to slow down the pipetting speed when moving out of a liquid after aspirating as well. Begin able to modify these speeds has helped optimize the speed of my protocols and the reliability of the pipetting.

The “InstrumentContext.default_speed” would slow down the entire gantry; does that include the Z axis, or is it just the X and Y movement? The example given in the API docs shows it used with pipette.move_to ; will it only work movement type commands like “.move_to”? Or will it work if I wrap it around pipetting commands?

I.e. is what I normally do below:

        protocol_context.max_speeds['Z'] = 20 # Set Speed of Z Axis
        pipette.well_bottom_clearance.aspirate = 1
        pipette.well_bottom_clearance.dispense = 1
        pipette.mix(1, dilution_volume_1, dilution_buffer)
        pipette.well_bottom_clearance.aspirate = 1
        pipette.well_bottom_clearance.dispense = 1
        pipette.aspirate(dilution_volume_1, dilution_buffer)
        protocol_context.max_speeds['Z'] = 100 # Set Speed of Z Axis
        pipette.dispense(dilution_volume_1, target)
        protocol_context.max_speeds['Z'] = 20 # Set Speed of Z Axis
        protocol_context.delay(seconds=3)
        pipette.blow_out(target.bottom(9.5))
        pipette.touch_tip(v_offset=-5, speed=50, radius=0.5)
        protocol_context.max_speeds['Z'] = 100 # Set Speed of Z Axis

the same as below:

        pipette.default_speed = 100 = 20 # Set Speed of Z Axis
        pipette.well_bottom_clearance.aspirate = 1
        pipette.well_bottom_clearance.dispense = 1
        pipette.mix(1, dilution_volume_1, dilution_buffer)
        pipette.well_bottom_clearance.aspirate = 1
        pipette.well_bottom_clearance.dispense = 1
        pipette.aspirate(dilution_volume_1, dilution_buffer)
        pipette.default_speed = 100 # Set Speed of Z Axis
        pipette.dispense(dilution_volume_1, target)
        pipette.default_speed = 20 # Set Speed of Z Axis
        protocol_context.delay(seconds=3)
        pipette.blow_out(target.bottom(9.5))
        pipette.touch_tip(v_offset=-5, speed=50, radius=0.5)
        pipette.default_speed = 100 # Set Speed of Z Axis

If not, any ideas on how to accomplish what I am trying to do in the meantime?

What is the plan for the “protocol_context.max_speeds”? Is this being deprecated? Or is there a fix in the works? I poked around on the Opentrons GitHub but couldn’t find any current issues raised about it. If the above code will not accomplish what I am looking to do, then I won’t be able to use the new liquid commands until it is fixed.

@MeghanFerzoco , who should I tag in the future for questions like this? I’m very happy to see Opentrons officially more active on the forum!

Hey @UCantBcereus, you can tag @FLEXpert on the forum to answer these as well. For our documentation purposes, can you send this to support@opentrons.com and copy me (meghan.ferzoco@opentrons.com)? I will make sure that the answer also gets posted here (like the last question). This allows us to update our internal help center and keeps things centralized.

Thank you!

1 Like

Hi!

I am a Support Engineer at Opentrons who was discussing this issue with UCantBcereus.

Here is a review of the options with a couple of application notes:

  • If you set a default speed, it is for all motor axises.
  • If you set a default speed, you need to set it back to its default speed of 400 meters/second

Use default_speed


def aspirate_special(vol, location)
     pipette.default_speed = 50
     pipette.aspirate(vol, location)
     pipette.default_speed = 400 

In this step, I exclusively lowered the aspirate step. This can be a bit of a pain even when using “find and replace” tools to then keep resetting it to default.

I might make a custom aspirate function and call that instead

The alternative, calling it every time

If you call it every time, the code gets pretty hectic with having to reset the default speed to normal every time.

    protocol.comment('going to do it with default_speed lowering to 50 only for the aspirate')
    p300_multi.pick_up_tip()
    p300_multi.default_speed = 50
    p300_multi.aspirate(100, well_plate['A1'].bottom())
    p300_multi.default_speed = 400
    p300_multi.dispense(100, well_plate['A1'].bottom())
    p300_multi.drop_tip()
Use move_to
    p300_multi.pick_up_tip()
    protocol.comment('Here is a move_to with a speed of 20mm/sec')
    p300_multi.move_to(well_plate['A1'].bottom(), speed = 50)
    p300_multi.aspirate(100)
    p300_multi.dispense(100)
    p300_multi.drop_tip()

Stick to 2.13 and below

This is not really a “solution” so much as an option in case you don’t want to update each protocol for 2.14.

Long term

I have posted this as a feature request to our product team and we will look into it long-term.

2 Likes

Hi All,

We were finally able to test this last week.

When switching to

pipette.default_speed = 20 # Set Speed of Z Axis

from

protocol_context.max_speeds['Z'] = 20# Set Speed of Z Axis

we found that for 8 samples, this increased our run time 1.5 minutes( 9.5 minutes to 11 minutes). If we ran our Max Sample # of 48 samples, then we would have increased our run time 9 minutes. If we were processing a full 96 samples, then this would increase the run time by 18 minutes.

I’m not sure if the increased run time is worth the additional Visual Prompts you get during protocol setup, but it may be for others.

Hope this helps someone in the future!

3 Likes

@MeghanFerzoco in the time since I posted, has the “protocol_context.max_speeds” function been fixed in any of the versioned releases since 2.14? I took a look at the version history and it wasn’t clear to me if this is working yet.

If not fixed, does this only affect the OT2? Or is there another way on the Flex to only change the speed of Z movements, without sacrificing the speed for X and Y movements?

Hey @UCantBcereus - checking with our internal support team!

@UCantBcereus - here was our internal response:
Creating a custom function might make this easier to implement:
Original code:
protocol_context.max_speeds[‘Z’] = 20 # Set Speed of Z Axis
pipette.well_bottom_clearance.aspirate = 1
pipette.well_bottom_clearance.dispense = 1
pipette.mix(1, dilution_volume_1, dilution_buffer)
pipette.well_bottom_clearance.aspirate = 1
pipette.well_bottom_clearance.dispense = 1
pipette.aspirate(dilution_volume_1, dilution_buffer)
protocol_context.max_speeds[‘Z’] = 100 # Set Speed of Z Axis
pipette.dispense(dilution_volume_1, target)
protocol_context.max_speeds[‘Z’] = 20 # Set Speed of Z Axis
protocol_context.delay(seconds=3)
pipette.blow_out(target.bottom(9.5))
pipette.touch_tip(v_offset=-5, speed=50, radius=0.5)
protocol_context.max_speeds[‘Z’] = 100 # Set Speed of Z Axis
New code:
def slow_move(pip, well, z=1):
pip.move_to(well.top())
pip.default_speed = 20
pip.move_to(well.bottom(z))
pip.default_speed = 100
pipette.well_bottom_clearance.aspirate = 1
pipette.well_bottom_clearance.dispense = 1
slow_move(pipette, dilution_buffer, z=1)
pipette.mix(1, dilution_volume_1, dilution_buffer)
pipette.aspirate(dilution_volume_1, dilution_buffer)
pipette.dispense(dilution_volume_1, target)
protocol_context.delay(seconds=3)
slow_move(pipette, target, z=9.5)
pipette.blow_out()
pipette.touch_tip(v_offset=-5, speed=50, radius=0.5)

Thanks @MeghanFerzoco! I hadn’t thought of splitting the movement into moving it to the well tip, and then adjusting the default speed parameter for only the moves to and from the Z-Bottom. This is an excellent alternative, and since the speed of the Z axis will be the default faster speed until it gets to the top, it should be a little faster overall to boot.

Thanks again for the feedback!

1 Like