惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
云风的 BLOG
云风的 BLOG
小众软件
小众软件
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Apple Machine Learning Research
Apple Machine Learning Research
博客园 - 司徒正美
博客园 - 聂微东
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
美团技术团队
宝玉的分享
宝玉的分享
量子位
V
Visual Studio Blog
罗磊的独立博客
Vercel News
Vercel News
B
Blog
J
Java Code Geeks
S
SegmentFault 最新的问题
Recent Announcements
Recent Announcements
有赞技术团队
有赞技术团队
P
Proofpoint News Feed
GbyAI
GbyAI
G
Google Developers Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC

Nemo

20260819 - Nemo 再见,2025 Complete ORB-SLAM3 Setup Guide for Jetson Xavier NX with RealSense D455 20250723 再见,2024 Using CertBot for Automatic Secure EMQX Broker Create Your Own GPS Data Publisher Support SSL AGV Dispatching System Technical Documentation Finding Nemo No title 香港賽馬會呈獻系列:黑白——攝影敘事 再见,2023 团团是只猫 Design an FSM for Robot State Machines – Basics of Computer Science Data-driven robot lifespan: Collection 数据驱动的机器人寿命:收集、诊断、预测 Exploring the design space of binary search trees 特首来了 Large language models, explained with a minimum of math and jargon 设计有缓存异步逻辑的监控脚本并测试其资源占用 在vscode的Dev Container中构建.NET开发环境及使用doxygen和graphviz绘制函数调用图 State or Status? A*算法两种时间复杂度 /A* Algorithm: Two Types of Time Complexity 使用KD-Tree快速收敛到最近坐标点/Fast convergence to the nearest coordinate point using KD-Tree 翻译 || 总结 - Go语言中的空结构体(The empty struct) 再见,2022 从PE工作报告中能读出什么 Give me miles, give me truth AMR调度系统性能优化/AMR Dispatch System Performance Optimization
使用loguru记录串口数据并使用Docker搭建ARM开发环境
Nemo · 2023-05-31 · via Nemo

背景

开发过程中需要记录机器人串口发送的数据,就想到之前体验感很好的loguru,就想着继续用它做一个:

  • 可以定期、定量、动作触发打包压缩,
  • 日志等级分类
  • 配置简单
  • 占用资源低

的机器人本体日志记录的系统,同时也要有很好的功能扩展性,后期扩展功能方便等

loguru配置

loguru是最早开发AMR调度系统发现的,比起去大量自定义logging,实在是太方便了,同时也是异步的,不会阻塞当前逻辑。

可以按照官方文档配置:

logger.add(
    logPath, 
    format="{time:YYYY-MM-DD at HH:mm:ss:SSS} | {message}",
    rotation=logger_rotation,
    retention=logRetention,
    compression=logCompression,
    encoding=logEncoding,
    enqueue=True,
)

这里为了是配置文件外置成config.ini,使用了configparser库,这样可以在不修改代码的情况下,通过修改配置文件来改变功能,这里不再赘述。

rotation函数

着重要说明的是rotation函数的配置,如果使用默认的rotation=00:00或者rotation=500 MB只能单一的实现每天或定量打包压缩日志,如果想要两种条件都匹配以及自定义一些条件,比如启动时打包便于本体开发人员截断之前日志等等,就需要使用自定义函数来实现,参考Log rotate by time and size #241:

class Rotator:

    def __init__(self, size, at):
        self._size = size
        now = datetime.now()
        today_at_time = now.replace(hour=at.hour, minute=at.minute, second=at.second)
        if now >= today_at_time:
            # the current time is already past the target time so it would rotate already
            # add one day to prevent an immediate rotation
            self._next_rotate = today_at_time + timedelta(days=1)
        else:
            self._next_rotate = today_at_time

    def should_rotate(self, message, file):
        file.seek(0, 2)
        if file.tell() + len(message) > self._size:
            return True
        if message.record["time"].timestamp() > self._next_rotate.timestamp():
            self._next_rotate += timedelta(days=1)
            return True
        return False

要实现每次启动程序,我的做法是定义一个全局变量和seek()值来判断是否需要return True,如果有更优美的实现请告诉我。这里也考虑过读取日志文件的创建时间来做判断,使用get_crtimes(os.path.abspath(file.name))在Linux系统下获得的时间为文件最后修改的时间,而不是文件创建的时间。

使用serial_asyncio

对于这种IO密集的逻辑和仅仅只是日志记录,最好的选择还是异步架构,Python的asyncio ,读取串口数据的https://pyserial-asyncio.readthedocs.io/en/latest/,程序伪代码如下:

async def handle_serial_data(reader, writer):
    while True:
        try:
            ByteData = await reader.readline()
            if ByteData and len(ByteData) > 3:  # filter of 3 strings data
                StrData = ByteData.decode(
                    "utf-8", errors='ignore')  # fix error
                StrData.strip().split("\n")
                logger.info(StrData)
            else:
                pass
        except Exception as e:
            # exception handle

async def main():
    try:
        loop = asyncio.get_event_loop()
        reader, writer = await serial_asyncio.open_serial_connection(
            loop=loop, url=serialPort, baudrate=serialBaudrate
        )

    except Exception:
        # exception handle
        return

    try:
        await handle_serial_data(reader, writer)
    finally:
        writer.close()
        await writer.wait_closed()

if __name__ == '__main__':
    try:
        asyncio.run(main())
    except Exception as e:
        logger.exception("main error!", type(e).__name__)

至此基本功能就实现了,结束了吗?

没~

因为忽视了程序实际运行的环境是树莓派而不是以往的服务器,而树莓派的eMMC寿命有限,所以日志内容分级处理和加入buffer,降低IO压力,以及使用Log2RAM,这里不谈。

在Ubuntu上使用Docker搭建ARM开发环境

由于日常开发的系统是Ubuntu 22.04,而树莓派则是ARM环境,一般来说,把Python当脚本跑的话没有区别的,但要是想用Pyinstaller打包的话,则要考虑运行环境了,为了后续开发需要,就使用Docker在Ubuntu上构建一个ARM环境。

docker buildx

docker buildx的Docker CLI插件,它允许你在不同的架构上构建Docker镜像。首先,安装并启用buildx插件:

# 安装docker buildx插件
docker buildx install

# 创建一个名为multiarch的构建实例,支持多种架构
docker buildx create --name multiarch

# 使用刚创建的multiarch实例
docker buildx use multiarch

安装QEMU模拟器:

sudo apt-get update && sudo apt-get install -y qemu-user-static

devcontainer.json

{
     "name": "ARM Python Development Environment",
     "dockerFile": "Dockerfile",
     "settings": {
       "terminal.integrated.shell.linux": "/bin/bash"
     },
     "extensions": [
       "ms-python.python"
     ],
     "remoteUser": "root"
   }

Dockerfile

   # 使用 ARM 架构的 Python 基础镜像
   FROM arm32v7/python:3.9-slim

   # 更新软件包列表并安装一些基本的工具
   RUN apt-get update && apt-get install -y --no-install-recommends \
       build-essential \
       curl \
       git \
       libssl-dev \
       zlib1g-dev \
       libbz2-dev \
       libreadline-dev \
       libsqlite3-dev \
       wget \
       llvm \
       libncursesw5-dev \
       xz-utils \
       tk-dev \
       libxml2-dev \
       libxmlsec1-dev \
       libffi-dev \
       liblzma-dev \
   && rm -rf /var/lib/apt/lists/*

   # 在此处添加其他 Docker 指令

构建完成后,查看系统环境:

dpkg --print-architecture