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

2023-05-16

一、需求描述

通过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

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

二进制序列类型 --- bytes, bytearray, memoryview

操作二进制数据的核心内置类型是 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()

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

随机推荐

  • 动态库和静态库

    概念 什么是库 库是写好的 xff0c 现有的 xff0c 成熟的 xff0c 可以复用的代码 现实中每个程序都要依赖很多基础的底层库 xff0c 不可能每个人的代码都从零开始 xff0c 因此库的存在意义非同寻常 本质上来说 xff0c
  • Linux 内核设计与实现 —— 1.内核简介

    文章目录 操作系统和内核简介内核包括 xff1a 内核与应用程序 xff1a 内核与硬件设备 xff1a 内核的运行模式 xff1a Linux内核与Unix内核比较单内核与微内核设计之比较Linux内核与Unix内核差异 操作系统和内核简
  • VIM基础操作

    方向键 xff1a hjkl输入 向后输入 xff1a a shift最前向前输入 xff1a i shift最后向下新生成一行输入 xff1a o shift上一行删掉当前字符并写入 xff1a s 撤销 xff1a u键盘重映设 vim
  • Git基础操作

    Git基础操作 仓库配置初始化仓库查看当前所在仓库修改远端仓库 代码提交基础操作tag操作删除最后N次提交储藏 代码管理分支操作patch操作查看指定文件的修改历史统计代码量 仓库配置 初始化仓库 1 xff0c 安装 sudo apt g
  • Google SRE 自我评分——《Google SRE: How Google runs production systems》

    Google SRE How Google runs production systems 0 xff1a you are unfamiliar with the subject area 0 xff1a 不熟悉的领域 也就是说对相关的领域
  • java基础之byte转换工具类

    1 Byte 转 byte public static byte toPrimitives Byte oBytes byte bytes 61 new byte oBytes length for int i 61 0 i lt oByte
  • VLAN技术

    大家好呀 xff0c 我是请假君 xff0c 今天又来和大家一起学习数通了 xff0c 今天要分享的知识是VLAN技术 介绍VLAN之前先来了解一下广播风暴 xff1a 在交换式以太网出现后 xff0c 同一个交换机下不同的端口处于不同的冲
  • shell基础操作

    shell基础操作 有待补充基础操作基础素养 使用需要转译的字符休眠账户密码权限管道清屏让命令在后台运行黑洞 硬件查看显示分辨率 软件删除软件和配置文件 文件操作复制 目录返回上一次目录 查找findgrep 文件夹大小对比文件类型删除文件
  • makefile基础

    makefile基础 编译和链接makefile基础格式clean的写法makefile构成使用其他的makefile引用其他的makefile引用方式makefile 查找方式 make 的工作方式添加define警告编译不过 编译和链接
  • DHCP协议

    DHCP协议 动态主机设置协议 xff0c 使用UDP协议工作用途 自动分配IP管理 服务器端使用67 udp xff0c 客户端使用68 udp基本过程 请求IP租约 xff08 discover xff09 客户端广播 xff0c 服务
  • Linux驱动程序概述

    怎么写驱动 看原理图写驱动写测试程序 流程 分配file operations结构体设置结构体成员 open 61 led open 配置为输出引脚 wirte 61 led write 设置引脚状态 注册 xff08 告诉内核 xff09
  • Linux boot API

    Linux boot API 代码主控头文件U BOOT CMD 终端命令sf nand xff08 nor nand flash xff09 具体用法使用范例 setenv 命令 代码 主控头文件 boot主逻辑在此 xff0c 以Ing
  • dash与bash

    dash与bash 背景dash与bash的区别shell配置错误的可能特征坑 xff01 xff01 xff01 查看shell真实配置切换 背景 平台Linux编译 xff0c 可能会遇到脚本设置类型不同 xff08 如 xff1a 芯
  • Linux 基础概念

    文章目录 POSIX动态库和静态库ioctl MCU架构RISC与CISCx86 ARM MIPS架构 U bootBootloader 两种模式主机和板子的传输方式启动顺序bootloader两个阶段U Boot 移植 分区Linux s
  • rosdep update遇到ERROR: error loading sources list: The read operation timed out问题

    链接 xff1a https blog csdn net DDonggggg article details 115798335
  • C++ HTTP实现

    文章目录 一 HTTP基础知识1 什么是Http协议 xff1a 2 常见HTTP头3 HTTP请求头概述 xff08 HttpServletRequest xff09 4 URL格式详解 xff1a 二 WinHttp入门及实战1 介绍2
  • ros+ Optitrack控制bebop2无人机

    准备工作 xff1a Motive的安装及使用 xff1a https blog csdn net weixin 41536025 article details 89913961 ros订阅 OptiTrack 的消息通过vrpn cli
  • stm32中typedef的用法

    C语言允许用户使用 typedef 关键字来定义自己习惯的数据类型名称 xff0c 来替代系统默认的基本类型名称 数组类型名称 指针类型名称与用户自定义的结构型名称 共用型名称 枚举型名称等 一旦用户在程序中定义了自己的数据类型名称 xff
  • Python系列 49 内置模块:subprocess

    subprocess简介 subprocess模块最早在Python2 4中引入 xff0c 它会生成一个子进程 xff0c 该子进程可以执行shell命令 xff0c 并且会监听系统的标准输入管道 标准输出管道 标准错误管道 xff0c
  • python使用serial模块,通过串口控制云台(基于PELCO-D协议)

    一 需求描述 通过python实现对云台的控制 xff0c 使用到的相关模块 xff1a 1 pyserial xff0c 串口模块 xff0c 用于连接串口 xff0c 并通过串口发送指令 2 bytes xff0c 内置模块 xff0c