Hi everyone,
This is a big one:
The internal Swivel Arm Plate Handler (iSWAP) is one of Hamilton STAR(let)'s most powerful device Capabilities.
It enables…
- movement of resources with force-sensed feedback for secure pickup, transfer and placement
- movement to off-deck sites due to its swivel/SCARA design → ~90 mm out of the main chassis to the left, and ~20 mm out to the right
- instantly enables horizontal integration of other devices into a workcell, i.e. without the need for additional arms
Depending on the model and firmware version it can pick up plates in either just portrait mode or portrait and landscape mode.
However, even in the OEM software the iSWAP is infamous for being a bit difficult to use.
Since we have total control over the devices we own with PyLabRobot and like to make the difficult parts of our automation life easy, I started an epic (long-term project with multiple sub-components) to make the iSWAP smarter and easier to use.
I call this epic “Tame the iSWAP”.
In this thread I would like to showcase some of the current issues with the iSWAP and how we are openly developing it into a more advanced resource transfer system:
Background: iSWAP Geometry
Fundamentally the iSWAP has 3 components:
- A Cartesian robot - moves the base/rotation drive/θ₁ of the iSWAP in…
- x` (via the X-arm motor),
- `y` (via the iSWAP channel y-motor), and
- `z` (via the iSWAP channel z-motor).
- A SCARA robot
- 2 joints - representing…
- the “rotation drive” θ₁
- the “wrist drive” θ₂
and
- 2 linkages
- linkage_1: rotation drive ↔ wrist drive
- linkage_2: wrist drive ↔ gripper center
- 2 joints - representing…
- A Gripper with a linear motor
**More details on the SCARA parts**:
Each joint has its own specific range of motion and associated with their specific values an Orientation *with regard to* (`wrt`) the STAR coordinate system:
-
Joint 1: θ₁ has 3 “standard” yaw angles (Orientation around the Z-axis)
- -90° / ~ -29_000 increments → LEFT (`wrt` the STAR)
- 0° / ~ 0 increments → FRONT
- 90° / ~ 29_000 increments → RIGHT
-
Joint 2: θ₂ has 4 “standard” yaw angles (Orientation around the Z-axis)
- -135° / ≈ -26_577 increments → RIGHT
- -45° / ≈ -8_860 increments → STRAIGHT
- +45° / ≈ +9_044 increments → LEFT
- +135° / ≈ +26_858 increments → REVERSE
This means there are 3 x 4 = 12 “standard” iSWAP-SCARA poses.
Moving from any given pose to any other therefore gives 12 x 11 = 132 nontrivial standard movement patterns.
Note: there is no reason why the hardware-firmware would not be able to move to any “non-standard” pose, i.e. arbitrary joint configurations (e.g. θ₁=-20°,θ₂=100°).
Current Issues
What is the problem?
The standard commands we currently have in PLR to use the iSWAP are based on the `C0` command module.
It is too (inconsistently) smart
When using our current `.iswap_get_plate()` / `C0PP` or `.move_resource()` / `C0PM` commands we only tell the commands the “grip direction” the gripper should have with regard to (`wrt`) the STAR’s entire coordinate system:
i.e. when saying `GripperDirection=Back` the gripper fingers point to the front, and the gripper is positioned behind the resource being picked up.
But … what is the angle of the two joints? ![]()
With `GripperDirection=Back` there are 3 different joint configurations which can achieve this GripDirection:
- θ₁=LEFT, θ₂=LEFT
- θ₁=FRONT, θ₂=STRAIGHT
- θ₁=RIGHT, θ₂=RIGHT
Which one is going to be chosen? … we don’t know - because the firmware makes this decision (with these ‘smart’ commands).
But the firmware has no deck awareness which means it might decide to orient the joints in a manner that crashes into another resource (e.g. a tip carrier next to our plate)… and we have no knowledge of when and why it would happen.
i.e. the currently exposed iSWAP movement commands are a black-box decision maker.
(Note: some movements are only possible in a specific joint configuration. e.g.: moving out of the main chassis to either side requires the linkages to straighten completely in that direction.
As a result, and confusingly, some commands do behave as we expect, clouding expected behaviour with position-dependency)
Firmware doesn’t cover all safety/risk scenarios
PLR already exposed some low-level commands to control some motors of the iSWAP assembly:
e.g. we can rotate joint_1 and joint_2 independently.
However, these commands do not perform a check whether…
- all channels are out of the way (i.e. are they out of x-y reach for the upcoming iSWAP movement → side-crash risk), nor
- out of z-height (i.e. whether the channels are raised so the iSWAP can move below → side-crash risk), nor
- the channels are out of z-height but have tips mounted which bring the tips into the path of the iSWAP movement → channel stop-disk beheading risk
That is dangerous.
Potential Firmware Issues
It appears that everyone I know uses an iSWAP control board from 2012 - not an issue by itself (and the iSWAP is being replaced by the Internal Plate Gripper (IPG) in current STAR releases) but iSWAP-equipped STARs will be in service for years and I found some issues at the firmware level:
As part of the “Tame the iSWAP” project, I looked carefully into the joint angles of each movement.
When parked/just initialised → the iSWAP grip center position returned by the firmware doesn’t match the actual grip center:
This shows that the parking position overwrites the firmware return value for the iSWAP grip center to the center_x-center_y position of the joint_1 / rotation drive.
-> That is off by 32.8 mm in x and 138 mm in y ![]()
Interestingly, iSWAP movement commands in `x`, `y`, or combined joint rotation do *not* update the iSWAP grip center either!
This looks like it could be a firmware bug, though I can’t rule out that I’m missing context.
It indicates a crucial find:
`STARBackend.request_iswap_position()` / `C0QG` does *not* appear to perform a forward kinematics calculation of where the grip center is based on queried motor encoder state.
It seems likely to me that only certain firmware commands actually update an internal tracker of the state of the iSWAP instead.
For any form of more complex/advanced iSWAP behaviour we must have a command that reliably returns the actual grip center, no matter what commands have been executed beforehand.
Why do I say that only certain commands appear to update the firmware-internal tracker system:
From the lowest-level commands, only single angle rotations instantly update the tracker to the correct position.
i.e. the firmware commands inconsistently update the internal tracker.
Should we inform Hamilton directly? Considerations:
- OEMs don’t like know-it-alls

- Lab automation OEMs do not provide an incentive to do so (no bug bounties in this industry)
- Maybe we are wrong / don’t know the full picture, maybe they are aware and don’t consider it obstructive to most users’ needs
- It appears this bug has been around for 14 years … it might be that nobody has detected it because the thoroughness of PyLabRobot has not existed for most of that time period, or if detected a cost/benefit analysis might have concluded not to update but instead to build a new “Internal Plate Gripper” instead
…these are all speculations; please let me know what you think is happening here
What is happening?
Via a series of PRs I’ve created a plan to engineer us out of this dilemma:
- Expose all position request commands, for all reference points.
- Replace (presumably) broken firmware `park_iswap` with PLR-based transparent primitive and update `setup_iswap` with it.
- Build forward kinematics model to always calculate the true position of the grip center.
- Add advanced safety features
- Build new commands from primitives which have well-defined default joint behaviour (e.g. always right-back) where possible, but log a warning if user hasn’t specified 2 intuitive parameters: joint_1 orientation AND grip direction (*both* now `wrt` STAR deck!).
- Build (maybe simplified) inverse kinematics model that gives granular control over grip center speed and acceleration (with caveats accepted where the firmware does not expose the required granularity) - i.e. no more “violent spinning” + speed control for anti-splashing feature during transfer.
The current list of PRs in this epic:
-
Part 8: Expose iswap rotation drive move z by rickwierenga · Pull Request #1021 · PyLabRobot/pylabrobot · GitHub (authored by Rick)
This means we have just reached step 3 and 4.
What’s next?
We continue to move on to the next steps.
We wanted to inform the community about this big project and the new stories this will enable, e.g.:
- “iSWAP never crashes into the STAR anymore”
- “liquid doesn’t splash out of plate when moved via iSWAP”
…
future stories this enables us to build:
- “PLR warns us that iSWAP would crash into resources like carriers, other plates”,
- “PLR’s simulator keeps complete track of the iSWAP state, including position and orientation ..”
- “… enabling auto-computation of fastest movement patterns”
- “… enabling automated ‘avoidance zones’ (e.g. to avoid moving over specific plates without having to declare specific paths)”

Call to action
Please review the new iSWAP code, if you have improvement ideas please let us know or submit a PR.
Even though this explainer is already quite long, a lot of detail has been omitted for clarity.
Please ask questions. The more people understand this upcoming PLR feature the faster we can build it and build it even better.
Happy automation ![]()





