diff --git a/README.rst b/README.rst index 882c569..8cc503b 100644 --- a/README.rst +++ b/README.rst @@ -42,6 +42,7 @@ Actuators * **PRM1Z8_pylablib**: DC servo motorized 360° rotation mount (Thorlabs PRM1Z8) using the pylablib control module. The Thorlabs APT software should be installed: https://www.thorlabs.com/newgrouppage9.cfm?objectgroup_id=9019. * **BrushlessDCMotor**: Kinesis control of DC Brushless Motor (tested with the BBD201 controller) * **Kinesis_KPZ101**: Piezo Electric Stage Kinesis series (KPZ101) +* **Kinesis_KIM101**: Four Channel Piezo Inertia Motion Kinesis series (KIM101) Viewer0D diff --git a/src/pymodaq_plugins_thorlabs/daq_move_plugins/daq_move_KIM101.py b/src/pymodaq_plugins_thorlabs/daq_move_plugins/daq_move_KIM101.py new file mode 100644 index 0000000..c4e6280 --- /dev/null +++ b/src/pymodaq_plugins_thorlabs/daq_move_plugins/daq_move_KIM101.py @@ -0,0 +1,127 @@ +from pymodaq.control_modules.move_utility_classes import ( + DAQ_Move_base, comon_parameters_fun, main, DataActuatorType, DataActuator) +from pymodaq.utils.daq_utils import ThreadCommand +from pymodaq.utils.parameter import Parameter +from pymodaq_plugins_thorlabs.hardware.kinesis import serialnumbers_inertial_motor, KIM101 +from pymodaq.utils.logger import set_logger, get_module_name + +class DAQ_Move_KIM101(DAQ_Move_base): + """ Instrument plugin class for an actuator. + + This object inherits all functionalities to communicate with PyMoDAQ’s DAQ_Move module through inheritance via + DAQ_Move_base. It makes a bridge between the DAQ_Move module and the Python wrapper of a particular instrument. + + Attributes: + ----------- + controller: object + The particular object that allow the communication with the hardware, in general a python wrapper around the + hardware library. + + """ + _controller_units = KIM101.default_units + is_multiaxes = True + _axes_names = {'1': 1, '2': 2, '3': 3, '4': 4} + _epsilon = 0.01 + data_actuator_type = DataActuatorType.DataActuator + params = [ + {'title': 'Serial Number:', 'name': 'serial_number', 'type': 'list', + 'limits': serialnumbers_inertial_motor, 'value': serialnumbers_inertial_motor[0]}, + + ] + comon_parameters_fun(is_multiaxes, axes_names=_axes_names, epsilon=_epsilon) + + def ini_attributes(self): + self.controller: KIM101 = None + + def get_actuator_value(self): + """Get the current value from the hardware with scaling conversion. + + Returns + ------- + DataActuator: The position obtained after scaling conversion. + """ + pos = DataActuator( + data=self.controller.get_position(channel=self.axis_value), + units=self.controller.get_units() + ) + pos = self.get_position_with_scaling(pos) + return pos + + def close(self): + """Terminate the communication protocol""" + if self.is_master: + self.controller.close() + + def commit_settings(self, param: Parameter): + """Apply the consequences of a change of value in the detector settings + + Parameters + ---------- + param: Parameter + A given parameter (within detector_settings) whose value has been changed by the user + """ + pass + + def ini_stage(self, controller=None): + """Actuator communication initialization + + Parameters + ---------- + controller: (object) + custom object of a PyMoDAQ plugin (Slave case). None if only one actuator by controller (Master case) + + Returns + ------- + info: str + initialized: bool + False if initialization failed otherwise True + """ + + if self.is_master: + self.controller = KIM101() + self.controller.connect(self.settings['serial_number']) + else: + self.controller = controller + + self.axis_unit = self._controller_units + + info = "KIM101 Initialized" + initialized = True + return info, initialized + + def move_abs(self, value: DataActuator): + """ Move the actuator to the absolute target defined by value + + Parameters + ---------- + value: (DataActuator) value of the absolute target positioning + """ + value = self.check_bound(value) + self.target_value = value + value = self.set_position_with_scaling(value) + self.controller.move_abs(int(value.value()), self.axis_value) + + def move_rel(self, value: DataActuator): + """ Move the actuator to the relative target actuator value defined by value + + Parameters + ---------- + value: (float) value of the relative target positioning + """ + value = self.check_bound(self.current_position + value) - self.current_position + self.target_value = value + self.current_position + value = self.set_position_relative_with_scaling(value) + + self.controller.move_rel(int(value.value()), self.axis_value) + + + def move_home(self): + """Call the reference method of the controller""" + self.controller.home(self.axis_value) + + def stop_motion(self): + """Stop the actuator and emits move_done signal""" + self.controller.stop() + + +if __name__ == '__main__': + main(__file__, init=False) diff --git a/src/pymodaq_plugins_thorlabs/daq_move_plugins/daq_move_KPZ101.py b/src/pymodaq_plugins_thorlabs/daq_move_plugins/daq_move_KPZ101.py index 7625b43..14bba5b 100644 --- a/src/pymodaq_plugins_thorlabs/daq_move_plugins/daq_move_KPZ101.py +++ b/src/pymodaq_plugins_thorlabs/daq_move_plugins/daq_move_KPZ101.py @@ -89,7 +89,7 @@ def ini_stage(self, controller=None): self.axis_unit = self._controller_units - info = f'{self.controller.name} - {self.controller.serial_number}' + info = "KIM101 Initialized" initialized = True return info, initialized @@ -103,7 +103,7 @@ def move_abs(self, value: DataActuator): value = self.check_bound(value) self.target_value = value value = self.set_position_with_scaling(value) - self.controller.move_abs(value.value()) + self.controller.move_abs(value.value(), axis=self.axis) def move_rel(self, value: DataActuator): """ Move the actuator to the relative target actuator value defined by value @@ -119,7 +119,7 @@ def move_rel(self, value: DataActuator): def move_home(self): """Call the reference method of the controller""" - self.controller.home() + self.controller.home(axis=self.axis) def stop_motion(self): """Stop the actuator and emits move_done signal""" diff --git a/src/pymodaq_plugins_thorlabs/hardware/kinesis.py b/src/pymodaq_plugins_thorlabs/hardware/kinesis.py index 9cdd755..168017b 100644 --- a/src/pymodaq_plugins_thorlabs/hardware/kinesis.py +++ b/src/pymodaq_plugins_thorlabs/hardware/kinesis.py @@ -6,6 +6,7 @@ from System import Action from System import UInt64 from System import UInt32 +import logging kinesis_path = 'C:\\Program Files\\Thorlabs\\Kinesis' sys.path.append(kinesis_path) @@ -19,6 +20,7 @@ clr.AddReference("Thorlabs.MotionControl.FilterFlipperCLI") clr.AddReference("Thorlabs.MotionControl.Benchtop.BrushlessMotorCLI") clr.AddReference("Thorlabs.MotionControl.KCube.PiezoCLI") +clr.AddReference("Thorlabs.MotionControl.KCube.InertialMotorCLI") import Thorlabs.MotionControl.FilterFlipperCLI as FilterFlipper import Thorlabs.MotionControl.IntegratedStepperMotorsCLI as Integrated @@ -26,6 +28,7 @@ import Thorlabs.MotionControl.GenericMotorCLI as Generic import Thorlabs.MotionControl.Benchtop.BrushlessMotorCLI as BrushlessMotorCLI import Thorlabs.MotionControl.KCube.PiezoCLI as KCubePiezo +import Thorlabs.MotionControl.KCube.InertialMotorCLI as InertialMotor Device.DeviceManagerCLI.BuildDeviceList() serialnumbers_integrated_stepper = [str(ser) for ser in @@ -36,6 +39,8 @@ Device.DeviceManagerCLI.GetDeviceList(BrushlessMotorCLI.BenchtopBrushlessMotor.DevicePrefix)] serialnumbers_piezo = [str(ser) for ser in Device.DeviceManagerCLI.GetDeviceList(KCubePiezo.KCubePiezo.DevicePrefix)] +serialnumbers_inertial_motor = [str(ser) for ser in Device.DeviceManagerCLI.GetDeviceList(InertialMotor.KCubeInertialMotor.DevicePrefix_KIM101)] + class Kinesis: default_units = '' @@ -323,6 +328,56 @@ def get_position(self) -> float: def stop(self): pass +class KIM101(Kinesis): + default_units = ' ' + + def __init__(self): + self._device: InertialMotor.KCubeInertialMotor = None + self._channel = [] + + def connect(self, serial: int): + if serial in serialnumbers_inertial_motor: + self._device = InertialMotor.KCubeInertialMotor.CreateKCubeInertialMotor(serial) + self._device.Connect(serial) + self._device.WaitForSettingsInitialized(5000) + self._device.StartPolling(250) + self._device.EnableDevice() + self._channel = [ + InertialMotor.InertialMotorStatus.MotorChannels.Channel1, + InertialMotor.InertialMotorStatus.MotorChannels.Channel2, + InertialMotor.InertialMotorStatus.MotorChannels.Channel3, + InertialMotor.InertialMotorStatus.MotorChannels.Channel4 + ] + + def move_abs(self, position: int, channel: int): + self._device.MoveTo(self._channel[channel-1], position, 6000) + + def move_rel(self, increment: int, channel: int): + target_position = self.get_position(channel) + increment + while (increment != 0): + if (increment > 0): + step = min(increment, 1) + else: + step = max(increment, -1) + position_new = self.get_position(channel) + step + self.move_abs(position_new, channel) + increment = target_position - self.get_position(channel) + + + def get_position(self, channel: int): + return self._device.GetPosition(self._channel[channel-1]) + + def home(self, channel: int): + self._device.MoveTo(self._channel[channel-1], 0, 6000) + + def stop(self): + pass + + def close(self): + self._device.StopPolling() + self._device.Disconnect() + + if __name__ == '__main__': controller = BrushlessDCMotor() controller.connect(serialnumbers_brushless[0])