基于RT-Thread与N32G457的工业UART网关设计与实现

news2026/5/22 3:59:31
1. 项目概述与核心价值最近在做一个工业数据采集的项目现场有十几台不同品牌、不同协议的串口设备PLC、仪表、传感器什么都有它们的数据都需要汇总到一台中心服务器上。最头疼的是这些设备分布在车间各处拉线成本高维护也麻烦。于是一个想法就冒出来了能不能做一个“串口翻译官”或者说“串口集线器”它一端通过多个串口连接这些五花八门的设备另一端通过以太网或者Wi-Fi把数据规规矩矩地送到服务器。这就是我们常说的UART网关或者叫串口服务器。我最终选定的方案是基于RT-Thread操作系统和N32G457这款国产MCU。为什么是它俩RT-Thread的实时性和丰富的网络组件像SAL套接字抽象层、lwIP协议栈对于网关这种需要同时处理多路数据转发和网络通信的场景简直是量身定做开发效率比裸机高出一大截。而N32G457是国民技术的一款高性能ARM Cortex-M4F内核MCU主频高达144MHz关键是它有多达8个UART接口还有以太网MAC控制器硬件资源上完全Hold住这个项目。这个网关的核心任务就三件事收、转、发。稳定可靠地接收来自各个串口的数据根据预设的规则比如协议解析、数据打包进行处理然后通过TCP/IP网络发送到指定的服务器。听起来简单但里面的门道可不少比如多串口数据如何不丢包地并发处理、网络异常了数据怎么缓存、不同设备的通信参数波特率、数据位如何动态管理等等。接下来我就把自己从硬件选型到软件调试一步步实现这个网关的过程和踩过的坑详细拆解一遍。2. 硬件平台选型与核心电路设计2.1 MCU选型为什么是N32G457做网关MCU是大脑选型决定了项目的天花板。我列了几个硬性指标第一UART数量必须够多至少4个以上才能有实际应用价值第二要有以太网MAC保证网络吞吐量和稳定性比外接串口转以太网模块方案更集成、更可靠第三性能要足够因为要跑操作系统并处理多路数据第四性价比和供货要稳定。N32G457几乎完美匹配它内置了8个UARTUSART1/2/3 UART4/5 LPUART1 还有两个可通过串行接口复用其中3个支持硬件流控应对现场复杂的RS-485/RS-422环境很合适。它集成了10/100M以太网MAC控制器只需外接一个PHY芯片我用的LAN8720A就能搞定网络。144MHz的M4F内核带FPU跑RT-Thread和处理数据解析绰绰有余。此外它的Flash有512KBRAM有144KB对于网关应用来说也足够。国产芯片的供货和资料支持现在也越来越好这也是一个加分项。2.2 核心电路设计要点电路设计上有几个关键部分需要特别注意电源与复位电路网关通常需要7-24V宽电压输入我用了MP2451开关降压芯片转到5V再用LDO转到3.3V给MCU和PHY供电。复位电路要可靠除了RC复位我还加了看门狗芯片如MAX706防止程序跑飞。串口接口电路这是网关的“手”要适应不同电气标准。TTL/UART电平直接连接注意加上拉电阻必要时加TVS管做静电防护。RS-485电路这是工业现场最常用的。每个RS-485接口都需要一个独立的收发器芯片如SP3485。最关键的是控制收发器的方向引脚DE/RE。必须由MCU的GPIO精确控制在发送前拉高发送完成后立即拉低切换回接收模式。硬件上A、B线要加终端电阻120Ω并在A、B线对地加TVS管和稳压管进行保护。RS-232电路如果需要可以用MAX3232这类芯片转换。以太网电路这是网关的“嘴”。N32G457的MAC通过RMII接口连接PHY芯片LAN8720A。设计时要注意时钟为LAN8720A提供稳定的50MHz晶振。网络变压器RJ45接口和PHY之间必须使用网络变压器通常集成在RJ45座子里用于信号耦合和隔离。布线RMII的TX、RX数据线时钟线要等长尽量短远离干扰源。调试与存储接口预留SWD/JTAG调试口和UART1作为日志输出口方便调试。外挂一颗SPI Flash如W25Q64用于存储配置参数、日志或者临时缓存网络异常时的数据。注意RS-485总线在布线时一定要使用双绞线并且避免星型连接应采用总线型拓扑。每个网口的保护电路不能省工业现场雷击、浪涌、静电很常见。3. 软件架构设计与RT-Thread环境搭建3.1 软件整体架构设计在RT-Thread上我们可以用多线程来优雅地解决多任务并发问题。我的软件架构主要分为四层硬件驱动层基于RT-Thread的PIN、UART、ETH设备驱动框架完成对N32G457所有外设的初始化与操作封装。重点是确保每个UART都能以中断DMA方式高效接收数据。数据链路与协议层这是核心。串口数据接收线程为每个物理串口创建一个独立的线程或使用同一个线程循环处理。该线程阻塞在rt_device_read()上一旦有数据到达立刻被唤醒将数据放入对应串口的环形缓冲区Ring Buffer。协议解析模块如果串口设备是Modbus RTU、自定义文本协议等需要在这里进行解析。可以设计成插件形式每个协议一个解析函数。数据打包与转发线程这是一个高优先级线程。它不断检查各个环形缓冲区如果有有效数据包经过解析或直接原始数据就按照预设的规则如JSON格式、自定义二进制格式进行打包然后通过TCP Socket发送到服务器。网络通信层使用RT-Thread的SALSocket Abstraction Layer组件。网关作为TCP客户端主动连接服务器。需要实现断线重连机制、心跳包保活机制和数据发送队列防止网络瞬时拥堵。配置与管理层提供一个命令行Finsh或Web Server基于HTTP组件的接口用于动态配置串口参数波特率、数据位、服务器IP/端口、协议类型等。配置保存在SPI Flash中。3.2 RT-Thread工程创建与配置我使用RT-Thread Studio进行开发步骤很清晰创建基于芯片的工程选择N32G457系列RT-Thread会自动生成基础工程包含该芯片的BSP板级支持包。使用ENV工具配置系统这是RT-Thread的灵魂。在工程根目录打开menuconfig。必选组件RT-Thread Components - Network - Socket abstraction layer 启用SAL。RT-Thread Components - Network - light weight TCP/IP stack 启用lwIP。RT-Thread Components - Network - Ethernet device drivers 并勾选你的PHY驱动如lan8720a。RT-Thread Components - Command shell 启用Finsh命令行调试神器。关键配置RT-Thread Kernel - Kernel Object - the max size of kernel object name 设大一点比如16方便给设备起名。lwIP - Enable lwIP stack debug 调试阶段可以开启后期关闭。在Hardware Drivers Config - On-chip Peripheral Drivers中开启你计划使用的所有UART设备并设置好对应的引脚。生成工程与编译保存menuconfig配置后使用scons --targetmdk5生成Keil工程文件然后用Keil打开进行编译和下载。实操心得在menuconfig中配置以太网时注意ETH_PHY_ADDRESS这个参数它对应PHY芯片的地址LAN8720A默认为0。如果硬件上PHY的地址线接了不同电平这里一定要改对否则永远link down。4. 多路UART数据接收与环形缓冲区实现4.1 串口设备初始化与DMA接收稳定、不丢包地接收多路串口数据是网关的基石。裸机里用中断一个个字节搬太累RT-Thread的UART设备框架配合DMA是绝配。首先在board.c的rt_hw_board_init()函数之后初始化所有要用到的串口设备。这里以UART2和UART3为例假设都用作RS-485#include rtdevice.h #define UART2_DEVICE_NAME uart2 #define UART3_DEVICE_NAME uart3 static rt_device_t serial_uart2, serial_uart3; static void uart_init(void) { struct serial_configure config RT_SERIAL_CONFIG_DEFAULT; // 获取默认配置 config.baud_rate BAUD_RATE_9600; // 修改波特率 config.data_bits DATA_BITS_8; config.stop_bits STOP_BITS_1; config.parity PARITY_NONE; config.bufsz 512; // 设置硬件接收缓冲区大小很重要 /* 查找串口设备 */ serial_uart2 rt_device_find(UART2_DEVICE_NAME); serial_uart3 rt_device_find(UART3_DEVICE_NAME); if (serial_uart2 RT_NULL || serial_uart3 RT_NULL) { rt_kprintf(find uart device failed!\n); return; } /* 修改配置并打开设备 以可中断接收和DMA接收方式打开 */ rt_device_control(serial_uart2, RT_DEVICE_CTRL_CONFIG, config); rt_device_open(serial_uart2, RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_DMA_RX); // 同理初始化uart3... }关键点在于RT_DEVICE_FLAG_DMA_RX标志它告诉驱动使用DMA接收。这样当串口收到数据时硬件DMA会自动将数据搬运到我们指定的内存缓冲区config.bufsz定义的大小搬运完成才产生中断大大减轻了CPU负担。4.2 环形缓冲区Ring Buffer的设计与应用DMA解决了硬件搬运问题但数据处理需要时间。为了避免数据处理线程来不及消费而导致新数据覆盖旧数据必须在驱动层和应用层之间加一个软件环形缓冲区。RT-Thread内核提供了非常好用的环形缓冲区对象struct rt_ringbuffer和相关API。我为每个串口创建一个独立的环形缓冲区。#include rtthread.h #include rtdevice.h #define RING_BUF_SIZE 1024 // 每个串口的环形缓冲区大小 static struct rt_ringbuffer uart2_rb, uart3_rb; static rt_uint8_t uart2_rb_pool[RING_BUF_SIZE]; static rt_uint8_t uart3_rb_pool[RING_BUF_SIZE]; /* 在初始化函数中创建环形缓冲区 */ rt_ringbuffer_init(uart2_rb, uart2_rb_pool, sizeof(uart2_rb_pool)); rt_ringbuffer_init(uart3_rb, uart3_rb_pool, sizeof(uart3_rb_pool));接下来需要为每个串口设备绑定一个接收回调函数。当DMA接收完成或收到指定长度数据触发中断时这个回调函数会被调用我们需要在这里将硬件FIFO或DMA缓冲区中的数据快速写入对应的软件环形缓冲区。/* 串口接收回调函数 */ static rt_err_t uart2_rx_ind(rt_device_t dev, rt_size_t size) { rt_uint8_t ch; while (rt_device_read(dev, 0, ch, 1) 0) { // 将接收到的字节放入环形缓冲区 rt_ringbuffer_putchar(uart2_rb, ch); } // 发送信号量或事件通知数据处理线程有数据到来 rt_sem_release(uart2_rx_sem); return RT_EOK; } /* 设置接收回调 */ rt_device_set_rx_indicate(serial_uart2, uart2_rx_ind);4.3 串口数据接收线程的实现每个串口或每几个串口可以分配一个独立的线程其任务就是等待信号量然后从环形缓冲区中读取数据。static void uart2_rx_thread_entry(void *parameter) { rt_uint8_t read_buf[128]; rt_size_t read_len; while (1) { // 等待信号量说明有数据到达 if (rt_sem_take(uart2_rx_sem, RT_WAITING_FOREVER) RT_EOK) { // 从环形缓冲区读取数据 read_len rt_ringbuffer_get(uart2_rb, read_buf, sizeof(read_buf)); if (read_len 0) { // 将数据投递到协议解析队列或直接交给转发线程 process_uart2_data(read_buf, read_len); } } } }注意事项环形缓冲区的大小需要仔细权衡。太小容易满导致丢包太大会增加内存占用和数据延迟。通常根据波特率和数据处理的最大可能间隔来计算。例如115200波特率下每秒最多接收约11.5KB数据。如果数据处理线程可能阻塞1秒缓冲区至少需要12KB。我这里设1024字节是针对9600波特率及较低延迟的场景。5. 网络通信与数据转发线程实现5.1 TCP客户端连接管理与断线重连网关作为TCP客户端需要稳定地连接服务器。在RT-Thread中我们使用标准的BSD Socket API但通过SAL层进行了封装用法和PC上几乎一样。首先创建一个网络连接管理线程static int gw_network_thread_init(void) { rt_thread_t tid; tid rt_thread_create(net_cli, network_entry, RT_NULL, 2048, 15, 5); if (tid ! RT_NULL) { rt_thread_startup(tid); } return RT_EOK; } INIT_APP_EXPORT(gw_network_thread_init); // 自动初始化在network_entry线程函数中实现连接逻辑static void network_entry(void *parameter) { int sock -1; struct sockaddr_in server_addr; rt_bool_t is_connected RT_FALSE; while (1) { if (!is_connected) { // 1. 创建socket if ((sock socket(AF_INET, SOCK_STREAM, 0)) 0) { rt_kprintf(Socket create error\n); rt_thread_mdelay(2000); continue; } // 2. 设置服务器地址 server_addr.sin_family AF_INET; server_addr.sin_port htons(SERVER_PORT); // 服务器端口 server_addr.sin_addr.s_addr inet_addr(SERVER_IP); // 服务器IP // 3. 连接服务器 rt_kprintf(Try to connect server %s:%d ...\n, SERVER_IP, SERVER_PORT); if (connect(sock, (struct sockaddr *)server_addr, sizeof(server_addr)) 0) { rt_kprintf(Connect failed!\n); closesocket(sock); rt_thread_mdelay(3000); // 连接失败等待3秒重试 continue; } rt_kprintf(Connect to server success!\n); is_connected RT_TRUE; // 连接成功可以启动心跳包线程等 } // 4. 连接保持阶段可以在这里处理接收服务器指令或发送心跳 // ... (心跳包逻辑) // 5. 检查连接是否异常 // 一种简单方法设置socket为非阻塞尝试recv或通过发送心跳包超时判断 if (/* 连接断开判断 */) { rt_kprintf(Connection lost.\n); closesocket(sock); is_connected RT_FALSE; rt_thread_mdelay(1000); } rt_thread_mdelay(100); // 短暂延时让出CPU } }5.2 数据打包与发送队列数据处理线程如process_uart2_data不应该直接调用send()发送数据因为网络发送可能阻塞TCP窗口满。我们需要一个发送队列。创建消息队列用于存放待发送的数据包。#define MAX_SEND_QUEUE_MSG 32 static rt_mq_t send_mq; send_mq rt_mq_create(send_mq, 256, MAX_SEND_QUEUE_MSG, RT_IPC_FLAG_FIFO);数据处理线程投递数据到队列当串口数据准备好后打包成一个结构体包含数据指针、长度、来源等发送到消息队列。struct data_packet { rt_uint8_t from_port; rt_uint16_t len; rt_uint8_t data[0]; // 柔性数组 }; static void process_uart2_data(rt_uint8_t *buf, rt_size_t len) { struct data_packet *pkt; pkt (struct data_packet *)rt_malloc(sizeof(struct data_packet) len); if (pkt) { pkt-from_port 2; // 标识来自UART2 pkt-len len; rt_memcpy(pkt-data, buf, len); // 投递到发送队列如果队列满则等待一段时间 if (rt_mq_send(send_mq, pkt, sizeof(struct data_packet) len) ! RT_EOK) { rt_kprintf(Send queue full, packet dropped!\n); rt_free(pkt); } } }网络线程从队列取数据并发送在网络连接正常的阶段网络线程不断从消息队列中取数据并发送。// 在网络线程的循环中 if (is_connected) { struct data_packet *recv_pkt; rt_int32_t mq_recv_len; // 非阻塞方式接收消息 mq_recv_len rt_mq_recv(send_mq, recv_pkt, sizeof(recv_pkt), 0); if (mq_recv_len 0) { // 发送数据 int sent_len send(sock, recv_pkt-data, recv_pkt-len, 0); if (sent_len 0) { // 发送失败可能是连接已断开 is_connected RT_FALSE; rt_kprintf(Send error, connection may be lost.\n); } else if (sent_len ! recv_pkt-len) { // 部分发送需要处理...对于TCP通常内核会处理重传但应用层需记录 rt_kprintf(Partial send: %d/%d\n, sent_len, recv_pkt-len); } rt_free(recv_pkt); // 释放内存 } }这种“生产者-消费者”模型有效解耦了数据接收和网络发送即使网络短暂中断数据也能在队列中缓存一段时间取决于队列大小和产生速度提高了系统的鲁棒性。6. 协议解析、配置管理与系统优化6.1 灵活可配置的协议解析模块现场设备协议各异网关需要灵活适配。我设计了一个简单的协议解析插件机制。首先定义一个统一的协议解析函数指针类型typedef rt_err_t (*protocol_parser_t)(rt_uint8_t port, rt_uint8_t *data, rt_size_t len, void *user_data);然后为每个串口维护一个解析函数指针和用户数据struct uart_protocol { rt_uint8_t enabled; protocol_parser_t parser; void *user_data; // 例如指向Modbus RTU的从站地址表 }; static struct uart_protocol proto_table[UART_MAX_NUM];在process_uartx_data函数中不再直接打包原始数据而是先调用协议解析函数static void process_uart2_data(rt_uint8_t *buf, rt_size_t len) { if (proto_table[UART2_INDEX].enabled proto_table[UART2_INDEX].parser) { // 调用解析函数解析函数内部决定如何打包有效数据并投递到发送队列 proto_table[UART2_INDEX].parser(UART2_INDEX, buf, len, proto_table[UART2_INDEX].user_data); } else { // 原始数据透传模式 send_raw_data(UART2_INDEX, buf, len); } }这样要新增一个协议如Modbus RTU解析为JSON只需要实现对应的protocol_parser_t函数并在配置系统中将其注册到对应的串口即可。6.2 基于FinSH或Web的配置管理网关的参数串口波特率、服务器IP、协议类型必须能在现场方便地修改。RT-Thread内置的FinSH组件提供了强大的命令行交互能力。我们可以定义一系列MSH命令// 设置UART2参数 static void set_uart2(int argc, char **argv) { if (argc ! 4) { rt_kprintf(Usage: set_uart2 baud databits stopbits\n); return; } rt_uint32_t baud atoi(argv[1]); rt_uint8_t data_bits atoi(argv[2]); rt_uint8_t stop_bits atoi(argv[3]); // 调用设备控制接口重新配置串口 // ... rt_kprintf(UART2 config updated.\n); } MSH_CMD_EXPORT(set_uart2, set uart2 parameters);更友好的方式是集成一个轻量级的Web服务器如webnet通过网页进行配置。这需要开启RT-Thread的webnet软件包并编写简单的CGI处理程序来接收表单数据更新配置并保存到SPI Flash。6.3 系统稳定性优化与调试技巧看门狗IWDG必须启用独立看门狗在主线程或关键任务中定期喂狗。防止程序跑飞导致网关“僵死”。内存管理频繁的数据包分配释放容易导致内存碎片。可以使用RT-Thread的内存池mempool功能预先分配固定大小的数据包内存块。线程优先级与栈大小网络发送线程优先级应较高确保数据能及时送出。串口接收线程优先级也应较高保证数据能及时从硬件缓冲区搬走。协议解析线程可以适中。栈大小网络线程栈建议不小于2KB协议解析如果较复杂也需要较大栈空间。务必使用list_thread命令定期查看线程栈使用情况防止溢出。日志系统使用ulog组件将日志输出到UART的同时也写入文件系统或通过网络发送便于远程诊断。性能监测使用list_timer、list_mempool等命令监控系统定时器和内存使用。7. 常见问题与排查实录在实际调试和部署中我遇到了不少问题这里记录几个典型的问题一串口数据接收不完整或乱码。可能原因及排查波特率不匹配这是最常见的原因。用示波器或逻辑分析仪测量实际波形计算波特率。确保网关和设备配置完全一致包括数据位、停止位、校验位。硬件流控未正确处理如果设备使用了RTS/CTS流控而网关未启用或引脚接错会导致数据丢失。检查电路和软件配置。中断优先级与DMA冲突如果串口接收中断优先级过低被其他高优先级中断长时间阻塞可能导致DMA缓冲区溢出。调整中断优先级NVIC配置确保串口中断能及时响应。环形缓冲区溢出打印环形缓冲区的读写指针检查是否因数据处理太慢导致缓冲区被写满。增大缓冲区或优化数据处理逻辑。问题二网络连接频繁断开。可能原因及排查物理链路问题检查网线、交换机。尝试直连电脑测试。PHY芯片初始化失败检查ETH_PHY_ADDRESS配置测量PHY芯片的复位和时钟信号。在list_device命令中查看以太网设备是否成功注册。lwIP配置问题检查IP地址、网关、子网掩码配置。ping一下网关和服务器看链路层是否通。服务器端问题检查服务器防火墙设置、端口是否监听。用网络调试工具如NetAssist模拟客户端连接测试。心跳包机制TCP是面向连接的但中间路由器可能清除长时间无活动的连接。必须实现应用层心跳包。如果心跳包发送失败应主动断开重连。问题三多路数据转发时其中一路数据延迟明显增大。可能原因及排查线程优先级设置不当如果处理慢速串口数据的线程和处理高速串口数据的线程优先级相同且慢速线程因协议解析复杂而长时间占用CPU就会导致高速数据被阻塞。合理设置优先级让数据接收线程的优先级最高。共享资源竞争如果多个线程共用一个发送队列或日志输出等资源且未做好互斥保护使用互斥锁rt_mutex会导致线程长时间等待。使用list_mutex命令查看锁的持有情况。内存分配阻塞在中断或高优先级线程中使用了rt_malloc而系统内存不足时可能会触发内存整理导致不可预测的延迟。建议在高优先级线程中使用内存池。问题四设备运行一段时间后死机。可能原因及排查栈溢出这是RTOS中最常见的问题。使用list_thread命令查看max used栏位如果接近stack size说明栈快溢出了立即增大该线程栈大小。内存泄漏每次分配的数据包是否都正确释放了使用list_memheap命令观察内存使用是否随时间增长。确保rt_free配对使用。看门狗未及时喂狗检查喂狗线程是否被阻塞或优先级过低。硬件故障或电源干扰检查电源纹波在关键信号线增加滤波电容。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2622922.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;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…