Skip to content

Instantly share code, notes, and snippets.

@rwb27
Created November 28, 2019 12:28
Show Gist options
  • Save rwb27/c728016f0f95f977034c26760ff9b6b7 to your computer and use it in GitHub Desktop.
Save rwb27/c728016f0f95f977034c26760ff9b6b7 to your computer and use it in GitHub Desktop.
Controlling ThorLabs Piezo from Python
"""
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)
@rwb27
Copy link
Author

rwb27 commented Nov 28, 2019

This worked for me to control a ThorLabs piezo from Windows in Python 3. A minimal example:

(win-py3-scipy) PS C:\Users\rwb34\dev\kinesis_testing> ipython                                                          Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)]
Type 'copyright', 'credits' or 'license' for more information
IPython 7.8.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: import benchtop_piezo

In [2]: pz = benchtop_piezo.BenchtopPiezoWrapper(benchtop_piezo.list_devices()[0])

In [3]: pz.connect()

In [4]: pz.output_voltages
Out[4]: [-0.0411999877925962, 0.00457777642139958, -0.0183111056855983]

In [5]: pz.output_voltages = [1,2,3]

In [6]: pz.output_voltages
Out[6]: [0.956755272072512, 2.00506607257302, 2.98242133854183]

In [7]: pz.close()

NB have a look in the program files/kinesis folder for the API documentation in CHM format, which is really helpful and full of examples.

@amyas-bay
Copy link

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)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment