小车上位机控制
实现步骤¶
准备工作¶
- 安装固件
recv_send.bin
- 启动小车会自动开启wifi,名称是
smart_car
,密码是12345678
,电脑连接这个wifi
代码实现¶
- 搭建ui界面
- 连接小车服务器
- 发送指令给小车服务器执行
协议说明¶
控制协议¶
帧头 | 类型 | 数据长度 | 数据位 | 校验位 |
---|---|---|---|---|
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_())