391 lines
10 KiB
Markdown
Executable File
391 lines
10 KiB
Markdown
Executable File
# ThorLabs BBD203 Motor Controller Driver
|
||
|
||
## Overview
|
||
|
||
This directory contains a complete implementation of the ThorLabs BBD203 3-channel benchtop brushless DC motor controller driver. The driver implements the full APT (Advanced Positioning Technology) binary communications protocol as specified in the BBD203_Communications_Protocol.md document.
|
||
|
||
## Architecture
|
||
|
||
The BBD203 driver is split into three layers:
|
||
|
||
### 1. Protocol Layer (`bbd203_protocol.py`)
|
||
|
||
Low-level protocol implementation that handles:
|
||
- Binary message construction and parsing
|
||
- APT protocol message IDs and structures
|
||
- Unit conversions (mm ↔ encoder counts, velocity/acceleration scaling)
|
||
- Status bit definitions
|
||
|
||
**Key Classes:**
|
||
- `MessageID`: Enumeration of all APT message IDs
|
||
- `APTMessage`: Message builder and parser for binary protocol
|
||
- `APTProtocol`: High-level protocol interface with unit conversions
|
||
|
||
### 2. Driver Layer (`bbd203_driver.py`)
|
||
|
||
Complete driver implementation providing:
|
||
- Serial communication with automatic message reception thread
|
||
- 3-channel management (independent motor control)
|
||
- Blocking and non-blocking move operations
|
||
- Status monitoring with automatic updates
|
||
- Event callbacks for move/home completion
|
||
- Thread-safe operation
|
||
|
||
**Key Classes:**
|
||
- `BBD203Channel`: Represents state of a single motor channel
|
||
- `BBD203Driver`: Main driver class for controller communication
|
||
|
||
### 3. Stage Interface Layer (`thorlabs_stage.py`)
|
||
|
||
Application-specific wrapper that:
|
||
- Maps 3 motor channels to X/Y/Z axes
|
||
- Provides simplified API for stage control
|
||
- Integrates with the nueScan application
|
||
- Maintains compatibility with existing UI
|
||
|
||
**Channel Mapping:**
|
||
- Channel 1 → X-axis
|
||
- Channel 2 → Y-axis
|
||
- Channel 3 → Z-axis (optional)
|
||
|
||
## Features
|
||
|
||
### Communication
|
||
- Binary APT protocol over USB/RS232
|
||
- Baud rate: 115200 (configurable)
|
||
- Automatic message reception in background thread
|
||
- Command/response handling with proper timeout
|
||
|
||
### Motion Control
|
||
- Absolute positioning
|
||
- Relative moves
|
||
- Velocity control
|
||
- Immediate and profiled stops
|
||
- Configurable acceleration
|
||
|
||
### Position Feedback
|
||
- Real-time position updates (encoder counts)
|
||
- Position in mm (with configurable scaling)
|
||
- Status bit monitoring (homing, moving, errors, etc.)
|
||
|
||
### Homing
|
||
- Individual axis homing
|
||
- All-axes homing
|
||
- Blocking or non-blocking operation
|
||
- Completion callbacks
|
||
|
||
### Safety
|
||
- Interlock checking before moves
|
||
- Error detection and reporting
|
||
- Motion error monitoring
|
||
- Limit switch status
|
||
|
||
## Usage
|
||
|
||
### Connection Methods
|
||
|
||
The driver supports two connection methods:
|
||
|
||
#### Method 1: Connect by Serial Number (Recommended)
|
||
|
||
Similar to ThorLabs Kinesis library - automatically finds the USB device:
|
||
|
||
```python
|
||
from hardware.bbd203_driver import BBD203Driver
|
||
|
||
# Create driver instance
|
||
driver = BBD203Driver(encoder_counts_per_mm=20000)
|
||
|
||
# List available ThorLabs devices
|
||
devices = driver.list_thorlabs_devices()
|
||
for device in devices:
|
||
print(f"Serial: {device['serial']}, Port: {device['port']}")
|
||
|
||
# Connect by serial number (auto-finds the port)
|
||
driver.connect_by_serial('83123456') # Serial printed on controller
|
||
```
|
||
|
||
#### Method 2: Connect by Port Name
|
||
|
||
Direct connection to a specific port:
|
||
|
||
```python
|
||
# Connect to specific port
|
||
driver.connect('/dev/ttyUSB0') # or 'COM3' on Windows
|
||
```
|
||
|
||
### Basic Movement
|
||
|
||
```python
|
||
|
||
# Enable all channels
|
||
driver.enable_channel(1, True) # X-axis
|
||
driver.enable_channel(2, True) # Y-axis
|
||
driver.enable_channel(3, True) # Z-axis
|
||
|
||
# Home all channels (blocking)
|
||
driver.home_all_channels(wait=True, timeout=60)
|
||
|
||
# Set velocity parameters
|
||
driver.set_velocity_params(channel=1, max_vel_mm_s=5.0, accel_mm_s2=10.0)
|
||
|
||
# Move to absolute position (non-blocking)
|
||
driver.move_absolute(channel=1, position_mm=10.0, wait=False)
|
||
|
||
# Move to absolute position (blocking)
|
||
driver.move_absolute(channel=2, position_mm=25.0, wait=True, timeout=30)
|
||
|
||
# Move relative
|
||
driver.move_relative(channel=1, distance_mm=-5.0, wait=True)
|
||
|
||
# Stop motion
|
||
driver.stop(channel=1, immediate=True)
|
||
|
||
# Get position
|
||
pos = driver.get_position(channel=1)
|
||
print(f"Position: {pos} mm")
|
||
|
||
# Get detailed status
|
||
status = driver.get_channel_status(channel=1)
|
||
print(f"Enabled: {status['enabled']}")
|
||
print(f"Homed: {status['homed']}")
|
||
print(f"Moving: {status['moving']}")
|
||
|
||
# Disconnect
|
||
driver.disconnect()
|
||
```
|
||
|
||
### Using the Stage Interface
|
||
|
||
```python
|
||
from hardware.thorlabs_stage import ThorLabsStage
|
||
|
||
# Create stage controller
|
||
stage = ThorLabsStage(encoder_counts_per_mm=20000)
|
||
|
||
# List available devices
|
||
devices = stage.list_devices()
|
||
for device in devices:
|
||
print(f"BBD203 Serial: {device['serial']}")
|
||
|
||
# Connect by serial number (automatically enables all channels)
|
||
stage.connect('83123456') # Serial number from controller label
|
||
|
||
# Home all axes
|
||
stage.home_all_axes(wait=True)
|
||
|
||
# Move to position
|
||
stage.move_absolute(x=10.0, y=20.0, wait=True)
|
||
|
||
# Move relative
|
||
stage.move_relative(dx=5.0, dy=-2.5, wait=True)
|
||
|
||
# Get position
|
||
pos = stage.get_position()
|
||
print(f"X: {pos['x']} mm, Y: {pos['y']} mm, Z: {pos['z']} mm")
|
||
|
||
# Check status
|
||
status = stage.get_status()
|
||
print(f"Ready: {status['ready']}")
|
||
print(f"X Homed: {status['x_homed']}")
|
||
|
||
# Disconnect
|
||
stage.disconnect()
|
||
```
|
||
|
||
### Event Callbacks
|
||
|
||
```python
|
||
# Define callback function
|
||
def on_move_complete(channel):
|
||
print(f"Channel {channel} move completed!")
|
||
|
||
# Register callback
|
||
driver.register_move_complete_callback(1, on_move_complete)
|
||
|
||
# Start non-blocking move - callback will be called when complete
|
||
driver.move_absolute(channel=1, position_mm=50.0, wait=False)
|
||
```
|
||
|
||
## Configuration
|
||
|
||
### Encoder Scaling
|
||
|
||
The encoder resolution must be configured to match your specific motor/stage combination:
|
||
|
||
```python
|
||
# Example: MLS203 stage with 20,000 counts/mm
|
||
driver = BBD203Driver(encoder_counts_per_mm=20000)
|
||
|
||
# Example: Custom stage with 2,000 counts/mm
|
||
driver = BBD203Driver(encoder_counts_per_mm=2000)
|
||
```
|
||
|
||
Common encoder resolutions:
|
||
- **MLS203**: 20,000 counts/mm
|
||
- **DDS220**: 2,000 counts/mm
|
||
- **Custom**: Varies (check motor specifications)
|
||
|
||
### Velocity and Acceleration
|
||
|
||
Velocity and acceleration use the APT scaling formulas:
|
||
|
||
```
|
||
VEL_APT = EncCnt × 102.4e-6 × 65536 × Vel
|
||
ACC_APT = EncCnt × (102.4e-6)² × 65536 × Acc
|
||
```
|
||
|
||
The driver handles these conversions automatically:
|
||
|
||
```python
|
||
# Set velocity to 5 mm/s, acceleration to 10 mm/s²
|
||
driver.set_velocity_params(
|
||
channel=1,
|
||
max_vel_mm_s=5.0,
|
||
accel_mm_s2=10.0
|
||
)
|
||
```
|
||
|
||
## Integration with nueScan
|
||
|
||
The driver is integrated into nueScan through the `ThorLabsStage` wrapper class. The connection is simplified using serial number auto-detection:
|
||
|
||
### Connecting in the UI
|
||
|
||
1. **Find Serial Number**: Look at the label on your BBD203 controller (e.g., `83123456`)
|
||
2. **Enter Serial**: Type the serial number in the "ThorLABS MLS Stage Serial" field
|
||
3. **Connect**: Click "Connect" button
|
||
- Driver automatically finds the USB device
|
||
- All 3 channels are enabled
|
||
- Status updates begin
|
||
4. **Ready**: The controller is now ready to home and move axes
|
||
|
||
### Connection Process
|
||
|
||
When you click "Connect":
|
||
- The driver scans all USB ports for ThorLabs devices (FTDI VID: 0x0403)
|
||
- Finds the device matching your serial number
|
||
- Automatically uses the correct COM port
|
||
- Enables all channels (X/Y/Z axes)
|
||
- Sets default velocity parameters
|
||
|
||
### Troubleshooting Connection
|
||
|
||
If connection fails, a dialog will show:
|
||
- The serial number you entered
|
||
- List of all detected ThorLabs devices with their serial numbers
|
||
- Helps you identify the correct serial to use
|
||
|
||
## Protocol Details
|
||
|
||
### Message Structure
|
||
|
||
All APT messages consist of:
|
||
- 6-byte header (message ID, length, destination, source)
|
||
- Optional data packet (variable length)
|
||
|
||
### Destination Addressing
|
||
|
||
- `0x21`: Channel 1 (X-axis)
|
||
- `0x22`: Channel 2 (Y-axis)
|
||
- `0x23`: Channel 3 (Z-axis)
|
||
- `0x11`: All channels
|
||
- `0x50`: USB interface
|
||
|
||
### Status Bits
|
||
|
||
Key status bits monitored by the driver:
|
||
|
||
| Bit | Mask | Meaning |
|
||
|-----|------|---------|
|
||
| HOMING | 0x00000200 | Homing in progress |
|
||
| HOMED | 0x00000400 | Axis has been homed |
|
||
| TRACKING | 0x00001000 | Following target position |
|
||
| SETTLED | 0x00002000 | Position settled |
|
||
| MOTION_ERROR | 0x00004000 | Following error exceeded |
|
||
| MOTOR_ENABLED | 0x80000000 | Motor drive enabled |
|
||
| IN_MOTION_FORWARD | 0x00000010 | Moving forward |
|
||
| IN_MOTION_REVERSE | 0x00000020 | Moving reverse |
|
||
|
||
## Troubleshooting
|
||
|
||
### Connection Issues
|
||
|
||
**Problem:** Cannot connect to controller
|
||
|
||
**Solutions:**
|
||
- Verify COM port name is correct (`ThorLabsStage.list_available_ports()`)
|
||
- Check USB cable connection
|
||
- Verify no other software has the port open
|
||
- Try different baud rate (default: 115200)
|
||
- Check device permissions on Linux
|
||
|
||
### Homing Fails
|
||
|
||
**Problem:** Homing timeout or never completes
|
||
|
||
**Solutions:**
|
||
- Increase homing timeout parameter
|
||
- Check limit switches are functioning
|
||
- Verify motor is enabled
|
||
- Check for mechanical obstructions
|
||
- Review homing parameters (direction, velocity)
|
||
|
||
### Position Errors
|
||
|
||
**Problem:** Reported position doesn't match reality
|
||
|
||
**Solutions:**
|
||
- Verify `encoder_counts_per_mm` setting matches your stage
|
||
- Check encoder connections
|
||
- Reset position counter if needed: `driver.cmd_set_position_counter()`
|
||
- Verify stage is homed before moves
|
||
|
||
### Communication Errors
|
||
|
||
**Problem:** Commands not acknowledged or responses missing
|
||
|
||
**Solutions:**
|
||
- Increase serial timeout
|
||
- Check for message buffer overflow
|
||
- Verify automatic status updates are enabled
|
||
- Add delays between rapid commands
|
||
|
||
## Debug Output
|
||
|
||
The driver provides extensive debug output:
|
||
|
||
```
|
||
INFO: Messages about successful operations
|
||
DEBUG: Detailed command/response information
|
||
ERROR: Error conditions and failures
|
||
WARNING: Potential issues
|
||
```
|
||
|
||
Enable Python logging to capture all output:
|
||
|
||
```python
|
||
import logging
|
||
logging.basicConfig(level=logging.DEBUG)
|
||
```
|
||
|
||
## Performance Notes
|
||
|
||
- Message processing runs in separate thread (no blocking)
|
||
- Typical command response time: 10-50ms
|
||
- Position updates: ~10Hz when status messages enabled
|
||
- Move completion detected via asynchronous message
|
||
- Thread-safe for concurrent channel operations
|
||
|
||
## References
|
||
|
||
- **Protocol Documentation**: `BBD203_Communications_Protocol.md`
|
||
- **APT Protocol Version**: 42.1
|
||
- **Product Manual**: Available from Thorlabs.com
|
||
- **Technical Support**: techsupport@thorlabs.com
|
||
|
||
## License
|
||
|
||
Copyright (C) 2025 Thomas Ales
|
||
Licensed under GNU General Public License v2.0
|