跳转至

小车上位机控制

实现步骤

准备工作

  1. 安装固件recv_send.bin
  2. 启动小车会自动开启wifi,名称是smart_car,密码是12345678,电脑连接这个wifi

代码实现

  1. 搭建ui界面
  2. 连接小车服务器
  3. 发送指令给小车服务器执行

协议说明

控制协议

帧头 类型 数据长度 数据位 校验位
0xff 0xff(-128) 0x01 0x01 0x00 0x04

注意:

数据长度不包含校验位

只有0x16设置左右轮速度时需要加上数据位

指令类型

类型 类型标识
0x01 打开LED灯
0x02 关闭LED灯
0x03 切换LED灯状态
0x11 前进
0x12 后退
0x13 左转
0x14 右转
0x15 停止
0x16 设置左右轮速度运行

获取传感器数据协议

帧头 类型 数据长度 数据位 校验位
0xff 0xff(-128) 0x01 0x01 0x00 0x04

注意:

数据长度不包含校验位

指令类型

类型 类型标识
0x01 红外传感器
0x02 超声波传感器

数据长度

类型 数据长度
红外传感器 0x01
超声波传感器 0x04

代码实现

from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QHBoxLayout, QGroupBox, QPushButton,QLineEdit
from widget.ArrowWidget import ArrowWidget, Direction, COLOR
import struct
import socket
import sys


class MainWindow(QWidget):
    def __init__(self):
        super(MainWindow, self).__init__()
        # -------------- 界面 -------------- #
        self.init_ui()
        # -------------- socket初始化 -------------- #
        self.soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # -------------- 常用数据准备 -------------- #
        self.init_data()

    def init_ui(self):
        self.setWindowTitle('传智无人车控制')
        # 整体布局
        whole_layout = QVBoxLayout()
        self.setLayout(whole_layout)
        # 分组布局
        connect_gb = QGroupBox('连接小车')
        led_gb = QGroupBox('LED灯操作')
        car_gb = QGroupBox('小车操作')
        car_move_gb = QGroupBox('速度操作')
        # 添加控件
        whole_layout.addWidget(connect_gb)
        whole_layout.addWidget(led_gb)
        whole_layout.addWidget(car_gb)
        whole_layout.addWidget(car_move_gb)
        # 布局
        connect_layout = QHBoxLayout(connect_gb)
        led_layout = QHBoxLayout(led_gb)
        car_layout = QHBoxLayout(car_gb)
        car_move_layout = QHBoxLayout(car_move_gb)

        # -------------- 小车连接 -------------- #
        self.ip_edit = QLineEdit()
        self.port_edit = QLineEdit()
        self.ip_edit.setPlaceholderText('请输入小车ip')
        self.port_edit.setPlaceholderText('请输入小车端口')
        self.ip_edit.setText('192.168.10.1')
        self.port_edit.setText('8080')
        connect_btn = QPushButton('连接')
        connect_layout.addWidget(self.ip_edit)
        connect_layout.addWidget(self.port_edit)
        connect_layout.addWidget(connect_btn)
        # -------------- LED灯操作 -------------- #
        open_btn = QPushButton('开灯')
        close_btn = QPushButton('关灯')
        toggle_btn = QPushButton('切换状态')
        led_layout.addWidget(open_btn)
        led_layout.addWidget(close_btn)
        led_layout.addWidget(toggle_btn)
        # -------------- 小车操作布局 -------------- #
        # 中间竖直布局
        center_layout = QVBoxLayout()
        # 第二部分
        yL = ArrowWidget('左', Direction.LEFT)
        yR = ArrowWidget('右', Direction.RIGHT)
        xU = ArrowWidget('前', Direction.UP, COLOR.RED)
        xD = ArrowWidget('后', Direction.DOWN, COLOR.RED)
        center_layout.addWidget(xU)
        center_layout.addWidget(xD)

        car_layout.addWidget(yL)
        car_layout.addLayout(center_layout)
        car_layout.addWidget(yR)

        # -------------- 小车速度控制 -------------- #
        self.left_edit = QLineEdit()
        self.right_edit = QLineEdit()
        self.left_edit.setPlaceholderText('左轮速度')
        self.right_edit.setPlaceholderText('右轮速度')
        self.left_edit.setText('60')
        self.right_edit.setText('60')
        move_btn = QPushButton('移动')
        car_move_layout.addWidget(self.left_edit)
        car_move_layout.addWidget(self.right_edit)
        car_move_layout.addWidget(move_btn)

        # -------------- 信号和槽绑定 -------------- #
        # connect
        connect_btn.clicked.connect(self.connect_car)
        # led
        open_btn.clicked.connect(self.open_led)
        close_btn.clicked.connect(self.close_led)
        toggle_btn.clicked.connect(self.toggle_led)
        # 小车
        xU.arrow_pressed.connect(self.arrow_press)
        xD.arrow_pressed.connect(self.arrow_press)
        yL.arrow_pressed.connect(self.arrow_press)
        yR.arrow_pressed.connect(self.arrow_press)

        xU.arrow_released.connect(self.arrow_release)
        xD.arrow_released.connect(self.arrow_release)
        yL.arrow_released.connect(self.arrow_release)
        yR.arrow_released.connect(self.arrow_release)
        # 速度控制
        move_btn.clicked.connect(self.car_speed_move)

    def connect_car(self):
        ip = self.ip_edit.text()
        port = int(self.port_edit.text())
        self.soc.connect((ip, port))

    def init_data(self):
        # 标识位
        head = -128
        self.head_data = struct.pack('<b',head)
        # 常用的类型
        self.led_open = 0x01
        self.led_close = 0x02
        self.led_toggle = 0x03
        self.car_up = 0x11
        self.car_back = 0x12
        self.car_left = 0x13
        self.car_right = 0x14
        self.car_stop = 0x15
        self.car_move = 0x16
        self.led_open_data = struct.pack('<b',self.led_open)
        self.led_close_data = struct.pack('<b',self.led_close)
        self.led_toggle_data = struct.pack('<b',self.led_toggle)
        self.car_up_data = struct.pack('<b',self.car_up)
        self.car_back_data = struct.pack('<b',self.car_back)
        self.car_left_data = struct.pack('<b',self.car_left)
        self.car_right_data = struct.pack('<b',self.car_right)
        self.car_stop_data = struct.pack('<b',self.car_stop)
        self.car_move_data = struct.pack('<b',self.car_move)

    def send_instructions(self, type,type_data,length,data=()):
        '''
        发送请求小车
        :param type: 请求类型
        :param type_data: 请求类型需要发送的数据
        :param length: 数据长度
        :param data: 数据(元组类型)
        :return:
        '''
        if length!=len(data):
            print('传递的数据长度不一致')
            return

        # 数据长度
        length_data = struct.pack('<b',length)
        # 数据发送的数据
        data_data = None
        # 总数
        data_total = 0
        for ele in data:
            data_total += ele
            if not data_data:
                data_data = struct.pack('<b',ele)
                continue
            data_data += struct.pack('<b',ele)

        # 校验位  注意:这里是无符号的校验位  方便服务器处理
        check = (type + length + data_total) & 0xff
        check_data = struct.pack('<B',check)
        # 需要发送的数据
        if data_data:
            # 发送的数据不为空
            send_data = self.head_data+self.head_data+type_data+length_data+data_data+check_data
        else:
            # 发送的数据为空
            send_data = self.head_data+self.head_data+type_data+length_data+check_data
        print(f'发送的数据:{send_data}')
        self.soc.send(send_data)

    def car_speed_move(self):
        # 获取两个轮子速度
        left = int(self.left_edit.text())
        right = int(self.right_edit.text())
        self.send_instructions(self.car_move,self.car_move_data,2,(left,right))

    def open_led(self):
        self.send_instructions(self.led_open,self.led_open_data,0)

    def close_led(self):
        self.send_instructions(self.led_close,self.led_close_data,0)

    def toggle_led(self):
        self.send_instructions(self.led_toggle,self.led_toggle_data,0)

    def arrow_release(self):
        self.send_instructions(self.car_stop,self.car_stop_data,0)

    def arrow_press(self, text):
        # 查看socket是否初始化
        if text == '前':
            self.send_instructions(self.car_up,self.car_up_data,0)
        elif text == '后':
            self.send_instructions(self.car_back,self.car_back_data,0)
        elif text == '左':
            self.send_instructions(self.car_left,self.car_left_data,0)
        elif text == '右':
            self.send_instructions(self.car_right,self.car_right_data,0)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())