一、需求描述
通過(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ì)象?b,b[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()