|
【前言】 心心验验的智能小车底盘终于到货了 ![]()
今天把蓝牙远程控制的简易界面写了出来,并进行验证。
【实验器材】
1、【AIMB-2210】电脑主机
2、智小能套件:
![]() 3、蓝牙透传模块。
【实现原理】
1、使用两个可以透传的蓝牙模块,将PC端的远程控制命令通过蓝牙模块发送给小车。
2、智能小车将协议接收后传给智能小车主控,实现转向、前进等功能。
【代码编写】
在AIMB-2210上安装vscode+AI代码生成控制界面。 ![]()
在AIMB强大的支持下,快速生了python代码。界面如下:![]()
【使用方法】
当我向三个速度数据框写入速度数据后,蓝牙小车就实时进行前进以及转弯了。
【项目推荐计划】
1、实时获取小车的信息,同时将小车的定位也通过电脑的定位模块进行定位,为下一步,远程自动控制做准备。
附AI生成的源码:- import serial
- import time
- import struct
- import tkinter as tk
- from tkinter import ttk, messagebox
- import serial.tools.list_ports
- def calculate_checksum(data):
- """
- 计算校验和:前面数据累加和的低8位
- """
- return sum(data) & 0xFF
- def send_x_protocol_frame(ser, frame_code, data):
- """
- 发送X-Protocol帧
-
- Args:
- ser: 串口对象
- frame_code: 帧码
- data: 数据字节串
- """
- # 构造帧(不包括校验和)
- frame = bytearray()
- frame.append(0xAA) # 帧头1
- frame.append(0x55) # 帧头2
- frame.append(5 + len(data)) # 帧长
- frame.append(frame_code) # 帧码
- frame.extend(data) # 数据
-
- # 计算校验和(从第0位开始计算整个帧)
- checksum = calculate_checksum(frame)
- frame.append(checksum) # 校验和
-
- # 发送帧
- ser.write(frame)
- print(f"发送帧: {' '.join(f'{b:02X}' for b in frame)}")
- return len(frame)
- def send_values(ser, frame_code, value1, value2, value3):
- """
- 发送三个16位整数
-
- Args:
- ser: 串口对象
- frame_code: 帧码
- value1, value2, value3: 三个16位整数
- """
- # 构造6字节的有效数据
- data = bytearray()
- data.extend(struct.pack('>h', value1)) # 第一个16位整数
- data.extend(struct.pack('>h', value2)) # 第二个16位整数
- data.extend(struct.pack('>h', value3)) # 第三个16位整数
-
- # 发送帧
- bytes_sent = send_x_protocol_frame(ser, frame_code, data)
- print(f"发送数据: {value1}, {value2}, {value3}")
- return bytes_sent
- class XProtocolGUI:
- def __init__(self, root):
- self.root = root
- self.root.title("X-Protocol 串口发送工具")
- self.root.geometry("400x300")
- self.ser = None
-
- self.create_widgets()
- self.refresh_ports()
-
- def create_widgets(self):
- # 主框架
- main_frame = ttk.Frame(self.root, padding="10")
- main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
-
- # 串口选择
- ttk.Label(main_frame, text="串口:").grid(row=0, column=0, sticky=tk.W, pady=5)
- self.port_var = tk.StringVar()
- self.port_combo = ttk.Combobox(main_frame, textvariable=self.port_var, width=15)
- self.port_combo.grid(row=0, column=1, sticky=(tk.W, tk.E), pady=5)
-
- # 刷新按钮
- refresh_btn = ttk.Button(main_frame, text="刷新", command=self.refresh_ports)
- refresh_btn.grid(row=0, column=2, padx=(10,0), pady=5)
-
- # 连接按钮
- self.connect_btn = ttk.Button(main_frame, text="连接", command=self.toggle_connection)
- self.connect_btn.grid(row=0, column=3, padx=(10,0), pady=5)
-
- # 分隔线
- separator = ttk.Separator(main_frame, orient='horizontal')
- separator.grid(row=1, column=0, columnspan=4, sticky=(tk.W, tk.E), pady=10)
-
- # 数据输入
- ttk.Label(main_frame, text="数据1:").grid(row=2, column=0, sticky=tk.W, pady=5)
- self.value1_var = tk.StringVar(value="100")
- ttk.Entry(main_frame, textvariable=self.value1_var, width=10).grid(row=2, column=1, sticky=tk.W, pady=5)
-
- ttk.Label(main_frame, text="数据2:").grid(row=3, column=0, sticky=tk.W, pady=5)
- self.value2_var = tk.StringVar(value="0")
- ttk.Entry(main_frame, textvariable=self.value2_var, width=10).grid(row=3, column=1, sticky=tk.W, pady=5)
-
- ttk.Label(main_frame, text="数据3:").grid(row=4, column=0, sticky=tk.W, pady=5)
- self.value3_var = tk.StringVar(value="0")
- ttk.Entry(main_frame, textvariable=self.value3_var, width=10).grid(row=4, column=1, sticky=tk.W, pady=5)
-
- # 帧码输入
- ttk.Label(main_frame, text="帧码:").grid(row=5, column=0, sticky=tk.W, pady=5)
- self.frame_code_var = tk.StringVar(value="50")
- ttk.Entry(main_frame, textvariable=self.frame_code_var, width=10).grid(row=5, column=1, sticky=tk.W, pady=5)
-
- # 发送按钮
- self.send_btn = ttk.Button(main_frame, text="发送数据", command=self.send_data, state=tk.DISABLED)
- self.send_btn.grid(row=6, column=0, columnspan=2, pady=20)
-
- # 状态栏
- self.status_var = tk.StringVar(value="请选择串口并连接")
- self.status_bar = ttk.Label(main_frame, textvariable=self.status_var, relief=tk.SUNKEN)
- self.status_bar.grid(row=7, column=0, columnspan=4, sticky=(tk.W, tk.E), pady=(10,0))
-
- # 配置网格权重
- self.root.columnconfigure(0, weight=1)
- self.root.rowconfigure(0, weight=1)
- main_frame.columnconfigure(1, weight=1)
-
- def refresh_ports(self):
- """刷新可用串口列表"""
- ports = [port.device for port in serial.tools.list_ports.comports()]
- self.port_combo['values'] = ports
- if ports:
- self.port_combo.set(ports[0])
-
- def toggle_connection(self):
- """切换串口连接状态"""
- if self.ser is None:
- self.connect_serial()
- else:
- self.disconnect_serial()
-
- def connect_serial(self):
- """连接串口"""
- try:
- port = self.port_var.get()
- if not port:
- messagebox.showerror("错误", "请选择串口")
- return
-
- self.ser = serial.Serial(port, 230400, timeout=1)
- self.connect_btn.config(text="断开")
- self.send_btn.config(state=tk.NORMAL)
- self.status_var.set(f"已连接到 {port}")
- print(f"串口 {port} 打开成功")
- except Exception as e:
- messagebox.showerror("串口错误", f"无法打开串口: {str(e)}")
- self.ser = None
-
- def disconnect_serial(self):
- """断开串口连接"""
- if self.ser:
- self.ser.close()
- self.ser = None
- self.connect_btn.config(text="连接")
- self.send_btn.config(state=tk.DISABLED)
- self.status_var.set("已断开连接")
-
- def send_data(self):
- """发送数据"""
- if not self.ser:
- messagebox.showerror("错误", "请先连接串口")
- return
-
- try:
- # 获取输入值
- value1 = int(self.value1_var.get())
- value2 = int(self.value2_var.get())
- value3 = int(self.value3_var.get())
- frame_code = int(self.frame_code_var.get(), 16) # 帧码以十六进制解析
-
- # 发送数据
- send_values(self.ser, frame_code, value1, value2, value3)
- self.status_var.set(f"已发送: {value1}, {value2}, {value3}")
-
- except ValueError as e:
- messagebox.showerror("输入错误", "请输入有效的整数")
- except Exception as e:
- messagebox.showerror("发送错误", f"发送失败: {str(e)}")
-
- def on_closing(self):
- """关闭程序时断开串口"""
- if self.ser:
- self.ser.close()
- self.root.destroy()
- def main():
- root = tk.Tk()
- app = XProtocolGUI(root)
- root.protocol("WM_DELETE_WINDOW", app.on_closing)
- root.mainloop()
- if __name__ == "__main__":
- main()
复制代码
|