Bootstrap

python使用serial模块,通过串口控制云台(基于PELCO-D协议)

一、需求描述

通过python实现对云台的控制,使用到的相关模块:

1、pyserial,串口模块,用于连接串口,并通过串口发送指令

2、bytes,内置模块,用于将16进制的指令转化成字节流

二、实现代码:版本v1

1、详细过程见代码备注,版本v2,请查阅“四”

2、关键一步是发送数据时的处理,使用到了bytes,请查阅“三”

3、环境搭建根据云台的说明书进行,这里采用的是串口连接设备

import serial
from common.config import serial_com


class SerialHandler(object):
    def __init__(self, port=serial_com, baudrate=9600, parity="无", bytesize=8, stopbits=1):
        """
        串口初始化,设置串口相关参数
        :param port: str类型, 例:COM1
        :param baudrate: int类型,取值范围:[4800, 9600, 19200, 18400, 57600, 115200]
        :param parit: str类型,N:"无", O:"奇", E:"偶"
        :param bytesize: int类型,取值范围:[7, 8]
        :param stopbits: int类型,取值范围:[1, 2]
        """
        # 根据上位机显示,转换成serial库识别的参数
        if parity == "无":
            parity = 'N'
        elif parity == "奇":
            parity = 'O'
        elif parity == "偶":
            parity = 'E'
        else:
            pass
        self.serial_handler = serial.Serial()
        self.serial_handler.port = port
        self.serial_handler.baudrate = baudrate
        self.serial_handler.bytesize = bytesize
        self.serial_handler.parity = parity
        self.serial_handler.stopbits = stopbits
        # 连接若超时1秒,则结束连接
        self.serial_handler.timeout = 15

    def open_and_write(self):
        '''
        # PELCO-D协议:
        一、数据格式:
        8位数据位、1位停止位,无效验位。波特率:2400
        二、命令格式:
        字节1       字节2     字节3   字节4    字节5    字节6   字节7
        同步字节    地址码   指令码1  指令码2  数据码1  数据码2  校验码
        三、命令解释:
        1.协议采用十六进制
        2.同步字节始终为FF
        3.地址码为云台地址号,地址范围请了解云台说明
        4.指令码用于表示不同的行为
        5.数据码1表示水平速度,2分别表示垂直速度
        6.校验码 = MOD[(字节2~6的值相加)/100H]
        四、命令例子:
        # 右下角:FF 01 00 12 20 20 53
        # 左上角:FF 01 00 0C 20 20 4D
        # 左下角:FF 01 00 14 20 20 55
        # 右上角:FF 01 00 0A 20 20 4B
        # 左:FF 01 00 04 20 00 25
        # 上:FF 01 00 08 00 20 29
        # 右:FF 01 00 02 20 00 23
        # 下:FF 01 00 10 00 20 31
        # 停止指令:FF 01 00 00 00 00 01
        :return:无
        '''
        self.serial_handler.timeout = 0.05 # 云台转动的时间,单位为秒
        self.serial_handler.open()
        # bytes().fromhex("FF 01 00 0A 20 20 4B")
        self.serial_handler.write(bytes().fromhex("FF 01 00 0A 20 20 4B"))
        res = self.serial_handler.readline()
        print(res)
        self.serial_handler.write(bytes().fromhex("FF 01 00 00 00 00 01")) # 停止转动指令,如果没有,则走到世界尽头
        # b"ff01000c20204d"
        # chr(0x06).encode("utf-8")
        # self.serial_handler.in_waiting(0xff010008002029)
        # self.serial_handler.write(b'FF010000000001')
        print("串口设置:", self.serial_handler)
        print("我请求了!!!")
        res = self.serial_handler.readline()
        print(res)
        self.serial_handler.close()

if __name__ == '__main__':
    # 控制云台
    SerialHandler(port="COM5", baudrate=2400, parity="无", bytesize=8, stopbits=1).open_and_write()

三、bytes()的用法:官方文档

https://docs.python.org/zh-cn/3.7/library/stdtypes.html#binary-sequence-types-bytes-bytearray-memoryview

 摘要(其实就是复制粘贴):

二进制序列类型 --- bytesbytearraymemoryview

操作二进制数据的核心内置类型是 bytes 和 bytearray。 它们由 memoryview 提供支持,该对象使用 缓冲区协议 来访问其他二进制对象所在内存,不需要创建对象的副本。

array 模块支持高效地存储基本数据类型,例如 32 位整数和 IEEE754 双精度浮点值。

bytes 对象

bytes 对象是由单个字节构成的不可变序列。 由于许多主要二进制协议都基于 ASCII 文本编码,因此 bytes 对象提供了一些仅在处理 ASCII 兼容数据时可用,并且在许多特性上与字符串对象紧密相关的方法。

class bytes([source[, encoding[, errors]]])

首先,表示 bytes 字面值的语法与字符串字面值的大致相同,只是添加了一个 b 前缀:

  • 单引号: b'同样允许嵌入 "双" 引号'

  • 双引号: b"同样允许嵌入 '单' 引号"

  • 三重引号: b'''三重单引号'''b"""三重双引号"""

bytes 字面值中只允许 ASCII 字符(无论源代码声明的编码为何)。 任何超出 127 的二进制值必须使用相应的转义序列形式加入 bytes 字面值。

像字符串字面值一样,bytes 字面值也可以使用 r 前缀来禁用转义序列处理。 请参阅 字符串和字节串字面值 了解有关各种 bytes 字面值形式的详情,包括所支持的转义序列。

虽然 bytes 字面值和表示法是基于 ASCII 文本的,但 bytes 对象的行为实际上更像是不可变的整数序列,序列中的每个值的大小被限制为 0 <= x < 256 (如果违反此限制将引发 ValueError)。 这种限制是有意设计用以强调以下事实,虽然许多二进制格式都包含基于 ASCII 的元素,可以通过某些面向文本的算法进行有用的操作,但情况对于任意二进制数据来说通常却并非如此(盲目地将文本处理算法应用于不兼容 ASCII 的二进制数据格式往往将导致数据损坏)。

除了字面值形式,bytes 对象还可以通过其他几种方式来创建:

  • 指定长度的以零值填充的 bytes 对象: bytes(10)

  • 通过由整数组成的可迭代对象: bytes(range(20))

  • 通过缓冲区协议复制现有的二进制数据: bytes(obj)

另请参阅 bytes 内置类型。

由于两个十六进制数码精确对应一个字节,因此十六进制数是描述二进制数据的常用格式。 相应地,bytes 类型具有从此种格式读取数据的附加类方法:

classmethod fromhex(string)

此 bytes 类方法返回一个解码给定字符串的 bytes 对象。 字符串必须由表示每个字节的两个十六进制数码构成,其中的 ASCII 空白符会被忽略。

>>> bytes.fromhex('2Ef0 F1f2  ')
b'.\xf0\xf1\xf2'

在 3.7 版更改: bytes.fromhex() 现在会忽略所有 ASCII 空白符而不只是空格符。

存在一个反向转换函数,可以将 bytes 对象转换为对应的十六进制表示。

hex()

返回一个字符串对象,该对象包含实例中每个字节的两个十六进制数字。

>>> b'\xf0\xf1\xf2'.hex()
'f0f1f2'

3.5 新版功能.

由于 bytes 对象是由整数构成的序列(类似于元组),因此对于一个 bytes 对象 bb[0] 将为一个整数,而 b[0:1] 将为一个长度为 1 的 bytes 对象。 (这与文本字符串不同,索引和切片所产生的将都是一个长度为 1 的字符串)。

bytes 对象的表示使用字面值格式 (b'...'),因为它通常都要比像 bytes([46, 46, 46]) 这样的格式更好用。 你总是可以使用 list(b) 将 bytes 对象转换为一个由整数构成的列表。

注解

针对 Python 2.x 用户的说明:在 Python 2.x 系列中,允许 8 位字符串( 2.x 所提供的最接近内置二进制数据类型的对象)与 Unicode 字符串进行各种隐式转换。 这是为了实现向下兼容的变通做法,以适应 Python 最初只支持 8 位文本而 Unicode 文本是后来才被加入这一事实。 在 Python 3.x 中,这些隐式转换已被取消 —— 8 位二进制数据与 Unicode 文本间的转换必须显式地进行,bytes 与字符串对象的比较结果将总是不相等。

四、实现代码:版本v2

1、新增功能:直接设置水平、俯仰角度

2、封装代码

import serial
from common.config import serial_com  # serial_com = "COM6"


class SerialHandler(object):
    def __init__(self, port=serial_com, baudrate=9600, parity="无", bytesize=8, stopbits=1):
        """
        串口初始化,设置串口相关参数
        :param port: str类型, 例:COM1
        :param baudrate: int类型,取值范围:[4800, 9600, 19200, 18400, 57600, 115200]
        :param parit: str类型,N:"无", O:"奇", E:"偶"
        :param bytesize: int类型,取值范围:[7, 8]
        :param stopbits: int类型,取值范围:[1, 2]
        """
        # 根据上位机显示,转换成serial库识别的参数
        if parity == "无":
            parity = 'N'
        elif parity == "奇":
            parity = 'O'
        elif parity == "偶":
            parity = 'E'
        else:
            pass
        self.serial_handler = serial.Serial()
        self.serial_handler.port = port
        self.serial_handler.baudrate = baudrate
        self.serial_handler.bytesize = bytesize
        self.serial_handler.parity = parity
        self.serial_handler.stopbits = stopbits
        # 连接若超时1秒,则结束连接
        self.serial_handler.timeout = 15

    def set_angle(self, devel_angle=None, elevation_angle=None):

        self.serial_handler.timeout = 0.5 # 云台转动的时间,单位为秒

        self.serial_handler.open()
        if devel_angle != None:
            # 右下角:FF 01 00 12 20 20 53
            # 左上角:FF 01 00 0C 20 20 4D
            # 40
            # FF 01 00 4D 0F A0 FD
            # FF01004D0FA0FD
            # 20
            # FF 01 00 4D 07 D0 25
            # FF01004D07D025

            devel_num = self.__devel(devel_angle)
            # print(hex(devel_num)[2:])
            d = hex(devel_num)[2:].upper()
            self.serial_handler.write(bytes().fromhex(d))
            self.serial_handler.readline()
            # self.serial_handler.write(bytes().fromhex("FF010000000001"))
        if elevation_angle != None:
            elevation_num = self.__elevation(elevation_angle)
            # print(hex(elevation_num)[2:])
            e = hex(elevation_num)[2:].upper()
            self.serial_handler.write(bytes().fromhex(e))
            self.serial_handler.readline()
            # self.serial_handler.write(bytes().fromhex("FF010000000001"))
        self.serial_handler.close()

    def open_and_write(self):
        # 已弃用,做测试使用
        '''
        # PELCO-D协议:
        一、数据格式:
        1位起始位、8位数据、1位停止位,无效验位。波特率:2400B/S
        二、命令格式:
        字节1       字节2     字节3   字节4    字节5    字节6   字节7
        同步字节    地址码   指令码1  指令码2  数据码1  数据码2  校验码
        三、命令解释:
        1.该协议中所有数值都为十六进制数
        2.同步字节始终为FF
        3.地址码为云台地址号,地址范围请了解云台说明
        4.指令码表示不同的动作
        5.数据码1、2分别表示水平、垂直方向速度(00-3FH),FFH表示“turbo”速度
        6.校验码 = MOD[(字节2 + 字节3 + 字节4 + 字节5 + 字节6)% 0x100]
        四、命令例子:
        # 右下角:FF 01 00 12 20 20 53
        # 左上角:FF 01 00 0C 20 20 4D
        # 左下角:FF 01 00 14 20 20 55
        # 右上角:FF 01 00 0A 20 20 4B
        # 左:FF 01 00 04 20 00 25
        # 上:FF 01 00 08 00 20 29
        # 右:FF 01 00 02 20 00 23
        # 下:FF 01 00 10 00 20 31
        # 停止指令:FF 01 00 00 00 00 01
        :return:
        '''
        self.serial_handler.timeout = 0.5 # 云台转动的时间,单位为秒
        self.serial_handler.open()
        # bytes().fromhex("FF 01 00 0A 20 20 4B")
        # FF01004D1388E9
        # FF01004D03E839
        self.serial_handler.write(bytes().fromhex("FF 01 00 14 20 20 55"))  # FF 01 00 4D 13 88 E9  :  FF01004B13884C
        res = self.serial_handler.readline()
        print(res)
        self.serial_handler.write(bytes().fromhex("FF010000000001")) # 停止转动指令,如果没有,则走到世界尽头
        # b"ff01000c20204d"
        # chr(0x06).encode("utf-8")
        # self.serial_handler.in_waiting(0xff010008002029)
        # self.serial_handler.write(b'FF010000000001')
        print("串口设置:", self.serial_handler)
        print("我请求了!!!")
        res = self.serial_handler.readline()
        print(res)
        self.serial_handler.close()

    def request(self, bytes_data=b'start'):
        '''
        通过串口发送字节数据,并返回字节类型的响应结果
        :param bytes_data:
        :return:
        '''
        print("串口设置:", self.serial_handler)
        print("请求数据:", bytes_data)
        self.serial_handler.open()
        self.serial_handler.write(bytes_data)
        res_list = []
        res_str = b""
        while 1:
            res = self.serial_handler.readline()
            print("res的值:",res)
            break
            # if res == b'':
            #     print("连接串口失败或没有响应数据!")
            #     break
            # elif res == b' ' or res == b"\x90":
            #     for ele in res_list:
            #         res_str += ele
            #     print("返回结果:", res_str)
            #     break
            # else:
            #     print("每一次返回结果:", res)
            #     res_list.append(res)
        self.serial_handler.close()
        # 返回二进制结果
        return res

    def __angle_per_num(self):
        # 返回:度/1数值
        d_min = 0x0000
        d_max = 0x8CA0
        differ = d_max - d_min  # 36000
        # print(differ)
        average = 360/differ  # 数值1则代表0.01度
        return average

    def __angle_to_num(self, angle):
        # 返回:num角度对应的数值(十进制)
        average = self.__angle_per_num()
        num = angle/average
        # print(int(num))
        return int(num)

    def __devel(self, angle):
        # 返回水平数值(十进制)
        # 将角度转换成数值
        num = self.__angle_to_num(angle)
        # 高位数值
        h = 0XFF01004B000000
        # print(h)
        # 中间数值
        middle = num*0x100
        # print(middle)
        # 末尾数值
        num_h_m = h+middle # 获取除校验位的所有数值
        check_num = self.__check_code(num_h_m)
        return h+middle+check_num

    def __elevation(self, angle):
        # 返回仰角数值(十进制)
        # 将角度转换成数值
        num = self.__angle_to_num(angle)
        # 高位数值
        h = 0XFF01004D000000
        # print(h)
        # 中间数值
        middle = num*0x100
        # print(middle)
        # 末尾数值
        num_h_m = h+middle # 获取除校验位的所有数值
        check_num = self.__check_code(num_h_m)
        return h+middle+check_num

    def __check_code(self, num):
        # 返回:校验码(十进制),算法是:校验码 = MOD[(字节2 + 字节3 + 字节4 + 字节5 + 字节6)/100H])
        num_hex = hex(num)
        # print(num_hex)  # 0xff01004b8ca078
        num_hex_deal = num_hex[4:-2]  # 掐头去尾01004b8ca0
        # print(num_hex_deal)
        num_sum = sum([int(num_hex_deal[i:i+2], 16) for i in range(len(num_hex_deal)) if i % 2 == 0])
        check_num = num_sum % 0x100
        # print(check_num)
        return check_num



if __name__ == '__main__':

    # 控制云台
    SerialHandler(port="COM6", baudrate=2400, parity="无", bytesize=8, stopbits=1).set_angle(66, 19.88)

五、实现代码:版本v3

1、实现角度获取功能

2、变更了部分方法

3、关键方法:将返回字节数值进行转换(对小白很蓝的啦,特地拿出来)

binascii.b2a_hex(res).decode()
# -*- coding: UTF-8 -*-
# coding=gb18030
'''控制串口,也控制串口依赖的设备,如云台'''
import struct
import threading
import time
import serial
from common.config import serial_com
import binascii


class SerialHandler(object):
    def __init__(self, port=serial_com, baudrate=9600, parity="无", bytesize=8, stopbits=1):
        """
        串口初始化,设置串口相关参数
        :param port: str类型, 例:COM1
        :param baudrate: int类型,取值范围:[4800, 9600, 19200, 18400, 57600, 115200]
        :param parit: str类型,N:"无", O:"奇", E:"偶"
        :param bytesize: int类型,取值范围:[7, 8]
        :param stopbits: int类型,取值范围:[1, 2]
        """
        # 根据上位机显示,转换成serial库识别的参数
        if parity == "无":
            parity = 'N'
        elif parity == "奇":
            parity = 'O'
        elif parity == "偶":
            parity = 'E'
        else:
            pass
        self.serial_handler = serial.Serial()
        self.serial_handler.port = port
        self.serial_handler.baudrate = baudrate
        self.serial_handler.bytesize = bytesize
        self.serial_handler.parity = parity
        self.serial_handler.stopbits = stopbits
        # # 连接若超时n秒,则结束连接
        self.serial_handler.timeout = 15

    def set_angle(self, devel_angle=None, elevation_angle=None):

        self.serial_handler.timeout = 0.5 # 云台转动的时间,单位为秒

        self.serial_handler.open()
        if devel_angle != None:
            # 右下角:FF 01 00 12 20 20 53
            # 左上角:FF 01 00 0C 20 20 4D
            # 40
            # FF 01 00 4D 0F A0 FD
            # FF01004D0FA0FD
            # 20
            # FF 01 00 4D 07 D0 25
            # FF01004D07D025

            devel_num = self.__devel(devel_angle)
            # print(hex(devel_num)[2:])
            d = hex(devel_num)[2:].upper()
            self.serial_handler.write(bytes().fromhex(d))
            self.serial_handler.readline()
            # self.serial_handler.write(bytes().fromhex("FF010000000001"))
        if elevation_angle != None:
            elevation_num = self.__elevation(elevation_angle)
            # print(hex(elevation_num)[2:])
            e = hex(elevation_num)[2:].upper()
            self.serial_handler.write(bytes().fromhex(e))
            self.serial_handler.readline()
            # self.serial_handler.write(bytes().fromhex("FF010000000001"))
        self.serial_handler.close()

    def open_and_write(self, cmd="FF 01 00 14 20 20 55"):
        # 更改为写入信息并返回角度
        '''
        # PELCO-D协议:
        一、数据格式:
        1位起始位、8位数据、1位停止位,无效验位。波特率:2400B/S
        二、命令格式:
        字节1       字节2     字节3   字节4    字节5    字节6   字节7
        同步字节    地址码   指令码1  指令码2  数据码1  数据码2  校验码
        三、命令解释:
        1.该协议中所有数值都为十六进制数
        2.同步字节始终为FF
        3.地址码为云台地址号,地址范围请了解云台说明
        4.指令码表示不同的动作
        5.数据码1、2分别表示水平、垂直方向速度(00-3FH),FFH表示“turbo”速度
        6.校验码 = MOD[(字节2 + 字节3 + 字节4 + 字节5 + 字节6)/100H]
        四、命令例子:
        # 右下角:FF 01 00 12 20 20 53
        # 左上角:FF 01 00 0C 20 20 4D
        # 左下角:FF 01 00 14 20 20 55
        # 右上角:FF 01 00 0A 20 20 4B
        # 左:FF 01 00 04 20 00 25
        # 上:FF 01 00 08 00 20 29
        # 右:FF 01 00 02 20 00 23
        # 下:FF 01 00 10 00 20 31
        # 停止指令:FF 01 00 00 00 00 01
        :return:
        '''
        self.serial_handler.timeout = 1 # 云台转动的时间,单位为秒
        self.serial_handler.open()
        # bytes().fromhex("FF 01 00 0A 20 20 4B")
        # FF01004D1388E9
        # FF01004D03E839
        self.serial_handler.write(bytes().fromhex(cmd))  # FF 01 00 4D 13 88 E9  :  FF01004B13884C
        res = self.serial_handler.read(100)
        # print(res)
        # b"ff01000c20204d"
        # chr(0x06).encode("utf-8")
        # self.serial_handler.in_waiting(0xff010008002029)
        # self.serial_handler.write(b'FF010000000001')
        # print("串口设置:", self.serial_handler)
        # print("我请求了!!!")
        # self.serial_handler.write(bytes().fromhex("FF010000000001")) # 停止转动指令,如果没有,则走到世界尽头
        # res = self.serial_handler.readline()
        # print(res)
        self.serial_handler.close()
        # print(binascii.b2a_hex(res).decode())
        res_str = binascii.b2a_hex(res).decode()[8:12]  # 字节转换成字符串,并截取角度部分
        # print(res_str)
        res_16 = int(res_str, 16)  # 转化成16进制
        return res_16*self.__angle_per_num()

    # 水平
    def get_devel(self, cmd="FF 01 00 51 00 00 52"):
        # print("水平")
        # 查询指令:  FF 01 00 51 00 00 52
        res = self.open_and_write(cmd)
        return res

    # 仰角
    def get_elevation(self, cmd="FF 01 00 53 00 00 54"):
        # print("仰角")
        # 查询指令:  FF 01 00 53 00 00 54
        res = self.open_and_write(cmd)
        return res

    def request(self, bytes_data=b'start'):
        '''
        通过串口发送字节数据,并返回字节类型的响应结果
        :param bytes_data:
        :return:
        '''
        try:
            print("串口设置:", self.serial_handler)
            print("请求数据:", bytes_data)
            self.serial_handler.open()
            self.serial_handler.write(bytes_data)
            res_list = []
            res_str = b""
            while 1:
                res = self.serial_handler.readline()
                print("res的值:",res)
                break
                # if res == b'':
                #     print("连接串口失败或没有响应数据!")
                #     break
                # elif res == b' ' or res == b"\x90":
                #     for ele in res_list:
                #         res_str += ele
                #     print("返回结果:", res_str)
                #     break
                # else:
                #     print("每一次返回结果:", res)
                #     res_list.append(res)
            self.serial_handler.close()
            # 返回二进制结果
            return res
        except:
            print("串口出问题了,请检查串口相关配置和硬件连线")
            # return res
            # raise IOError("串口出问题了,请检查串口相关配置和硬件连线")


    def request_for_connect(self, bytes_data=b'start'):
        '''
        专门设计一个函数供给逻辑函数使用
        通过串口发送字节数据,并返回字节类型的响应结果
        :param bytes_data:
        :return:
        '''
        try:
            print("串口设置:", self.serial_handler)
            print("请求数据:", bytes_data)
            self.serial_handler.open()
            self.serial_handler.write(bytes_data)
            res_list = []
            res_str = b""
            num = 0
            time_0 = time.time()
            # while 1:
            # print("qqqqqqqqqqqqqqqqqqqqqq")
            res = self.serial_handler.read(10000)
            # print("sdsdsdsdsdsdsdsdsdsdsdsdsdsdsdsdsdsdsdsdsdsdsdsd")
            print("res的值:",res)
            # time_1 = time.time()
            # break
            # if res == b'':
            #     print("连接串口失败或没有响应数据!")
            #     if time_1 - time_0 >= num:
            #         break
            # elif res == b' ' or res == b"\x90":
            #     for ele in res_list:
            #         res_str += ele
            #     print("返回结果:", res_str)
            #     if time_1 - time_0 >= num:
            #         break
            # else:
            #     print("每一次返回结果:", res)
            #     res_list.append(res)
            self.serial_handler.close()
            # 返回二进制结果
            return res
        except:
            print("串口出问题了,请检查串口相关配置和硬件连线")

    def __angle_per_num(self):
        # 返回:度/1数值
        d_min = 0x0000
        d_max = 0x8CA0
        differ = d_max - d_min  # 36000
        # print(differ)
        average = 360/differ  # 数值1则代表0.01度
        return average

    def __angle_to_num(self, angle):
        # 返回:num角度对应的数值(十进制)
        average = self.__angle_per_num()
        num = angle/average
        # print(int(num))
        return int(num)

    def __devel(self, angle):
        # 返回水平数值(十进制)
        # 将角度转换成数值
        num = self.__angle_to_num(angle)
        # 高位数值
        h = 0XFF01004B000000
        # print(h)
        # 中间数值
        middle = num*0x100
        # print(middle)
        # 末尾数值
        num_h_m = h+middle # 获取除校验位的所有数值
        check_num = self.__check_code(num_h_m)
        return h+middle+check_num

    def __elevation(self, angle):
        # 返回仰角数值(十进制)
        # 将角度转换成数值
        num = self.__angle_to_num(angle)
        # 高位数值
        h = 0XFF01004D000000
        # print(h)
        # 中间数值
        middle = num*0x100
        # print(middle)
        # 末尾数值
        num_h_m = h+middle # 获取除校验位的所有数值
        check_num = self.__check_code(num_h_m)
        return h+middle+check_num

    def __check_code(self, num):
        # 返回:校验码(十进制),算法是:校验码 = MOD[(字节2 + 字节3 + 字节4 + 字节5 + 字节6)/100H])
        num_hex = hex(num)
        # print(num_hex)  # 0xff01004b8ca078
        num_hex_deal = num_hex[4:-2]  # 掐头去尾01004b8ca0
        # print(num_hex_deal)
        num_sum = sum([int(num_hex_deal[i:i+2], 16) for i in range(len(num_hex_deal)) if i % 2 == 0])
        check_num = num_sum % 0x100
        # print(check_num)
        return check_num



if __name__ == '__main__':
    res = SerialHandler().request_for_connect()
    # SerialHandler().request()
    # print(res)

    # 控制云台
    # SerialHandler(port="COM6", baudrate=2400, parity="无", bytesize=8, stopbits=1).open_and_write()
    # SerialHandler(port="COM6", baudrate=2400, parity="无", bytesize=8, stopbits=1).devel(50)
    # SerialHandler(port="COM6", baudrate=2400, parity="无", bytesize=8, stopbits=1).angle_to_num(53.02)
    # SerialHandler(port="COM6", baudrate=2400, parity="无", bytesize=8, stopbits=1).devel(360)
    # num = 0xFF01004B8CA078
    # SerialHandler(port="COM6", baudrate=2400, parity="无", bytesize=8, stopbits=1).check_code(num)
    # 结论:
    # 1、开机自检的初始位置不定,但能确定是上一次保留的位置
    # 2、俯仰角度范围:0到55.77   (水平于地面的位置为中间值,即0°实际角度是倾斜的)
    # 3、水平位置范围:0到349.99
    # 4、经测量,水平于地面的位置是19.88左右,误差2度
    # 5、设置的角度范围不能超过上述范围,否则设置不成功,而没有报错,也没有实际转动
    # SerialHandler(port="COM6", baudrate=2400, parity="无", bytesize=8, stopbits=1).set_angle(66, 19.88)

    # # 设置一个主函数,用来运行窗口,便于若其他地方下需要调用串口是可以直接调用main函数
    # ID, data = main()
    #
    # print("******")
    # print(ID, data)

    # # import serial
    # ser = serial.Serial()
    # ser.port = "COM1"
    # ser.baudrate = 9600
    # ser.bytesize = 8
    # ser.parity = "N"  # PARITY_NONE, PARITY_EVEN, PARITY_ODD PARITY_MARK, PARITY_SPACE
    # ser.stopbits = 1
    # ser.timeout = 1  # 连接超时,不再等待
    # print(ser)
    # ser.open()
    # ser.write(b'start')
    # res_list = []
    # res_str = b""
    # while 1:
    #     res = ser.read()
    #     # if res=="":
    #     #     ser.write(b'start')
    #     # print("响应字节:", res)
    #     if res == b'':
    #         break
    #     if b" "==res or b"\x90"==res:
    #         for ele in res_list:
    #             res_str += ele
    #         print(res_str)
    #         break
    #     else:
    #         res_list.append(res)
    #         # print(res)
    # # for i in range(10):
    # #     print(ser.read())
    # ser.close()
    # print(ser.read())
    # import io
    #
    # sio = io.TextIOWrapper(io.BufferedRWPair(ser, ser))
    # sio.write("start")
    # sio.flush()
    # hello = sio.readline()
    # ser.close()

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;