程序代码篇---多循环串口程序切换

上位机版(Python)

要实现根据串口接收结果高效切换四个 while 循环函数,我们可以采用状态机模式,配合非阻塞串口读取来设计程序结构。这种方式可以实现快速切换,避免不必要的资源消耗。

下面是一个高效的实现方案,使用状态变量控制当前执行的循环,并通过非阻塞方式检查串口数据:

import serial
import time
import threading
from enum import Enum# 定义状态枚举,清晰表示四个不同状态
class State(Enum):STATE_1 = 1STATE_2 = 2STATE_3 = 3STATE_4 = 4class SerialStateMachine:def __init__(self, port='COM3', baudrate=9600, timeout=0.1):# 初始化串口self.ser = serial.Serial(port, baudrate, timeout=timeout)self.current_state = State.STATE_1  # 默认初始状态self.running = True  # 程序运行标志self.lock = threading.Lock()  # 线程锁,确保状态切换安全# 启动串口监听线程self.serial_thread = threading.Thread(target=self._serial_listener, daemon=True)self.serial_thread.start()def _serial_listener(self):"""串口监听线程,非阻塞读取指令"""while self.running:if self.ser.in_waiting > 0:# 读取并处理串口数据command = self.ser.readline().decode().strip()self._process_command(command)time.sleep(0.01)  # 短暂休眠,降低CPU占用def _process_command(self, command):"""处理接收到的命令,切换状态"""command_map = {'1': State.STATE_1,'2': State.STATE_2,'3': State.STATE_3,'4': State.STATE_4,'exit': None  # 退出程序}new_state = command_map.get(command)if new_state is None and command == 'exit':self.running = Falseprint("收到退出指令,程序将终止")elif new_state:with self.lock:self.current_state = new_stateprint(f"切换到状态: {new_state}")def state_1_loop(self):"""第一个循环函数"""start_time = time.time()while True:# 检查是否需要切换状态with self.lock:if self.current_state != State.STATE_1:return# 状态1的业务逻辑elapsed = time.time() - start_timeprint(f"状态1运行中 - {elapsed:.2f}秒", end='\r')# 控制循环频率,避免CPU占用过高time.sleep(0.1)def state_2_loop(self):"""第二个循环函数"""start_time = time.time()while True:with self.lock:if self.current_state != State.STATE_2:return# 状态2的业务逻辑elapsed = time.time() - start_timeprint(f"状态2运行中 - {elapsed:.2f}秒", end='\r')time.sleep(0.1)def state_3_loop(self):"""第三个循环函数"""start_time = time.time()while True:with self.lock:if self.current_state != State.STATE_3:return# 状态3的业务逻辑elapsed = time.time() - start_timeprint(f"状态3运行中 - {elapsed:.2f}秒", end='\r')time.sleep(0.1)def state_4_loop(self):"""第四个循环函数"""start_time = time.time()while True:with self.lock:if self.current_state != State.STATE_4:return# 状态4的业务逻辑elapsed = time.time() - start_timeprint(f"状态4运行中 - {elapsed:.2f}秒", end='\r')time.sleep(0.1)def run(self):"""主运行函数,根据当前状态执行相应循环"""state_mapping = {State.STATE_1: self.state_1_loop,State.STATE_2: self.state_2_loop,State.STATE_3: self.state_3_loop,State.STATE_4: self.state_4_loop}print("程序启动,等待串口指令...")print("发送 1-4 切换状态,发送 exit 退出程序")while self.running:# 获取当前状态对应的函数并执行current_func = state_mapping.get(self.current_state)if current_func:current_func()time.sleep(0.05)# 清理资源self.ser.close()print("\n程序已退出")if __name__ == "__main__":try:# 根据实际情况修改串口参数machine = SerialStateMachine(port='COM3', baudrate=9600)machine.run()except serial.SerialException as e:print(f"串口错误: {e}")except KeyboardInterrupt:print("\n用户中断程序")

这个程序结构的高效性体现在以下几个方面:

  1. 独立串口监听线程:使用单独的线程处理串口数据接收,避免阻塞主循环,确保指令能被及时处理。

  2. 状态机设计:通过枚举类型明确定义四个状态,使状态切换逻辑清晰,易于维护和扩展。

  3. 非阻塞检查:每个循环函数内部定期检查状态是否变化,一旦收到切换指令能立即退出当前循环,切换到新状态。

  4. 线程安全:使用线程锁保证状态变量的线程安全访问,避免多线程操作导致的状态不一致。

  5. 资源控制:每个循环都有适当的休眠时间,避免 CPU 占用过高。

使用时,只需通过串口发送 "1"、"2"、"3"、"4" 即可切换到相应的循环,发送 "exit" 则退出程序。你可以根据实际需求,在四个状态循环函数中实现具体的业务逻辑。

Openmv版(MincroPython)

在 OpenMV 上提升代码逻辑的运行反应速度,需要结合其硬件特性(基于 MicroPython,资源有限,主打机器视觉)和应用场景(状态切换、串口通信、实时处理)进行针对性优化。以下是具体的优化方向和实现方法:

一、代码结构优化:减少冗余与阻塞

OpenMV 的 CPU 性能有限(如 H7 型号为 480MHz,但 MicroPython 解释执行效率低于原生 C),需避免冗余计算和长时间阻塞。

1. 简化状态切换逻辑,减少检查开销
  • 原状态机中若使用threading.Lock等线程同步机制,会引入额外开销。OpenMV 的线程支持较弱(无真正多线程,仅为分时调度),可改用轻量状态变量 + 非阻塞检查
  • 示例:用全局变量直接存储状态(无需锁),在循环中快速检查状态是否变化(避免复杂的上下文切换)。
import time
from pyb import UART# 状态定义(用整数代替Enum,减少属性查找开销)
STATE_1 = 1
STATE_2 = 2
STATE_3 = 3
STATE_4 = 4
current_state = STATE_1# 串口初始化(非阻塞模式)
uart = UART(3, 115200)  # 根据实际引脚配置
uart.init(115200, timeout_char=10)  # 缩短超时时间,快速返回def check_serial():"""非阻塞读取串口指令,直接修改全局状态"""global current_stateif uart.any():  # 检查是否有数据(比in_waiting更高效)cmd = uart.readline().strip().decode()if cmd == '1':current_state = STATE_1elif cmd == '2':current_state = STATE_2# ... 其他状态def state_1():while current_state == STATE_1:# 业务逻辑(避免复杂计算)check_serial()  # 循环内快速检查串口time.sleep_ms(10)  # 短延迟,平衡响应速度与CPU占用# 主循环
while True:if current_state == STATE_1:state_1()elif current_state == STATE_2:state_2()# ... 其他状态
2. 避免嵌套循环与深层调用
  • 嵌套循环会增加栈开销和条件判断时间,尽量将逻辑扁平化。
  • 减少函数调用层级:OpenMV 的函数调用开销较高,核心逻辑可适当 “内联”(避免频繁调用小函数)。

二、硬件资源优化:利用 OpenMV 专属特性

1. 优化串口通信效率
  • 降低波特率:若无需高速通信,可将波特率从 115200 降至 9600(减少误码率和处理时间)。
  • 固定指令格式:用单字节指令(如b'1'代替字符串'1'),避免复杂的字符串解析(decode()strip()耗时)。
# 优化:直接处理字节,避免字符串转换
def check_serial_fast():global current_stateif uart.any():cmd = uart.read(1)  # 读取1字节指令if cmd == b'1':current_state = STATE_1elif cmd == b'2':current_state = STATE_2

  • 使用中断接收:OpenMV 的 UART 支持中断(uart.irq()),可在数据到达时立即触发处理,避免循环中轮询的开销。
def uart_irq_handler(uart):global current_statecmd = uart.read(1)if cmd == b'1':current_state = STATE_1# ...# 注册中断
uart.irq(handler=uart_irq_handler, trigger=UART.IRQ_RXNE)  # 接收非空时触发
2. 控制循环延迟,平衡响应与性能
  • 原代码中time.sleep(0.1)(100ms)可能导致响应滞后,可缩短至time.sleep_ms(10)(10ms),但需避免过度缩短(导致 CPU 占用过高,反而卡顿)。
  • pyb.millis()(毫秒级定时器)替代time.time(),减少时间获取的开销(time.time()返回浮点数,计算耗时)。
# 优化:用毫秒计时,减少计算开销
def state_1():start_ms = pyb.millis()while current_state == STATE_1:elapsed = pyb.millis() - start_ms# ... 业务逻辑time.sleep_ms(10)  # 10ms延迟,响应更快

三、内存管理:减少 GC(垃圾回收)干扰

OpenMV 的内存较小(如 H7 为 32MB),频繁的内存分配会触发 GC,导致卡顿。

1. 预分配内存,避免动态创建对象
  • 循环中避免频繁创建列表、字符串等对象,提前定义并复用。
# 优化前:每次循环创建新字符串
while True:print(f"状态1运行中: {elapsed}ms")  # f-string会动态创建字符串# 优化后:复用缓冲区
buf = bytearray(32)  # 预分配缓冲区
while True:# 用sprintf格式化到缓冲区(C风格,更快)buf[:] = b''  # 清空pyb.uart_write_str(uart, "状态1: %dms\n" % elapsed)  # 避免字符串拼接
2. 禁用自动 GC,手动触发
  • 自动 GC 会在内存不足时突然执行,导致卡顿。可禁用自动 GC,在空闲时手动触发。
import gcgc.disable()  # 禁用自动垃圾回收def state_1():while current_state == STATE_1:# ... 业务逻辑if pyb.millis() % 1000 == 0:  # 每1秒手动回收一次gc.collect()

四、固件与工具链优化

1. 使用最新固件

OpenMV 官方会持续优化固件(如提升 MicroPython 执行效率、修复硬件驱动 bug),最新固件通常比旧版本运行更快。

  • 烧录方法:通过 OpenMV IDE 的「Tools → Install Firmware」直接升级(确保型号匹配)。
2. 开启代码编译优化
  • OpenMV IDE 支持将 Python 代码编译为字节码(.mpy),执行速度比纯脚本(.py)快 30% 以上。
  • 操作:「Tools → Save as .mpy」将main.py编译为main.mpy,放入 SD 卡(OpenMV 会优先执行.mpy)。

五、避免耗时操作阻塞主循环

1. 减少打印输出

print()会通过 USB 串口传输数据,速度慢且阻塞执行。调试完成后应删除冗余打印,或用条件编译关闭。

DEBUG = False  # 发布时设为Falseif DEBUG:print("调试信息")  # 仅调试时输出
2. 分离耗时任务(如图像处理)
  • 若涉及摄像头图像处理,尽量使用 OpenMV 内置的 C 实现函数(如img.find_blobs()),避免用纯 Python 处理像素(速度极慢)。
  • 将图像处理与状态切换分离:在单独的短周期内处理图像,结果存入全局变量,状态循环仅读取结果(不直接处理)。

总结:关键优化点

  1. 简化状态逻辑:用全局变量 + 轻量检查替代线程和锁。
  2. 优化串口:用中断接收 + 字节指令,减少解析开销。
  3. 控制延迟:缩短sleep时间至 10-50ms,平衡响应与性能。
  4. 内存管理:预分配对象 + 手动 GC,避免卡顿。
  5. 利用硬件特性:用内置函数(C 实现)、编译.mpy文件。

通过以上方法,可显著提升 OpenMV 代码的响应速度,尤其适合需要快速切换状态的串口控制场景。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/917431.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/917431.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

rk3568上,实现ota,计算hash,验证签名,判断激活分区,并通过dd命令,写入对应AB分区

通过自定义升级程序&#xff0c;更直观的理解ota升级原理。 一、模拟计算hash&#xff0c;验证签名&#xff0c;判断激活分区&#xff0c;并通过dd命令&#xff0c;写入对应分区 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <u…

数据分析—numpy库

numpy库NumPy 库全面指南NumPy (Numerical Python) 是 Python 科学计算的基础库&#xff0c;提供了高性能的多维数组对象和工具。以下是 NumPy 的核心功能和使用方法。一、安装与基础1. 安装 NumPypip install numpy2. 导入 NumPyimport numpy as np # 标准导入方式二、数组创建…

Vue3 setup、ref和reactive函数

一、setup函数1.理解&#xff1a;Vue3.0中一个新的配置项&#xff0c;值为一个函数。2.setup是所有Composition API(组合API)的“表演舞台”。3.组件中用到的&#xff1a;数据、方法等等&#xff0c;均要配置在setup中。4.setup函数的两种返回值&#xff1a;(1).若返回一个对象…

python中appium 的NoSuchElementException错误 原因以及解决办法

错误收集D:\Program\Util\python.exe "D:/Program/myUtil/PyCharm 2024.3.5/plugins/python-ce/helpers/pycharm/_jb_pytest_runner.py" --target demo.py::TestAppium Testing started at 15:57 ... Launching pytest with arguments demo.py::TestAppium --no-hea…

mybatis-plus从入门到入土(四):持久层接口之BaseMapper和选装件

大家好&#xff0c;今天继续更新MybatisPlus从入门到入土系列&#xff0c;上一次的持久层接口还没讲完&#xff0c;只讲了IService接口&#xff0c;今天我们继续来讲一下。 BaseMapper BaseMapper中的方法也比较简单&#xff0c;都是增删改查的基础API&#xff0c;不知道大家还…

数组和指针的关系

在 C 语言中&#xff0c;​指针和数组有着非常紧密的联系&#xff0c;但它们本质上是 ​不同的概念。理解它们的关系是掌握 C 语言内存操作的关键。下面我会从多个角度帮你梳理 ​指针和数组的直接联系&#xff0c;并解释它们的异同点。1. 数组和指针的本质区别​概念本质存储方…

AI大模型探索之路-实战篇:智能化IT领域搜索引擎之github网站在线搜索

系列篇章💥 No. 文章 1 AI大模型探索之路-实战篇:智能化IT领域搜索引擎的构建与初步实践 2 AI大模型探索之路-实战篇:智能化IT领域搜索引擎之GLM-4大模型技术的实践探索 3 AI大模型探索之路-实战篇:智能化IT领域搜索引擎之知乎网站数据获取(初步实践) 4 AI大模型探索之路…

从0到1学PHP(十二):PHP 框架入门与项目实战

目录一、主流 PHP 框架介绍1.1 Laravel1.2 ThinkPHP1.3 Yii1.4 框架的优势二、框架基本使用&#xff08;以 Laravel 为例&#xff09;2.1 框架的安装与配置2.2 路由定义、控制器创建、视图渲染2.3 数据库操作&#xff08;ORM 的使用&#xff09;三、小型项目实战3.1 项目需求分…

MPLS LSP

一、概述上一章我们已经介绍过,LSP是MPLS报文在MPLS网络中转发时经过的路径,可以看作是由报文传输方向节点为对应FEC分配的MPLS入标签组成的,因为每台设备上为每个FEC分配的入标签是唯一 的&#xff0c;并与由下游节点为本地节点上该FEC分配的出标签建立映射关系&#xff0c; 所…

图像、视频、音频多模态大模型中长上下文token压缩方法综述

多模态大模型MLLMs 能够处理高分辨率图像、长视频序列和冗长音频输入等复杂上下文&#xff0c;但自注意力机制的二次复杂度使得大量输入 token 带来了巨大的计算和内存需求。 如下图&#xff0c;上&#xff1a;图像、视频和音频数据类型可以在其表示维度上进行扩展&#xff0c;…

Spring MVC 九大组件源码深度剖析(一):MultipartResolver - 文件上传的幕后指挥官

文章目录一、为什么从 MultipartResolver 开始&#xff1f;二、核心接口&#xff1a;定义文件上传的契约三、实现解析&#xff1a;两种策略的源码较量1. StandardServletMultipartResolver&#xff08;Servlet 3.0 首选&#xff09;2. CommonsMultipartResolver&#xff08;兼容…

stm32是如何实现电源控制的?

STM32的电源控制主要通过内置的电源管理模块&#xff08;PWR&#xff09;实现&#xff0c;涵盖电压调节、功耗模式切换和电源监控等功能。以下是其核心机制及实现方式&#xff1a;​​1. 电源架构与供电区域​​STM32的电源系统分为多个供电区域&#xff0c;各司其职&#xff1…

《R for Data Science (2e)》免费中文翻译 (第3章) --- Data transformation(1)

写在前面 本系列推文为《R for Data Science (2)》的中文翻译版本。所有内容都通过开源免费的方式上传至Github&#xff0c;欢迎大家参与贡献&#xff0c;详细信息见&#xff1a; Books-zh-cn 项目介绍&#xff1a; Books-zh-cn&#xff1a;开源免费的中文书籍社区 r4ds-zh-cn …

rclone、rsync、scp使用总结

数据同步工具使用总结【rclone、rsync、scp】一、数据处理背景二、数据处理方法对比1、数据关系梳理2、不同工具处理方法3、经验总结三、工具扩展知识1、rclone工具介绍&#xff08;1&#xff09;、rclone概述&#xff08;2&#xff09;、安装工具及配置本地文件迁移到云上服务…

用latex+vscode+ctex写毕业论文

文章目录前言一、安装latex二、安装ctex包三、更新ctex包四、使用ctex文档类前言 用latexvscodectex写毕业论文。&#xff08;英文论文不用安装ctex&#xff09; CTEX 宏集是面向中文排版的通用 LATEX 排版框架&#xff0c;为中文 LATEX 文档提供了汉字输出支持、标点压缩、字…

深度学习·mmsegmentation基础教程

mmsegmentation的使用教程 mmsegmentation微调方法总结 自定义自己的数据集&#xff1a;mmsegmentation\configs\_base_\datasets\ZihaoDataset_pipeline.py注册&#xff1a;mmsegmentation\configs\_base_\datasets\__init__.py定义训练和测试的pipeline&#xff1a;mmsegme…

InfluxDB 与 Node.js 框架:Express 集成方案(二)

四、优化与注意事项 &#xff08;一&#xff09;性能优化技巧 连接池管理&#xff1a;使用连接池可以有效减少创建和销毁数据库连接的开销。在 Node.js 中&#xff0c;可以借助influx模块结合第三方连接池库&#xff0c;如generic-pool来实现连接池的管理 。通过设置连接池的…

单位长度上的RC参数

1inch1000mil25.4mm2.54cm 使用SI9000计算导线上电容电感参数并使用Q2D进行仿真验证。使用SI9000建立一个阻抗为50欧的微带线模型&#xff0c;后对该模型进行1GHz频域计算 通过计算得到结果&#xff0c;可知1GHz频率下单位传输线上的RLGC参数使用SI9000计算好单位长度上的RLGC参…

基于Dockerfile 部署一个 Flask 应用

Docker 与 Python&#xff1a;容器化部署应用&#xff0c;实现快速发布与弹性伸缩 以下是一个简单的 Flask 应用 # app.py - 一个简单的Flask应用 from flask import Flask import osapp Flask(__name__)app.route("/") def hello():env os.environ.get(FLASK_ENV,…

DFT设计中的不同阶段介绍

在DFT&#xff08;Design for Test&#xff0c;可测试性设计&#xff09;软件开发中&#xff0c;针对设计检测的完整流程通常包含Setup&#xff08;设置&#xff09;、Analysis&#xff08;分析&#xff09;、Insertion&#xff08;插入&#xff09;和Verification&#xff08;…