A simplified Python module for controlling Zaber linear stages with high-speed position reading, velocity control, and device management capabilities.
Author: Yilin Ma
Date: November 2025
University of Michigan Robotics - HDR Lab
- Device Discovery: Automatic scanning and detection of Zaber devices
- Configuration Persistence: Save/load device settings to JSON files
- High-Speed Position Reading: Up to 100 Hz continuous position monitoring
- Velocity Control: Precise velocity control within defined limits
- Position Control: Absolute and relative positioning
- Safety Features: Position limits and velocity clamping
- Thread-Safe: All operations are thread-safe
# Required: Install the Zaber Motion Library
pip install zaber-motion
# Or install all dependencies from requirements file
pip install -r requirements.txtRun the initialization script first:
python ONETIME_INIT.pyThis script will:
- Check and install
zaber-motionif not present - Discover all connected Zaber devices
- Create configuration files for quick setup
- Generate multiple configuration profiles (high-speed, precision, safety)
zaber_stage.py- Main control moduletest_zaber_stage.py- Comprehensive test suiteONETIME_INIT.py- First-time setup and device discoveryREADME.md- This documentation file
from zaber_stage import ZaberStage
# Create and connect to stage
stage = ZaberStage(
port="auto", # Auto-detect or specify "COM3"
position_limit_mm=(0, 100), # Set safe operating range
max_velocity_mm_s=10.0, # Maximum velocity
reading_rate_hz=100.0 # Position reading rate
)
# Connect and initialize
if stage.connect():
# Home the stage
stage.home()
# Move to position
stage.move_to(50.0) # Move to 50mm
# Velocity control
stage.set_velocity(5.0) # Move at 5 mm/s
time.sleep(2)
stage.stop()
# Read position
position = stage.get_position() # Current position in mm
distance = stage.get_distance_from_home() # Distance from home
# Get full status
status = stage.get_status()
print(f"Position: {status.position_mm:.3f} mm")
print(f"Moving: {status.is_moving}")
stage.disconnect()from zaber_stage import discover_all_devices
# Scan for all Zaber devices
devices = discover_all_devices()
for device in devices:
print(f"Found: {device.name}")
print(f" Port: {device.port}")
print(f" Serial: {device.serial_number}")
print(f" Type: {device.device_type}")# Save current configuration
stage.save_config("my_stage_config.json")
# Load from saved configuration
from zaber_stage import load_stage_from_config
stage = load_stage_from_config("my_stage_config.json")
if stage:
print("Loaded and connected successfully")ZaberStage(port="auto",
position_limit_mm=(0, 100),
max_velocity_mm_s=10.0,
reading_rate_hz=100.0,
config_file=None)Parameters:
port: Serial port name or "auto" for auto-detectionposition_limit_mm: Tuple of (min, max) position limits in mmmax_velocity_mm_s: Maximum allowed velocity in mm/sreading_rate_hz: Position reading rate in Hz (max 100)config_file: Optional path to configuration file
| Method | Description | Returns |
|---|---|---|
connect() |
Connect to the stage | bool - Success status |
disconnect() |
Disconnect from the stage | None |
home() |
Home the stage | bool - Success status |
stop() |
Emergency stop | bool - Success status |
| Method | Description | Parameters | Returns |
|---|---|---|---|
move_to(position_mm) |
Move to absolute position | position_mm: Target position |
bool - Success |
set_velocity(velocity_mm_s) |
Set continuous velocity | velocity_mm_s: Target velocity (-/+) |
bool - Success |
| Method | Description | Returns |
|---|---|---|
get_position() |
Get current position | float - Position in mm |
get_distance_from_home() |
Get absolute distance from home | float - Distance in mm |
get_status() |
Get complete status | StageStatus object |
| Method | Description | Returns |
|---|---|---|
is_connected() |
Check connection status | bool |
is_homed() |
Check if homed | bool |
is_moving() |
Check if moving | bool |
| Method | Description | Returns |
|---|---|---|
scan_devices() |
Scan for all Zaber devices | List[DeviceInfo] |
save_config(filename) |
Save configuration to JSON | bool - Success |
load_config(filename) |
Load configuration from JSON | bool - Success |
get_device_info() |
Get connected device info | DeviceInfo or None |
@dataclass
class StageStatus:
position_mm: float # Current position in mm
velocity_mm_s: float # Current velocity in mm/s
is_moving: bool # Movement status
is_homed: bool # Homing status
timestamp: float # Unix timestamp@dataclass
class DeviceInfo:
port: str # Serial port
device_id: int # Device ID
serial_number: str # Serial number
name: str # Device name
firmware_version: str # Firmware version
device_type: str # Device type
axis_count: int # Number of axes# Quick stage creation
stage = create_stage(port="auto", position_limits=(0, 100))
# Discover all devices
devices = discover_all_devices()
# Load from config
stage = load_stage_from_config("config.json")The module saves configurations in JSON format:
{
"port": "COM3",
"position_limits_mm": [0, 100],
"max_velocity_mm_s": 10.0,
"reading_rate_hz": 100.0,
"device_info": {
"port": "COM3",
"device_id": 1,
"serial_number": "12345",
"name": "X-LRM200A",
"firmware_version": "7.15",
"device_type": "Linear Stage",
"axis_count": 1
},
"timestamp": 1234567890.123,
"timestamp_readable": "2025-11-26 12:00:00"
}Device discovery results are saved as:
{
"discovered_devices": [
{
"port": "COM3",
"device_id": 1,
"serial_number": "12345",
"name": "X-LRM200A",
"firmware_version": "7.15",
"device_type": "Linear Stage",
"axis_count": 1
}
],
"scan_timestamp": 1234567890.123,
"scan_timestamp_readable": "2025-11-26 12:00:00",
"device_count": 1
}Run the comprehensive test suite:
# Run full test suite with interactive mode
python test_zaber_stage.py- Connection Test - Verify device connection and communication
- Homing Test - Stage homing and home position verification
- Position Reading Test - High-speed position reading at 100 Hz
- Absolute Movement Test - Precise position moves with error checking
- Velocity Control Test - Continuous velocity control in both directions
- Position Limits Test - Software position limit enforcement
- Status Reporting Test - Real-time status information retrieval
- Stop Command Test - Emergency stop functionality
- Performance Benchmark - Speed and response time measurements
- Interactive Mode - Manual control for hands-on testing
- Functional Tests: Connection, homing, movement, velocity control
- Performance Tests: Reading rate, response time, tracking accuracy
- Safety Tests: Position limits, emergency stop
- Interactive Mode: Manual testing with commands (h, m, v, s, p, status, q)
| Specification | Value |
|---|---|
| Maximum Position Reading Rate | 100 Hz |
| Position Resolution | 0.001 mm |
| Thread Safety | Yes |
| Typical Command Response Time | < 10 ms |
| Position Update Latency | < 10 ms |
The module includes comprehensive error handling:
# Example with error handling
try:
stage = ZaberStage()
if not stage.connect():
print("Failed to connect")
# Check logs for details
if not stage.home():
print("Homing failed")
# Safe movement with limits
stage.move_to(200) # Will be clamped to max limit
except Exception as e:
print(f"Error: {e}")
finally:
stage.disconnect()The module uses Python's logging system:
import logging
# Enable debug logging
logging.basicConfig(level=logging.DEBUG)
# Module logger name: "ZaberStage"
logger = logging.getLogger("ZaberStage")- Position Limits: Automatic enforcement of min/max positions
- Velocity Clamping: Velocities limited to safe maximum
- Emergency Stop: Immediate stop function
- Thread Safety: All position reads are thread-safe
- Boundary Checking: Automatic stop at limits during velocity control
-
Import Error: "cannot import name 'Connection'"
- Run
python first_run_initialization.pyto install dependencies - Or manually:
pip install zaber-motion
- Run
-
Device Not Found
- Check USB/serial connection
- Run device discovery:
python first_run_initialization.py - Check device power
- Verify drivers are installed
-
"'Axis' object has no attribute 'enable'" Error
- This is fixed in the current version (uses zaber-motion 7.x API)
- Update your installation:
pip install --upgrade zaber-motion
-
Absolute Movement Test Fails
- Ensure stage is homed before testing
- Allow sufficient time for movements (already fixed in test suite)
- Check mechanical obstructions
-
Permission Errors (Linux/Mac)
# Add user to dialout group sudo usermod -a -G dialout $USER # Logout and login again
-
Slow Reading Rate
- Check USB connection quality
- Reduce reading rate if needed:
reading_rate_hz=50 - Check system performance and CPU usage
# Constant velocity pull test
stage.home()
stage.move_to(0) # Start position
# Pull at constant rate
stage.set_velocity(0.5) # 0.5 mm/s
# Monitor position
while stage.get_position() < 50:
position = stage.get_position()
force = read_load_cell() # Your force reading
print(f"Position: {position:.3f} mm, Force: {force:.3f} N")
time.sleep(0.01)
stage.stop()# Cyclic movement between positions
for cycle in range(10):
stage.move_to(0)
while stage.is_moving():
time.sleep(0.1)
stage.move_to(10)
while stage.is_moving():
time.sleep(0.1)
print(f"Cycle {cycle + 1} complete")# Synchronize with external sensor
target_positions = [10, 20, 30, 40, 50]
for target in target_positions:
stage.move_to(target)
# Wait for movement and sensor
while stage.is_moving():
time.sleep(0.01)
sensor_reading = read_sensor() # Your sensor
actual_position = stage.get_position()
print(f"Target: {target}, Actual: {actual_position:.3f}, Sensor: {sensor_reading}")python first_run_initialization.pypython test_zaber_stage.pyfrom zaber_stage import load_stage_from_config
stage = load_stage_from_config('zaber_config.json')
stage.home()
stage.move_to(50.0)After running first_run_initialization.py, you'll have:
zaber_config.json- Default configurationhigh_speed_config.json- High-speed testing (0-50mm, 20mm/s)precision_config.json- Precision positioning (0-200mm, 1mm/s)safety_config.json- Safety-limited (10-40mm, 5mm/s)discovered_devices.json- List of all found devices
This module is part of the University of Michigan Robotics HDR Lab project.
For issues or questions:
- Run
first_run_initialization.pyfor device discovery - Check test output:
python test_zaber_stage.py - Enable debug logging for detailed trace
- Use
scan_devices()to verify device connectivity - Refer to Zaber Motion Library documentation for advanced features
- v1.0 (November 2025): Initial release with core functionality
- Device discovery and configuration persistence
- High-speed position reading (100 Hz)
- Velocity and position control
- Comprehensive test suite
- First-run initialization script
- Compatible with zaber-motion 7.x
Author: Yilin Ma
Institution: University of Michigan
Lab: Robotics - HDR Lab
Date: November 2025
This module is part of a larger project that includes:
- LCR Module - E4980A/AL LCR Meter control for electrical measurements
- Zaber Module - Linear stage control (this module)
Both modules follow similar design patterns for ease of integration.