Created
November 28, 2019 12:28
-
-
Save rwb27/c728016f0f95f977034c26760ff9b6b7 to your computer and use it in GitHub Desktop.
Controlling ThorLabs Piezo from Python
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
This is a Python 3 wrapper for the Thorlabs BPC203 Benchtop Piezo controller. | |
It relies on the Thorlabs Kinesis API (so you should copy in, or add to your | |
Python path, the Kinesis DLLs). The easiest way to copy the right DLLs is | |
to use the "DLL copying utility" which is probably located in | |
c:/Program Files/Thorlabs/Kinesis | |
I also use the excellent ``pythonnet`` package to get access to the .NET API. | |
This is by far the least painful way to get Kinesis to work nicely as it | |
avoids the low-level faffing about. | |
""" | |
import clr # provided by pythonnet, .NET interface layer | |
import sys | |
import time | |
# this is seriously nasty. Points for a better way of fixing this! | |
sys.path.append(r"C:\Program Files\Thorlabs\Kinesis") | |
# NB the | |
clr.AddReference("Thorlabs.MotionControl.Benchtop.PiezoCLI") | |
clr.AddReference("Thorlabs.MotionControl.DeviceManagerCLI") | |
clr.AddReference("System") | |
from Thorlabs.MotionControl.Benchtop.PiezoCLI import BenchtopPiezo | |
from Thorlabs.MotionControl.DeviceManagerCLI import DeviceManagerCLI | |
from System import Decimal | |
def list_devices(): | |
"""Return a list of Kinesis serial numbers""" | |
DeviceManagerCLI.BuildDeviceList() | |
return DeviceManagerCLI.GetDeviceList() | |
class BenchtopPiezoWrapper(): | |
def __init__(self, serial_number): | |
self._ser = str(serial_number) | |
DeviceManagerCLI.BuildDeviceList() | |
self._piezo = BenchtopPiezo.CreateBenchtopPiezo(self._ser) | |
self.channels = [] | |
self.connected = False | |
def connect(self): | |
"""Initialise communications, populate channel list, etc.""" | |
assert not self.connected | |
self._piezo.Connect(self._ser) | |
self.connected = True | |
assert len(self.channels) == 0, "Error connecting: we've already initialised channels!" | |
for i in range(self._piezo.ChannelCount): | |
chan = self._piezo.GetChannel(i+1) # Kinesis channels are one-indexed | |
chan.WaitForSettingsInitialized(5000) | |
chan.StartPolling(250) # getting the voltage only works if you poll! | |
time.sleep(0.5) # ThorLabs have this in their example... | |
chan.EnableDevice() | |
# I don't know if the lines below are necessary or not - but removing them | |
# may or may not work... | |
time.sleep(0.5) | |
config = chan.GetPiezoConfiguration(chan.DeviceID) | |
info = chan.GetDeviceInfo() | |
max_v = Decimal.ToDouble(chan.GetMaxOutputVoltage()) | |
self.channels.append(chan) | |
def close(self): | |
"""Shut down communications""" | |
if not self.connected: | |
print(f"Not closing piezo device {self._ser}, it's not open!") | |
return | |
for chan in self.channels: | |
chan.StopPolling() | |
self.channels = [] | |
self._piezo.Disconnect(True) | |
def __del__(self): | |
try: | |
if self.connected: | |
self.close() | |
except: | |
print(f"Error closing communications on deletion of device {self._ser}") | |
def set_output_voltages(self, voltages): | |
"""Set the output voltage""" | |
assert len(voltages) == len(self.channels), "You must specify exactly one voltage per channel" | |
for chan, v in zip (self.channels, voltages): | |
chan.SetOutputVoltage(Decimal(v)) | |
def get_output_voltages(self): | |
"""Retrieve the output voltages as a list of floating-point numbers""" | |
return [Decimal.ToDouble(chan.GetOutputVoltage()) for chan in self.channels] | |
output_voltages = property(get_output_voltages, set_output_voltages) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks rwb27. Really helpful. Thought I'd be days trying to work out how to talk to my Thorlabs controller (I'm a newbie to Python)