简体   繁体   English

为什么从 MATLAB 调用此 python 脚本时无法正常工作?

[英]Why is this python script not working properly when called from MATLAB?

I am using a python script to control a stepper motor which drives a translation stage.我正在使用 python 脚本来控制驱动平移台的步进电机。
The stage has two limit switches, one near the motor, which sits on the bottom, and one away from the motor (top).平台有两个限位开关,一个靠近电机,位于底部,一个远离电机(顶部)。
I reference the motor at the limit switch near the motor, which sets the internal stepcount of the controller to 0 at that location.我在电机附近的限位开关处引用电机,该位置将 controller 的内部步数设置为 0。

I however, want the default 0 position to always be at the top.但是,我希望默认的 0 position 始终位于顶部。 So depending on which point I choose as reference, I have to recalculate the stepcount before sending it to the controller.因此,根据我选择作为参考的哪一点,我必须在将步数发送到 controller 之前重新计算步数。

Like so:像这样:

 if self.__reference_point == 'near':
            steps = steps - 50000

When I call the function move_absolute(0) the motor then always moves to the top.当我调用 function move_absolute(0)时,电机总是移动到顶部。

However, if I call this script from MATLAB with但是,如果我从 MATLAB 调用这个脚本

lt = py.lt_control.LT('COM4');
lt.do_referencing();
lt.move_absolute(0);

the stage moves back to the bottom.舞台移回底部。 So it seems the conversion of the stepcount fails somehow.所以似乎步数的转换以某种方式失败了。
Why is that?这是为什么?
I have python 3.7.8 x64 installed.我安装了 python 3.7.8 x64。
The stepper controller is a nanotec SMCI33-1, if that is useful.如果有用的话,步进器 controller 是 nanotec SMCI33-1。

Here is most of the python file, i removed irrelevant functions.这是 python 文件的大部分内容,我删除了不相关的功能。

import serial
import time
from decimal import Decimal
import signal

class MotorNotReferencedError(Exception):
    def __init__(self):
        super().__init__('Motor not referenced! Please call do_referencing()!')

class LT(object):
    
    def __init__(self, portname, reference='near', com_timeout=1):
        """
        Create a new motor control object.

        :param portname: The Port for the serial connection (e.g. 'COM4' for Windows, '/dev/ttyS1' for Linux)
        :param reference: Which limit switch will be used for referencing the motor, far means away from motor, near means near, coordinates are positive away from reference
        :param com_timeout: Timeout in sek for serial port
        """
        signal.signal(signal.SIGINT, self.sig_handler)
        signal.signal(signal.SIGTERM, self.sig_handler)
        self.__serial_port = serial.Serial(portname, 115200, timeout=com_timeout)
        self.__debug = False
        self.__substeps = 8 #does not do anything right now, for future
        self.__reference_point = reference
        self.__reference_changed = False
        self.__status = 1
        self.__positioning_error = 0
        self.setup_defaults()
        if not self.is_referenced():
            print('Referencing is required!')

    def __delete__(self, instance):
        if self.__serial_port:
            self.__serial_port.close()

    def __del__(self):
        if self.__serial_port:
            self.__serial_port.close()

    def sig_handler(self, sig, fr):
        self.stop()
        print('Keyboard Interrupt: Motor stopped!')

    def close(self):
        self.__serial_port.close()

    def query(self, message):
        """
        Send a message to the controller with newline char and reads response.

        :param message: The message to send, should be in proper syntax
        :returns: The response without newline
        :rtype: String
        """
        msg = message + '\r'
        if self.__debug: print(message)
        self.__serial_port.write(msg.encode())
        ans = self.__serial_port.read_until(b'\r').decode('utf-8')
        if len(ans) == 0:
            raise TimeoutError
        else:
            if self.__debug: print(ans.strip())
            return ans.strip()

    def is_referenced(self):
        """
        Check whether the motor is referenced.

        This is necessary after every loss of power to the controller
        :returns: True if motor is referenced
        :rtype: Boolean
        """
        ans = self.query('#1:is_referenced')
        if ans[-1] == '1' and not self.__reference_changed:
            return True
        else:
            return False

    def setup_defaults(self):
        self.command('#1g8') # microstepping, 8 microsteps per step
    
    def command(self, message):
        """
        Send commands to controller. Check if write was successful.

        This is simply a query wrapper that does a validity check on the commandsand checks the response of the controller. 

        :param message: Query like string, following the proper syntax
        :returns: True if operation was successful, else False
        :rtype: Boolean
        """
        ans = self.query(message)
        if ans[-1] == '?':
            return False
        else:
            return True

    def fetch_status(self):
        """
        Fetch status from controller, extract integer value, save positioning error and state to internal variable for later use.
        
        :returns: The status value
        :rtype: int
        .. seealso:: is_control_ready(), has_position_error()
        """
        #extract int value and mask only useful 4 bits
        tmp = self.query('#1$')[-3:]
        tmp = int(tmp)
        self.__status = tmp & 0xF
        #self.__status = int(self.query('#1$')[-3:]) & 0xF
        self.__positioning_error = (self.__status & 0b0100) >> 2
        return self.__status

    def is_control_ready(self):
        """
        Check if the control is ready for movement
        :returns: True if control is ready
        :rtype: Boolean
        """
        ans = self.fetch_status()
        if ans & 1:
            return True
        else:
            return False

    def has_positioning_error(self):
        """
        Check control if a positioning error has occured since last clear_positioning_error()
        :returns: True if a positioning error has occured
        :rtype: Boolean
        .. seealso:: clear_position_error
        """
        self.fetch_status()
        if self.__positioning_error:
            return True
        else:
            return False

    def clear_positioning_error(self, reset_position=False):
        """
        Clear the positioning error to reenable movement.

        :param reset_postiton: False (default): Absolute position does not change, True: Set current absolute position to 0
        """
        self.__positioning_error = 0
        if reset_position:
            self.query('#1D0')#1C')
            ans = ans[2:] # extract position
            self.query('#1D' + ans) # clear error and set position

    def steps_to_mm(self, steps):
        """
        Converts the number of steps to distance in millimeters

        :param steps: The number of steps
        :returns: The distance in millimeters
        """
        steps_per_turn = self.__substeps * 200
        return (1.25/steps_per_turn) * steps

    def mm_to_steps(self, mm):
        """
        Converts the distance in millimeters to number of steps

        :param mm: The distance in millimeters
        :returns: The number of steps
        """
        steps_per_turn = self.__substeps * 200
        return int(round(Decimal((steps_per_turn/1.25) * mm)))

    def wait_movement(self):
        """
        Wait for movement ot finish.
        """
        while not self.is_control_ready():
                time.sleep(0.1)       
        if self.has_positioning_error():
            # in endschalter gelaufen
            # reset position
            self.clear_positioning_error()
            print('Movement ended prematurely, limit switch engaged!')

    def stop(self):
        """
        Immediately stops the motor
        """
        self.command('#1S')

    def set_reference_point(self, reference):
        """
        Redefine the reference point.

        :param reference: Which limit switch will be used for referencing the motor, 'far' means away from motor, 'near' means near, coordinates are positive away from reference
        """
        self.__reference_point = reference
        self.__reference_changed = True
        print('Please reference motor again!')

    def do_referencing(self):
        """
        Reference the motor to the limit switch defined by the reference
        point.
        """
        self.command('#1p4')
        self.command('#1l5154') # endschalterverhalten auf r�ckw�rts vom schalter runterfahren
        if self.__reference_point == 'near':
            self.command('#1d1')
        else:
            self.command('#1d0')
        self.command('#1o4000')
        self.command('#1A')
        self.wait_movement()
        self.__reference_changed = False
        
    def move_relative(self, steps, speed=4000):
        """Move the Stage relative to its current position.

        :param steps: number of steps to travel, max 50000
        steps < 0: Movement towards reference
        steps > 0: Movemento away from reference
        :param speed: Speed in steps/s (max 16000)
        """
        if abs(steps) > 50000:
            print("Relative Movement: Too many steps!")
            return
        if self.__reference_point == 'far':
            self.command('#1d1')
        else:
            self.command('#1d0')
        self.command('#1o' + str(speed))
        self.command('#1s' + str(steps))
        self.command('#1A')
        self.wait_movement()

    def move_absolute(self, steps, speed=4000):
        """Move the Stage to absolute position, 0 is opposite of motor.

        :param steps: Absolute position between 0 and 50000
        :param speed: Speed in steps/s
        """
        if not self.is_referenced():
            raise MotorNotReferencedError
        if abs(steps) > 50000:
            print('Absolute Movement: Too many steps!')
            return
        print("" + str(steps))
        if not self.__reference_point == 'far':
            steps = steps - 50000
        print("" + str(steps))
        self.command('#1p2')
        self.command('#1o' + str(speed))
        self.command('#1s' + str(steps))
        self.command('#1A')
        self.wait_movement()

    def move_relative_mm(self, distance_mm, speed=3):
        """Move the Stage relative to its current position.

        :param distance_mm: travel distance in millimeters
         < 0: Movement away from motor
         > 0: Movement towards motor
        :param speed: Speed in mm/s
        """
        steps = self.mm_to_steps(distance_mm)
        speed = self.mm_to_steps(speed)
        self.move_relative(steps, speed)

    def move_absolute_mm(self, position_mm, speed=3):
        """Move the Stage to absolute position, 0 ist limit switch opposite of motor.

        :param position_mm: Absolute position between 0 and 39
        :param speed: Speed in mm/s
        """
        if not self.is_referenced():
            raise MotorNotReferencedError
        steps = self.mm_to_steps(position_mm)
        speed = self.mm_to_steps(speed)
        self.move_absolute(steps, speed)

    def get_position(self):
        """Return absolute position in steps.

        :returns: absolute position in steps
        """
        real_pos = int(self.query('#1C')[2:])
        if self.__reference_point == 'far':
            disp_pos = real_pos
        else:
            disp_pos = real_pos + 50000
        return disp_pos

So it seems, that str(steps) returns the floating point representation '0.0' when called from MATLAB, where it needs to be an integer '0' to work.所以看起来, str(steps)在从 MATLAB 调用时返回浮点表示'0.0' ,它需要是 integer '0'才能工作。

Changing it to str(int(steps)) fixed the problem.将其更改为str(int(steps))解决了问题。
But its strange that the normal python interpreter didn't have a problem with that.但奇怪的是,普通的 python 解释器对此没有问题。

As Cris Luengo pointed out: when calling lt.move_absolute(0) from MATLAB, the 0 is a floating point number.正如 Cris Luengo 指出的:当从 MATLAB 调用lt.move_absolute(0)时, 0是一个浮点数。 This is the difference to calling the same expression from a python command line.这是从 python 命令行调用相同表达式的区别。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 函数被方法调用时无法正常工作:Python - Function not working properly when called by a method : Python 从 VBA 调用的 Python 脚本从 Python 调用不工作 - Python script called from VBA called from Python is not working 当从python脚本调用时,为什么我的bash脚本不能从文件中读取行? - Why doesn't my bash script read lines from a file when called from a python script? 从python调用时挂起shell脚本 - Hung shell script when called from python 为什么 Python 脚本可以从 CLI 运行,但从 cron 作业调用时却不能运行? - Why does a Python script work from the CLI, but not when called from a cron job? 为什么在使用“来自tensorflow import keras”调用python脚本时,MATLAB会产生错误? - Why does MATLAB produce an error when calling a python script with “from tensorflow import keras”? 从python脚本调用时,从shell脚本返回值 - Return a value from shell script when it is called from a python script Hive在Python脚本中调用时尝试读取当前工作目录 - Hive trying to read current working directory when called in Python script 当任务计划程序调用时,Python脚本无法正常工作 - Python script not working as intended when called by task scheduler 当通过对Matlab中的操作系统的命令在python脚本中调用Wand Python模块时,它似乎不起作用 - Wand Python module does not seem to work when called in a python script through a command to the operating system in matlab
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM