MicroPython ESP32 UART Modbus 故障诊断与主从切换

news2026/3/14 18:27:30
1. 从“偷听”开始理解UART监听Modbus的核心价值大家好我是老张在工业自动化和物联网这块摸爬滚打了十几年。今天想和大家聊聊一个非常实用但又常常被新手朋友觉得有点“玄乎”的场景用一块小小的ESP32开发板运行MicroPython去“偷听”一条Modbus RTU总线上的对话并且还能在关键时刻挺身而出接管通信。这听起来是不是有点像电影里的特工其实原理没那么复杂。想象一下你车间里有一台PLC可编程逻辑控制器作为主站它正通过RS485总线按照固定的节奏询问各个传感器、电机驱动器这些就是从站“1号你温度多少”“2号你转速如何”。这条总线上流淌着的就是Modbus RTU协议的数据帧像一封封格式固定的电报。我们的ESP32在这里扮演的角色就是一个“总线监听者”。它不主动发言只是默默地接在RS485的A和B-两根线上通过UART通用异步收发器这个硬件模块把总线上所有的“电报”字节流都收下来。这有什么用呢用处可大了最直接的就是故障诊断。PLC问了一句对应的从站有没有回答回答的内容对不对如果超过一段时间总线上一片寂静是不是通信链路断了传统的做法可能要靠人工排查或者等设备停机了才发现问题。而我们这个方案能实现实时监控、自动判断、甚至故障接管让系统变得更智能、更可靠。对于刚接触MicroPython和ESP32的朋友可能会觉得又是协议又是硬件的头大。别担心咱们今天就把这个事掰开了、揉碎了讲。你不需要是通信协议专家只要跟着步骤来就能搭建起一个属于自己的“总线哨兵”。这个项目非常适合用来学习嵌入式网络通信、理解工业协议甚至作为毕业设计或实际小改造项目性价比和实用性都非常高。2. 搭建你的监听哨所硬件连接与MicroPython环境工欲善其事必先利其器。在开始写代码之前咱们得先把“战场”布置好。这一部分我会详细说说硬件怎么连软件环境怎么搭避开那些我当年踩过的坑。2.1 硬件清单与接线图你需要准备的东西不多都是些常见器件ESP32开发板一块比如NodeMCU-32S、ESP32-DevKitC都行。RS485转TTL模块一个这是关键因为ESP32的UART是TTL电平而RS485总线是差分信号必须靠这个模块转换。常用的有MAX485芯片的模块。USB数据线一根用于给ESP32供电和烧录程序。杜邦线若干。接线是重中之重接错了轻则没数据重则烧芯片。记住一个核心原则ESP32和RS485模块共地。具体的接法如下ESP32侧3.3V- 连接RS485模块的VCC注意绝大多数RS485模块工作电压是5V但市面上也有很多兼容3.3V的购买时请确认。如果模块是5V的请接ESP32的VIN引脚或外部5V电源并确保共地。GND- 连接RS485模块的GND。选择一个UART的TX引脚例如GPIO17 - 连接RS485模块的DI数据输入。选择一个UART的RX引脚例如GPIO16 - 连接RS485模块的RO数据输出。RS485模块侧A- 连接RS485总线的A线。B- 连接RS485总线的B-线。RE和DE引脚接收使能和发送使能这是我们实现“只听不说”的关键。将这两个引脚直接连接到ESP32的一个GPIO例如GPIO4上并通过程序将这个GPIO置为低电平。当RE和DE为低时模块始终处于接收模式只能听不能发。这就保证了我们的监听器不会干扰总线上的正常通信。注意接线时务必断电操作。RS485总线通常是24V或5V虽然电流不大但带电插拔容易产生瞬间高压打坏芯片。2.2 MicroPython固件刷写与基础库硬件接好了接下来给ESP32“灌输灵魂”——刷入MicroPython固件。去MicroPython官网下载最新的ESP32通用固件.bin文件。使用刷写工具比如esptool.py。打开命令行进入固件所在目录执行类似下面的命令请根据你的串口号和固件名调整esptool.py --chip esp32 --port COM3 --baud 921600 write_flash -z 0x1000 esp32-xxx.bin刷写成功后用串口工具如PuTTY、Thonny连接ESP32波特率115200你应该能看到MicroPython的交互式解释器REPL提示符。现在ESP32已经是一个Python解释器了我们可以直接在上面运行Python代码。为了开发方便我强烈推荐使用Thonny IDE。它集成了MicroPython管理、代码编辑、文件上传和REPL交互对新手极其友好。在Thonny里选择正确的解释器和串口就能像在电脑上写Python一样给ESP32编程了。3. 核心代码拆解如何“听懂”Modbus电报环境搭好我们进入核心环节——写代码。别被“协议解析”吓到Modbus RTU的帧格式其实很规整。我们一段段来看。3.1 UART初始化的那些坑首先我们要配置好UART。这里有个我踩过的大坑超时timeout设置。很多教程里初始化UART就设个波特率但在监听高速、连续数据的场景下这会导致问题。from machine import UART, Pin import time # 首先设置控制RS485收发方向的引脚为低电平确保模块处于接收模式 de_re_pin Pin(4, Pin.OUT) de_re_pin.value(0) # 低电平 接收模式 # 初始化UART # 关键参数timeout 和 timeout_char uart UART(2, baudrate9600, bits8, parityNone, stop1, tx17, rx16, timeout50, timeout_char2)我来解释一下这两个关键参数timeout50单位是毫秒。它定义了uart.read()方法等待数据的最长时间。如果设为0read()方法会立刻返回当前缓冲区里所有的数据可能不完整。在监听Modbus时一帧数据可能被分多次收到。设置一个合理的超时比如50ms可以让read()稍微“等一等”尽量把属于同一帧的数据收集全再返回。这个值需要根据波特率和帧长估算9600波特率下一个字节大约1ms一帧20字节就是20ms50ms是个比较安全的余量。timeout_char2单位是毫秒。它定义了等待下一个字符的最长时间。这个值通常设得比帧间间隔小。Modbus RTU协议规定帧间至少要有3.5个字符时间的静默。在9600波特率下3.5个字符时间大约是3.6ms。我们设置timeout_char2意味着如果超过2ms没收到新字符read()方法就会认为当前帧结束了返回已收到的数据。这帮助我们从连续的字节流中切分出独立的数据帧。3.2 数据接收与十六进制展示数据接收回来是一串字节bytes直接看是乱码。转换成十六进制HEX显示是最直观的调试方式。import binascii def print_hex_data(data): 将字节数据转换为格式化的HEX字符串并打印 if not data: return # binascii.hexlify 将字节转成十六进制表示的字节串如 b010300000002 hex_str binascii.hexlify(data).decode(utf-8).upper() # 格式化一下每两个字符一个字节加一个空格方便阅读 formatted_hex .join([hex_str[i:i2] for i in range(0, len(hex_str), 2)]) print(f[{time.ticks_ms()}] HEX: {formatted_hex}) # 同时打印ASCII字符对于可打印字符辅助判断 # ascii_part .join([chr(b) if 32 b 127 else . for b in data]) # print(f ASC: {ascii_part})这个函数会把像b\x01\x03\x00\x00\x00\x02\xC4\x0B这样的字节序列转换成01 03 00 00 00 02 C4 0B这样清晰可读的格式并打上时间戳。这是你调试时最重要的工具总线上一来一回的数据长什么样一目了然。3.3 解析Modbus RTU帧与帧头比对Modbus RTU一帧数据的基本结构是[从站地址][功能码][数据][CRC校验]。我们的故障诊断核心逻辑就是比对“问”和“答”的帧头是否一致。def parse_modbus_frame(data): 解析Modbus RTU帧返回帧头地址功能码和CRC校验结果 if len(data) 4: # 至少包含地址、功能码和2字节CRC print(帧数据过短无效) return None, False slave_addr data[0] func_code data[1] # 将地址和功能码组合成一个16位的帧头方便比较 # 例如地址0x01功能码0x03帧头就是 0x0103 frame_header (slave_addr 8) | func_code # 提取接收到的CRC最后两个字节 received_crc data[-2:] # 计算实际数据的CRC排除最后两个CRC字节 calculated_crc modbus_crc(data[:-2]) # 校验CRC crc_ok (received_crc[0] calculated_crc[0]) and (received_crc[1] calculated_crc[1]) return frame_header, crc_ok def modbus_crc(data: bytearray) - bytes: 计算Modbus CRC16校验码 crc 0xFFFF for pos in data: crc ^ pos for _ in range(8): if (crc 0x0001) ! 0: crc 1 crc ^ 0xA001 else: crc 1 # 注意Modbus CRC是小端序低位在前 return bytes([crc 0xFF, (crc 8) 0xFF])有了解析函数主循环的逻辑就清晰了。我们需要一个缓冲区来暂存可能不完整的数据并实现简单的状态机来区分请求帧和响应帧。一个简单的思路是监听到一帧数据后如果其功能码是请求类的如0x03读保持寄存器就把它暂存为“主站请求”紧接着监听到的下一帧如果其地址与暂存的请求帧地址相同就认为是“从站响应”然后进行比对。# 全局变量用于暂存上一次收到的请求帧 last_request_header None last_request_time 0 def main_loop(): global last_request_header, last_request_time buffer bytearray() print(Modbus总线监听器启动...) while True: if uart.any(): data uart.read() if data: buffer.extend(data) # 尝试从缓冲区中解析一帧完整的数据 frame, buffer extract_frame_from_buffer(buffer) if frame: process_frame(frame) # 这里可以添加总线寂静检测后面会讲 time.sleep_ms(1) # 短暂延时避免空转耗电 def extract_frame_from_buffer(buf): 从缓冲区中提取一帧完整的Modbus RTU数据。 简易实现寻找3.5个字符时间的静默作为帧间隔。 更严谨的做法需要计时。这里先用长度和CRC做简单判断。 if len(buf) 4: return None, buf # 不够一帧最小长度 # 简易帧提取假设我们“知道”请求和响应的大致长度 # 例如读寄存器请求帧长8字节响应帧长度可变。 # 更优解是结合超时和CRC校验来动态判断帧结束。 # 这里为简化我们演示固定长度例如8字节的请求帧判断 if len(buf) 8: # 检查前8字节的CRC是否正确 test_frame buf[:8] _, crc_ok parse_modbus_frame(test_frame) if crc_ok: # CRC正确认为是一帧有效数据 return bytes(test_frame), buf[8:] # 返回帧和剩余缓冲区 # 如果没有找到完整帧返回None和原缓冲区 return None, buf def process_frame(frame): global last_request_header, last_request_time header, crc_ok parse_modbus_frame(frame) if not crc_ok: print(CRC校验失败帧数据可能损坏。) return # 判断是请求帧还是响应帧 # 简单策略如果功能码是常见请求码如0x01, 0x03, 0x05, 0x06, 0x10等认为是主站请求 func_code frame[1] if func_code in [0x01, 0x03, 0x05, 0x06, 0x0F, 0x10]: print_hex_data(frame) print(f 主站请求 - 从站地址: {frame[0]:02X}, 功能码: {func_code:02X}) last_request_header header last_request_time time.ticks_ms() else: # 可能是响应帧或其他帧 print_hex_data(frame) print(f 从站响应 - 从站地址: {frame[0]:02X}, 功能码: {func_code:02X}) # 进行比对 if last_request_header is not None and header last_request_header: print( √ 帧头匹配通信正常) else: print(f × 帧头不匹配或未找到对应请求当前响应头: {header:04X}) # 清空请求记录准备下一轮 last_request_header None这段代码已经可以实现基本的监听和帧头比对。当你运行它并接上真实的Modbus总线时终端上就会滚动显示主从站之间的对话并在每次问答成功后打上一个勾。那种感觉就像真的听懂了设备们在说什么一样非常有成就感。4. 进阶诊断总线寂静检测与故障判定光是监听和比对还只是个“记录员”。我们要让它成为“医生”能主动诊断总线健康状态。这里引入两个重要的诊断功能总线寂静检测和超时无应答判定。4.1 实现精准的“总线寂静”检测“总线寂静”指的是总线上在一段时间内没有任何数据通信。这可能是主站PLC故障、总线断路、电源问题或严重干扰导致的。检测这个状态是我们实现故障切换的前提。原理很简单我们维护一个计时器每次收到任何一帧数据无论是请求还是响应就把这个计时器重置。如果这个计时器超过了我们设定的阈值比如1秒我们就判定总线进入寂静状态。class BusMonitor: def __init__(self, silence_threshold_ms1000): self.silence_threshold silence_threshold_ms self.last_activity_time time.ticks_ms() self.is_silent False def update_activity(self): 更新最后一次总线活动时间 self.last_activity_time time.ticks_ms() if self.is_silent: print(总线活动恢复退出寂静状态。) self.is_silent False def check_silence(self): 检查是否进入总线寂静状态 current_time time.ticks_ms() # 处理时间回绕 time_diff time.ticks_diff(current_time, self.last_activity_time) if time_diff self.silence_threshold and not self.is_silent: print(f警告总线寂静超过 {self.silence_threshold}ms可能发生故障。) self.is_silent True return True return False # 在主循环中使用 bus_monitor BusMonitor(silence_threshold_ms1000) def main_loop(): # ... 之前的缓冲区处理和帧提取代码 ... while True: if uart.any(): data uart.read() if data: bus_monitor.update_activity() # 收到数据更新活动时间 # ... 处理数据 ... # 每次循环都检查寂静状态 if bus_monitor.check_silence(): # 触发故障处理逻辑例如准备切换为主站 handle_bus_fault() time.sleep_ms(1)这里用了time.ticks_ms()和time.ticks_diff()这是MicroPython里处理毫秒级计时和防止计数器回绕的标准做法比直接相减更可靠。4.2 设计容错与故障切换机制检测到故障后怎么办我们的目标是让ESP32能够接替失效的主站临时担当起轮询从站的任务维持系统部分功能或至少获取关键设备状态。这需要实现一个状态机。系统通常有三种状态监听状态LISTEN默认状态只监听不发送。故障判定状态FAULT_DETECTED当总线寂静超时或连续多次帧头不匹配时进入此状态。此时可以尝试发送一两个简单的Modbus请求比如读取某个已知从站的状态寄存器来确认是主站故障还是总线物理层故障。主站切换状态MASTER_TAKEOVER如果确认原主站无响应而总线物理层正常测试请求能收到回复则ESP32切换为发送模式开始按预定顺序轮询关键从站。import uasyncio as asyncio class ModbusFaultHandler: def __init__(self, uart_instance, de_re_pin): self.uart uart_instance self.de_re_pin de_re_pin # 控制RS485收发方向的引脚 self.state LISTEN self.fault_counter 0 self.critical_slaves [1, 2, 3] # 需要优先保障的关键从站地址列表 async def handle_bus_fault(self): 处理总线故障的协程 print(进入故障处理流程...) self.state FAULT_DETECTED # 1. 首先尝试读取一个从站检查总线物理层是否正常 test_slave self.critical_slaves[0] if await self.probe_slave(test_slave): print(f从站{test_slave}可访问总线物理层正常。原主站可能故障。) # 2. 等待一段时间确认原主站是否恢复 await asyncio.sleep(2000) # 等待2秒 # 再次检查总线是否依然寂静由外部监控 # 如果依然寂静则执行切换 self.state MASTER_TAKEOVER print(切换为临时主站开始轮询关键从站。) await self.start_backup_polling() else: print(探测从站失败可能是总线物理层故障断线、短路等。) # 触发更高级别的报警需要人工干预 self.state LISTEN # 返回监听持续监测 async def probe_slave(self, slave_addr): 探测指定从站是否在线 # 切换为发送模式 self.de_re_pin.value(1) await asyncio.sleep_ms(1) # 等待收发器稳定 # 构造一个简单的Modbus读保持寄存器请求功能码0x03 # 例如读地址0x0000的一个寄存器 req bytearray([slave_addr, 0x03, 0x00, 0x00, 0x00, 0x01]) crc modbus_crc(req) req.extend(crc) self.uart.write(req) await asyncio.sleep_ms(50) # 发送完成后等待一段时间 # 切换回接收模式 self.de_re_pin.value(0) # 等待并读取响应这里需要异步接收简化起见用循环等待 start time.ticks_ms() while time.ticks_diff(time.ticks_ms(), start) 100: # 等待100ms if self.uart.any(): resp self.uart.read() if resp and len(resp) 5 and resp[0] slave_addr and resp[1] 0x03: # 收到有效响应 return True await asyncio.sleep_ms(5) return False async def start_backup_polling(self): 作为备份主站开始轮询 polling_interval 500 # 轮询间隔500ms while self.state MASTER_TAKEOVER: for slave in self.critical_slaves: # 发送读取请求例如读取设备状态 # ... 构造请求帧切换发送模式发送 ... print(f轮询从站 {slave} ...) # 这里需要实现完整的发送-接收-解析流程 await asyncio.sleep_ms(100) # 每个从站查询间隔 await asyncio.sleep_ms(polling_interval) # 可以在轮询间隙检查原主站是否恢复监听总线 # 如果监听到原主站的活动可以退出主站模式回归监听这个故障处理机制相对复杂因为它涉及到状态的切换、定时任务和异步操作。我强烈建议使用uasyncio库来管理这些并发任务它比用while循环和time.sleep要清晰和高效得多。上面的代码给出了一个基于异步IO的框架你需要根据实际从站的支持情况填充具体的轮询命令。5. 从监听者到接管者实现主从模式动态切换这是整个项目最精彩的部分——让ESP32在监听者和主站两个角色间无缝切换。关键在于对RS485收发方向的精确控制和无冲突的通信时序管理。5.1 安全的收发模式切换还记得我们硬件接线时把RS485模块的RE和DE引脚连到了一个GPIO上吗现在它派上大用场了。class RS485Controller: def __init__(self, uart_num, tx_pin, rx_pin, de_re_pin_num, baudrate9600): self.de_re_pin Pin(de_re_pin_num, Pin.OUT) self.uart UART(uart_num, baudratebaudrate, txtx_pin, rxrx_pin, timeout50, timeout_char2) self.set_receive_mode() # 初始化为接收模式 def set_receive_mode(self): 设置为接收模式只听不说 self.de_re_pin.value(0) time.sleep_us(50) # 等待收发器模式稳定时间依芯片手册而定 def set_transmit_mode(self): 设置为发送模式只说不停 self.de_re_pin.value(1) time.sleep_us(50) def write_as_master(self, data): 以主站模式发送数据 self.set_transmit_mode() self.uart.write(data) # 确保数据全部发送完成 time.sleep_ms(len(data) * 10 // (self.uart.baudrate // 1000) 2) # 估算发送时间并加余量 self.set_receive_mode() # 发送完成后立即切回接收模式 def read_as_listener(self): 以监听者模式读取数据 # 确保处于接收模式通常已经是 if self.de_re_pin.value() 1: self.set_receive_mode() return self.uart.read() if self.uart.any() else None这个RS485Controller类封装了底层操作。write_as_master方法会在发送前自动切换到发送模式发送完成后立刻切回接收模式最大限度地减少对总线的影响。这里的时间延迟sleep_us和sleep_ms很重要不同的RS485芯片从模式切换到稳定需要一定时间具体要看数据手册通常几十微秒就够了。5.2 设计稳健的主站轮询逻辑当ESP32切换为主站后它的轮询逻辑需要精心设计避免和可能恢复的原主站冲突也要考虑从站的响应时间。async def backup_master_task(rs485_ctrl, critical_slaves): print(备份主站任务启动。) poll_index 0 while True: slave_addr critical_slaves[poll_index] # 1. 发送查询请求 query build_read_holding_registers(slave_addr, 0x0000, 1) # 示例读一个寄存器 rs485_ctrl.write_as_master(query) # 2. 等待并读取响应 await asyncio.sleep_ms(50) # 给从站响应留出时间 response rs485_ctrl.read_as_listener() if response: if validate_modbus_response(response, slave_addr): print(f从站{slave_addr}响应正常: {binascii.hexlify(response)}) # 更新该从站的状态信息 update_slave_status(slave_addr, ONLINE, parse_register_data(response)) else: print(f从站{slave_addr}响应错误或超时。) update_slave_status(slave_addr, TIMEOUT, None) else: print(f从站{slave_addr}无响应。) update_slave_status(slave_addr, NO_RESPONSE, None) # 3. 轮询下一个从站 poll_index (poll_index 1) % len(critical_slaves) # 4. 在轮询间隙可以短暂监听总线检查原主站是否恢复 await asyncio.sleep_ms(200) # 轮询间隔 def build_read_holding_registers(slave, start_addr, num_regs): 构建读保持寄存器请求帧 frame bytearray([slave, 0x03, (start_addr 8) 0xFF, start_addr 0xFF, (num_regs 8) 0xFF, num_regs 0xFF]) crc modbus_crc(frame) frame.extend(crc) return frame在实际项目中你需要根据从站设备的实际功能码和寄存器地址来构造查询帧。最好把每个从站需要查询的指令封装成配置表这样程序更灵活。同时轮询间隔要设置合理太密集会加重总线负担太稀疏则数据更新慢。5.3 状态同步与优雅降级一个完善的系统还需要考虑“如何知道原主站恢复了”以及“恢复后如何交还控制权”。一个实用的策略是在作为备份主站轮询的间隙留出一些时间窗口主动切换回纯监听模式扫描总线。如果在这个窗口内监听到了符合原主站特征比如特定的源地址或请求模式的有效数据帧并且持续一段时间就可以认为原主站恢复了。此时备份主站应停止主动轮询清空自己的发送缓冲区并完全切回监听模式实现“优雅降级”。这个过程需要仔细设计状态标志和判断条件避免在边界情况下频繁切换。你可以引入一个“原主站活动置信计数器”只有连续监听到多次有效活动才确认恢复从而提高系统的稳定性。6. 项目优化与实战经验分享代码跑起来只是第一步要让它在复杂的工业环境里稳定工作还需要很多优化。这里分享几个我实战中总结的经验。首先抗干扰处理。RS485总线在工厂环境里容易受到电磁干扰。除了硬件上做好屏蔽、接地和终端电阻匹配外软件上也要加强。增加CRC校验的严格性不仅要计算CRC还要对帧长度、功能码合法性做检查丢弃一切可疑帧。实现软件滤波对于瞬间的毛刺干扰可以设置一个“有效帧计数器”。连续收到N帧比如3帧格式正确、CRC正确的数据才认为总线通信真正恢复而不是干扰脉冲。超时与重试机制备份主站轮询时如果某个从站无响应不要立即判其死刑。可以尝试重试1-2次并记录“通信失败次数”只有连续失败超过阈值才标记为故障。其次资源管理与看门狗。ESP32运行MicroPython内存和稳定性需要关注。使用uasyncio进行异步编程这是管理多个并发任务监听、诊断、轮询、状态上报的最佳实践比多线程更节省资源逻辑也更清晰。定期回收内存长时间运行后可以使用gc.collect()手动触发垃圾回收。启用硬件看门狗MicroPython的machine.WDT()可以设置一个看门狗定时器。如果主程序卡死看门狗超时会导致系统重启这是最后一道防线。from machine import WDT wdt WDT(timeout5000) # 5秒看门狗 # 在主循环中定期喂狗 wdt.feed()最后日志与状态上报。这个监听器不能只是一个“黑盒”。它应该把关键的诊断信息记录下来并上报给更上层的监控平台比如通过Wi-Fi上传到服务器。结构化日志不要只用print。可以将日志分为INFO、WARNING、ERROR等级别并带上时间戳和模块名写入到ESP32的Flash文件系统中。关键状态上报当检测到通信故障、切换为主站、从站离线等关键事件时通过MQTT、HTTP等协议发送告警信息。这能让远程运维人员第一时间知晓现场情况。这个项目我从一个简单的监听脚本开始不断迭代最终形成了一个小型的边缘计算故障转移网关。它成本极低一块ESP32几十元但带来的可靠性提升是巨大的。特别是在一些对连续性要求高但又无法部署冗余PLC的场合这种轻量级的备份方案非常有效。希望我分享的这些代码片段和经验能帮你少走弯路更快地搭建出属于自己的智能总线监控系统。

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