python串行读取的CPU负载

Posted

技术标签:

【中文标题】python串行读取的CPU负载【英文标题】:CPU load of python serial read 【发布时间】:2020-06-11 09:43:15 【问题描述】:

我有一个应用程序,我需要从 Python 线程中的串行端口读取数据。由于它被放置在一个线程中,我虽然可以使用:

timeout = None

并在线程中使用以下代码:

# Setup
ser = serial.Serial(port, 38400, timeout = None)

# Loop
data = ser.read(2)
if data[0] == 2:
    data += ser.read(data[1]-2) # Expected length of message
    return data
else: # Handle error message

但是,当我将其与其他线程的频率进行比较时,我发现频率增加了近 200%,而是使用:

# Setup
ser = serial.Serial(port, 38400, timeout = 0)

# Loop
data = ser.read(1)
if data and data[0] == 2:
    while len(data) < 2:
        data += ser.read(1)
    while len(data) < data[1]:
        data += ser.read(1)
    return data
else: # Handle error message

此外,在模拟过程中,数据被收集,然后使用套接字“重放”,当使用套接字时,我得到了更好的结果,让我相信我应该能够提高整体频率该应用程序甚至是实时的。但是我找不到任何关于 Python 中串行读取时间复杂度的相关讨论。

编辑:

经过进一步的修补和测试,我仍然无法找出读取串行端口的最佳方法......关于时间复杂度读取串行端口的最佳方法是否有任何“正确”答案?

// 雅各布

【问题讨论】:

你确定数据的结尾是可靠的吗?我总是等待一个特殊字符,因为在串行通信期间总是会超时 【参考方案1】:

没有足够的信息来确切地知道发生了什么,但您似乎正在尝试接收采用以下形式之一的消息:

“数据”消息,如下所示:| 2 | Length | d1 | d2 | ... | dN | 一些错误指示...您没有提到,但可能只是一个不是 2 的字节?

在您的第一个实现中,data = ser.read(2) 行将“永远等待,直到收到至少两个字节”。但是如果发送一个 1 字节的消息,那么代码将一直等待,直到收到另一条消息。

虽然没有“正确”的方式从串口读取数据,但您可能想尝试将字节接收和消息解析分开。

例如,你可能有这样的事情:

from collections import deque


def receive_bytes(ser, buffer):
    """Read bytes from serial port and append them to the buffer"""
    data = ser.read(1)
    while data:
        buffer.append(data[0])
        data = ser.read(1)


def consume_messages(buffer):
    """Consume available messages from the buffer destructively"""

    # This loop uses the first byte as the key for what
    # message should be in the buffer. We only consume
    # a message from the buffer when it is present in
    # its entirety (i.e. we only read a data mesage once
    # all the bytes are present).
    messages = []
    while len(buffer) > 0:
        # Check if we have a data message
        if buffer[0] == 2 and len(buffer) > 1:
            data_message_len = buffer[1]
            if len(buffer) - 2 > data_message_len:
                # looks like enough info for a data message
                buffer.popleft()  # the 2 byte
                buffer.popleft()  # the length byte
                data_message = [
                    buffer.popleft() for _ in range(data_message_len)
                ]
                messages.append(data_message)

        # Check if we have a "1" message
        elif buffer[0] == 1:
            messages.append(buffer.popleft())

        else:
            # looks like the first byte is something we don't
            # know about...likely a synchronization error, so just
            # drop it
            buffer.popleft()

    return messages


def handle_messages(messages):
    for m in messages:
        print(m)


if __name__ == "__main__":
    # setup
    ser = serial.Serial(port, 38400, timeout=0)
    buf = deque()

    # loop
    while True:
        receive_bytes(ser, buf)
        messages = consume_messages(buf)
        handle_messages(messages)

FWIW,我认为时间复杂度与此无关。原始代码的阻塞特性以及应用程序代码、串行库和底层操作系统之间的交互都是比时间复杂度更大的因素。

基本上...您的计算机比控制串行端口的硬件快得多,因此与没有超时的检查相比,使用阻塞等待方法通常会浪费更多的计算时间 - 即使您正在检查数千/数百万字节之间的时间。

【讨论】:

我对复杂性有同样的想法,但是在模拟过程中我使用内部套接字而不是串行端口,并且使用内部套接字时应用程序的更新频率要高得多。而且由于大多数应用程序都受 CPU 限制,这表明 CPU 读取串行端口会产生一些负载。 我猜是好点,但可能是底层操作系统检查其网络堆栈以查看是否有 2 个字节可用,而不是检查您的串行硬件(这可能是USB上的串行端口仿真器对吗?)。如果您只要求 1 个字节,如果您发现阻塞检查之间的差异会很有趣。 我使用的是 USB 串口。该应用程序是自动叉车的实时导航系统,通过串行端口发送数据的传感器是车轮编码器【参考方案2】:

尝试强制串口等待 I/O ser.read(ser.inWaiting() + 32)

我看到 serial_port.read(serial_port.in_waiting) 读取速度非常快,它避免了缓冲区中字节累积的问题

如果你在 Python3 上,那么 ser.read() 返回字节,所以任何比较都是错误的。调试:data[0] == 2 需要是 data[0:1] == b'2'

玩:

ser = serial.Serial(port, 38400)
while(True):        
    print('ser_is_open')
    ser.read(ser.in_waiting() + 32)                   
    print('ser_in_waiting')

【讨论】:

在这种情况下,串行 I/O 是一个轮式编码器,如果将多个消息堆叠在缓冲区中,那么读取多个消息似乎是个坏主意。我正在使用 python 3,data[0] == 2 效果很好......

以上是关于python串行读取的CPU负载的主要内容,如果未能解决你的问题,请参考以下文章

python通过内置模块监控磁盘内存CPU负载

Linux服务器CPU内存磁盘空间负载情况查看python脚本

【Linux】Linux系统下简单模拟高CPU\高内存\高负载的方法

CPU负载监控

python实现对本机和批量远程服务器的cpu负载的获取,怎么实现

树莓派CPUGPU磁盘内存负载监控Python脚本