From 2a42718fa0cf1d5a1399bc0337da7ad0f8fa7128 Mon Sep 17 00:00:00 2001 From: Vasco Tenner Date: Tue, 2 Jan 2018 15:01:24 +0100 Subject: [PATCH 1/4] Fixed errounous test and changed depricated warn --- lantz/processors.py | 2 +- lantz/testsuite/test_feat.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lantz/processors.py b/lantz/processors.py index 65ee831..c6eb158 100644 --- a/lantz/processors.py +++ b/lantz/processors.py @@ -88,7 +88,7 @@ def _inner(value): elif on_dimensionless == 'warn': msg = 'Assuming units `{1.units}` for {0}'.format(value, units) warnings.warn(msg, DimensionalityWarning) - _LOG.warn(msg) + _LOG.warning(msg) # on_incompatible == 'ignore' return float(value) diff --git a/lantz/testsuite/test_feat.py b/lantz/testsuite/test_feat.py index 752a68b..e0b464d 100644 --- a/lantz/testsuite/test_feat.py +++ b/lantz/testsuite/test_feat.py @@ -261,14 +261,15 @@ def eggs(self_, value): obj.eggs = x obj.eggs = x + 1 - self.assertEqual(hdl.history, ['Created Spam17', + #First item number depends on number of calls of Spam(), hence ignore + self.assertEqual(hdl.history[1:], ['Created Spam17', 'Getting eggs', '(raw) Got 9 for eggs', 'Got 9 for eggs', 'No need to set eggs = 9 (current=9, force=False)', 'Setting eggs = 10 (current=9, force=False)', '(raw) Setting eggs = 10', - 'eggs was set to 10']) + 'eggs was set to 10'][1:]) def test_units(self): From 46ed66bbbcfc4ad23bdf21fac3a035ff13075887 Mon Sep 17 00:00:00 2001 From: Vasco Tenner Date: Fri, 12 Jan 2018 17:47:13 +0100 Subject: [PATCH 2/4] First version of driver for PI piezo driver --- lantz/drivers/pi/__init__.py | 18 ++++ lantz/drivers/pi/piezo.py | 173 +++++++++++++++++++++++++++++++++++ 2 files changed, 191 insertions(+) create mode 100644 lantz/drivers/pi/__init__.py create mode 100644 lantz/drivers/pi/piezo.py diff --git a/lantz/drivers/pi/__init__.py b/lantz/drivers/pi/__init__.py new file mode 100644 index 0000000..4e6f16c --- /dev/null +++ b/lantz/drivers/pi/__init__.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +""" + lantz.drivers.pi + ~~~~~~~~~~~~~~~~~~~~~~ + + :company: PhysikInstrumente + :description: Motion and positioning components + :website: https://www.physikinstrumente.com + + ---- + + :copyright: 2017 by Vasco Tenner + :license: BSD, see LICENSE for more details. +""" + +from .piezo import Piezo + +__all__ = ['Piezo'] diff --git a/lantz/drivers/pi/piezo.py b/lantz/drivers/pi/piezo.py new file mode 100644 index 0000000..d773f48 --- /dev/null +++ b/lantz/drivers/pi/piezo.py @@ -0,0 +1,173 @@ +""" + lantz.drivers.pi.piezo + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Implements the drivers to control pi piezo motion controller + via serial. It uses the PI General Command Set. + + For USB, one first have to install the windows driver from PI. + :copyright: 2017, see AUTHORS for more details. + :license: GPL, see LICENSE for more details. + + Todo: via network? + Source: Instruction Manual (PI) +""" + +from lantz.feat import Feat +from lantz.action import Action +#from lantz.serial import SerialDriver +from lantz.messagebased import MessageBasedDriver +from pyvisa import constants +#from lantz.visa import GPIBVisaDriver +from lantz import Q_, ureg +from lantz.processors import convert_to +import time +import numpy as np +import copy + +#TODO +#Replace all send with write and remove \n +#Ask for errors often + +def to_int(val): + if not is_numeric(val): + val = val.strip() + if val == '': + val = 0 + return int(val) + +def to_float(val): + if not is_numeric(val): + val = val.strip() + if val == '': + val = 0 + return float(val) + +class Piezo(MessageBasedDriver): + """ PI piezo motion controller. It assumes all axes to have units um + + Params: + axis: axis number to controll""" + + DEFAULTS = { + 'COMMON': {'write_termination': '\r\n', + 'read_termination': '\r\n',}, +# TODO: set via PI software +# 'ASRL':{ +# 'timeout': 4000, #ms +# 'encoding': 'ascii', +# 'data_bits': 8, +# 'baud_rate': 19200, +# 'parity': constants.Parity.none, +# 'stop_bits': constants.StopBits.one, +# 'flow_control': constants.VI_ASRL_FLOW_RTS_CTS,#constants.VI_ASRL_FLOW_NONE, +# }, + } + + def initialize(self, axis=1, sleeptime_after_move=0*ureg.ms): + super().initialize() + self.axis = 1 + self.sleeptime_after_move = sleep_after_move + + def recv_numeric(self,length): + try: + message = self.stage.recv(length) + except (socket.timeout, socket.error), error: + print 'Stage not responsive! ', error + if self.stayconnected: + self.retries = self.retries + 1 + if self.retries < 10: + print 'Retry to connect %i/%i' % (self.retries, 10) + self.connect() + else: + print 'To many retries. Not connected to socket' + self.retries = 0 + message = '' + message = message.strip() + if message == '': + message = 0 + return message + + @Action() + def errors(self): + error = int(self.query('ERR?')) + if self.error != 0: + self.log_error('Stage error: code {}'.format(self.error)) + return self.error + + @Feat() + def idn(self): + self.stage.send("*IDN?\n") + idn = self.recv(256) + return idn + + @Action() + def stop(): + '''Stop all motions''' + self.servo = False + self.write("#24") + + def finalize(self): + """ Disconnects stage """ + self.stop() + super().finalize() + + @Feat(values={True: '1', False: '0'}) + def servo(self, state): + ''' Set the stage control in open- or closed-loop (state = False or True)''' + return to_int(self.query('SVO?') + + @serco.setter + def servo(self, state): + self.send('SVO 1 %d\n' % state) + + @Feat(units='um/s') + def velocity(self): + ''' Set the stage velocity (closed-loop only)''' + return self.query('VEL?') + + @velocity.setter + def velocity(self, velocity): + return self.write('VEL 1 %f' % velocity) + + @Feat(units='um'): + def position(self): + ''' Move to an absolute position the stage (closed-loop only)''' + return self.query('POS?') + + @position.setter + def position(self, position): + self.move_to(position) + + @Action(units=('um','ms')) + def move_to(self, position, timeout=None): + ''' Move to an absolute position the stage (closed-loop only)''' + ret = self.write('MOV %i %f' % self.axis, position) + timeout = self.sleeptime_after_move if timeout is None: + time.sleep(timeout.to('s')) # Give the stage time to move! (in seconds!) + return ret + + @Feat(units='um'): + def read_stage_position(self, nr_avg = 1): + ''' Read the current position from the stage''' + positions = [self.position for n in nr_avg] + return np.avg(positions) + +# before operating, ensure that the notch filters are set to the right frequencies. These frequencies depend on the load on the piezo, and can be determined in NanoCapture. +if __name__=='__main__': + stage = Piezo('') + stage.sleeptime_after_move = 15*ureg.ms + + time.sleep(1) + + stage.servo = True + time.sleep(1) + stage.velocity = 0.5*ureg.um/ureg.s + + stage.position = 0 + print 'stage position measured: ', stage.position + + steps = 200 + stepsize = 100.0*ureg.nm + for n in range(steps): + stage.position = n*stepsize + print 'stage position measured: ', stage.position From b4a0c9aaaa78c5f7973a635b119635945d5c55bd Mon Sep 17 00:00:00 2001 From: Vasco Tenner Date: Wed, 17 Jan 2018 17:12:33 +0100 Subject: [PATCH 3/4] Added support for PI E-709 piezo driver --- lantz/drivers/pi/__init__.py | 4 +- lantz/drivers/pi/error_codes.py | 219 +++++++++++++++++++++ lantz/drivers/pi/piezo.py | 339 ++++++++++++++++++++------------ 3 files changed, 438 insertions(+), 124 deletions(-) create mode 100644 lantz/drivers/pi/error_codes.py diff --git a/lantz/drivers/pi/__init__.py b/lantz/drivers/pi/__init__.py index 4e6f16c..320ab6a 100644 --- a/lantz/drivers/pi/__init__.py +++ b/lantz/drivers/pi/__init__.py @@ -13,6 +13,6 @@ :license: BSD, see LICENSE for more details. """ -from .piezo import Piezo +from .piezo import Piezo, parse_line, parse_multi -__all__ = ['Piezo'] +__all__ = ['Piezo','parse_line','parse_multi'] diff --git a/lantz/drivers/pi/error_codes.py b/lantz/drivers/pi/error_codes.py new file mode 100644 index 0000000..c81e79b --- /dev/null +++ b/lantz/drivers/pi/error_codes.py @@ -0,0 +1,219 @@ +error_codes = {0: "PI_CNTR_NO_ERROR No error", +1: "PI_CNTR_PARAM_SYNTAX Parameter syntax error", +2: "PI_CNTR_UNKNOWN_COMMAND Unknown command", +3: "PI_CNTR_COMMAND_TOO_LONG Command length out of limits orcommand buffer overrun", +4: "PI_CNTR_SCAN_ERROR Error while scanning", +5: "PI_CNTR_MOVE_WITHOUT_REF_OR_NO_SERVO Unallowable move attempted onunreferenced axis, or move attemptedwith servo off", +6: "PI_CNTR_INVALID_SGA_PARAM Parameter for SGA not valid", +7: "PI_CNTR_POS_OUT_OF_LIMITS Position out of limits", +8: "PI_CNTR_VEL_OUT_OF_LIMITS Velocity out of limits", +9: "PI_CNTR_SET_PIVOT_NOT_POSSIBLE Attempt to set pivot point while U,V andW not all 0", +10: "PI_CNTR_STOP Controller was stopped by command", +11: "PI_CNTR_SST_OR_SCAN_RANGE Parameter for SST or for one of theembedded scan algorithms out of range", +12: "PI_CNTR_INVALID_SCAN_AXES Invalid axis combination for fast scan", +13: "PI_CNTR_INVALID_NAV_PARAM Parameter for NAV out of range", +14: "PI_CNTR_INVALID_ANALOG_INPUT Invalid analog channel", +15: "PI_CNTR_INVALID_AXIS_IDENTIFIER Invalid axis identifier", +16: "PI_CNTR_INVALID_STAGE_NAME Unknown stage name", +17: "PI_CNTR_PARAM_OUT_OF_RANGE Parameter out of range", +18: "PI_CNTR_INVALID_MACRO_NAME Invalid macro name", +19: "PI_CNTR_MACRO_RECORD Error while recording macro", +20: "PI_CNTR_MACRO_NOT_FOUND Macro not found", +21: "PI_CNTR_AXIS_HAS_NO_BRAKE Axis has no brake", +22: "PI_CNTR_DOUBLE_AXIS Axis identifier specified more than once", +23: "PI_CNTR_ILLEGAL_AXIS Illegal axis", +24: "PI_CNTR_PARAM_NR Incorrect number of parameters", +25: "PI_CNTR_INVALID_REAL_NR Invalid floating point number", +26: "PI_CNTR_MISSING_PARAM Parameter missing", +27: "PI_CNTR_SOFT_LIMIT_OUT_OF_RANGE Soft limit out of range", +28: "PI_CNTR_NO_MANUAL_PAD No manual pad found", +29: "PI_CNTR_NO_JUMP No more step-response values", +30: "PI_CNTR_INVALID_JUMP No step-response values recorded", +31: "PI_CNTR_AXIS_HAS_NO_REFERENCE Axis has no reference sensor", +32: "PI_CNTR_STAGE_HAS_NO_LIM_SWITCH Axis has no limit switch", +33: "PI_CNTR_NO_RELAY_CARD No relay card installed", +34: "PI_CNTR_CMD_NOT_ALLOWED_FOR_STAGE Command not allowed for selectedstage(s)", +35: "PI_CNTR_NO_DIGITAL_INPUT No digital input installed", +36: "PI_CNTR_NO_DIGITAL_OUTPUT No digital output configured", +37: "PI_CNTR_NO_MCM No more MCM responses", +38: "PI_CNTR_INVALID_MCM No MCM values recorded", +39: "PI_CNTR_INVALID_CNTR_NUMBER Controller number invalid", +40: "PI_CNTR_NO_JOYSTICK_CONNECTED No joystick configured", +41: "PI_CNTR_INVALID_EGE_AXIS Invalid axis for electronic gearing, axiscannot be slave", +42: "PI_CNTR_SLAVE_POSITION_OUT_OF_RANGE Position of slave axis is out of range", +43: "PI_CNTR_COMMAND_EGE_SLAVE Slave axis cannot be commandeddirectly when electronic gearing isenabled", +44: "PI_CNTR_JOYSTICK_CALIBRATION_FAILED Calibration of joystick failed", +45: "PI_CNTR_REFERENCING_FAILED Referencing failed", +46: "PI_CNTR_OPM_MISSING OPM (Optical Power Meter) missing", +47: "PI_CNTR_OPM_NOT_INITIALIZED OPM (Optical Power Meter) notinitialized or cannot be initialized", +48: "PI_CNTR_OPM_COM_ERROR OPM (Optical Power Meter)communication error", +49: "PI_CNTR_MOVE_TO_LIMIT_SWITCH_FAILED Move to limit switch failed", +50: "PI_CNTR_REF_WITH_REF_DISABLED Attempt to reference axis withreferencing disabled", +51: "PI_CNTR_AXIS_UNDER_JOYSTICK_CONTROL Selected axis is controlled by joystick", +52: "PI_CNTR_COMMUNICATION_ERROR Controller detected communicationerror", +53: "PI_CNTR_DYNAMIC_MOVE_IN_PROCESS MOV! motion still in progress", +54: "PI_CNTR_UNKNOWN_PARAMETER Unknown parameter", +55: "PI_CNTR_NO_REP_RECORDED No commands were recorded with REP", +56: "PI_CNTR_INVALID_PASSWORD Password invalid", +57: "PI_CNTR_INVALID_RECORDER_CHAN Data record table does not exist", +58: "PI_CNTR_INVALID_RECORDER_SRC_OPT Source does not exist; number too lowor too high", +59: "PI_CNTR_INVALID_RECORDER_SRC_CHAN Source record table number too low ortoo high", +60: "PI_CNTR_PARAM_PROTECTION Protected Param: Current CommandLevel (CCL) too low", +61: "PI_CNTR_AUTOZERO_RUNNING Command execution not possible whileautozero is running", +62: "PI_CNTR_NO_LINEAR_AXIS Autozero requires at least one linearaxis", +63: "PI_CNTR_INIT_RUNNING Initialization still in progress", +64: "PI_CNTR_READ_ONLY_PARAMETER Parameter is read-only", +65: "PI_CNTR_PAM_NOT_FOUND Parameter not found in nonvolatilememory", +66: "PI_CNTR_VOL_OUT_OF_LIMITS Voltage out of limits", +67: "PI_CNTR_WAVE_TOO_LARGE Not enough memory available forrequested wave curve", +68: "PI_CNTR_NOT_ENOUGH_DDL_MEMORY Not enough memory available for DDLtable; DDL cannot be started", +69: "PI_CNTR_DDL_TIME_DELAY_TOO_LARGE Time delay larger than DDL table; DDLcannot be started", +70: "PI_CNTR_DIFFERENT_ARRAY_LENGTH The requested arrays have differentlengths; query them separately", +71: "PI_CNTR_GEN_SINGLE_MODE_RESTART Attempt to restart the generator while itis running in single step mode", +72: "PI_CNTR_ANALOG_TARGET_ACTIVE Motion commands and wave generatoractivation are not allowed when analogtarget is active", +73: "PI_CNTR_WAVE_GENERATOR_ACTIVE Motion commands are not allowedwhen wave generator is active", +74: "PI_CNTR_AUTOZERO_DISABLED No sensor channel or no piezo channelconnected to selected axis (sensor andpiezo matrix)", +75: "PI_CNTR_NO_WAVE_SELECTED Generator started (WGO) withouthaving selected a wave table (WSL).", +76: "PI_CNTR_IF_BUFFER_OVERRUN Interface buffer overran and commandcouldn't be received correctly", +77: "PI_CNTR_NOT_ENOUGH_RECORDED_DATA Data record table does not hold enoughrecorded data", +78: "PI_CNTR_TABLE_DEACTIVATED Data record table is not configured forrecording", +79: "PI_CNTR_OPENLOOP_VALUE_SET_WHEN_SERVO_ON Open-loop commands (SVA, SVR) arenot allowed when servo is on", +80: "PI_CNTR_RAM_ERROR Hardware error affecting RAM", +81: "PI_CNTR_MACRO_UNKNOWN_COMMAND Not macro command", +82: "PI_CNTR_MACRO_PC_ERROR Macro counter out of range", +83: "PI_CNTR_JOYSTICK_ACTIVE Joystick is active", +84: "PI_CNTR_MOTOR_IS_OFF Motor is off", +85: "PI_CNTR_ONLY_IN_MACRO Macro-only command", +86: "PI_CNTR_JOYSTICK_UNKNOWN_AXISInvalid joystick axis", +87: "PI_CNTR_JOYSTICK_UNKNOWN_IDJoystick unknown", +88: "PI_CNTR_REF_MODE_IS_ONMove without referenced stage", +89: "PI_CNTR_NOT_ALLOWED_IN_CURRENT_MOTION_MODE Command not allowed in current motionmode", +90: "PI_CNTR_DIO_AND_TRACING_NOT_POSSIBLENo tracing possible while digital IOs areused on this HW revision. Reconnect toswitch operation mode.", +91: "PI_CNTR_COLLISIONMove not possible, would causecollision", +92: "PI_CNTR_SLAVE_NOT_FAST_ENOUGH Stage is not capable of following themaster. Check the gear ratio.", +93: "PI_CNTR_CMD_NOT_ALLOWED_WHILE_AXIS_IN_MOTION This command is not allowed while theaffected axis or its master is in motion.", +94: "PI_CNTR_OPEN_LOOP_JOYSTICK_ENABLED Servo cannot be switched on whenopen-loop joystick control is enabled.", +95: "PI_CNTR_INVALID_SERVO_STATE_FOR_PARAMETER This parameter cannot be changed incurrent servo mode.", +96: "PI_CNTR_UNKNOWN_STAGE_NAME Unknown stage name", +100: "PI_LABVIEW_ERROR PI LabVIEW driver reports error. Seesource control for details.", +200: "PI_CNTR_NO_AXIS No stage connected to axis", +201: "PI_CNTR_NO_AXIS_PARAM_FILE File with axis parameters not found", +202: "PI_CNTR_INVALID_AXIS_PARAM_FILE Invalid axis parameter file", +203: "PI_CNTR_NO_AXIS_PARAM_BACKUP Backup file with axis parameters notfound", +204: "PI_CNTR_RESERVED_204 PI internal error code 204", +205: "PI_CNTR_SMO_WITH_SERVO_ON SMO with servo on", +206: "PI_CNTR_UUDECODE_INCOMPLETE_HEADER uudecode: incomplete header", +207: "PI_CNTR_UUDECODE_NOTHING_TO_DECODE uudecode: nothing to decode", +208: "PI_CNTR_UUDECODE_ILLEGAL_FORMAT uudecode: illegal UUE format", +209: "PI_CNTR_CRC32_ERROR CRC32 error", +210: "PI_CNTR_ILLEGAL_FILENAME Illegal file name (must be 8-0 format)", +211: "PI_CNTR_FILE_NOT_FOUND File not found on controller", +212: "PI_CNTR_FILE_WRITE_ERROR Error writing file on controller", +213: "PI_CNTR_DTR_HINDERS_VELOCITY_CHANGE VEL command not allowed in DTRcommand mode", +214: "PI_CNTR_POSITION_UNKNOWN Position calculations failed", +215: "PI_CNTR_CONN_POSSIBLY_BROKEN The connection between controller andstage may be broken", +216: "PI_CNTR_ON_LIMIT_SWITCH The connected stage has driven into alimit switch, some controllers need CLRto resume operation", +217: "PI_CNTR_UNEXPECTED_STRUT_STOP Strut test command failed because ofan unexpected strut stop", +218: "PI_CNTR_POSITION_BASED_ON_ESTIMATION While MOV! is running position can onlybe estimated!", +219: "PI_CNTR_POSITION_BASED_ON_INTERPOLATION Position was calculated during MOVmotion", +230: "PI_CNTR_INVALID_HANDLE Invalid handle", +231: "PI_CNTR_NO_BIOS_FOUND No bios found", +232: "PI_CNTR_SAVE_SYS_CFG_FAILED Save system configuration failed", +233: "PI_CNTR_LOAD_SYS_CFG_FAILED Load system configuration failed", +301: "PI_CNTR_SEND_BUFFER_OVERFLOW Send buffer overflow", +302: "PI_CNTR_VOLTAGE_OUT_OF_LIMITS Voltage out of limits", +303: "PI_CNTR_OPEN_LOOP_MOTION_SET_WHEN_SERVO_ON Open-loop motion attempted whenservo ON", +304: "PI_CNTR_RECEIVING_BUFFER_OVERFLOW Received command is too long", +305: "PI_CNTR_EEPROM_ERROR Error while reading/writing EEPROM", +306: "PI_CNTR_I2C_ERROR Error on I2C bus", +307: "PI_CNTR_RECEIVING_TIMEOUT Timeout while receiving command", +308: "PI_CNTR_TIMEOUT A lengthy operation has not finished inthe expected time", +309: "PI_CNTR_MACRO_OUT_OF_SPACE Insufficient space to store macro", +310: "PI_CNTR_EUI_OLDVERSION_CFGDATA Configuration data has old versionnumber", +311: "PI_CNTR_EUI_INVALID_CFGDATA Invalid configuration data", +333: "PI_CNTR_HARDWARE_ERROR Internal hardware error", +400: "PI_CNTR_WAV_INDEX_ERROR Wave generator index error", +401: "PI_CNTR_WAV_NOT_DEFINED Wave table not defined", +402: "PI_CNTR_WAV_TYPE_NOT_SUPPORTED Wave type not supported", +403: "PI_CNTR_WAV_LENGTH_EXCEEDS_LIMIT Wave length exceeds limit", +404: "PI_CNTR_WAV_PARAMETER_NR Wave parameter number error", +405: "PI_CNTR_WAV_PARAMETER_OUT_OF_LIMIT Wave parameter out of range", +406: "PI_CNTR_WGO_BIT_NOT_SUPPORTED WGO command bit not supported", +500: "PI_CNTR_EMERGENCY_STOP_BUTTON_ACTIVATED The \"red knob\" is still set and disablessystem", +501: "PI_CNTR_EMERGENCY_STOP_BUTTON_WAS_ACTIVATE The \"red knob\" was activated and stillDdisables system - reanimation required", +502: "PI_CNTR_REDUNDANCY_LIMIT_EXCEEDED Position consistency check failed", +503: "PI_CNTR_COLLISION_SWITCH_ACTIVATED Hardware collision sensor(s) areactivated", +504: "PI_CNTR_FOLLOWING_ERROR Strut following error occurred, e.g.caused by overload or encoder failure", +505: "PI_CNTR_SENSOR_SIGNAL_INVALID One sensor signal is not valid", +506: "PI_CNTR_SERVO_LOOP_UNSTABLE Servo loop was unstable due to wrongparameter setting and switched off toavoid damage.", +530: "PI_CNTR_NODE_DOES_NOT_EXIST A command refers to a node that doesnot exist", +531: "PI_CNTR_PARENT_NODE_DOES_NOT_EXIST A command refers to a node that hasno parent node", +532: "PI_CNTR_NODE_IN_USE Attempt to delete a node that is in use", +533: "PI_CNTR_NODE_DEFINITION_IS_CYCLIC Definition of a node is cyclic", +534: "PI_CNTR_NODE_CHAIN_INVALID The node chain does not end in the \"0\"node", +535: "PI_CNTR_NODE_DEFINITION_NOT_CONSISTENT The definition of a coordinatetransformation is erroneous", +536: "PI_CNTR_HEXAPOD_IN_MOTION Transformation cannot be defined aslong as Hexapod is in motion", +537: "PI_CNTR_TRANSFORMATION_TYPE_NOT_SUPPORTED Transformation node cannot beactivated", +538: "PI_CNTR_NODE_TYPE_DIFFERS A node can only be replaced by a nodeof the same type", +539: "PI_CNTR_NODE_PARENT_IDENTICAL_TO_CHILD A node cannot be linked to itself", +540: "PI_CNTR_NODE_DEFINITION_INCONSISTENT Node definition is erroneous or notcomplete (replace or delete it)", +541: "PI_CNTR_ZERO_NODE_CANNOT_BE_CHANGED_OR_REPLACED 0 is the root node and cannot bemodified", +542: "PI_CNTR_NODES_NOT_IN_SAME_CHAIN The nodes are not part of the samechain", +543: "PI_CNTR_NODE_MEMORY_FULL Unused nodes must be deleted beforenew nodes can be stored", +544: "PI_CNTR_PIVOT_POINT_FEATURE_NOT_SUPPORTED With some transformations pivot pointusage is not supported", +555: "PI_CNTR_UNKNOWN_ERROR BasMac: unknown controller error", +601: "PI_CNTR_NOT_ENOUGH_MEMORY Not enough memory", +602: "PI_CNTR_HW_VOLTAGE_ERROR Hardware voltage error", +603: "PI_CNTR_HW_TEMPERATURE_ERROR Hardware temperature out of range", +604: "PI_CNTR_POSITION_ERROR_TOO_HIGH Position error of any axis in the systemis too high", +606: "PI_CNTR_INPUT_OUT_OF_RANGE Maximum value of input signal hasbeen exceeded", +1000: "PI_CNTR_TOO_MANY_NESTED_MACROS Too many nested macros", +1001: "PI_CNTR_MACRO_ALREADY_DEFINED Macro already defined", +1002: "PI_CNTR_NO_MACRO_RECORDING Macro recording not activated", +1003: "PI_CNTR_INVALID_MAC_PARAM Invalid parameter for MAC", +1004: "PI_CNTR_RESERVED_1004 PI internal error code 1004", +1005: "PI_CNTR_CONTROLLER_BUSY Controller is busy with some lengthyoperation (e.g. reference move, fastscan algorithm)", +1006: "PI_CNTR_INVALID_IDENTIFIER Invalid identifier (invalid specialcharacters, ...)", +1007: "PI_CNTR_UNKNOWN_VARIABLE_OR_ARGUMENT Variable or argument not defined", +1008: "PI_CNTR_RUNNING_MACRO Controller is (already) running a macro", +1009: "PI_CNTR_MACRO_INVALID_OPERATOR Invalid or missing operator for condition.Check necessary spaces aroundoperator.", +1010: "PI_CNTR_MACRO_NO_ANSWER No answer was received whileexecuting WAC/MEX/JRC/...", +1011: "PI_CMD_NOT_VALID_IN_MACRO_MODE Command not valid during macroexecution", +1024: "PI_CNTR_MOTION_ERROR Motion error: position error too large,servo is switched off automatically", +1063: "PI_CNTR_EXT_PROFILE_UNALLOWED_CMD User profile mode: command is notallowed, check for required preparatorycommands", +1064: "PI_CNTR_EXT_PROFILE_EXPECTING_MOTION_ERROR User profile mode: first target position inuser profile is too far from currentposition", +1065: "PI_CNTR_PROFILE_ACTIVE Controller is (already) in user profilemode", +1066: "PI_CNTR_PROFILE_INDEX_OUT_OF_RANGE User profile mode: block or data setindex out of allowed range", +1071: "PI_CNTR_PROFILE_OUT_OF_MEMORY User profile mode: out of memory", +1072: "PI_CNTR_PROFILE_WRONG_CLUSTER User profile mode: cluster is notassigned to this axis", +1073: "PI_CNTR_PROFILE_UNKNOWN_CLUSTER_IDENTIFIER Unknown cluster identifier", +1090: "PI_CNTR_TOO_MANY_TCP_CONNECTIONS_OPEN There are too many open tcpipconnections", +2000: "PI_CNTR_ALREADY_HAS_SERIAL_NUMBER Controller already has a serial number", +4000: "PI_CNTR_SECTOR_ERASE_FAILED Sector erase failed", +4001: "PI_CNTR_FLASH_PROGRAM_FAILED Flash program failed", +4002: "PI_CNTR_FLASH_READ_FAILED Flash read failed", +4003: "PI_CNTR_HW_MATCHCODE_ERROR HW match code missing/invalid", +4004: "PI_CNTR_FW_MATCHCODE_ERROR FW match code missing/invalid", +4005: "PI_CNTR_HW_VERSION_ERROR HW version missing/invalid", +4006: "PI_CNTR_FW_VERSION_ERROR FW version missing/invalid", +4007: "PI_CNTR_FW_UPDATE_ERROR FW update failed", +5000: "PI_CNTR_INVALID_PCC_SCAN_DATA PicoCompensation scan data is notvalid", +5001: "PI_CNTR_PCC_SCAN_RUNNING PicoCompensation is running, someactions cannot be executed duringscanning/recording", +5002: "PI_CNTR_INVALID_PCC_AXIS Given axis cannot be defined as PPCaxis", +5003: "PI_CNTR_PCC_SCAN_OUT_OF_RANGE Defined scan area is larger than thetravel range", +5004: "PI_CNTR_PCC_TYPE_NOT_EXISTING Given PicoCompensation type is notdefined", +5005: "PI_CNTR_PCC_PAM_ERROR PicoCompensation parameter error", +5006: "PI_CNTR_PCC_TABLE_ARRAY_TOO_LARGE PicoCompensation table is larger thanmaximum table length", +5100: "PI_CNTR_NEXLINE_ERROR Common error in NEXLINE® firmwaremodule", +5101: "PI_CNTR_CHANNEL_ALREADY_USED Output channel for NEXLINE® cannotbe redefined for other usage", +5102: "PI_CNTR_NEXLINE_TABLE_TOO_SMALL Memory for NEXLINE® signals is toosmall", +5103: "PI_CNTR_RNP_WITH_SERVO_ON RNP cannot be executed if axis is inclosed loop", +5104: "PI_CNTR_RNP_NEEDED Relax procedure (RNP) needed", +5200: "PI_CNTR_AXIS_NOT_CONFIGURED Axis must be configured for this action", +6000: "PI_CNTR_SENSOR_ABS_INVALID_VALUE Invalid preset value of absolute sensor", +6001: "PI_CNTR_SENSOR_ABS_WRITE_ERROR Error while writing to sensor", +6002: "PI_CNTR_SENSOR_ABS_READ_ERROR Error while reading from sensor", +6003: "PI_CNTR_SENSOR_ABS_CRC_ERROR Checksum error of absolute sensor", +6004: "PI_CNTR_SENSOR_ABS_ERROR General error of absolute sensor", +6005: "PI_CNTR_SENSOR_ABS_OVERFLOW Overflow of absolute sensor position", +} diff --git a/lantz/drivers/pi/piezo.py b/lantz/drivers/pi/piezo.py index d773f48..b3e3d9f 100644 --- a/lantz/drivers/pi/piezo.py +++ b/lantz/drivers/pi/piezo.py @@ -1,173 +1,268 @@ """ lantz.drivers.pi.piezo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Implements the drivers to control pi piezo motion controller - via serial. It uses the PI General Command Set. - - For USB, one first have to install the windows driver from PI. - :copyright: 2017, see AUTHORS for more details. + Implements the drivers to control pi E-709 piezo motion controller + via USB or via serial. It uses the PI General Command Set. + + For USB, one first have to install the driver from PI. + :copyright: 2018, see AUTHORS for more details. :license: GPL, see LICENSE for more details. - - Todo: via network? + Source: Instruction Manual (PI) """ from lantz.feat import Feat from lantz.action import Action -#from lantz.serial import SerialDriver from lantz.messagebased import MessageBasedDriver -from pyvisa import constants -#from lantz.visa import GPIBVisaDriver +# from pyvisa import constants from lantz import Q_, ureg -from lantz.processors import convert_to +# from lantz.processors import convert_to import time import numpy as np -import copy - -#TODO -#Replace all send with write and remove \n -#Ask for errors often - -def to_int(val): - if not is_numeric(val): - val = val.strip() - if val == '': - val = 0 - return int(val) - -def to_float(val): - if not is_numeric(val): - val = val.strip() - if val == '': - val = 0 - return float(val) +# import copy +from collections import OrderedDict + +if __name__=='__main__': + from error_codes import error_codes +else: + from .error_codes import error_codes + + +def parse_line(line): + line = line.strip() # remove whitespace at end + return line.split('=') + + +def parse_multi(message): + '''Return an ordered dictionary containing the returned parameters''' + assert isinstance(message, list) + return OrderedDict([parse_line(line) for line in message]) + class Piezo(MessageBasedDriver): """ PI piezo motion controller. It assumes all axes to have units um + + Important: + before operating, ensure that the notch filters are set to the right + frequencies. These frequencies depend on the load on the piezo, and can + be determined in NanoCapture. + + Example: + import numpy as np + import lantz + import visa + import lantz.drivers.pi.piezo as pi + rm = visa.ResourceManager('@py') + lantz.messagebased._resource_manager = rm + ureg = lantz.ureg + try: + lantzlog + except NameError: + #lantzlog = lantz.log.log_to_screen(level=lantz.log.INFO) + lantzlog = lantz.log.log_to_screen(level=lantz.log.ERROR) + + import warnings + warnings.filterwarnings(action='ignore') + + print('Available devices:', lantz.messagebased._resource_manager.list_resources()) + print('Connecting to: ASRL/dev/ttyUSB0::INSTR') + stage = pi.Piezo('ASRL/dev/ttyUSB0::INSTR') + stage.initialize() + stage.sleeptime_after_move = 10*ureg.ms + stage.axis = 'X' + idn = stage.idn + + stage.servo = True + stage.velocity = 10*ureg.um/ureg.ms + + stage.position = 0*ureg.um + print('stage position measured: ', stage.position) + + steps = 10 + stepsize = 100.0*ureg.nm + for n in range(steps): + stage.position = n*stepsize + print('stage position measured: ', stage.position) + + print('Measuring step response') + + # For jupyter notebooks + %matplotlib inline + + stage.servo = True + stage.position = 0 + import time + time.sleep(1) + + stepsize = 10*lantz.ureg.um + timepoints = 100 + + timepos = stage.measure_step_response(stepsize, timepoints) + + lines = stage.plot_step_response(timepos) + + stage.finalize() # nicely close stage and turn of servo - Params: - axis: axis number to controll""" - - DEFAULTS = { - 'COMMON': {'write_termination': '\r\n', - 'read_termination': '\r\n',}, -# TODO: set via PI software -# 'ASRL':{ -# 'timeout': 4000, #ms -# 'encoding': 'ascii', -# 'data_bits': 8, -# 'baud_rate': 19200, -# 'parity': constants.Parity.none, -# 'stop_bits': constants.StopBits.one, -# 'flow_control': constants.VI_ASRL_FLOW_RTS_CTS,#constants.VI_ASRL_FLOW_NONE, -# }, - } - - def initialize(self, axis=1, sleeptime_after_move=0*ureg.ms): + + """ + + DEFAULTS = {'COMMON': {'write_termination': '\n', + 'read_termination': '\n', + 'baud_rate': 57600, + 'timeout': 20}, } + + def initialize(self, axis='X', sleeptime_after_move=0*ureg.ms): + self.axis = axis + self.sleeptime_after_move = sleeptime_after_move super().initialize() - self.axis = 1 - self.sleeptime_after_move = sleep_after_move - - def recv_numeric(self,length): + + def finalize(self): + """ Disconnects stage """ + self.stop() + super().finalize() + + def query_multiple(self, query): + """Read a multi line response""" + self.write(query) + loop = True + ans = [] + while loop: + ret = self.read() + if ret != '': + ans.append(ret) + else: + return ans + + def parse_multiaxis(self, message, axis=None): + "Parse a multi-axis message, return only value for self.axis" + if axis is None: + axis = self.axis + message_parts = message.split(' ') try: - message = self.stage.recv(length) - except (socket.timeout, socket.error), error: - print 'Stage not responsive! ', error - if self.stayconnected: - self.retries = self.retries + 1 - if self.retries < 10: - print 'Retry to connect %i/%i' % (self.retries, 10) - self.connect() - else: - print 'To many retries. Not connected to socket' - self.retries = 0 - message = '' - message = message.strip() - if message == '': - message = 0 - return message - - @Action() + message_axis = [part for part in message_parts if part[0] == axis][0] + except IndexError: + self.log_error('Axis {} not found in returned message: {}'.format(axis, message)) + values = message_axis.split('=') + return values[-1] + + @Feat() def errors(self): error = int(self.query('ERR?')) - if self.error != 0: - self.log_error('Stage error: code {}'.format(self.error)) - return self.error - + if error != 0: + self.log_error('Stage error code {}: {}'.format(error, error_codes[error])) + return error + @Feat() def idn(self): - self.stage.send("*IDN?\n") - idn = self.recv(256) + idn = self.query("*IDN?") + self.errors return idn - + @Action() - def stop(): + def stop(self): '''Stop all motions''' self.servo = False - self.write("#24") - - def finalize(self): - """ Disconnects stage """ - self.stop() - super().finalize() - - @Feat(values={True: '1', False: '0'}) - def servo(self, state): + self.write('STP') + self.errors + + @Feat(values={True: '1', False: '0'}, read_once=True) + def servo(self): ''' Set the stage control in open- or closed-loop (state = False or True)''' - return to_int(self.query('SVO?') - - @serco.setter + return self.parse_multiaxis(self.query('SVO?')) + + @servo.setter def servo(self, state): - self.send('SVO 1 %d\n' % state) + self.write('SVO {} {}'.format(self.axis, state) ) + return self.errors @Feat(units='um/s') def velocity(self): ''' Set the stage velocity (closed-loop only)''' - return self.query('VEL?') - + return self.parse_multiaxis(self.query('VEL?')) + @velocity.setter def velocity(self, velocity): - return self.write('VEL 1 %f' % velocity) + self.write('VEL {} {}'.format(self.axis, velocity)) + return self.errors - @Feat(units='um'): + @Feat(units='um') def position(self): ''' Move to an absolute position the stage (closed-loop only)''' - return self.query('POS?') + return self.parse_multiaxis(self.query('POS?')) @position.setter def position(self, position): - self.move_to(position) + return self.move_to(position, self.sleeptime_after_move) @Action(units=('um','ms')) def move_to(self, position, timeout=None): ''' Move to an absolute position the stage (closed-loop only)''' - ret = self.write('MOV %i %f' % self.axis, position) - timeout = self.sleeptime_after_move if timeout is None: - time.sleep(timeout.to('s')) # Give the stage time to move! (in seconds!) - return ret - - @Feat(units='um'): + self.write('MOV {} {}'.format(self.axis, position)) + time.sleep(timeout * 1e-3) # Give the stage time to move! (in seconds!) + return self.errors + + @Feat(units='um') def read_stage_position(self, nr_avg = 1): ''' Read the current position from the stage''' - positions = [self.position for n in nr_avg] - return np.avg(positions) + positions = [self.position.magnitude for n in range(nr_avg)] + return np.mean(positions) -# before operating, ensure that the notch filters are set to the right frequencies. These frequencies depend on the load on the piezo, and can be determined in NanoCapture. -if __name__=='__main__': - stage = Piezo('') - stage.sleeptime_after_move = 15*ureg.ms - - time.sleep(1) + @Action(units=('um',None)) + def measure_step_response(self, stepsize, points): + '''Python implementation to measure a step response of size stepsize. + Measure number of points as fast as possible. - stage.servo = True - time.sleep(1) - stage.velocity = 0.5*ureg.um/ureg.s + Servo should be on - stage.position = 0 - print 'stage position measured: ', stage.position + Note: a higher temporal resolution can be aquired by the data-recorder + in the driver + + Example: + stage.servo = True + stage.position = 0 + time.sleep(1) + + stepsize = 10*lantz.ureg.um + timepoints = 100 + + timepos = stage.measure_step_response(stepsize, timepoints) + + lines = stage.plot_step_response(timepos) + ''' + if not self.servo: + self.log.error('Servo should be on') + return + import time + self.move_to(self.position + stepsize*ureg.um, 0) + timepos = np.array([[time.time(), self.position.magnitude] for i in range(points)]) + timepos[:,0] -= timepos[0,0] #correct time offset + timepos[:,0] *= 1000 # make it milliseconds + + return timepos + + def plot_step_response(self, timepos): + '''Helper function to visualize a step respons''' + import matplotlib.pyplot as plt + lines = plt.plot(*timepos.T) + plt.xlabel('Time [ms]') + plt.ylabel('Position [um]') + return lines + +if __name__=='__main__': + # before operating, ensure that the notch filters are set to the right + # frequencies. These frequencies depend on the load on the piezo, and can + # be determined in NanoCapture. + from lantz.ui.app import start_test_app + + import lantz + import visa + import lantz.drivers.pi.piezo as pi + rm = visa.ResourceManager('@py') + lantz.messagebased._resource_manager = rm + try: + lantzlog + except NameError: + lantzlog = lantz.log.log_to_screen(level=lantz.log.INFO) + with Piezo('ASRL/dev/ttyUSB0::INSTR') as inst: + start_test_app(inst) - steps = 200 - stepsize = 100.0*ureg.nm - for n in range(steps): - stage.position = n*stepsize - print 'stage position measured: ', stage.position From ffda6461888c0e098802c084d4d9304838f478b3 Mon Sep 17 00:00:00 2001 From: Vasco Tenner Date: Wed, 17 Jan 2018 17:15:45 +0100 Subject: [PATCH 4/4] Added my to authors --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index a6ad339..292c7fa 100644 --- a/AUTHORS +++ b/AUTHORS @@ -7,3 +7,4 @@ Martin Masip Pablo Jais Martin Caldarola Federico Barabas +Vasco Tenner