news 2026/7/28 9:06:30

行空板K10离线语音识别控制智能小车:从硬件连接到状态机设计

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
行空板K10离线语音识别控制智能小车:从硬件连接到状态机设计

1. 项目缘起:当语音指令遇上智能小车

最近在捣鼓行空板K10,手边正好有一辆基于Arduino的智能小车底盘,一个想法就冒了出来:能不能让小车“听懂”人话?比如,我说“前进”,它就往前走;我说“左转”,它就乖乖向左拐。这个“行空板K10之语音识别控车”的项目,就是一次将离线语音识别与运动控制结合的趣味实践。

行空板K10本身集成了麦克风和离线语音识别引擎,这让我们无需依赖网络,就能在本地完成简单的语音指令识别。而通过串口或GPIO控制一个常见的智能小车底盘,则是创客领域的经典操作。两者的结合,看似简单,实则涉及到硬件接口、通信协议、语音算法应用和运动逻辑控制等多个环节。这个项目非常适合对嵌入式开发、物联网交互感兴趣的朋友,无论是用于教学演示、科创比赛,还是个人兴趣探索,都能让你对软硬件协同有更深的体会。接下来,我就把自己从零搭建、调试到最终跑通的全过程,以及中间踩过的坑和总结的经验,毫无保留地分享出来。

2. 核心硬件选型与连接方案解析

要实现语音控车,我们需要一个“大脑”来处理语音和决策,还需要一个“身体”来执行运动指令。在这个项目中,行空板K10担任“大脑”,而智能小车底盘则是“身体”。

2.1 为什么选择行空板K10作为控制核心?

市面上主控板很多,比如树莓派、Arduino、Micro:bit等。选择行空板K10,主要基于以下几个核心考量:

  1. 内置离线语音识别能力:这是最关键的一点。K10板载了高性能麦克风和专门的语音处理芯片,支持离线语音唤醒和关键词识别。这意味着我们不需要额外连接USB麦克风或复杂的语音模块,也完全不用操心联网带来的延迟和隐私问题。它开箱即用,提供了简洁的Python API来调用语音识别功能,极大降低了开发门槛。
  2. 强大的计算与丰富的接口:K10基于高性能处理器,运行完整的Linux系统,可以轻松运行Python程序处理复杂的逻辑。同时,它提供了USB、串口、I2C、GPIO等多种接口,为连接小车底盘提供了极大的灵活性。特别是其内置的Wi-Fi和蓝牙,为未来扩展(如手机APP遥控、数据回传)预留了可能。
  3. 完善的生态与开发环境:行空板配套的Jupyter编程环境对初学者非常友好,支持直接在网页上写Python代码、运行和调试。官方也提供了丰富的库和示例,关于语音识别的pinpong库封装得很好,让我们可以更关注业务逻辑而非底层驱动。

2.2 智能小车底盘的选择与通信方式确定

小车底盘的选择同样重要,它决定了我们如何驱动和控制。常见的小车底盘主要有两类驱动方式:

  • L298N/DRV8833等电机驱动模块+直流减速电机:这是最经典、最灵活的方案。你需要自己连接电机、驱动模块和电池。控制端通过输出PWM(脉宽调制)信号来控制电机转速,通过高低电平控制电机转向(正转/反转)。
  • 集成式智能小车底盘:这类底盘通常已经将电机、驱动、电源管理集成在一起,并提供一个统一的控制接口,最常见的是通过串口(UART)接收指令。例如,发送“FF000100FF”这样的十六进制字符串来控制前进。这种方案接线简单,上层逻辑清晰。

对于本项目,我强烈推荐使用集成串口通信的智能小车底盘。原因如下:

  • 简化连接:通常只需要连接行空板K10的TX、RX、GND三根线到底盘的串口接收端,无需额外连接多个GPIO口来控制电机驱动。
  • 协议清晰:底盘厂商会提供明确的串口通信协议文档,我们只需要按照格式拼接数据并发送即可,避开了复杂的PWM频率设置、电机死区调试等问题。
  • 稳定性高:集成底盘经过厂家测试,电机匹配和电源管理更优,减少了自行组装可能带来的不稳定因素。

我本次使用的就是一款支持串口控制的四轮智能小车底盘。如果你的底盘是L298N驱动的,那么控制逻辑会转向GPIO和PWM输出,虽然接线和代码会稍复杂,但原理相通,后文我也会简要提及其实现思路。

2.3 硬件连接实战与电源考量

确定了以串口通信为主的方案后,连接就变得非常简洁:

  1. 行空板K10端:找到其上的UART引脚(通常是TXRXGND)。请注意,行空板的硬件串口可能被系统默认用于调试,我们需要在软件中指定一个可用的软串口(如/dev/ttyS1)或未被占用的硬件串口。
  2. 小车底盘端:找到其串口指令接收接口(通常标识为RXDTXDGND)。
  3. 连线:遵循“交叉连接”原则,即行空板的TX连接底盘端的RXD,行空板的RX连接底盘端的TXD,两边的GND相连。切记:TX接RXD,RX接TXD,GND接GND。接反了会导致通信失败。
  4. 电源隔离这是一个至关重要的安全细节!行空板K10和小车底盘必须独立供电。切勿使用行空板的5V输出直接给电机驱动或底盘供电,因为电机启动和堵转时会产生很大的瞬间电流和电压波动,极易损坏行空板的核心电路。行空板通过USB供电,小车底盘使用自带的电池组(如7.4V锂电池)供电。两者之间只有信号线(TX、RX)和地线(GND)相连,实现“共地”,确保信号电平基准一致即可。

注意:如果你的底盘是L298N方案,连接会涉及将行空板的多个GPIO引脚连接到L298N的输入引脚(IN1, IN2, IN3, IN4等),并为L298N提供独立的外接电源(通常7-12V)。同时,行空板、L298N的逻辑供电端、电机供电端的地(GND)需要连接在一起。

3. 行空板语音识别功能深度配置与测试

硬件连接好后,我们首先需要确保行空板的“耳朵”和“嘴巴”工作正常。这里的“耳朵”是语音识别,“嘴巴”是语音反馈(可选,用于提示识别状态)。

3.1 离线语音识别引擎的初始化与关键词训练

行空板的离线语音识别功能主要通过pinpong库的Speech模块实现。它不是万能的,只能识别预先训练好的、数量有限的关键词。

from pinpong.board import Board from pinpong.libs.dfrobot_speech import Speech import time # 初始化行空板 Board().begin() # 创建语音识别对象 speech = Speech() # 定义需要识别的关键词列表 keywords = ["前进", "后退", "左转", "右转", "停止", "开始巡逻"] # 示例关键词 # 在实际项目中,关键词需要提前在行空板的语音模型中进行训练和导入。 # 训练通常通过配套的PC端工具或行空板上的特定应用完成,生成一个模型文件。 # 此处代码假设模型已训练并加载。 # 设置识别模式,例如设置为关键词识别模式 # speech.set_mode(speech.KEYWORD) # 具体API请参考最新版pinpong库文档

关键点与踩坑记录:

  • 关键词数量与长度限制:离线识别引擎对关键词的数量和音节长度有严格限制(例如最多支持50个词,每个词2-4个字)。设计指令时,应选择发音清晰、不易混淆的词语,如“前进”比“走”更好,“左转”比“向左”更明确。
  • 训练环境要求:在训练关键词时,务必在相对安静的环境下,用平稳的语速、正常的音量录制3-5次。背景噪音过大或发音差异太大会严重影响识别率。我最初在办公室训练,空调声导致“前进”和“停止”经常误识别,后来在安静房间重新训练后效果大幅提升。
  • 模型加载:训练好的模型文件需要放置在行空板文件系统的特定目录下,并在代码中指定路径加载。务必确认文件路径正确,且Python程序有读取权限。

3.2 语音识别循环与指令获取

初始化完成后,我们需要在一个循环中不断监听语音,并获取识别结果。

def listen_and_act(): while True: # 启动一次语音识别,并设置超时时间(例如3秒) # result = speech.recognize(timeout=3000) # 伪代码,实际API可能不同 # 假设recognize()函数返回识别到的关键词字符串,未识别到则返回None或空字符串 # 示例逻辑(需替换为实际API调用): # if result == "前进": # send_car_command("forward") # elif result == "左转": # send_car_command("turn_left") # elif result == "停止": # send_car_command("stop") # else: # print("未识别到有效指令") time.sleep(0.1) # 短暂延时,避免CPU占用过高 # 在实际编码中,你需要查阅pinpong库Speech类的最新API文档。 # 常见的模式可能是:speech.async_recognize(callback) 异步回调,或者 speech.recognize() 阻塞式识别。

经验分享:识别结果的后处理直接拿到的识别结果可能并不完美。为了提高系统鲁棒性,我通常会增加一个简单的后处理逻辑:

  1. 置信度过滤:如果识别API能返回置信度分数,可以设置一个阈值(如0.6),低于阈值的结果视为无效,要求重新说。
  2. 指令缓存与去抖:连续识别到同一个指令时,不要立即重复执行。可以设置一个时间窗口(如1秒),在这个窗口内,相同的指令只执行一次。防止因识别抖动导致小车“抽搐”。
  3. 添加唤醒词:更复杂的应用可以设置一个唤醒词(如“小车小车”),只有听到唤醒词后,接下来的几秒钟内才会识别运动指令,这样可以避免日常对话误触发小车动作。

4. 串口通信协议与小车主控逻辑实现

这是连接“大脑”(K10)和“身体”(底盘)的桥梁。我们需要根据小车底盘的协议文档,编写准确的串口数据发送函数。

4.1 解析小车底盘串口协议

不同厂家的协议不同,但格式大同小异。假设我们小车的协议如下(常见格式):

  • 数据帧格式:帧头 + 命令字 + 数据长度 + 数据内容 + 校验和 + 帧尾
  • 示例:控制小车以速度100前进的命令可能是0xFF 0x00 0x01 0x64 0xXX 0xFE
    • 0xFF帧头
    • 0x00命令字(代表前进)
    • 0x01数据长度(后面有1个字节的数据)
    • 0x64数据(速度值,十进制100)
    • 0xXX校验和(前面所有字节的和,取低8位)
    • 0xFE帧尾

你必须找到你所购买底盘的详细协议手册!没有手册,一切无从谈起。如果实在没有,可以尝试联系卖家,或者用串口调试工具监听底盘原配遥控器发出的数据来反向分析。

4.2 在行空板上实现串口数据发送

Python中使用pyserial库进行串口通信非常方便。首先确保行空板上已安装pyserial(通常已内置)。

import serial import time class CarController: def __init__(self, port='/dev/ttyS1', baudrate=9600): """ 初始化串口连接 :param port: 串口设备文件,如 /dev/ttyS1, /dev/ttyAMA0等,需要根据实际连接和系统查看 :param baudrate: 波特率,必须与小车底盘设置一致 """ try: # 初始化串口,设置超时时间 self.ser = serial.Serial(port=port, baudrate=baudrate, timeout=1) print(f"串口 {port} 打开成功") time.sleep(2) # 等待串口稳定,非常重要! except Exception as e: print(f"打开串口失败: {e}") self.ser = None def _calculate_checksum(self, data_bytes): """计算校验和(示例:简单求和取低8位)""" return sum(data_bytes) & 0xFF def send_command(self, cmd_byte, data_byte=None): """ 根据协议组装并发送一帧数据 :param cmd_byte: 命令字(整数) :param data_byte: 数据字节(整数),可选 """ if self.ser is None or not self.ser.is_open: print("串口未就绪") return False # 1. 组装数据部分 if data_byte is not None: data_length = 1 data_content = bytes([data_byte]) else: data_length = 0 data_content = b'' # 2. 组装完整帧(不含校验和) frame_head = b'\xFF' frame_cmd = bytes([cmd_byte]) frame_len = bytes([data_length]) frame_tail = b'\xFE' frame_without_checksum = frame_head + frame_cmd + frame_len + data_content # 3. 计算校验和(对帧头到数据内容的部分计算) checksum = self._calculate_checksum(frame_without_checksum) frame_checksum = bytes([checksum]) # 4. 最终帧 full_frame = frame_without_checksum + frame_checksum + frame_tail # 5. 发送 try: self.ser.write(full_frame) print(f"发送指令: {full_frame.hex().upper()}") return True except Exception as e: print(f"发送指令失败: {e}") return False def close(self): """关闭串口""" if self.ser and self.ser.is_open: self.ser.close() print("串口已关闭") # 定义命令字映射(根据你的协议手册修改) CMD_FORWARD = 0x00 CMD_BACKWARD = 0x01 CMD_TURN_LEFT = 0x02 CMD_TURN_RIGHT = 0x03 CMD_STOP = 0x04 SPEED_SLOW = 50 SPEED_NORMAL = 100 # 使用示例 if __name__ == "__main__": car = CarController(port='/dev/ttyS1', baudrate=9600) if car.ser: car.send_command(CMD_FORWARD, SPEED_NORMAL) # 前进 time.sleep(2) car.send_command(CMD_STOP) # 停止 time.sleep(1) car.send_command(CMD_TURN_LEFT, SPEED_SLOW) # 左转 time.sleep(1) car.send_command(CMD_STOP) car.close()

调试过程中的核心技巧:

  • 串口设备确认:使用命令ls /dev/tty*查看行空板上的串口设备列表。连接线后,拔插一下,观察列表变化,找到新增的设备(如/dev/ttyUSB0)。如果是板载串口,可能是/dev/ttyS1/dev/ttyAMA0,需要查阅行空板文档确认哪个可用且未被系统占用。
  • 波特率匹配必须与小车底盘设定的波特率完全一致,常见的有9600, 115200等。不匹配会导致收到乱码,小车无反应。
  • 上电顺序与延时:务必先给小车底盘上电,再运行行空板程序。在程序打开串口后,添加一个time.sleep(2)的延时,让串口硬件和底盘控制器完成初始化,这是避免第一条指令丢失的关键。
  • 善用调试工具:在编写正式代码前,可以先用minicomscreen或简单的Python脚本先测试串口收发是否正常。也可以使用USB转TTL模块,在电脑上用串口调试助手(如Putty、SecureCRT)模拟行空板向小车发送数据,验证协议是否正确。

5. 系统整合与主循环逻辑设计

当语音识别和串口控制两个核心模块都调试通过后,就可以将它们整合到一个主程序中,实现完整的语音控制逻辑。

5.1 状态机设计:让控制更智能

一个简单的“听到指令就执行”的逻辑可能会很生硬。例如,正在前进时听到“左转”,小车应该如何处理?直接左转会侧翻。更好的方式是引入一个简单的状态机。

class CarState: IDLE = "idle" # 空闲停止状态 MOVING = "moving" # 正在移动状态 # 可以扩展更多状态,如 TURNING, PATROLLING 等 class VoiceControlledCar: def __init__(self): self.state = CarState.IDLE self.current_speed = 0 self.car_controller = CarController() # 复用之前的串口控制类 # 初始化语音识别... self.speech = Speech() self._setup_keyword_callbacks() def _setup_keyword_callbacks(self): """绑定关键词与对应的处理函数""" self.keyword_actions = { "前进": self._action_forward, "后退": self._action_backward, "左转": self._action_turn_left, "右转": self._action_turn_right, "停止": self._action_stop, "开始巡逻": self._action_start_patrol, } def _action_forward(self): """处理‘前进’指令""" if self.state != CarState.MOVING: # 如果当前不是移动状态,则启动 self.car_controller.send_command(CMD_FORWARD, SPEED_NORMAL) self.state = CarState.MOVING self.current_speed = SPEED_NORMAL print("执行:前进") else: print("已在移动中,忽略重复指令") def _action_turn_left(self): """处理‘左转’指令""" if self.state == CarState.MOVING: # 移动中左转:先减速,再发送左转指令,短暂延时后恢复前进 self.car_controller.send_command(CMD_STOP) time.sleep(0.1) self.car_controller.send_command(CMD_TURN_LEFT, SPEED_SLOW) time.sleep(0.5) # 左转持续时间,可调整 self.car_controller.send_command(CMD_FORWARD, self.current_speed) print("执行:移动中左转") else: # 静止时左转:原地左转一定角度 self.car_controller.send_command(CMD_TURN_LEFT, SPEED_SLOW) time.sleep(0.8) # 原地转向时间 self.car_controller.send_command(CMD_STOP) print("执行:原地左转") def _action_stop(self): """处理‘停止’指令""" self.car_controller.send_command(CMD_STOP) self.state = CarState.IDLE self.current_speed = 0 print("执行:停止") # ... 其他动作函数类似 def run(self): """主循环""" print("语音控车系统启动...") while True: # 假设 speech.get_keyword() 是阻塞或非阻塞获取关键词的函数 keyword = self.speech.get_keyword() # 此处需替换为实际语音识别获取关键词的方法 if keyword and keyword in self.keyword_actions: self.keyword_actions[keyword]() # 执行对应的动作 time.sleep(0.05) # 短延时,降低CPU占用 if __name__ == "__main__": vcc = VoiceControlledCar() try: vcc.run() except KeyboardInterrupt: print("\n程序被用户中断") vcc.car_controller.send_command(CMD_STOP) vcc.car_controller.close()

这个状态机虽然简单,但解决了“移动中转向”的安全性问题。通过判断当前状态,执行不同的动作序列,使得控制逻辑更符合真实物理世界的约束。

5.2 多线程与异步处理考量

上面的主循环中,语音识别是阻塞的(speech.get_keyword()会一直等待直到识别到内容)。在实际应用中,你可能希望同时做一些其他事情,比如用LED灯显示状态,或者处理来自其他传感器(如超声波避障)的数据。这时就需要引入多线程或异步编程。

一个简单的改进是,将语音识别放在一个独立的线程中:

import threading class VoiceControlledCar: # ... __init__ 等部分不变 ... def _voice_listen_thread(self): """语音监听线程""" while self.running: keyword = self.speech.recognize(timeout=1000) # 带超时的识别 if keyword and keyword in self.keyword_actions: # 将识别到的指令放入一个线程安全的队列中,主线程从队列取指令执行 self.command_queue.put(keyword) # 即使没识别到,也继续循环 def run(self): print("系统启动") self.running = True self.command_queue = queue.Queue() # 启动语音监听线程 voice_thread = threading.Thread(target=self._voice_listen_thread, daemon=True) voice_thread.start() # 主线程负责从队列取指令并执行,同时可以处理其他任务(如灯光、避障) while self.running: try: keyword = self.command_queue.get(timeout=0.1) # 非阻塞获取 if keyword in self.keyword_actions: self.keyword_actions[keyword]() except queue.Empty: pass # 队列为空,执行其他循环任务,如检查避障传感器 # 其他任务逻辑... voice_thread.join()

使用线程后,主循环就不会被语音识别阻塞,可以更灵活地集成更多功能。但要注意线程间共享资源(如串口对象self.car_controller)的访问安全,避免多个线程同时发送串口指令导致数据混乱。简单的做法是让所有串口发送操作都在主线程中执行。

6. 进阶优化与功能扩展思路

基础功能跑通后,我们可以从稳定性、交互性和智能化三个方面进行优化和扩展。

6.1 提升语音识别鲁棒性

离线语音识别在复杂环境下的表现是挑战。除了之前提到的训练优化,还可以:

  • 声学前端处理(AEC, NS):如果行空板SDK支持,尝试开启回声消除(AEC)和噪声抑制(NS)功能,能有效提升嘈杂环境下的识别率。
  • 多关键词融合判决:对于重要指令(如“停止”),可以要求连续识别到两次才执行,防止误触发。
  • 视觉反馈:在行空板屏幕上显示当前识别到的指令文字,或者用板载LED灯的颜色变化(如识别中闪烁蓝色,识别成功亮绿色,失败亮红色)来提供即时反馈,让用户知道系统“听到”和“理解”的状态。

6.2 丰富控制模式与交互

  • 速度分级控制:定义“慢速前进”、“全速前进”等指令,通过发送不同的速度值给小车。
  • 组合指令与宏命令:实现“绕桩行驶”、“原地转一圈”这样的复杂动作。这需要在_action_函数中编写一系列顺序执行的底层指令(前进、转向、等待)。
  • 融合其他传感器:为小车增加超声波或红外避障传感器。在主循环中持续读取传感器数据,当检测到前方障碍物时,无论当前接收到什么语音指令(除了“后退”),都优先执行“停止”或“绕行”逻辑,实现基础的安全避障。这实现了“语音控制”与“自动安全”的结合。

6.3 项目延伸:从遥控到自主

这个项目是“语音遥控车”。你可以很容易地将其延伸为“语音指令交互的自主巡逻车”:

  1. 地图构建与路径规划(高级):结合摄像头或激光雷达(需额外硬件和强大算力,行空板可能吃力),让小车先构建环境地图。然后通过语音指令“去客厅”,小车就能自主规划路径并移动过去。
  2. 云端联动与智能家居:利用行空板的Wi-Fi,将识别到的指令上传到云平台(如阿里云IoT、Home Assistant)。当你对小车说“打开客厅灯”,小车将指令上传,云端再控制智能插座,实现语音控车与智能家居的联动。这打开了物联网应用的大门。

7. 完整项目代码框架与部署要点

最后,我将一个高度整合、考虑了基本异常处理和状态反馈的简化版项目框架整理如下,你可以在此基础上进行修改和填充。

#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 行空板K10语音控制智能小车 - 主程序 """ import serial import time import threading import queue from pinpong.board import Board from pinpong.libs.dfrobot_speech import Speech # 假设使用此库,请根据实际情况调整 # ==================== 硬件控制层 ==================== class SerialCarController: """串口小车控制器""" # ... (包含之前的 __init__, send_command, close 等方法,并增加异常重试机制) ... def send_command_with_retry(self, cmd, data=None, retries=3): for i in range(retries): if self.send_command(cmd, data): return True print(f"指令发送失败,第{i+1}次重试...") time.sleep(0.05) print("指令发送多次失败,请检查硬件连接") return False # ==================== 语音识别层 ==================== class VoiceRecognizer: """语音识别封装""" def __init__(self, keyword_list): Board().begin() self.speech = Speech() # 此处应包含加载特定关键词模型的代码 self.keyword_list = keyword_list self._last_recognized_time = 0 self._debounce_window = 1.0 # 指令去抖时间窗口(秒) def listen_one_keyword(self, timeout_ms=3000): """监听并返回一个关键词,支持简单的去抖""" # 此处调用实际语音识别API,例如: # result = self.speech.recognize_keyword(timeout=timeout_ms) result = None # 模拟结果,需替换为真实API调用 current_time = time.time() if result and result in self.keyword_list: if current_time - self._last_recognized_time > self._debounce_window: self._last_recognized_time = current_time return result else: print(f"[去抖] 忽略短时间内重复指令: {result}") return None return result # ==================== 业务逻辑层 ==================== class VoiceControlledCarApp: """主应用""" def __init__(self, serial_port, baudrate): self.running = False self.command_queue = queue.Queue() # 初始化硬件 print("初始化小车控制器...") self.car = SerialCarController(port=serial_port, baudrate=baudrate) if not self.car.ser or not self.car.ser.is_open: raise RuntimeError("小车控制器初始化失败,请检查串口连接和电源。") # 初始化语音 print("初始化语音识别...") self.keywords = ["前进", "后退", "左转", "右转", "停止", "加速", "减速"] self.recognizer = VoiceRecognizer(self.keywords) # 状态与映射 self.state = "IDLE" self.speed = 80 self._init_action_map() print("系统初始化完成。") def _init_action_map(self): """初始化指令到动作的映射""" self.action_map = { "前进": self._act_forward, "后退": self._act_backward, "左转": self._act_turn_left, "右转": self._act_turn_right, "停止": self._act_stop, "加速": self._act_speed_up, "减速": self._act_speed_down, } def _act_forward(self): if self.state != "MOVING": if self.car.send_command_with_retry(0x00, self.speed): self.state = "MOVING" print("动作:前进") # ... 其他 _act_ 函数定义 ... def _voice_thread_func(self): """语音监听线程函数""" print("语音监听线程启动") while self.running: cmd = self.recognizer.listen_one_keyword(2000) # 监听2秒 if cmd: print(f"识别到指令: {cmd}") self.command_queue.put(cmd) time.sleep(0.05) def _control_loop(self): """主控制循环""" print("主控制循环启动") while self.running: # 1. 处理语音指令 try: voice_cmd = self.command_queue.get_nowait() action_func = self.action_map.get(voice_cmd) if action_func: action_func() except queue.Empty: pass # 2. 此处可插入其他周期性任务,例如: # - 读取避障传感器数据并做安全处理 # - 更新行空板屏幕显示 # - 检查电池电压 time.sleep(0.02) # 主循环频率约50Hz def run(self): """启动应用""" self.running = True # 启动语音监听线程 voice_thread = threading.Thread(target=self._voice_thread_func, daemon=True) voice_thread.start() # 启动主控制循环(在主线程) try: self._control_loop() except KeyboardInterrupt: print("\n接收到中断信号,开始安全停止...") finally: self.shutdown() def shutdown(self): """安全关闭""" print("正在关闭系统...") self.running = False self._act_stop() # 发送停止指令 time.sleep(0.5) self.car.close() print("系统已安全关闭。") # ==================== 程序入口 ==================== if __name__ == "__main__": # 配置参数:请根据你的实际硬件连接修改! SERIAL_PORT = '/dev/ttyS1' # 行空板上的串口设备文件 BAUDRATE = 9600 # 必须与小车底盘波特率一致 app = VoiceControlledCarApp(SERIAL_PORT, BAUDRATE) app.run()

部署与运行要点:

  1. 权限问题:在行空板上运行串口程序可能需要权限。可以通过将用户加入dialout组,或者直接使用sudo运行(不推荐长期使用)。
  2. 开机自启动:若想上电即运行,需要将Python脚本设置为系统服务。可以编写一个systemd服务单元文件,或者简单地将启动命令添加到/etc/rc.local文件中(注意使用绝对路径)。
  3. 日志记录:在生产环境中,建议将print语句替换为更正式的日志模块(如logging),将运行状态和错误信息记录到文件中,方便后期排查问题。
  4. 电量监测:可以编写一个简单的线程,通过ADC读取电池电压(如果硬件支持),当电压低于阈值时,自动执行停止命令并语音提示“电量低”,然后安全关机。

这个项目从想法到实现,涉及了硬件接口、通信协议、语音算法应用和状态机逻辑等多个层面。调试过程可能会遇到语音识别不灵、串口无响应、小车动作异常等各种问题,但每一个问题的解决都会让你对嵌入式系统有更深的理解。希望这份详细的指南能帮助你顺利实现自己的语音控制智能小车,并在此基础上探索出更多有趣的应用。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/28 9:06:28

基于ESP32与LVGL的硬件音乐播放器:从硬件选型到软件架构全解析

1. 项目缘起:为什么是“千千静听”?最近在整理旧电脑,翻出来一个老古董MP3播放器,插上电居然还能响。听着那些十几年前的老歌,突然就想起了当年在Windows XP上,那个蓝色界面、功能纯粹、音质还不错的“千千…

作者头像 李华
网站建设 2026/7/28 9:05:48

摸鱼低代码平台终极指南:如何让开发像摸鱼一样轻松

摸鱼低代码平台终极指南:如何让开发像摸鱼一样轻松 【免费下载链接】mfish-nocode 摸鱼低代码平台,是一款致力于让开发像摸鱼一样轻松的低代码/无代码平台。首创微服务单体服务一体化架构。基于SpringBoot4、Spring Ai、Vue3等最新前后端技术栈。已用于多…

作者头像 李华
网站建设 2026/7/28 9:02:57

C++实现亚马逊商品数据采集:高性能爬虫架构与实战指南

1. 项目概述与核心价值最近在技术社区和项目群里,经常看到有朋友在讨论如何用Python去抓取亚马逊的商品信息,做价格监控或者竞品分析。这确实是个热门需求,Python生态里的Requests、Scrapy、Selenium用起来也确实方便。但不知道你有没有想过&…

作者头像 李华
网站建设 2026/7/28 9:02:44

从入门到精通:jellyfin-ffmpeg支持的20+音视频编解码器全清单

从入门到精通:jellyfin-ffmpeg支持的20音视频编解码器全清单 【免费下载链接】jellyfin-ffmpeg FFmpeg for Jellyfin with custom extensions and enhancements 项目地址: https://gitcode.com/gh_mirrors/je/jellyfin-ffmpeg jellyfin-ffmpeg是专为Jellyfin…

作者头像 李华
网站建设 2026/7/28 9:01:35

G-Helper:3步彻底告别华硕笔记本性能管理烦恼

G-Helper:3步彻底告别华硕笔记本性能管理烦恼 【免费下载链接】g-helper Lightweight Armoury Crate alternative for Asus laptops with nearly the same functionality. Works with ROG Zephyrus, Flow, TUF, Strix, Scar, ProArt, Vivobook, Zenbook, Expertbook…

作者头像 李华
网站建设 2026/7/28 9:00:14

告别过去还是拥抱回忆?ex.skill版本管理与/move-on命令使用指南

告别过去还是拥抱回忆?ex.skill版本管理与/move-on命令使用指南 【免费下载链接】ex-skill 致你忘不掉的那个TA,你们干大模型都是码圣 Its giving rebirth era. Welcome to Digital Life 1.0. 🫶 项目地址: https://gitcode.com/gh_mirrors…

作者头像 李华