Agilent VSpin centrifuge quickstart#
The Agilent VSpin is a two-bucket microplate centrifuge. This quickstart connects directly to the centrifuge, calibrates its bucket positions, runs one balanced spin, and disconnects. The optional Access2 plate loader settings are covered at the end.
Property |
Value |
|---|---|
Communication |
USB through an FTDI interface |
Capacity |
Two opposing microplates |
Relative centrifugal force |
1–1000 × g (driver range) |
Time at speed |
At least 1 second |
Acceleration and deceleration |
Fractions of maximum, greater than 0 and at most 1 |
Warning
Follow the centrifuge manufacturer’s installation, plate-compatibility, balancing, and safety
instructions. Before every run, use two opposing loads of equal mass and make sure both are fully
seated. stop_spin() provides a controlled software abort, but always keep the instrument’s
physical controls accessible.
How it communicates#
PyLabRobot talks to the VSpin controller over its USB FTDI interface. The asynchronous methods below initialize the controller, send its binary commands, and poll the instrument until each movement or spin has completed.
Install the FTDI dependencies#
Install PyLabRobot with its FTDI dependencies in the notebook’s Python environment.
%pip install "pylabrobot[ftdi]"
Physical setup#
Install and power the VSpin according to the manufacturer’s instructions, then connect its USB cable to the computer. Keep the rotor, door, and surrounding workspace clear while connecting.
Find the FTDI serial number:
python -m pylibftdi.examples.list_devices
Use the reported serial number as device_id below. An explicit ID selects the correct device
when several FTDI devices are attached and lets PyLabRobot reload this centrifuge’s saved bucket
calibration.
Connect#
Create the centrifuge and call setup(). Setup opens the USB connection and initializes and homes
the controller, so keep the machine clear while this cell runs.
from pylabrobot.agilent.vspin import VSpin
vspin = VSpin(name="vspin", device_id="YOUR_FTDI_SERIAL")
await vspin.setup()
Check the interlocks#
Read the door and bucket-lock sensors before loading anything.
print("Door open:", await vspin.request_door_open())
print("Door locked:", await vspin.request_door_locked())
print("Bucket locked:", await vspin.request_bucket_locked())
Calibrate bucket 1 on first use#
PyLabRobot needs one reference position to return either bucket to the loading opening after a spin. Skip this section if this VSpin has already been calibrated on this computer.
First, open the door after setup has completed its homing motion.
await vspin.open_door()
Confirm that a bucket is centered at the loading opening and designate it as bucket 1. If no bucket is centered, do not force the rotor by hand; use the manufacturer’s approved alignment procedure.
Run the next cell only while bucket 1 is centered. The calibration is saved for this FTDI serial
number in ~/.pylabrobot/vspin_bucket_calibrations.json, so it does not need to be repeated unless
the rotor alignment changes.
await vspin.set_bucket_1_position_to_current()
Move bucket 1 to the loading opening#
go_to_bucket1() closes and locks the door, rotates to the calibrated position, and opens the
door when the bucket is ready to access.
await vspin.go_to_bucket1()
Load the sample plate#
Place the sealed sample plate fully into bucket 1.
Move bucket 2 to the loading opening#
Move the opposing bucket into position before loading the balance plate.
await vspin.go_to_bucket2()
Load the balance plate#
Prepare an opposing load with the same total mass as the sample plate and place it fully into bucket 2. Matching the plate type alone does not guarantee that the physical loads are balanced; verify their total masses.
Run a spin cycle#
spin() closes and locks the door, accelerates to the requested relative centrifugal force,
holds it for duration seconds, then decelerates. acceleration and deceleration are fractions
of the device’s maximum rates. The call returns after the run has completed.
Start with settings appropriate for your plates and protocol. This example runs at 500 × g for 60 seconds.
await vspin.spin(
g=500,
duration=60,
acceleration=0.8,
deceleration=0.8,
)
Abort an active spin safely#
If another task needs to abort a running spin() call, use stop_spin(). It sends a controlled
zero-velocity trajectory and returns only after the tachometer confirms that the rotor stopped.
The physical emergency stop remains the authority for an emergency.
await vspin.stop_spin(deceleration=0.8)
Return to bucket 1#
After a spin, the rotor is no longer parked at a known bucket. Return bucket 1 to the loading opening before unloading it.
await vspin.go_to_bucket1()
Unload the sample plate#
Remove the sample plate from bucket 1.
Return to bucket 2#
Move the balance plate back to the loading opening.
await vspin.go_to_bucket2()
Unload the balance plate#
Remove the balance plate from bucket 2.
Disconnect#
stop() returns the controller to its initialized state and closes the USB connection. It is a
teardown method, not an emergency stop for a spin in progress.
await vspin.stop()
Optional Access2 loader parameters#
Use these examples before disconnecting, with an initialized Access2 loader named loader
paired with the connected, homed vspin.
Before load(), present an empty bucket and place and assign the plate on the loader stage;
before unload(), present the occupied bucket and leave the loader stage empty. See the
state-machine guide for transfer preconditions.
Pass plate-specific settings to each load() or unload() call. They are not stored on
the loader, and changing a plate resource’s dimensions does not change the motion settings.
All values below are in millimeters:
Parameter |
|
|
|---|---|---|
|
10 |
10 |
|
3 |
3 |
|
3 |
3 |
|
3 |
0 |
|
0 |
0 |
|
5.68 |
5.68 |
|
1.5 |
1.5 |
source_z_offset applies at pickup and destination_z_offset at placement: the source
is the loader stage for load() and the presented bucket for unload(). plate_height
is passed to the controller for all three teachpoint moves, including return to park.
Gripper positions are absolute axis coordinates, not plate widths or jaw gaps.
The gripper can stop on plate contact before its closed target; acceptance requires
motion completion, the close threshold, and plate detection.
For example, explicitly passing the default settings for a load looks like:
await loader.load(
plate_height=10,
source_z_offset=3,
destination_z_offset=3,
park_z_offset=3,
gripper_open_position=0,
gripper_closed_position=5.68,
gripper_close_threshold=1.5,
)
Use settings established for the plate and teachpoints in your setup. Every value must
be finite, height must be positive, and gripper settings must satisfy
gripper_open_position < gripper_close_threshold <= gripper_closed_position.
Invalid settings are rejected before loader actuation.
Each transfer movement also accepts a speed preset: "slow", "medium", or "fast".
These are controller presets, not velocities in millimeters per second.
Parameter |
Movement |
Default for both directions |
|---|---|---|
|
Approach the pickup teachpoint |
|
|
Carry the plate to placement |
|
|
Return to park after release |
|
|
Open before pickup |
|
|
Close on the plate |
|
|
Open to release the plate |
|
Standalone loader movements follow the same per-call pattern:
await loader.driver.park(plate_height=15, z_offset=8, speed="slow")
await loader.driver.open_gripper(gripper_open_position=0, speed="slow")
await loader.driver.close_gripper(
gripper_closed_position=5.68, gripper_close_threshold=1.5, speed="slow"
)
The examples show method defaults. Standalone park() has its own height and offset
defaults; it does not reuse the preceding transfer’s settings. Speed presets are
validated before actuation, including movements scheduled later in a transfer.