天天看點

序列槽總線舵機之socket網絡指令調試工具一、控制指令二、代碼

我們如何通過外部去控制樹莓派序列槽總線舵機和PWM舵機。

網絡無疑是一個很好的選擇。

這一部分是在lsc.py檔案中

關于socketserver部分參考

socketserver

文章目錄

  • 調試工具
    • 1.網絡調試助手
    • 2.樹莓派
  • 一、控制指令
    • 1.設定PWM舵機位置
    • 2.運作動作組
    • 3.複位兩個PWM舵機
    • 4.讀出舵機的電壓值
  • 二、代碼

調試工具

1.網絡調試助手

序列槽總線舵機之socket網絡指令調試工具一、控制指令二、代碼

2.樹莓派

序列槽總線舵機之socket網絡指令調試工具一、控制指令二、代碼

一、控制指令

1.設定PWM舵機位置

設定PWM舵機的位置,一次設定一個舵機

指令格式

55 55 08 3 xx xx xx id posl posh

id = 6 或者是等于7

7是控制攝像頭上下的舵機

6是控制攝像頭左右的舵機

posl :位置的低位

posh:位置的高位

示例

55 55 08 3 0 0 0 7 0 0
           

這是設定舵機ID7設定位置為3000-1200 = 1800

說明:

位置小于1200,就是1200,

位置大于1900,就是1900

測試

序列槽總線舵機之socket網絡指令調試工具一、控制指令二、代碼

這裡的0是傳進去的參數,實際的值沒有輸出,懶的改了

2.運作動作組

發送這個指令可以運作一組動作

指令格式

55 55 05 06 action_num action_timel action_timeh

action_num :運作的動作組的名字

action_timel :運作的次數的低位

action_timeh:運作的次數的高位

示例

55 55 05 06 0 0 2
           
運作立正動作 2次

測試

序列槽總線舵機之socket網絡指令調試工具一、控制指令二、代碼

3.複位兩個PWM舵機

運作這個指令,可以使兩個PWM舵機複位到中位的位置

指令格式

55 55 02 03

沒有參數

示例

55 55 02 03
           
将兩個PWM舵機複位到1500的位置

測試

序列槽總線舵機之socket網絡指令調試工具一、控制指令二、代碼

4.讀出舵機的電壓值

運作這個指令可以讀出舵機的電壓值

指令格式

55 55 03 0f id

id:是舵機的ID

示例

55 55 03 0f 6
           
這裡讀取的是ID号為6的舵機的電壓值

測試

樹莓派運作結果:

序列槽總線舵機之socket網絡指令調試工具一、控制指令二、代碼

網絡助手運作結果:

序列槽總線舵機之socket網絡指令調試工具一、控制指令二、代碼

二、代碼

# 網絡控制
#!/usr/bin/python3
# -*- coding: UTF-8 -*-

import socketserver # 提供了伺服器中心類,可以簡化網絡伺服器的開發
import PWMServo  # 導入PWM舵機二次封裝
import Serial_Servo_Running as SSR # 動作組調用
import threading # 線程
import get_data # 從檔案中擷取PID的值
import math # 導入數學庫
from config_serial_servo  import  serial_servo_read_vin #導入讀取舵機的電壓
import time # 導入時間子產品
import os # 導入系統子產品

DEBUG = False # 開啟調試模式

client_socket = []  # 建立一個用戶端清單 

# 開啟動作組線程,動作一發生變化,就會去執行動作
SSR.start_action_thread() # 開啟動作組線程

# TCPServer是接收到請求後執行handle方法,如果前一個的handle沒有結束,那麼其他的請求将不會受理,新的用戶端也無法加入。
# 而ThreadingTCPServer和ForkingTCPServer則允許前一連接配接的handle未結束也可受理新的請求和連接配接新的用戶端,
# 差別在于前者用建立新線程的方法運作handle,後者用新程序的方法運作handle
# 機器人的服務類 負責綁定,監聽,運作
class LobotServer(socketserver.ThreadingTCPServer):  # 繼承ThreadingTCPServer
    allow_reuse_address = True  # 允許位址重用
    
# 機器人的請求處理類  負責處理使用者所發送的資料  定義一個類,繼承socketserver.BaseRequestHandler
class LobotServerHandler(socketserver.BaseRequestHandler): 
    global client_socket # 全局變量 用戶端清單
    ip = "" # ip位址
    port = None # 端口号
    # setup在handle()之前被調用,主要的作用就是執行請求之前的初始化相關的工作。
    def setup(self):
        # strip()方法用于移除字元串頭尾指定的字元(預設為空格或換行符)或字元序列
        # client_address擷取的是一個元組 包含ip位址和端口
        self.ip = self.client_address[0].strip() # 擷取用戶端1的IP位址
        self.port = self.client_address[1]  # 擷取端口号
        print("connected\tIP:"+self.ip+"\tPort:"+str(self.port))
        client_socket.append(self.request)  # 将此連接配接加入用戶端清單
        #self.request.settimeout(6)  # 逾時時間為6秒
    # 這個方法必須有,每一個用戶端連結之後都會執行這個函數
    # 工作就是做那些所有與處理請求相關的工作
    def handle(self):
        global action_num, action_times, inf_flag
        conn = self.request  # 擷取連接配接的用戶端的對象
        recv_data = b''  # 接收資料都是位元組類型的
        Flag = True  # 循環标志  循環
        stop = False # 結束标志  
        t2 = 0
        while Flag:    # 循環
            try:
                buf = conn.recv(128)  # 接收128個位元組資料 可以小于這個長度
                # Python3的字元串的編碼語言用的是unicode編碼,由于Python的字元串類型是str,在記憶體中以Unicode表示,
                # 一個字元對應若幹位元組,如果要在網絡上傳輸,或儲存在磁盤上就需要把str變成以位元組為機關的bytes
                if buf == b'': # 空字元串  沒有接收到資料
                    Flag = False # 退出循環
                else: # 接收到資料
                    # 列印出資料
                 
                    print('收到的資料是:',(buf))
                        
                    recv_data = recv_data + buf # 将字元串轉化為位元組類型
                    if len(recv_data) <= 13:     # 接收資料長度小于等于13
                        print('收到%d個位元組' %(len(recv_data)))
                        # 解決斷包問題,接收到完整指令後才發送到序列槽,防止出錯
                        while True: # 死循環
                            try:
                                index = recv_data.index(b'\x55\x55')  # 搜尋資料中的0x55 0x55的下标
                                print('index is :',index)
                                # 加3 index傳回的是第一個0x55的位置,兩個0x55 2個位元組 加3表示後面還有資料
                                if len(recv_data) >= index+3:  # 緩存中的資料長度是否足夠
                                # 判斷下面接受的指令
                                    recv_data = recv_data[index:] # 截取完整的指令 包含幀頭
                                    # recv_data[2]是去掉幀頭的第一個資料 也就是cmd資料的長度 -2
                                    # 下面是判斷資料是否正确 cmd小于一幀資料
                                    if recv_data[2] + 2 <= len(recv_data):  # 緩存中的資料長度是否足夠
                                        cmd = recv_data[0:(recv_data[2]+2)]    # 取出指令,包含幀頭
                                        #print(cmd)
                                        recv_data = recv_data[(recv_data[2]+3):]  # 去除已經取出的指令
                                        # 0x55 0x55 08 03 xx xx xx id posl posh  # 設定PWM舵機的位置 id=6/7
                                        # 55 55 03 0F id  # 讀舵機的電壓值
                                        # 55 55 05 06 action_num action_timesl action_timesh # 運作動作組
                                        # 55 55 02 03   # 複位兩個PWM舵機
                                        if cmd[0] and cmd[1] is 0x55:
                                            if cmd[2] == 0x08 and cmd[3] == 0x03:  # 資料長度和控制單舵機指令
                                               
                                                id = cmd[7]
                                                pos = 0xffff & cmd[8] | 0xff00 & (cmd[9] << 8)
                                                # print('id:', cmd[7], 'pos:', pos)
                                                print('設定舵機%d的位置為%d:' %(id,pos))
                                                if id == 7:
                                                    if 1900 < pos:
                                                        pos = 1900
                                                    elif pos < 1200:
                                                        pos = 1200
                                                    PWMServo.setServo(1, 3000 - pos, 20)
                                                elif id == 6:
                                                    PWMServo.setServo(2, 3000 - pos, 20)
                                                else:
                                                    pass
                                            # 55 55 05 06 action_num action_timesl action_timesh
                                            elif cmd[2] == 0x05 and cmd[3] == 0x06: # 資料長度和控制舵機動作組指令
                                                action_num = cmd[4]
                                                action_times = 0xffff & cmd[5] | 0xff00 & cmd[6] << 8
                                                print('運作動作組')
                                                # 站立動作 
                                                if action_num == 0:
                                                    SSR.stop_action_group()
                                                    # 緩慢站立 運作一次
                                                    SSR.change_action_value('stand_slow', 1)
                                                    try:
                                                        # 讀取pid檔案
                                                        data = get_data.read_data()                                          
                                                        if data[0] != 'K':
                                                            os.system('sudo kill -9 ' + str(data[0]))
                                                            PWMServo.setServo(1, 1500, 300)
                                                            PWMServo.setServo(2, 1500, 300)                                                        
                                                            get_data.write_data("K", "0")
                                                    except BaseException as e:
                                                        print(e)
                                                # 無限次   
                                                elif action_times == 0:  # 無限次                                                    
                                                    print('action', action_num, 'times', action_times)
                                                    SSR.change_action_value(str(action_num), action_times)
                                                    stop = True
                                                else:
                                                    print('action', action_num, 'times', action_times)
                                                    if stop:                                                       
                                                        SSR.stop_action_group()
                                                        # 緩慢站立 執行一次
                                                        SSR.change_action_value('stand_slow', 1)
                                                        stop = False
                                                    else:
                                                        print('action', action_num, 'times', action_times)
                                                        SSR.change_action_value(str(action_num), action_times)
                                            
                                            # 55 55 02 03               
                                            # 複位兩個PWM舵機
                                            elif cmd[2] == 0x02 and cmd[3] == 0x03:
                                                print('複位兩個PWM舵機')
                                                PWMServo.setServo(1, 1500, 100)
                                                PWMServo.setServo(2, 1500, 100)
                                                time.sleep(0.1)
                                            # 55 55 03 0F id
                                            # 讀舵機的電壓值
                                            elif cmd[2] == 0x03 and cmd[3] == 0x0F:
                                                # 如果動作完成了
                                                print('讀取舵機的電壓值')
                                                if SSR.action_finish():
                                                    try:
                                                        time.sleep(0.05)
                                                        # ath.ceil 傳回數字的上入整數
                                                        v = math.ceil(serial_servo_read_vin(cmd[4]))
                                                        print('電壓值是:',v)
                                                        buf = [0x55, 0x55, 0x04, 0x0F, 0x00, 0x00]
                                                        buf[4] = v & 0xFF
                                                        buf[5] = (v >> 8) & 0xFF
                                                        # sendall 發送全部的資料
                                                        conn.sendall(bytearray(buf))
                                                        print('讀取電壓值成功')
                                                    except BaseException as e:
                                                        print(e)
                                                else:
                                                    print('正在運作動作,請稍後')
                                       # 調試,列印指令cmd
                                        if DEBUG is True:
                                            for i in cmd:
                                                print (hex(i))
                                            print('*' * 30)
                                    else: # 接收資料不正确
                                        break
                                else: # 資料長度不夠
                                    break
                            except Exception as e:   # 在recv_data 中搜不到 '\x55\x55'子串
                                break
                        recv_data = b'' # 把資料緩沖區清空
                        action_times = None # 動作次數
                        action_num = None # 運作動作組名字
                    else:  # 接收資料大于13個  是錯誤的資料
                        recv_data = b'' # 把資料緩沖區清空
                        pass
            except Exception as e:  # 接收資料錯誤
                print(e)
                Flag = False # 退出循環
                break
     
    # 資料發送完成,并且不再使用此用戶端 斷開此用戶端連接配接
    # 在handle()方法之後被調用,執行當處理完請求後的清理工作,預設不會做任何事
    def finish(self):

       client_socket.remove(self.request)  # 從用戶端清單中剔除此連接配接
       print("disconnected\tIP:"+self.ip+"\tPort:"+str(self.port))


if __name__ == "__main__":
    server = LobotServer(("", 9029), LobotServerHandler)    # 建立伺服器
    try:
        server.serve_forever()  # 開始伺服器循環
    except Exception as e:
        print(e)
        server.shutdown() # 關閉伺服器
        server.server_close() # 關閉服務