Arduino与CircuitPython通过SPI Flash和FATFS实现数据无缝交换

news2026/5/15 1:27:41
1. 项目概述与核心价值在嵌入式开发领域数据存储与交换一直是个绕不开的经典话题。无论是记录传感器数据、保存设备配置还是实现固件的在线更新我们都需要一个可靠、高效且易于管理的存储方案。SPI Flash芯片以其小巧的体积、低廉的成本和稳定的非易失性存储特性成为了众多微控制器项目的首选。然而直接操作Flash的扇区进行读写对于开发者来说既繁琐又容易出错尤其是在需要存储结构化数据或大量文件时。这时引入一个轻量级的文件系统就显得尤为重要。FATFS这个在嵌入式世界声名显赫的文件系统库正是为了解决这个问题而生。它让我们能在像Arduino这样的资源受限平台上用上类似fopen、fwrite这样熟悉的文件操作接口极大地提升了开发效率和数据管理的便捷性。但故事到这里并没有结束。在实际项目中我们常常面临一个更具体的需求如何让存储在SPI Flash里的数据能够被PC或其他系统方便地读取和修改毕竟我们不能总是依赖串口调试助手一行行地打印日志文件。这就是CircuitPython登场的时候了。CircuitPython有一个非常酷的特性它可以将板载的SPI Flash或外部Flash挂载为一个名为CIRCUITPY的U盘Mass Storage Device。这意味着你只需要一根USB线就能像操作普通U盘一样直接拖拽文件进行读写。那么一个很自然的想法就产生了能否让运行Arduino固件、使用FATFS进行数据记录的程序与CircuitPython的这个“U盘模式”和谐共处实现数据的无缝交换呢答案是肯定的而且这套组合拳的威力远超你的想象。本文将深入拆解这一完整的技术链路。我会从最基础的Arduino SPI Flash库和FATFS文件系统的使用讲起手把手带你实现数据的写入与读取。然后我们会聚焦于那个关键的“魔法时刻”——如何让Arduino写好的数据文件通过CircuitPython的U盘模式在电脑上直接可见、可编辑。整个过程涉及固件切换、文件系统兼容性、数据备份等实操细节我会结合我多年在物联网设备开发中积累的经验分享那些官方文档里不会写的“坑”和技巧。无论你是正在为数据记录项目寻找方案的工程师还是对嵌入式文件系统感兴趣的技术爱好者这篇文章都将为你提供一条清晰、可复现的实现路径。2. 核心组件与工作原理深度解析在动手写代码之前我们必须先理解手中的“武器”。这套方案的核心在于三个技术组件的协同工作SPI Flash物理层、FATFS逻辑层以及CircuitPython的USB大容量存储驱动。只有摸清了它们各自的脾气和交互方式我们才能避免后续开发中的许多困惑。2.1 SPI Flash你的微型硬盘SPI Flash全称Serial Peripheral Interface Flash Memory是一种通过SPI总线接口访问的NOR型闪存。你可以把它想象成一块极其微型的固态硬盘。与我们在Arduino中常用的EEPROM相比SPI Flash的容量要大得多通常从512KB到16MB甚至更大读写速度也更快但它的操作单位是“扇区”Sector通常4KB或“页”Page通常256字节不能像EEPROM那样直接进行单个字节的随机改写。在进行写操作前必须先将目标扇区擦除置为0xFF然后才能写入新数据。这种特性决定了我们必须通过文件系统来管理它否则自己处理擦写均衡和坏块管理会非常头疼。市面上常见的SPI Flash芯片如Winbond的W25Q系列、Adafruit自家板载的Flash其驱动逻辑大同小异。Adafruit的SPI Flash库Adafruit_SPIFlash为我们封装了底层细节提供了统一的Adafruit_FlashTransport和Adafruit_SPIFlash对象让我们可以像操作一个块设备Block Device一样去读写Flash。2.2 FATFS嵌入式世界的文件系统标准FATFS是一个为小型嵌入式系统设计的通用FATFile Allocation Table文件系统模块。它完全用ANSI C编写与平台无关资源占用极小。FATFS在SPI Flash库中是通过一个名为Adafruit_FatFs的类来集成的。这个类本质上是一个适配层Adapter它让FATFS能够调用我们上面提到的Adafruit_SPIFlash对象提供的底层块设备读写接口。这里有一个至关重要的概念文件系统的“挂载”。当你调用fatfs.begin()时库会做两件事1. 检查Flash的指定区域通常是从起始地址开始是否存在一个有效的FAT文件系统结构如引导扇区、FAT表等。2. 如果存在就将其信息加载到内存中建立好文件访问的通道如果不存在你可以选择格式化该区域创建一个新的空白FAT文件系统。这个“指定区域”就是文件系统所占用的Flash物理空间它必须是擦除扇区大小的整数倍。2.3 CircuitPython的“魔法”USB Mass StorageCircuitPython之所以能让开发板变身U盘奥秘在于其固件内部实现了一个USB大容量存储设备Mass Storage Class, MSC的驱动。当CircuitPython启动时它会检测板载的SPI Flash并尝试将其中的某个分区通常是第一个以FAT文件系统的形式通过USB接口暴露给主机电脑。电脑会将其识别为一个可移动磁盘CIRCUITPY。这里就引出了兼容性的核心为了让Arduino和CircuitPython都能读写同一块Flash它们必须就两件事达成一致文件系统类型双方都必须使用FAT文件系统通常是FAT12/FAT16对于小容量Flash。数据存储的物理位置它们必须“约定”好文件系统从Flash的哪个物理地址开始。如果Arduino把文件系统创建在了Flash的0x0000地址而CircuitPython却试图从0x1000地址去挂载那双方肯定看不到彼此的文件。幸运的是Adafruit的生态系统考虑到了这一点。其Arduino SPI Flash库和CircuitPython固件对于Adafruit自家的开发板如Feather M0 Express, Metro M0 Express等通常使用默认的、相同的起始地址。但对于其他板型或自定义板这个地址可能需要我们手动配置这是后续操作中一个需要留意的关键点。3. 环境搭建与基础文件读写实战理论铺垫完毕现在让我们进入实战环节。我将以Adafruit Feather M0 Express这块经典板子为例因为它同时完美支持Arduino和CircuitPython。请确保你已安装好Arduino IDE并添加了Adafruit的板卡支持包。3.1 库安装与硬件连接首先我们需要安装必要的Arduino库。打开Arduino IDE的库管理器工具 - 管理库搜索并安装以下库Adafruit SPIFlash这是核心的Flash驱动库。Adafruit BusIOSPI Flash库的依赖项。SdFat - Adafruit ForkAdafruit维护的FATFS库分支提供了Adafruit_FatFs类。安装完成后你的代码中就可以通过#include Adafruit_SPIFlash.h和#include Adafruit_FatFs.h来引入它们了。硬件方面对于Feather M0 Express这类板子SPI Flash已经直接集成在板子上并通过硬件SPI接口与主控芯片SAMD21连接无需我们额外接线。如果你使用的是其他主板外部SPI Flash模块则需要根据芯片数据手册正确连接SCK、MISO、MOSI、CS这四根SPI线并确保供电稳定。3.2 初始化Flash与挂载文件系统一切从初始化开始。下面这段代码是几乎所有相关操作的基石#include Adafruit_SPIFlash.h #include Adafruit_FatFs.h // 对于板载Flash使用预定义的flash传输对象 Adafruit_FlashTransport_SPI flashTransport(SS1, SPI1); // SS1是Feather M0 Express的Flash片选引脚 Adafruit_SPIFlash flash(flashTransport); // 创建FatFs对象 Adafruit_FatFs fatfs; void setup() { Serial.begin(115200); while (!Serial) delay(10); // 等待串口打开仅用于调试实际产品中可去掉 Serial.println(Adafruit SPI Flash FATFS Example); // 1. 初始化Flash芯片 if (!flash.begin()) { Serial.println(Error, failed to initialize SPI flash!); while(1); } Serial.print(Flash chip JEDEC ID: 0x); Serial.println(flash.getJEDECID(), HEX); Serial.print(Flash size: ); Serial.print(flash.size() / 1024); Serial.println( KB); // 2. 挂载文件系统 if (!fatfs.begin(flash)) { Serial.println(Failed to mount filesystem! Attempting to format...); // 尝试格式化创建新的FAT文件系统 if (!fatfs.format()) { Serial.println(Format failed! Check flash chip.); while(1); } Serial.println(Formatted new filesystem.); // 格式化后需要重新初始化flash并挂载 flash.end(); flash.begin(); if (!fatfs.begin(flash)) { Serial.println(Mount after format failed!); while(1); } } Serial.println(Filesystem mounted successfully!); }关键点解析与避坑指南while (!Serial);的取舍这行代码会让程序暂停直到你打开串口监视器。这在调试时非常有用可以确保你能看到所有初始化信息。但在最终的数据记录产品中务必注释掉或删除这行否则设备在无人连接USB时无法启动。格式化操作fatfs.begin()失败最常见的原因就是Flash上还没有有效的文件系统。这时调用fatfs.format()会创建一个全新的、空白的FAT文件系统。请注意格式化会清空整个分配给文件系统的Flash区域如果你的Flash里已经有重要数据请勿轻易格式化。Flash容量识别通过flash.size()可以获取芯片的实际容量。确保你后续创建的文件总大小不超过这个容量尤其是进行长时间数据记录时。3.3 文件写入创建与追加数据假设我们要做一个简单的温度数据记录器每分钟记录一次时间和温度。我们需要将数据写入一个CSV文件。void logDataToFile() { // 打开文件用于写入追加模式。如果文件不存在则创建它。 // FILE_WRITE 模式等同于 a (读写追加) File dataFile fatfs.open(/datalog.csv, FILE_WRITE); if (!dataFile) { Serial.println(Failed to open file for writing!); return; } // 模拟获取一些数据 unsigned long timestamp millis() / 1000; // 模拟秒数 float temperature readTemperature(); // 假设的函数返回温度值 // 将数据格式化为CSV行 String dataLine String(timestamp) , String(temperature, 2) \n; // 将字符串写入文件 int bytesWritten dataFile.print(dataLine); if (bytesWritten 0) { Serial.println(Write failed!); } else { Serial.print(Bytes written: ); Serial.println(bytesWritten); Serial.print(Data: ); Serial.print(dataLine); } // 非常重要关闭文件这确保了所有数据从缓存写入Flash并更新文件系统结构。 dataFile.close(); Serial.println(File closed.); } // 在loop中每隔一段时间调用一次 void loop() { static unsigned long lastLogTime 0; const unsigned long logInterval 60000; // 60秒 if (millis() - lastLogTime logInterval) { lastLogTime millis(); logDataToFile(); } // ... 其他任务 }实操心得文件路径FATFS支持类似Unix的路径格式。以/开头表示根目录。你也可以创建子目录例如/sensor/logs/data.csv。打开模式FILE_WRITE模式会从文件末尾开始写入追加。如果你想覆盖文件需要先删除旧文件或者使用FILE_WRITE打开后调用dataFile.seek(0)定位到文件开头再写但这不会自动清空原有内容可能会造成新旧数据混杂。print()与write()File对象的print()和println()非常方便它们负责将数据转换为字符串后写入。如果你要写入二进制数据应使用write()函数。关闭文件的重要性这是新手最容易忽略的一点。close()操作不仅仅是释放资源它还会将文件句柄缓冲区中的数据真正刷入flushFlash并更新文件的元信息如大小、修改时间。如果不关闭文件就断电极有可能导致文件损坏或数据丢失。对于数据记录应用每次写入后都关闭文件是稳妥的做法尽管会带来一些性能开销。3.4 文件读取遍历与解析内容当我们需要检查记录的数据或者设备启动时需要读取配置时就需要读取文件。void readDataFromFile() { Serial.println(\n--- Reading datalog.csv ---); // 打开文件用于读取 File dataFile fatfs.open(/datalog.csv, FILE_READ); if (!dataFile) { Serial.println(Failed to open file for reading!); return; } // 方法1逐字符读取适用于任何文件内存占用最小 Serial.println(Method 1: Read character by character:); while (dataFile.available()) { char c dataFile.read(); Serial.write(c); // 使用Serial.write避免println的额外格式转换 } dataFile.seek(0); // 将读指针重置回文件开头 // 方法2按行读取适用于文本文件更符合直觉 Serial.println(\n\nMethod 2: Read line by line:); while (dataFile.available()) { String line dataFile.readStringUntil(\n); line.trim(); // 去除换行符 if (line.length() 0) { Serial.print(Line: ); Serial.println(line); // 这里可以添加CSV解析逻辑 // int commaIndex line.indexOf(,); // long ts line.substring(0, commaIndex).toInt(); // float temp line.substring(commaIndex1).toFloat(); } } dataFile.close(); Serial.println(\n--- File read complete. ---); }注意事项文件是否存在在打开文件读取前最好先用fatfs.exists(/datalog.csv)检查一下避免打开失败。available()与文件结束available()函数返回文件中剩余的可读字节数。当读到文件末尾EOF时available()返回0read()返回-1。内存考虑readStringUntil会将一行内容读入String对象。如果文件中的某一行非常长比如错误的二进制数据可能会导致内存耗尽。对于不可信的输入更安全的方法是使用固定大小的缓冲区配合read()来逐段读取。4. 与CircuitPython进行数据交互的完整流程这是本项目的精华所在。我们的目标是让Arduino程序将数据写入SPI Flash的文件中然后通过切换到CircuitPython模式让这些文件像普通U盘文件一样在电脑上显示和编辑。4.1 工作流程全景图整个交互流程是一个有顺序的“双系统切换”操作阶段一Arduino数据记录开发板运行Arduino固件。Arduino程序通过Adafruit_SPIFlash和Adafruit_FatFs库将传感器数据或其他信息写入SPI Flash的FAT文件系统中例如/data/log.txt。阶段二切换至CircuitPython U盘模式通过双击复位按钮等方式让开发板进入引导加载程序Bootloader模式。将CircuitPython的UF2固件文件拖入出现的BOOT驱动器完成固件烧录。开发板重启后运行CircuitPython固件。CircuitPython会自动将SPI Flash中Arduino创建的那个FAT文件系统分区挂载为CIRCUITPY驱动器。阶段三在电脑上直接访问文件此时电脑上会出现一个名为CIRCUITPY的可移动磁盘。你可以直接双击打开CIRCUITPY盘找到data文件夹下的log.txt文件用记事本、Excel等任何软件查看、编辑、复制或删除它。阶段四切换回Arduino模式可选如果你需要继续用Arduino进行数据记录可以再次进入Bootloader模式。在进入Bootloader前CircuitPython会在BOOT驱动器中生成一个名为CURRENT.UF2的文件这是当前CircuitPython固件的备份。你可以先把这个CURRENT.UF2文件备份到电脑。然后将你原来的Arduino程序编译生成的UF2文件或通过Arduino IDE选择“使用引导加载程序编程”方式上传拖入BOOT驱动器设备就会恢复运行Arduino程序并且之前由Arduino或CircuitPython写入的文件只要文件系统没被破坏就依然存在。4.2 关键步骤详解与避坑步骤1确保文件系统兼容性这是成功的前提。Arduino通过Adafruit_FatFs和CircuitPython必须使用相同参数格式化Flash。对于Adafruit的官方板卡支持包它们通常使用默认设置所以是兼容的。但如果你手动调用了fatfs.format()或者使用了非Adafruit的库需要注意格式化参数如扇区大小、簇大小、FAT类型。最保险的方法是让Arduino侧先完成格式化操作。使用我们上面示例代码中的格式化逻辑这样创建的文件系统CircuitPython基本都能识别。步骤2理解Bootloader与UF2文件Adafruit的M0/M4系列板卡使用了一种名为UF2的USB闪存格式。Bootloader是板卡上预先烧录好的一个微型程序它启动时检查特定引脚如双击复位如果满足条件就让自己变成一个名为BOOT或METROBOOT等的U盘。你只需将.uf2格式的固件文件拖进去Bootloader就会自动将其烧写到主Flash中然后重启运行新固件。CircuitPython和Arduino当选择“使用引导加载程序编程”时都提供UF2格式的固件。步骤3备份当前程序CURRENT.UF2这是一个极其重要的好习惯在准备从Arduino模式切换到CircuitPython之前如果条件允许可以先进入Bootloader将出现的BOOT盘里的CURRENT.UF2文件复制出来并妥善命名如MyDatalogger_Backup.uf2。这个文件就是你当前正在运行的Arduino程序的完整副本。当你需要切回来时直接把这个备份文件拖回BOOT盘即可无需重新编译和上传。步骤4在CircuitPython中访问文件切换到CircuitPython后CIRCUITPY盘里的文件结构就是你Arduino程序中FAT文件系统的内容。你可以自由操作。但请注意安全弹出在Windows上操作完文件后务必点击任务栏的“安全弹出硬件”再拔线。在macOS/Linux上确保文件复制操作完成。直接拔线可能导致CircuitPython端的文件系统缓存未写入造成数据损坏。CircuitPython的运行CircuitPython本身也会在CIRCUITPY盘上运行code.py等脚本。请不要删除或重命名code.py、boot.py等系统文件也尽量避免在根目录存放大量数据文件最好在Arduino程序里就创建好专用的子目录如/data。4.3 自动化脚本思路高级对于需要频繁切换的场景手动拖拽UF2文件略显繁琐。你可以编写简单的Python或Shell脚本来自动化这个过程。脚本的逻辑可以是通过串口发送特定命令让Arduino程序软复位进入Bootloader需要Arduino程序支持。脚本检测BOOT盘出现。自动将指定的CircuitPython UF2文件复制到BOOT盘。等待CIRCUITPY盘出现然后执行文件操作如复制日志文件到电脑。再次触发复位将备份的Arduino UF2文件复制回BOOT盘切换回去。这需要结合操作系统级的驱动器监控和文件操作API这里不展开但提供了产品化设计的一个方向。5. 常见问题排查与实战经验录即使按照步骤操作也难免会遇到一些问题。下面是我在多个项目中总结出的常见“坑”及其解决方案。5.1 文件系统相关问题问题1fatfs.begin()失败格式化后依然失败。可能原因SPI通信引脚配置错误仅限外接Flash模块、Flash芯片型号不被库自动识别、供电不足。排查步骤检查flash.begin()是否成功并打印JEDEC ID。与芯片数据手册核对ID是否正确。如果是外接模块确认Adafruit_FlashTransport_SPI初始化时使用的片选CS引脚号是否正确。尝试降低SPI时钟频率。在flash.begin()后可以尝试flash.setClockSpeed(1000000)1MHz以低速模式测试。确保Flash芯片的WP写保护和HOLD引脚已被上拉或正确配置通常需要接高电平。问题2能创建文件但写入数据后重新上电发现数据丢失或文件损坏。可能原因文件没有正确关闭close()、电源在写入过程中意外断开、Flash扇区擦写寿命到期概率极低。解决方案务必在每次写入操作后调用file.close()。对于关键数据可以考虑实现一个简单的写前日志WAL机制先将数据写入一个临时文件写完并关闭后再重命名为目标文件。这样即使中途断电原始文件也是完整的。增加电源监控电路在检测到电压跌落时尽快完成文件关闭操作。问题3Arduino创建的文件在CircuitPython的CIRCUITPY盘里看不到。可能原因1文件系统起始地址不一致。这是最可能的原因。解决检查Arduino代码中fatfs.begin()是否使用了默认参数。查看Adafruit SPI Flash库和CircuitPython针对你这款板子的源码确认它们使用的FLASH_FILESYSTEM_START_ADDR或类似定义是否相同。你可以在Arduino代码中手动指定起始地址但需要与CircuitPython匹配。可能原因2CircuitPython挂载失败。解决连接CircuitPython的串口REPL例如使用Mu编辑器输入import storage然后storage.getmount(/)查看文件系统信息。如果挂载失败会有错误信息。可能需要重新格式化注意备份。5.2 固件切换与USB识别问题问题4双击复位键板子没有进入Bootloader模式NeoPixel没有变绿。可能原因双击节奏不对、当前运行的固件禁用了双击复位功能、USB线或端口有问题。解决多试几次双击节奏是“咔哒-咔哒”中间间隔约500ms。尝试先长按复位键然后快速双击。换一根确认能传输数据的USB线。很多手机充电线是仅供电的。如果Arduino程序中有while(!Serial);并且USB没有连接电脑程序会卡住。此时需要先连接USB到电脑或者修改代码去掉这行。问题5进入Bootloader后电脑没有出现BOOT驱动器。可能原因驱动程序问题Windows常见、USB线或端口问题、板子Bootloader损坏。解决Windows打开设备管理器查看“通用串行总线控制器”或“其他设备”中是否有未知设备。尝试安装Adafruit的Windows驱动程序包。换一个USB端口最好是主板原生的USB口避免使用扩展坞。如果以上都不行可能需要尝试通过外部编程器如J-Link重新烧写Bootloader这属于高级操作。问题6从CircuitPython切换回Arduino后串口无法识别或程序不运行。可能原因上传的UF2文件不对、Bootloader损坏、Arduino程序本身有死循环或错误。解决确保你拖入BOOT盘的是正确的Arduino编译生成的UF2文件或者是你之前备份的CURRENT.UF2。尝试上传一个最简单的Blink例程来测试。在Arduino IDE中打开“详细输出”查看上传日志。在上传过程中当IDE显示“正在上传”时双击板子复位键强制进入Bootloader让IDE能发现并上传。5.3 性能与优化建议写入速度SPI Flash的写入速度尤其是擦除速度是瓶颈。频繁地打开、关闭小文件会严重影响寿命和性能。对于高速数据记录建议在内存中缓存一定量的数据比如攒够512字节或一行完整数据再一次性写入文件。或者使用环形缓冲区结合定时写入的策略。文件系统磨损均衡基础的FATFS本身不提供磨损均衡Wear Leveling。如果你需要频繁擦写同一个文件如日志文件循环覆盖长期下来会导致该文件占用的Flash扇区提前损坏。对于这种场景可以考虑使用库中可能提供的磨损均衡层如果支持。自己实现简单的策略例如将日志按日期或序号分成多个文件写满一个再写下一个。选用带有硬件磨损均衡的Flash芯片。电源管理在电池供电项目中SPI Flash在待机时也会消耗微安级的电流。如果对功耗要求极严可以在不需要读写时通过控制其片选CS引脚或电源引脚如果独立可控将其完全断电。通过以上从原理到实践从操作到排错的完整梳理你应该已经掌握了在Arduino和CircuitPython之间搭建SPI Flash数据桥梁的全套技能。这套方案的美妙之处在于它的灵活性和便捷性你用Arduino实现高性能、低功耗的数据采集与控制用CircuitPython充当一个即插即用的数据“搬运工”和“显示器”。两者各取所长通过一块小小的Flash芯片完美协作为你的嵌入式项目打开了更多可能性。

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

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

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…