• 正文
    • 一、需求描述
    • 二、實(shí)現(xiàn)代碼:版本v1
    • 三、bytes()的用法:官方文檔
    • 四、實(shí)現(xiàn)代碼:版本v2
    • 五、實(shí)現(xiàn)代碼:版本v3
  • 相關(guān)推薦
申請(qǐng)入駐 產(chǎn)業(yè)圖譜

python使用serial模塊,通過(guò)串口控制云臺(tái)(基于PELCO-D協(xié)議)

2024/12/13
563
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

一、需求描述

通過(guò)python實(shí)現(xiàn)對(duì)云臺(tái)的控制,使用到的相關(guān)模塊:

1、pyserial,串口模塊,用于連接串口,并通過(guò)串口發(fā)送指令

2、bytes,內(nèi)置模塊,用于將16進(jìn)制的指令轉(zhuǎn)化成字節(jié)流

二、實(shí)現(xiàn)代碼:版本v1

1、詳細(xì)過(guò)程見(jiàn)代碼備注,版本v2,請(qǐng)查閱“四”

2、關(guān)鍵一步是發(fā)送數(shù)據(jù)時(shí)的處理,使用到了bytes,請(qǐng)查閱“三”

3、環(huán)境搭建根據(jù)云臺(tái)的說(shuō)明書(shū)進(jìn)行,這里采用的是串口連接設(shè)備

import serial
from common.config import serial_com


class SerialHandler(object):
    def __init__(self, port=serial_com, baudrate=9600, parity="無(wú)", bytesize=8, stopbits=1):
        """
        串口初始化,設(shè)置串口相關(guān)參數(shù)
        :param port: str類(lèi)型, 例:COM1
        :param baudrate: int類(lèi)型,取值范圍:[4800, 9600, 19200, 18400, 57600, 115200]
        :param parit: str類(lèi)型,N:"無(wú)", O:"奇", E:"偶"
        :param bytesize: int類(lèi)型,取值范圍:[7, 8]
        :param stopbits: int類(lèi)型,取值范圍:[1, 2]
        """
        # 根據(jù)上位機(jī)顯示,轉(zhuǎn)換成serial庫(kù)識(shí)別的參數(shù)
        if parity == "無(wú)":
            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
        # 連接若超時(shí)1秒,則結(jié)束連接
        self.serial_handler.timeout = 15

    def open_and_write(self):
        '''
        # PELCO-D協(xié)議:
        一、數(shù)據(jù)格式:
        8位數(shù)據(jù)位、1位停止位,無(wú)效驗(yàn)位。波特率:2400
        二、命令格式:
        字節(jié)1       字節(jié)2     字節(jié)3   字節(jié)4    字節(jié)5    字節(jié)6   字節(jié)7
        同步字節(jié)    地址碼   指令碼1  指令碼2  數(shù)據(jù)碼1  數(shù)據(jù)碼2  校驗(yàn)碼
        三、命令解釋?zhuān)?
        1.協(xié)議采用十六進(jìn)制
        2.同步字節(jié)始終為FF
        3.地址碼為云臺(tái)地址號(hào),地址范圍請(qǐng)了解云臺(tái)說(shuō)明
        4.指令碼用于表示不同的行為
        5.數(shù)據(jù)碼1表示水平速度,2分別表示垂直速度
        6.校驗(yàn)碼 = MOD[(字節(jié)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:無(wú)
        '''
        self.serial_handler.timeout = 0.05 # 云臺(tái)轉(zhuǎn)動(dòng)的時(shí)間,單位為秒
        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")) # 停止轉(zhuǎn)動(dòng)指令,如果沒(méi)有,則走到世界盡頭
        # b"ff01000c20204d"
        # chr(0x06).encode("utf-8")
        # self.serial_handler.in_waiting(0xff010008002029)
        # self.serial_handler.write(b'FF010000000001')
        print("串口設(shè)置:", self.serial_handler)
        print("我請(qǐng)求了?。?")
        res = self.serial_handler.readline()
        print(res)
        self.serial_handler.close()

if __name__ == '__main__':
    # 控制云臺(tái)
    SerialHandler(port="COM5", baudrate=2400, parity="無(wú)", 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

摘要(其實(shí)就是復(fù)制粘貼):

二進(jìn)制序列類(lèi)型 ---?bytes,?bytearray,?memoryview

操作二進(jìn)制數(shù)據(jù)的核心內(nèi)置類(lèi)型是?bytes?和?bytearray。 它們由?memoryview?提供支持,該對(duì)象使用?緩沖區(qū)協(xié)議?來(lái)訪(fǎng)問(wèn)其他二進(jìn)制對(duì)象所在內(nèi)存,不需要?jiǎng)?chuàng)建對(duì)象的副本。

array?模塊支持高效地存儲(chǔ)基本數(shù)據(jù)類(lèi)型,例如 32 位整數(shù)和 IEEE754 雙精度浮點(diǎn)值。

bytes 對(duì)象

bytes 對(duì)象是由單個(gè)字節(jié)構(gòu)成的不可變序列。 由于許多主要二進(jìn)制協(xié)議都基于 ASCII 文本編碼,因此 bytes 對(duì)象提供了一些僅在處理 ASCII 兼容數(shù)據(jù)時(shí)可用,并且在許多特性上與字符串對(duì)象緊密相關(guān)的方法。

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

首先,表示 bytes 字面值的語(yǔ)法與字符串字面值的大致相同,只是添加了一個(gè)?b?前綴:

  • 單引號(hào):?b'同樣允許嵌入?"雙"?引號(hào)'
  • 雙引號(hào):?b"同樣允許嵌入?'單'?引號(hào)"。
  • 三重引號(hào):?b'''三重單引號(hào)''',?b"""三重雙引號(hào)"""

bytes 字面值中只允許 ASCII 字符(無(wú)論源代碼聲明的編碼為何)。 任何超出 127 的二進(jìn)制值必須使用相應(yīng)的轉(zhuǎn)義序列形式加入 bytes 字面值。

像字符串字面值一樣,bytes 字面值也可以使用?r?前綴來(lái)禁用轉(zhuǎn)義序列處理。 請(qǐng)參閱?字符串和字節(jié)串字面值?了解有關(guān)各種 bytes 字面值形式的詳情,包括所支持的轉(zhuǎn)義序列。

雖然 bytes 字面值和表示法是基于 ASCII 文本的,但 bytes 對(duì)象的行為實(shí)際上更像是不可變的整數(shù)序列,序列中的每個(gè)值的大小被限制為?0?<=?x?<?256?(如果違反此限制將引發(fā)?ValueError)。 這種限制是有意設(shè)計(jì)用以強(qiáng)調(diào)以下事實(shí),雖然許多二進(jìn)制格式都包含基于 ASCII 的元素,可以通過(guò)某些面向文本的算法進(jìn)行有用的操作,但情況對(duì)于任意二進(jìn)制數(shù)據(jù)來(lái)說(shuō)通常卻并非如此(盲目地將文本處理算法應(yīng)用于不兼容 ASCII 的二進(jìn)制數(shù)據(jù)格式往往將導(dǎo)致數(shù)據(jù)損壞)。

除了字面值形式,bytes 對(duì)象還可以通過(guò)其他幾種方式來(lái)創(chuàng)建:

  • 指定長(zhǎng)度的以零值填充的 bytes 對(duì)象:?bytes(10)
  • 通過(guò)由整數(shù)組成的可迭代對(duì)象:?bytes(range(20))
  • 通過(guò)緩沖區(qū)協(xié)議復(fù)制現(xiàn)有的二進(jìn)制數(shù)據(jù):?bytes(obj)

另請(qǐng)參閱?bytes?內(nèi)置類(lèi)型。

由于兩個(gè)十六進(jìn)制數(shù)碼精確對(duì)應(yīng)一個(gè)字節(jié),因此十六進(jìn)制數(shù)是描述二進(jìn)制數(shù)據(jù)的常用格式。 相應(yīng)地,bytes 類(lèi)型具有從此種格式讀取數(shù)據(jù)的附加類(lèi)方法:

classmethod?fromhex(string)

此?bytes?類(lèi)方法返回一個(gè)解碼給定字符串的 bytes 對(duì)象。 字符串必須由表示每個(gè)字節(jié)的兩個(gè)十六進(jìn)制數(shù)碼構(gòu)成,其中的 ASCII 空白符會(huì)被忽略。

>>> bytes.fromhex('2Ef0 F1f2  ')
b'.xf0xf1xf2'

在 3.7 版更改:?bytes.fromhex()?現(xiàn)在會(huì)忽略所有 ASCII 空白符而不只是空格符。

存在一個(gè)反向轉(zhuǎn)換函數(shù),可以將 bytes 對(duì)象轉(zhuǎn)換為對(duì)應(yīng)的十六進(jìn)制表示。

hex()

返回一個(gè)字符串對(duì)象,該對(duì)象包含實(shí)例中每個(gè)字節(jié)的兩個(gè)十六進(jìn)制數(shù)字。

>>> b'xf0xf1xf2'.hex()
'f0f1f2'

3.5 新版功能.

由于 bytes 對(duì)象是由整數(shù)構(gòu)成的序列(類(lèi)似于元組),因此對(duì)于一個(gè) bytes 對(duì)象?bb[0]?將為一個(gè)整數(shù),而?b[0:1]?將為一個(gè)長(zhǎng)度為 1 的 bytes 對(duì)象。 (這與文本字符串不同,索引和切片所產(chǎn)生的將都是一個(gè)長(zhǎng)度為 1 的字符串)。

bytes 對(duì)象的表示使用字面值格式 (b'...'),因?yàn)樗ǔ6家认?bytes([46,?46,?46])?這樣的格式更好用。 你總是可以使用?list(b)?將 bytes 對(duì)象轉(zhuǎn)換為一個(gè)由整數(shù)構(gòu)成的列表。

注解

針對(duì) Python 2.x 用戶(hù)的說(shuō)明:在 Python 2.x 系列中,允許 8 位字符串( 2.x 所提供的最接近內(nèi)置二進(jìn)制數(shù)據(jù)類(lèi)型的對(duì)象)與 Unicode 字符串進(jìn)行各種隱式轉(zhuǎn)換。 這是為了實(shí)現(xiàn)向下兼容的變通做法,以適應(yīng) Python 最初只支持 8 位文本而 Unicode 文本是后來(lái)才被加入這一事實(shí)。 在 Python 3.x 中,這些隱式轉(zhuǎn)換已被取消 —— 8 位二進(jìn)制數(shù)據(jù)與 Unicode 文本間的轉(zhuǎn)換必須顯式地進(jìn)行,bytes 與字符串對(duì)象的比較結(jié)果將總是不相等。

四、實(shí)現(xiàn)代碼:版本v2

1、新增功能:直接設(shè)置水平、俯仰角度

2、封裝代碼

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


class SerialHandler(object):
    def __init__(self, port=serial_com, baudrate=9600, parity="無(wú)", bytesize=8, stopbits=1):
        """
        串口初始化,設(shè)置串口相關(guān)參數(shù)
        :param port: str類(lèi)型, 例:COM1
        :param baudrate: int類(lèi)型,取值范圍:[4800, 9600, 19200, 18400, 57600, 115200]
        :param parit: str類(lèi)型,N:"無(wú)", O:"奇", E:"偶"
        :param bytesize: int類(lèi)型,取值范圍:[7, 8]
        :param stopbits: int類(lèi)型,取值范圍:[1, 2]
        """
        # 根據(jù)上位機(jī)顯示,轉(zhuǎn)換成serial庫(kù)識(shí)別的參數(shù)
        if parity == "無(wú)":
            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
        # 連接若超時(shí)1秒,則結(jié)束連接
        self.serial_handler.timeout = 15

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

        self.serial_handler.timeout = 0.5 # 云臺(tái)轉(zhuǎn)動(dòng)的時(shí)間,單位為秒

        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):
        # 已棄用,做測(cè)試使用
        '''
        # PELCO-D協(xié)議:
        一、數(shù)據(jù)格式:
        1位起始位、8位數(shù)據(jù)、1位停止位,無(wú)效驗(yàn)位。波特率:2400B/S
        二、命令格式:
        字節(jié)1       字節(jié)2     字節(jié)3   字節(jié)4    字節(jié)5    字節(jié)6   字節(jié)7
        同步字節(jié)    地址碼   指令碼1  指令碼2  數(shù)據(jù)碼1  數(shù)據(jù)碼2  校驗(yàn)碼
        三、命令解釋?zhuān)?
        1.該協(xié)議中所有數(shù)值都為十六進(jìn)制數(shù)
        2.同步字節(jié)始終為FF
        3.地址碼為云臺(tái)地址號(hào),地址范圍請(qǐng)了解云臺(tái)說(shuō)明
        4.指令碼表示不同的動(dòng)作
        5.數(shù)據(jù)碼1、2分別表示水平、垂直方向速度(00-3FH),FFH表示“turbo”速度
        6.校驗(yàn)碼 = MOD[(字節(jié)2 + 字節(jié)3 + 字節(jié)4 + 字節(jié)5 + 字節(jié)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 # 云臺(tái)轉(zhuǎn)動(dòng)的時(shí)間,單位為秒
        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")) # 停止轉(zhuǎn)動(dòng)指令,如果沒(méi)有,則走到世界盡頭
        # b"ff01000c20204d"
        # chr(0x06).encode("utf-8")
        # self.serial_handler.in_waiting(0xff010008002029)
        # self.serial_handler.write(b'FF010000000001')
        print("串口設(shè)置:", self.serial_handler)
        print("我請(qǐng)求了!!!")
        res = self.serial_handler.readline()
        print(res)
        self.serial_handler.close()

    def request(self, bytes_data=b'start'):
        '''
        通過(guò)串口發(fā)送字節(jié)數(shù)據(jù),并返回字節(jié)類(lèi)型的響應(yīng)結(jié)果
        :param bytes_data:
        :return:
        '''
        print("串口設(shè)置:", self.serial_handler)
        print("請(qǐng)求數(shù)據(jù):", 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("連接串口失敗或沒(méi)有響應(yīng)數(shù)據(jù)!")
            #     break
            # elif res == b' ' or res == b"x90":
            #     for ele in res_list:
            #         res_str += ele
            #     print("返回結(jié)果:", res_str)
            #     break
            # else:
            #     print("每一次返回結(jié)果:", res)
            #     res_list.append(res)
        self.serial_handler.close()
        # 返回二進(jìn)制結(jié)果
        return res

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

    def __angle_to_num(self, angle):
        # 返回:num角度對(duì)應(yīng)的數(shù)值(十進(jìn)制)
        average = self.__angle_per_num()
        num = angle/average
        # print(int(num))
        return int(num)

    def __devel(self, angle):
        # 返回水平數(shù)值(十進(jìn)制)
        # 將角度轉(zhuǎn)換成數(shù)值
        num = self.__angle_to_num(angle)
        # 高位數(shù)值
        h = 0XFF01004B000000
        # print(h)
        # 中間數(shù)值
        middle = num*0x100
        # print(middle)
        # 末尾數(shù)值
        num_h_m = h+middle # 獲取除校驗(yàn)位的所有數(shù)值
        check_num = self.__check_code(num_h_m)
        return h+middle+check_num

    def __elevation(self, angle):
        # 返回仰角數(shù)值(十進(jìn)制)
        # 將角度轉(zhuǎn)換成數(shù)值
        num = self.__angle_to_num(angle)
        # 高位數(shù)值
        h = 0XFF01004D000000
        # print(h)
        # 中間數(shù)值
        middle = num*0x100
        # print(middle)
        # 末尾數(shù)值
        num_h_m = h+middle # 獲取除校驗(yàn)位的所有數(shù)值
        check_num = self.__check_code(num_h_m)
        return h+middle+check_num

    def __check_code(self, num):
        # 返回:校驗(yàn)碼(十進(jìn)制),算法是:校驗(yàn)碼 = MOD[(字節(jié)2 + 字節(jié)3 + 字節(jié)4 + 字節(jié)5 + 字節(jié)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__':

    # 控制云臺(tái)
    SerialHandler(port="COM6", baudrate=2400, parity="無(wú)", bytesize=8, stopbits=1).set_angle(66, 19.88)

五、實(shí)現(xiàn)代碼:版本v3

1、實(shí)現(xiàn)角度獲取功能

2、變更了部分方法

3、關(guān)鍵方法:將返回字節(jié)數(shù)值進(jìn)行轉(zhuǎn)換(對(duì)小白很藍(lán)的啦,特地拿出來(lái))

binascii.b2a_hex(res).decode()
# -*- coding: UTF-8 -*-
# coding=gb18030
'''控制串口,也控制串口依賴(lài)的設(shè)備,如云臺(tái)'''
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="無(wú)", bytesize=8, stopbits=1):
        """
        串口初始化,設(shè)置串口相關(guān)參數(shù)
        :param port: str類(lèi)型, 例:COM1
        :param baudrate: int類(lèi)型,取值范圍:[4800, 9600, 19200, 18400, 57600, 115200]
        :param parit: str類(lèi)型,N:"無(wú)", O:"奇", E:"偶"
        :param bytesize: int類(lèi)型,取值范圍:[7, 8]
        :param stopbits: int類(lèi)型,取值范圍:[1, 2]
        """
        # 根據(jù)上位機(jī)顯示,轉(zhuǎn)換成serial庫(kù)識(shí)別的參數(shù)
        if parity == "無(wú)":
            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
        # # 連接若超時(shí)n秒,則結(jié)束連接
        self.serial_handler.timeout = 15

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

        self.serial_handler.timeout = 0.5 # 云臺(tái)轉(zhuǎn)動(dòng)的時(shí)間,單位為秒

        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"):
        # 更改為寫(xiě)入信息并返回角度
        '''
        # PELCO-D協(xié)議:
        一、數(shù)據(jù)格式:
        1位起始位、8位數(shù)據(jù)、1位停止位,無(wú)效驗(yàn)位。波特率:2400B/S
        二、命令格式:
        字節(jié)1       字節(jié)2     字節(jié)3   字節(jié)4    字節(jié)5    字節(jié)6   字節(jié)7
        同步字節(jié)    地址碼   指令碼1  指令碼2  數(shù)據(jù)碼1  數(shù)據(jù)碼2  校驗(yàn)碼
        三、命令解釋?zhuān)?
        1.該協(xié)議中所有數(shù)值都為十六進(jìn)制數(shù)
        2.同步字節(jié)始終為FF
        3.地址碼為云臺(tái)地址號(hào),地址范圍請(qǐng)了解云臺(tái)說(shuō)明
        4.指令碼表示不同的動(dòng)作
        5.數(shù)據(jù)碼1、2分別表示水平、垂直方向速度(00-3FH),FFH表示“turbo”速度
        6.校驗(yàn)碼 = MOD[(字節(jié)2 + 字節(jié)3 + 字節(jié)4 + 字節(jié)5 + 字節(jié)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 # 云臺(tái)轉(zhuǎn)動(dòng)的時(shí)間,單位為秒
        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("串口設(shè)置:", self.serial_handler)
        # print("我請(qǐng)求了?。?")
        # self.serial_handler.write(bytes().fromhex("FF010000000001")) # 停止轉(zhuǎn)動(dòng)指令,如果沒(méi)有,則走到世界盡頭
        # 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]  # 字節(jié)轉(zhuǎn)換成字符串,并截取角度部分
        # print(res_str)
        res_16 = int(res_str, 16)  # 轉(zhuǎn)化成16進(jìn)制
        return res_16*self.__angle_per_num()

    # 水平
    def get_devel(self, cmd="FF 01 00 51 00 00 52"):
        # print("水平")
        # 查詢(xún)指令:  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("仰角")
        # 查詢(xún)指令:  FF 01 00 53 00 00 54
        res = self.open_and_write(cmd)
        return res

    def request(self, bytes_data=b'start'):
        '''
        通過(guò)串口發(fā)送字節(jié)數(shù)據(jù),并返回字節(jié)類(lèi)型的響應(yīng)結(jié)果
        :param bytes_data:
        :return:
        '''
        try:
            print("串口設(shè)置:", self.serial_handler)
            print("請(qǐng)求數(shù)據(jù):", 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("連接串口失敗或沒(méi)有響應(yīng)數(shù)據(jù)!")
                #     break
                # elif res == b' ' or res == b"x90":
                #     for ele in res_list:
                #         res_str += ele
                #     print("返回結(jié)果:", res_str)
                #     break
                # else:
                #     print("每一次返回結(jié)果:", res)
                #     res_list.append(res)
            self.serial_handler.close()
            # 返回二進(jìn)制結(jié)果
            return res
        except:
            print("串口出問(wèn)題了,請(qǐng)檢查串口相關(guān)配置和硬件連線(xiàn)")
            # return res
            # raise IOError("串口出問(wèn)題了,請(qǐng)檢查串口相關(guān)配置和硬件連線(xiàn)")


    def request_for_connect(self, bytes_data=b'start'):
        '''
        專(zhuān)門(mén)設(shè)計(jì)一個(gè)函數(shù)供給邏輯函數(shù)使用
        通過(guò)串口發(fā)送字節(jié)數(shù)據(jù),并返回字節(jié)類(lèi)型的響應(yīng)結(jié)果
        :param bytes_data:
        :return:
        '''
        try:
            print("串口設(shè)置:", self.serial_handler)
            print("請(qǐng)求數(shù)據(jù):", 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("連接串口失敗或沒(méi)有響應(yīng)數(shù)據(jù)!")
            #     if time_1 - time_0 >= num:
            #         break
            # elif res == b' ' or res == b"x90":
            #     for ele in res_list:
            #         res_str += ele
            #     print("返回結(jié)果:", res_str)
            #     if time_1 - time_0 >= num:
            #         break
            # else:
            #     print("每一次返回結(jié)果:", res)
            #     res_list.append(res)
            self.serial_handler.close()
            # 返回二進(jìn)制結(jié)果
            return res
        except:
            print("串口出問(wèn)題了,請(qǐng)檢查串口相關(guān)配置和硬件連線(xiàn)")

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

    def __angle_to_num(self, angle):
        # 返回:num角度對(duì)應(yīng)的數(shù)值(十進(jìn)制)
        average = self.__angle_per_num()
        num = angle/average
        # print(int(num))
        return int(num)

    def __devel(self, angle):
        # 返回水平數(shù)值(十進(jìn)制)
        # 將角度轉(zhuǎn)換成數(shù)值
        num = self.__angle_to_num(angle)
        # 高位數(shù)值
        h = 0XFF01004B000000
        # print(h)
        # 中間數(shù)值
        middle = num*0x100
        # print(middle)
        # 末尾數(shù)值
        num_h_m = h+middle # 獲取除校驗(yàn)位的所有數(shù)值
        check_num = self.__check_code(num_h_m)
        return h+middle+check_num

    def __elevation(self, angle):
        # 返回仰角數(shù)值(十進(jìn)制)
        # 將角度轉(zhuǎn)換成數(shù)值
        num = self.__angle_to_num(angle)
        # 高位數(shù)值
        h = 0XFF01004D000000
        # print(h)
        # 中間數(shù)值
        middle = num*0x100
        # print(middle)
        # 末尾數(shù)值
        num_h_m = h+middle # 獲取除校驗(yàn)位的所有數(shù)值
        check_num = self.__check_code(num_h_m)
        return h+middle+check_num

    def __check_code(self, num):
        # 返回:校驗(yàn)碼(十進(jìn)制),算法是:校驗(yàn)碼 = MOD[(字節(jié)2 + 字節(jié)3 + 字節(jié)4 + 字節(jié)5 + 字節(jié)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)

    # 控制云臺(tái)
    # SerialHandler(port="COM6", baudrate=2400, parity="無(wú)", bytesize=8, stopbits=1).open_and_write()
    # SerialHandler(port="COM6", baudrate=2400, parity="無(wú)", bytesize=8, stopbits=1).devel(50)
    # SerialHandler(port="COM6", baudrate=2400, parity="無(wú)", bytesize=8, stopbits=1).angle_to_num(53.02)
    # SerialHandler(port="COM6", baudrate=2400, parity="無(wú)", bytesize=8, stopbits=1).devel(360)
    # num = 0xFF01004B8CA078
    # SerialHandler(port="COM6", baudrate=2400, parity="無(wú)", bytesize=8, stopbits=1).check_code(num)
    # 結(jié)論:
    # 1、開(kāi)機(jī)自檢的初始位置不定,但能確定是上一次保留的位置
    # 2、俯仰角度范圍:0到55.77   (水平于地面的位置為中間值,即0°實(shí)際角度是傾斜的)
    # 3、水平位置范圍:0到349.99
    # 4、經(jīng)測(cè)量,水平于地面的位置是19.88左右,誤差2度
    # 5、設(shè)置的角度范圍不能超過(guò)上述范圍,否則設(shè)置不成功,而沒(méi)有報(bào)錯(cuò),也沒(méi)有實(shí)際轉(zhuǎn)動(dòng)
    # SerialHandler(port="COM6", baudrate=2400, parity="無(wú)", bytesize=8, stopbits=1).set_angle(66, 19.88)

    # # 設(shè)置一個(gè)主函數(shù),用來(lái)運(yùn)行窗口,便于若其他地方下需要調(diào)用串口是可以直接調(diào)用main函數(shù)
    # 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  # 連接超時(shí),不再等待
    # 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("響應(yīng)字節(jié):", 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()

相關(guān)推薦