回答

收藏

[评测分享] 【AIMB-2210】AI编写小车控制界面

开发板专区 开发板专区 879 人阅读 | 0 人回复 | 2025-11-28

【前言】
心心验验的智能小车底盘终于到货了

今天把蓝牙远程控制的简易界面写了出来,并进行验证。
【实验器材】
1、【AIMB-2210】电脑主机
2、智小能套件:
3、蓝牙透传模块。
【实现原理】
1、使用两个可以透传的蓝牙模块,将PC端的远程控制命令通过蓝牙模块发送给小车。
2、智能小车将协议接收后传给智能小车主控,实现转向、前进等功能。
【代码编写】
在AIMB-2210上安装vscode+AI代码生成控制界面。

在AIMB强大的支持下,快速生了python代码。界面如下:

【使用方法】
当我向三个速度数据框写入速度数据后,蓝牙小车就实时进行前进以及转弯了。
【项目推荐计划】
1、实时获取小车的信息,同时将小车的定位也通过电脑的定位模块进行定位,为下一步,远程自动控制做准备。
附AI生成的源码:
  1. import serial
  2. import time
  3. import struct
  4. import tkinter as tk
  5. from tkinter import ttk, messagebox
  6. import serial.tools.list_ports

  7. def calculate_checksum(data):
  8.     """
  9.     计算校验和:前面数据累加和的低8位
  10.     """
  11.     return sum(data) & 0xFF

  12. def send_x_protocol_frame(ser, frame_code, data):
  13.     """
  14.     发送X-Protocol帧
  15.    
  16.     Args:
  17.         ser: 串口对象
  18.         frame_code: 帧码
  19.         data: 数据字节串
  20.     """
  21.     # 构造帧(不包括校验和)
  22.     frame = bytearray()
  23.     frame.append(0xAA)  # 帧头1
  24.     frame.append(0x55)  # 帧头2
  25.     frame.append(5 + len(data))  # 帧长
  26.     frame.append(frame_code)  # 帧码
  27.     frame.extend(data)  # 数据
  28.    
  29.     # 计算校验和(从第0位开始计算整个帧)
  30.     checksum = calculate_checksum(frame)
  31.     frame.append(checksum)  # 校验和
  32.    
  33.     # 发送帧
  34.     ser.write(frame)
  35.     print(f"发送帧: {' '.join(f'{b:02X}' for b in frame)}")
  36.     return len(frame)

  37. def send_values(ser, frame_code, value1, value2, value3):
  38.     """
  39.     发送三个16位整数
  40.    
  41.     Args:
  42.         ser: 串口对象
  43.         frame_code: 帧码
  44.         value1, value2, value3: 三个16位整数
  45.     """
  46.     # 构造6字节的有效数据
  47.     data = bytearray()
  48.     data.extend(struct.pack('>h', value1))  # 第一个16位整数
  49.     data.extend(struct.pack('>h', value2))  # 第二个16位整数
  50.     data.extend(struct.pack('>h', value3))  # 第三个16位整数
  51.    
  52.     # 发送帧
  53.     bytes_sent = send_x_protocol_frame(ser, frame_code, data)
  54.     print(f"发送数据: {value1}, {value2}, {value3}")
  55.     return bytes_sent

  56. class XProtocolGUI:
  57.     def __init__(self, root):
  58.         self.root = root
  59.         self.root.title("X-Protocol 串口发送工具")
  60.         self.root.geometry("400x300")
  61.         self.ser = None
  62.         
  63.         self.create_widgets()
  64.         self.refresh_ports()
  65.         
  66.     def create_widgets(self):
  67.         # 主框架
  68.         main_frame = ttk.Frame(self.root, padding="10")
  69.         main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
  70.         
  71.         # 串口选择
  72.         ttk.Label(main_frame, text="串口:").grid(row=0, column=0, sticky=tk.W, pady=5)
  73.         self.port_var = tk.StringVar()
  74.         self.port_combo = ttk.Combobox(main_frame, textvariable=self.port_var, width=15)
  75.         self.port_combo.grid(row=0, column=1, sticky=(tk.W, tk.E), pady=5)
  76.         
  77.         # 刷新按钮
  78.         refresh_btn = ttk.Button(main_frame, text="刷新", command=self.refresh_ports)
  79.         refresh_btn.grid(row=0, column=2, padx=(10,0), pady=5)
  80.         
  81.         # 连接按钮
  82.         self.connect_btn = ttk.Button(main_frame, text="连接", command=self.toggle_connection)
  83.         self.connect_btn.grid(row=0, column=3, padx=(10,0), pady=5)
  84.         
  85.         # 分隔线
  86.         separator = ttk.Separator(main_frame, orient='horizontal')
  87.         separator.grid(row=1, column=0, columnspan=4, sticky=(tk.W, tk.E), pady=10)
  88.         
  89.         # 数据输入
  90.         ttk.Label(main_frame, text="数据1:").grid(row=2, column=0, sticky=tk.W, pady=5)
  91.         self.value1_var = tk.StringVar(value="100")
  92.         ttk.Entry(main_frame, textvariable=self.value1_var, width=10).grid(row=2, column=1, sticky=tk.W, pady=5)
  93.         
  94.         ttk.Label(main_frame, text="数据2:").grid(row=3, column=0, sticky=tk.W, pady=5)
  95.         self.value2_var = tk.StringVar(value="0")
  96.         ttk.Entry(main_frame, textvariable=self.value2_var, width=10).grid(row=3, column=1, sticky=tk.W, pady=5)
  97.         
  98.         ttk.Label(main_frame, text="数据3:").grid(row=4, column=0, sticky=tk.W, pady=5)
  99.         self.value3_var = tk.StringVar(value="0")
  100.         ttk.Entry(main_frame, textvariable=self.value3_var, width=10).grid(row=4, column=1, sticky=tk.W, pady=5)
  101.         
  102.         # 帧码输入
  103.         ttk.Label(main_frame, text="帧码:").grid(row=5, column=0, sticky=tk.W, pady=5)
  104.         self.frame_code_var = tk.StringVar(value="50")
  105.         ttk.Entry(main_frame, textvariable=self.frame_code_var, width=10).grid(row=5, column=1, sticky=tk.W, pady=5)
  106.         
  107.         # 发送按钮
  108.         self.send_btn = ttk.Button(main_frame, text="发送数据", command=self.send_data, state=tk.DISABLED)
  109.         self.send_btn.grid(row=6, column=0, columnspan=2, pady=20)
  110.         
  111.         # 状态栏
  112.         self.status_var = tk.StringVar(value="请选择串口并连接")
  113.         self.status_bar = ttk.Label(main_frame, textvariable=self.status_var, relief=tk.SUNKEN)
  114.         self.status_bar.grid(row=7, column=0, columnspan=4, sticky=(tk.W, tk.E), pady=(10,0))
  115.         
  116.         # 配置网格权重
  117.         self.root.columnconfigure(0, weight=1)
  118.         self.root.rowconfigure(0, weight=1)
  119.         main_frame.columnconfigure(1, weight=1)
  120.         
  121.     def refresh_ports(self):
  122.         """刷新可用串口列表"""
  123.         ports = [port.device for port in serial.tools.list_ports.comports()]
  124.         self.port_combo['values'] = ports
  125.         if ports:
  126.             self.port_combo.set(ports[0])
  127.             
  128.     def toggle_connection(self):
  129.         """切换串口连接状态"""
  130.         if self.ser is None:
  131.             self.connect_serial()
  132.         else:
  133.             self.disconnect_serial()
  134.             
  135.     def connect_serial(self):
  136.         """连接串口"""
  137.         try:
  138.             port = self.port_var.get()
  139.             if not port:
  140.                 messagebox.showerror("错误", "请选择串口")
  141.                 return
  142.                
  143.             self.ser = serial.Serial(port, 230400, timeout=1)
  144.             self.connect_btn.config(text="断开")
  145.             self.send_btn.config(state=tk.NORMAL)
  146.             self.status_var.set(f"已连接到 {port}")
  147.             print(f"串口 {port} 打开成功")
  148.         except Exception as e:
  149.             messagebox.showerror("串口错误", f"无法打开串口: {str(e)}")
  150.             self.ser = None
  151.             
  152.     def disconnect_serial(self):
  153.         """断开串口连接"""
  154.         if self.ser:
  155.             self.ser.close()
  156.             self.ser = None
  157.         self.connect_btn.config(text="连接")
  158.         self.send_btn.config(state=tk.DISABLED)
  159.         self.status_var.set("已断开连接")
  160.         
  161.     def send_data(self):
  162.         """发送数据"""
  163.         if not self.ser:
  164.             messagebox.showerror("错误", "请先连接串口")
  165.             return
  166.             
  167.         try:
  168.             # 获取输入值
  169.             value1 = int(self.value1_var.get())
  170.             value2 = int(self.value2_var.get())
  171.             value3 = int(self.value3_var.get())
  172.             frame_code = int(self.frame_code_var.get(), 16)  # 帧码以十六进制解析
  173.             
  174.             # 发送数据
  175.             send_values(self.ser, frame_code, value1, value2, value3)
  176.             self.status_var.set(f"已发送: {value1}, {value2}, {value3}")
  177.             
  178.         except ValueError as e:
  179.             messagebox.showerror("输入错误", "请输入有效的整数")
  180.         except Exception as e:
  181.             messagebox.showerror("发送错误", f"发送失败: {str(e)}")
  182.             
  183.     def on_closing(self):
  184.         """关闭程序时断开串口"""
  185.         if self.ser:
  186.             self.ser.close()
  187.         self.root.destroy()

  188. def main():
  189.     root = tk.Tk()
  190.     app = XProtocolGUI(root)
  191.     root.protocol("WM_DELETE_WINDOW", app.on_closing)
  192.     root.mainloop()

  193. if __name__ == "__main__":
  194.     main()
复制代码




分享到:
回复

使用道具 举报

您需要登录后才可以回帖 注册/登录

本版积分规则

关闭

站长推荐上一条 /2 下一条