AlexShkarin / pyLabLib

Python package for device control and experiment automation
http://pylablib.readthedocs.io
GNU General Public License v3.0
142 stars 34 forks source link

BSC 203 - Detected, but unable to communicate #17

Open pl-deassis opened 2 years ago

pl-deassis commented 2 years ago

I know pyLabLib wasn't tested on the Thorlabs BSC 203, but when I can see it when I ask for a list of Thorlabs APT devices. However, whenever I try to communicate with it I get an error, stating that it sent less information than was expected. The thorlabs_apt_device 0.3.4 package recommends enabling virtual comm ports, but I don't know if that would be the case for pyLabLib.

I noticed in kinesis.py that the program should recognize a serial number starting in 70 as a BSC.03, but couldn't find specific code related to that... In any case it does correctly identify the serial number of my device, so it is getting some sort of communication through.

Has anyone experienced this sort of issue? I would be happy to (try to) contribute with BSC 203 support, and pointers on where to start and what to adapt would be greatly appreciated.

AlexShkarin commented 2 years ago

Hi!

Could you please attach the code that generates the error and the error message together with the stack trace? The problem shows up when you are trying to connect, right? Could you also make sure that you're using the latest pylablib version (1.4.0)? There have been some adjustments in several recent version regarding Thorlabs APT communication.

One option you can try is to use a more generic KinesisDevice class and supply is_rack_system=True to its constructor. If it connects without an issue, you can check some device-level methods like get_device_info and channel-level methods like _get_position (mind the underscore!) to see if any of them work.

Regarding the serial numbers: it's normally just used to double-check the model provided by the device itself and does not affect the behavior. Generally, the class behaves pretty much the same way for any motor, except for a couple of built-in scaling coefficients.

pl-deassis commented 2 years ago

Hello Alex! Thanks for the quick reply.

I am using a freshly installed pylablib, on a newly created environment just for it.

I start by making a

stage_list = Thorlabs.list_kinesis_devices(), which runs without error. A print(stage_list) returns

[('55263504', 'Kinesis K10CR1 Rotary Stage'), ('70262594', 'APT Stepper Motor Controller')]

Here is the code to create the BSC 203 object:

bsc = Thorlabs.KinesisDevice('70262594'), which runs without any error message.

Here is what I get when asking for full device info:

bsc.get_full_info()

Traceback (most recent call last):

File "C:\Users\aaa\AppData\Local\Temp\ipykernel_4612\29980927.py", line 1, in <cell line: 1> bsc.get_full_info()

File "C:\Users\aaa\.conda\envs\Automation\lib\site-packages\pylablib\core\devio\interface.py", line 239, in get_full_info return self._get_device_variables(["settings","status","info"],include=include)

File "C:\Users\aaa\.conda\envs\Automation\lib\site-packages\pylablib\core\devio\interface.py", line 195, in _get_device_variables info[k]=g()

File "C:\Users\aaa\.conda\envs\Automation\lib\site-packages\pylablib\devices\Thorlabs\kinesis.py", line 206, in get_device_info data=self.query(0x0005,dest=dest).data

File "C:\Users\aaa\.conda\envs\Automation\lib\site-packages\pylablib\devices\Thorlabs\kinesis.py", line 168, in query return self.recv_comm(expected_id=replyID)

File "C:\Users\aaa\.conda\envs\Automation\lib\site-packages\pylablib\devices\Thorlabs\kinesis.py", line 141, in recv_comm msg=self.instr.read(6)

File "C:\Users\aaa\.conda\envs\Automation\lib\site-packages\pylablib\core\devio\comm_backend.py", line 45, in wrapped return func(self,*args,**kwargs)

File "C:\Users\aaa\.conda\envs\Automation\lib\site-packages\pylablib\core\devio\comm_backend.py", line 34, in wrapped return func(self,*args,**kwargs)

File "C:\Users\aaa\.conda\envs\Automation\lib\site-packages\pylablib\core\devio\comm_backend.py", line 922, in read raise self.Error("read returned less data than expected")

ThorlabsBackendError: backend exception: 'read returned less data than expected' ('read returned less data than expected')

By the way, the rotation stage works without any issue, even if displacements are still taken in steps, instead of degrees (which I believe is the expected behavior).

AlexShkarin commented 2 years ago

Thanks for the info!

Could you try running

bsc = Thorlabs.KinesisDevice('70262594', is_rack_system='auto')
print(bsc._is_rack_system)
print(bsc.get_device_info())
print(bsc._get_position())

and see if any of these lines work?

Regarding K10CR1: it uses a different hardware setup, so it generally communicates without issues. If you want to use degrees instead of steps for the displacement (and, correspondinly, velocity and acceleration), you can specify scale='stage' in the constructor, i.e., you would need to create it as

k10cr = Thorlabs.KinesisMotor('55263504', scale='stage')
AlexShkarin commented 2 years ago

A couple more comments:

pl-deassis commented 2 years ago

Hello Alex,

Your suggestion for the BSC203 worked, but only when using is_rack_system=True. I can now communicate with it, but giving commands to the drives is still very unstable. I got one of the three motors to move, once, but mostly they just sit there. I sometimes get error messages. For instance, I used _get_position(channel=1) and got an error, because the controller sent back 0x0464 (move completed) instead of 0x0412 (the result of get poscounter).

I also tried using ._home(channel=X) for X = 1, 2, and 3, but nothing happened.

AlexShkarin commented 2 years ago

I'm glad that at least general communication works! The rest should be easier to figure out now.

To make things easier for now, I'd suggest editing Lib\site-packages\pylablib\devices\Thorlabs\kinesis.py in your Python folder and replace lines 998-999, which currently should read

    def __init__(self, conn, scale="step"):
        super().__init__(conn)

with

    def __init__(self, conn, scale="step", default_channel=1, is_rack_system=False):
        super().__init__(conn,default_channel=default_channel,is_rack_system=is_rack_system)

With that change, you can get back to using more advanced KinesisMotor class with the same rack system specification:

bsc = Thorlabs.KinesisMotor('70262594', is_rack_system=True)

This way you don't have to put underscores for all the methods. It also properly handles messages like 0x0464, so it should work more stable now.

Regarding motion and homing, I have a couple of suggestions:

Let me know if any of this helps, and if any problems remain.

pl-deassis commented 2 years ago

Hello again, Alex.

While general comms are working, the handling of channels is not. I noticed the code you wrote sets channel 1 as default. I imagine the numbering of channels goes 0, 1, 2, corresponding to physical channels 1, 2, and 3. Indeed Channel 2 did respond to some commands, such as move_by, but failed to respond to home. After a while it stopped responding to commands and I had to power cycle the controller.

When I ran bsc.get_status(), I got ["homed","tracking","enabled"], so it seems there are still problems to solve. I tried using all commands with the (channel = ) argument, but no matter what I chose, everything went to channel 1.

Here's the result of a get_full_info():

{'velocity_parameters': TVelocityParams(min_velocity=0.0, acceleration=8191940.005849554, max_velocity=8193082.172131147), 'jog_parameters': TJogParams(mode='step', step_size=204800, min_velocity=0.0, acceleration=204796.22766257636, max_velocity=409654.1169895678, stop_mode='profiled'), 'homing_parameters': THomeParams(home_direction='reverse', limit_switch='reverse', velocity=409654.1169895678, offset_distance=40960), 'gen_move_parameters': TGenMoveParams(backlash_distance=4096), 'limit_switch_parameters': TLimitSwitchParams(hw_kind_cw='make', hw_kind_ccw='make', hw_swapped=False, sw_position_cw=1228800, sw_position_ccw=409600, sw_kind='ignore'), 'position': 10240000, 'status': ['homed', 'tracking', 'enabled'], 'cls': 'KinesisMotor', 'conn': {'port': '70262594', 'baudrate': 115200, 'bytesize': 8, 'parity': 'N', 'stopbits': 1, 'xonxoff': 0, 'rtscts': True}, 'device_info': TDeviceInfo(serial_no=70000000, model_no='BSC203', fw_ver='2.3.3', hw_type=32, hw_ver=2, mod_state=0, nchannels=3, notes='APT Stepper Motor Controller'), 'channel': [1], 'scale_units': 'step', 'scale': (1, 53.68, 0.0110011792), 'stage': 'step'}

The fact that I only get one set of parameters makes me thing that while it does understand that there are three channels, only one is actually being recognized. Is there a way to create three objects, one for each channel?

pl-deassis commented 2 years ago

Update:

The bsc.blink(channel = n) command works properly for n = 1, 2, and 3. However, for the move_by command, channel = 1 actually drives channel 2, and n = 3 returns an error message.

I noticed that the blink command sends a comm to dest = "host", while move_by sends comm data to dest = ("channel",n).

AlexShkarin commented 2 years ago

I did some checks with simulated devices, and it looks like I was misunderstanding how the channels work for these multi-rack systems. Evidentally, they are selected only via the destination address (dest parameter), while the command channel parameter is set to 1. This is why the second channel, for which channel=1, worked, while others didn't.

I have modified the code, and here you can get the new version of kinesis.py. Simply replace your current version with this one. In addition, it fixes the channels numbering issue (which now go from 1), and adds a method .set_supported_channels, which lets you specify the number of channels on your system. This way get_full_info method woudl return the data on all the axes, not just the first one, and most methods can take channels='all', which makes them execute simultaneously for all channels.

Let me know if this works for you, and if any issues remain. Also, if the devices continues to occasionally stop responding, could you see if there is any pattern, e.g., if it always stops after a particular command?

Techn0ph0bia commented 11 months ago

Hello, please tell me who used the thorlabs_aptdevice library how to correctly call the status method to get the position in millimeters!