树莓派与PC间Socket通信实战:自定义协议与多线程收发
1. 项目缘起与整体设计思路1.1 为什么选择Socket做PC与树莓派的通信手里有一块树莓派一台日常用的PC想让它们之间互相传点数据——这大概是很多玩嵌入式或者物联网方向的朋友都会遇到的场景。我最初的想法很简单树莓派采集传感器数据PC端实时显示并下发控制指令。听起来不复杂但真动手的时候通信方案的选择就成了第一个岔路口。常见的方案无非这几种MQTT、HTTP轮询、串口通信、Socket直连。我挨个试过之后最终选了Socket原因很实在。MQTT需要额外搭一个Broker对于局域网内两台设备点对点通信来说多了一层中间商调试起来也麻烦HTTP轮询实时性差树莓派端要一直开着服务等着被请求资源占用不划算串口通信虽然稳定但需要物理连线PC和树莓派离得远了就不方便。Socket直连的好处是TCP协议本身可靠局域网内延迟极低Python标准库直接支持不需要装任何第三方依赖代码量也少调试直观。这个项目的核心目标就是在PC和树莓派之间建立一条双向通信通道PC可以发送指令控制树莓派上的外设比如点亮LED、读取温湿度树莓派也可以主动把采集到的数据推送给PC。整个架构采用经典的C/S模型树莓派作为服务端监听端口PC作为客户端发起连接。之所以这样设计是因为树莓派通常放在固定位置执行采集任务让它做Server更符合“始终在线等待指令”的角色定位。1.2 通信协议与数据格式的选型考量确定了用Socket之后下一个问题就是数据怎么组织我见过不少新手直接用send(hello)发字符串简单场景确实能用但一旦要传结构化数据比如温度、湿度、时间戳字符串拼接就容易出乱子——粘包、截断、编码错误每一个坑都够你调半天。我的做法是自定义一个轻量级的应用层协议。每条消息由两部分组成一个固定长度的头部4字节用struct.pack打包消息体长度后面紧跟消息体JSON格式的UTF-8字节流。这样接收端先读4字节拿到长度再按长度精确读取消息体从根本上解决了TCP粘包问题。JSON的好处是跨语言、可读性强调试的时候直接print出来就能看懂比二进制协议友好太多。注意TCP是字节流协议没有消息边界的概念。如果你不自己定义边界接收端就不知道一条消息到哪里结束。这是Socket编程里最容易翻车的地方没有之一。关于编码统一用UTF-8。树莓派上默认locale可能是ASCIIPC上可能是GBK不统一编码的话中文直接乱码。我在代码里所有encode和decode都显式指定utf-8避免依赖系统默认值。1.3 整体架构与数据流向整个系统的架构可以这样理解树莓派端启动一个TCP Server绑定在0.0.0.0的某个端口比如8888然后进入accept循环等待连接。PC端启动Client通过树莓派的局域网IP和端口发起连接。连接建立后双方各自开两个线程一个专门负责收消息一个专门负责发消息。为什么要分线程因为recv是阻塞的如果你在主线程里一边等接收一边想发送就会发现程序卡死在recv上根本轮不到发送的逻辑执行。数据流向是双向的PC端可以发送{cmd: led_on}这样的控制指令树莓派收到后解析JSON调用GPIO库点亮LED然后回传{status: ok, led: on}作为确认。树莓派也可以主动推送{type: sensor, temp: 25.3, humidity: 60}这样的传感器数据PC端收到后更新界面显示。这种双向通信模式比单纯的请求-响应灵活得多。2. 核心细节解析与实操要点2.1 树莓派端环境准备与依赖确认在动手写代码之前有几件事必须先确认。第一树莓派的Python版本。我用的树莓派4B系统自带Python 3.7socket和struct、json、threading这些都是标准库不需要额外安装。但如果你要用GPIO控制LED就需要确认RPi.GPIO库是否已安装。可以用python3 -c import RPi.GPIO测试一下如果报ModuleNotFoundError就执行sudo apt install python3-rpi.gpio。第二确认树莓派的局域网IP。在终端输入hostname -I通常会显示类似192.168.1.100的地址。记下这个IPPC端连接时要用。建议在路由器里给树莓派设置静态IP或者DHCP保留不然每次重启IP变了PC端代码就得跟着改很烦。第三检查防火墙。树莓派默认的ufw通常是关闭的但如果你之前开过需要放行对应端口sudo ufw allow 8888/tcp。PC端的Windows防火墙也可能拦截出站连接第一次运行时会弹窗提示记得允许。实操心得我习惯在树莓派上先跑一个最简单的nc -l 8888测试端口是否可达PC端用telnet 树莓派IP 8888连一下。如果通了再上Python代码。这样能把网络问题和代码问题分开排查省得混在一起抓瞎。2.2 消息边界处理struct打包与JSON序列化前面提到了自定义协议这里展开说具体实现。发送端逻辑是这样的import struct import json def send_message(sock, data_dict): body json.dumps(data_dict).encode(utf-8) header struct.pack(!I, len(body)) sock.sendall(header body)struct.pack(!I, length)里的!表示网络字节序大端I表示4字节无符号整数。为什么用网络字节序因为不同CPU架构的字节序可能不同树莓派是ARM小端PC通常是x86小端虽然这里都是小端但养成用网络字节序的习惯没坏处跨平台更稳妥。接收端对应逻辑def recv_exact(sock, n): buf b while len(buf) n: chunk sock.recv(n - len(buf)) if not chunk: raise ConnectionError(连接已断开) buf chunk return buf def recv_message(sock): header recv_exact(sock, 4) length struct.unpack(!I, header)[0] body recv_exact(sock, length) return json.loads(body.decode(utf-8))这里的关键是recv_exact函数。sock.recv(n)不保证一次返回n个字节它可能只返回一部分。所以必须循环读取直到凑够需要的字节数。这个细节很多人会忽略在小数据量、局域网环境下可能碰不到问题但一旦数据量大或者网络抖动就会出bug。2.3 多线程收发的同步与退出机制分线程收发是必须的但线程管理有几个坑。第一个坑是主线程结束后子线程还在跑程序退不干净。解决办法是把接收线程设为daemon线程threading.Thread(target..., daemonTrue)这样主线程退出时子线程自动结束。但daemon线程有个问题它可能在持有锁或者操作socket的时候被强制杀掉导致资源泄漏。更优雅的做法是用一个threading.Event作为退出信号线程循环里检查这个Event收到信号就清理资源后正常退出。第二个坑是两个线程同时操作同一个socket。一个在recv一个在send这本身是安全的TCP socket支持全双工。但如果一个线程在关闭socket另一个线程还在读写就会抛异常。所以关闭socket之前先设置退出Event等两个线程都join了再关socket。stop_event threading.Event() def recv_loop(sock): while not stop_event.is_set(): try: msg recv_message(sock) handle_message(msg) except (ConnectionError, OSError): break # 退出时 stop_event.set() recv_thread.join(timeout2) sock.close()注意sock.recv是阻塞的设置stop_event后接收线程可能还卡在recv上。解决办法是给socket设置超时sock.settimeout(1.0)这样recv最多阻塞1秒就抛socket.timeout异常线程可以检查Event后继续循环。或者用shutdown方法强制唤醒阻塞的recv。3. 实操过程与核心环节实现3.1 树莓派服务端完整代码与逐段解析先上服务端完整代码然后逐段拆解import socket import struct import json import threading HOST 0.0.0.0 PORT 8888 def recv_exact(conn, n): buf b while len(buf) n: chunk conn.recv(n - len(buf)) if not chunk: raise ConnectionError(客户端断开) buf chunk return buf def recv_message(conn): header recv_exact(conn, 4) length struct.unpack(!I, header)[0] body recv_exact(conn, length) return json.loads(body.decode(utf-8)) def send_message(conn, data): body json.dumps(data).encode(utf-8) conn.sendall(struct.pack(!I, len(body)) body) def handle_client(conn, addr): print(f客户端 {addr} 已连接) stop threading.Event() def recv_loop(): while not stop.is_set(): try: msg recv_message(conn) print(f收到: {msg}) # 根据指令执行动作 if msg.get(cmd) led_on: # GPIO.output(LED_PIN, True) send_message(conn, {status: ok, led: on}) elif msg.get(cmd) read_sensor: # 模拟传感器数据 send_message(conn, {temp: 25.3, humidity: 60}) else: send_message(conn, {status: unknown_cmd}) except (ConnectionError, OSError): break stop.set() t threading.Thread(targetrecv_loop, daemonTrue) t.start() t.join() conn.close() print(f客户端 {addr} 已断开) def main(): server socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server.bind((HOST, PORT)) server.listen(5) print(f服务端监听 {HOST}:{PORT}) while True: conn, addr server.accept() threading.Thread(targethandle_client, args(conn, addr), daemonTrue).start() if __name__ __main__: main()逐段说几个关键点。server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)这行很重要。不加的话服务端重启时可能报Address already in use因为TCP连接关闭后有个TIME_WAIT状态端口还被占着。加上这个选项允许立即重用端口。server.listen(5)的参数5是backlog表示等待连接队列的最大长度。局域网内两台设备通信5足够了。如果预期有多个客户端同时连接可以调大。handle_client里每个客户端连接开一个线程处理这样支持多客户端。虽然这个项目通常只有一台PC连接但写成多线程结构以后扩展更方便。3.2 PC客户端完整代码与交互逻辑客户端代码结构类似但多了用户输入交互import socket import struct import json import threading SERVER_IP 192.168.1.100 # 替换为树莓派实际IP SERVER_PORT 8888 def recv_exact(sock, n): buf b while len(buf) n: chunk sock.recv(n - len(buf)) if not chunk: raise ConnectionError(服务端断开) buf chunk return buf def recv_message(sock): header recv_exact(sock, 4) length struct.unpack(!I, header)[0] body recv_exact(sock, length) return json.loads(body.decode(utf-8)) def send_message(sock, data): body json.dumps(data).encode(utf-8) sock.sendall(struct.pack(!I, len(body)) body) def main(): sock socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((SERVER_IP, SERVER_PORT)) print(已连接到树莓派) stop threading.Event() def recv_loop(): while not stop.is_set(): try: msg recv_message(sock) print(f\n[树莓派] {msg}) print(请输入指令: , end, flushTrue) except (ConnectionError, OSError): print(连接断开) stop.set() break t threading.Thread(targetrecv_loop, daemonTrue) t.start() try: while not stop.is_set(): cmd input(请输入指令: ).strip() if cmd quit: break send_message(sock, {cmd: cmd}) except KeyboardInterrupt: pass finally: stop.set() sock.close() if __name__ __main__: main()客户端这边有个细节input()是阻塞的接收线程在后台跑收到消息直接print。但print会打断用户正在输入的指令看起来有点乱。解决办法是在print之前先输出一个换行然后重新打印提示符。上面代码里print(f\n[树莓派] {msg})后面的print(请输入指令: , end, flushTrue)就是干这个的。虽然不完美但够用。3.3 联调测试与数据验证代码写完后联调步骤是这样的树莓派端运行python3 server.py看到“服务端监听 0.0.0.0:8888”。PC端修改SERVER_IP为树莓派实际IP运行python3 client.py。树莓派端打印“客户端 (192.168.1.xxx, xxxxx) 已连接”。PC端输入led_on树莓派端打印“收到: {cmd: led_on}”PC端收到{status: ok, led: on}。PC端输入read_sensor收到模拟的温湿度数据。PC端输入quit双方断开。测试的时候我建议先用模拟数据跑通流程再接真实传感器。因为传感器接线、驱动库的问题会干扰你对通信链路的判断。等通信稳定了再把read_sensor分支里的模拟数据换成真实的DHT11或DS18B20读取代码。实操心得我在树莓派上跑服务端时习惯用nohup python3 server.py server.log 21 让它后台运行日志写到文件里。这样SSH断开后服务不会停排查问题也有日志可查。调试阶段则直接前台运行方便看实时输出。4. 常见问题与排查技巧实录4.1 连接失败类问题速查连接失败是最常见的问题原因五花八门。我整理了一个速查表按概率从高到低排列现象可能原因排查方法解决方案ConnectionRefusedError服务端没启动或端口不对树莓派上netstat -tlnp | grep 8888启动服务端确认端口一致ConnectionRefusedError防火墙拦截sudo ufw statussudo ufw allow 8888/tcpTimeoutErrorIP地址错误或不在同一网段PC上ping 树莓派IP确认IP检查路由器网段TimeoutError树莓派绑定的是127.0.0.1检查代码HOST变量改为0.0.0.0连接后立即断开服务端代码异常退出看树莓派端报错信息根据traceback修复ConnectionRefused和Timeout的区别很关键Refused说明网络通了但目标端口没有服务在监听Timeout说明网络根本不通包都没到。根据这个区分排查方向完全不同。4.2 数据收发异常与粘包问题粘包是Socket编程的经典问题。表现是发送端发了三条消息接收端一次recv收到了两条半或者一条消息被拆成两次收到。根本原因就是TCP是字节流没有消息边界。我前面用的“4字节长度头JSON体”方案就是解决这个的。但如果你没用这个方案而是直接send字符串那就要用分隔符比如\n来切分接收端按分隔符拆分。分隔符方案的问题是如果消息体本身包含分隔符就会误切。所以更推荐长度头方案。还有一个坑send的返回值。sock.send(data)返回实际发送的字节数可能小于len(data)。所以要用sendall它会循环发送直到全部发完。我见过有人用send然后不检查返回值大数据量时丢数据。4.3 线程阻塞与程序无法退出程序跑起来后按CtrlC退不掉或者关了客户端但服务端线程还挂着——这都是线程阻塞的锅。核心原因是recv默认是阻塞的没有数据来就一直等。解决方案有三个层次。最简单的给socket设超时sock.settimeout(1.0)recv最多等1秒超时抛异常线程检查退出标志后决定是否继续。进阶一点用select.select([sock], [], [], 1.0)来检查socket是否可读可读才recv不可读就检查退出标志。最彻底的用shutdown(socket.SHUT_RDWR)强制唤醒阻塞的recv但这个方法比较粗暴适合退出时使用。我个人的习惯是接收线程用settimeout(1.0)退出Event简单可靠。退出时先stop_event.set()然后thread.join(timeout3)如果3秒还没结束再shutdown强制唤醒。4.4 编码与中文乱码处理中文乱码的根源是编码不一致。树莓派上locale可能是C或POSIXPython的默认编码是ASCIIPC上Windows中文版默认GBK。如果代码里用str.encode()不指定编码就会用系统默认两边不一致就乱码。我的做法是所有网络传输的数据统一用UTF-8编码。发送端json.dumps(data, ensure_asciiFalse).encode(utf-8)接收端.decode(utf-8)。ensure_asciiFalse让JSON里的中文不被转义成\uXXXX可读性更好。如果还是乱码检查一下终端本身的编码设置树莓派上可以export LANGen_US.UTF-8或者zh_CN.UTF-8。注意json.dumps默认ensure_asciiTrue会把中文转成Unicode转义序列。虽然不影响解析但调试时看到一堆\u6e29\u5ea6很头疼。加上ensure_asciiFalse日志里直接显示“温度”清爽很多。4.5 性能优化与心跳保活局域网内Socket通信性能通常不是问题但如果你要传大量数据比如视频流或者高频传感器采样有几个优化点。第一增大SO_SNDBUF和SO_RCVBUF缓冲区sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 65536)。第二用sendall而不是多次send减少系统调用。第三如果数据量真的很大考虑用memoryview避免数据拷贝。心跳保活是另一个实用功能。长时间空闲的TCP连接可能被路由器或防火墙断开。解决办法是定期发送心跳包比如每30秒发一个{type: ping}对方回{type: pong}。如果连续几次没收到pong就判定连接断开触发重连逻辑。这个机制在需要7x24小时运行的场景里很有必要。def heartbeat_loop(sock, stop_event): while not stop_event.wait(30): try: send_message(sock, {type: ping}) except OSError: breakstop_event.wait(30)比time.sleep(30)好的地方在于退出时stop_event.set()能立即唤醒不用等30秒。这个细节在程序退出响应速度上差别很明显。5. 功能扩展与实战建议5.1 从单客户端到多设备管理现在的架构是树莓派做ServerPC做Client。如果你有多块树莓派想让一台PC同时管理有两种思路。第一种每块树莓派都跑ServerPC端开多个Client连接用不同的线程管理不同连接。第二种PC做Server所有树莓派做Client主动连接PCPC端维护一个设备列表。第二种更适合设备数量多、设备可能动态上下线的场景。我实际用下来第二种架构更省心。因为树莓派通常放在不同位置让它们主动连PCPC端统一管理不用去记每个树莓派的IP。PC端Server收到连接后根据第一条消息里的设备ID来区分是哪块树莓派。设备ID可以写在树莓派的配置文件里启动时读取。5.2 结合Web界面做远程监控命令行交互适合调试但实际使用中有个Web界面会方便很多。我的做法是PC端用Flask或者FastAPI起一个Web服务Socket客户端在后台跑收到树莓派数据后存到内存或SQLite里Web页面通过WebSocket或者轮询获取最新数据展示。控制指令则通过Web页面的按钮触发调用Socket客户端的发送函数。这样你可以在手机浏览器上打开PC的Web页面查看树莓派采集的温度曲线点击按钮控制LED。整个链路是手机浏览器 - PC Web服务 - Socket - 树莓派。PC在这里充当了网关的角色。5.3 数据持久化与历史查询传感器数据只显示实时值意义不大存下来做趋势分析才有价值。最简单的方案是存SQLitePython标准库自带sqlite3不需要额外安装。建一张表CREATE TABLE sensor_data (id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, temp REAL, humidity REAL)每次收到数据就INSERT一条。查询的时候按时间范围SELECT用matplotlib或者前端图表库画曲线。数据量大了之后SQLite可能不够用可以换TimescaleDB或者InfluxDB这类时序数据库。但对于个人项目SQLite存几个月的数据完全没问题几百万条记录查询也是毫秒级。5.4 安全加固认证与加密局域网内通信安全性通常不是首要考虑但如果你把树莓派暴露在公网比如通过端口映射那就必须加认证。最简单的方案是连接建立后客户端先发一个{auth: token}服务端校验token是否正确不正确就断开。token可以写在配置文件里两边一致即可。更安全的是用TLS加密Python的ssl模块可以给Socket套一层TLS。需要生成自签名证书服务端ssl.wrap_socket或者SSLContext客户端连接时验证证书。这样数据在传输过程中是加密的即使被截获也看不懂。不过TLS会带来额外的CPU开销和配置复杂度局域网内看情况使用。实操心得我一般会在服务端加一个简单的IP白名单只允许特定IP连接。虽然不是强安全措施但能挡住大部分扫描和误连。代码很简单accept之后检查addr[0]是否在白名单里不在就conn.close()。6. 踩坑记录与个人体会这个项目我从最初跑通到稳定运行前后踩了不少坑挑几个印象深刻的说说。第一个坑是树莓派休眠导致连接断开。树莓派默认没有屏保休眠但网卡在空闲时可能进入省电模式导致TCP连接被断开。解决办法是关闭网卡的省电模式sudo iw dev wlan0 set power_save off。或者更简单用心跳包保持连接活跃。我一开始没在意结果跑了几个小时就断排查了半天才定位到网卡省电。第二个坑是JSON序列化datetime对象报错。传感器数据里带时间戳我直接json.dumps({time: datetime.now()})结果抛TypeError: Object of type datetime is not JSON serializable。解决办法是自定义一个default函数json.dumps(data, defaultstr)把datetime转成字符串。或者手动datetime.now().isoformat()再放进字典。第三个坑是多线程print输出交错。两个线程同时print输出会混在一起日志很难看。解决办法是用logging模块它内部有锁不会交错。或者自己加一个threading.Lockprint之前先lock.acquire()print完lock.release()。我后来统一换成了logging日志级别、时间戳、线程名都自动带上排查问题方便很多。最后分享一个调试技巧在开发阶段我会在send_message和recv_message里各加一行日志打印消息内容和长度。这样每次收发都有记录出问题的时候一看日志就知道是发丢了还是收错了。等稳定运行后再把日志级别调高减少输出。这个习惯帮我省了大量排查时间强烈建议你也加上。

相关新闻

漂移工况下的非线性三自由度车辆模型搭建与Simulink仿真实现

漂移工况下的非线性三自由度车辆模型搭建与Simulink仿真实现

前一阵子做极限工况仿真,折腾了好几个礼拜,最后发现最卡脖子的不是控制算法,而是车辆模型本身。普通乘用车模型在常规工况下精度还行,但只要把车扔进漂移状态,模型就开始“胡来”——后轮侧偏角一超过20度,…

2026/10/12 23:34:29 阅读更多 →
STM32驱动MG90S舵机:PWM配置、供电与多路协同实战

STM32驱动MG90S舵机:PWM配置、供电与多路协同实战

1. 从一次“舵机抽搐”说起:为什么MG90S值得单独写一篇很多人第一次把MG90S接到STM32上,代码烧进去,舵机要么纹丝不动,要么像得了帕金森一样来回抖,要么转到一个角度就“咔咔”响然后发烫。我当年做第一个云台项目时&a…

2026/10/12 23:34:28 阅读更多 →
LIDC肺结节数据解析:从XML标注到深度学习标签生成全指南

LIDC肺结节数据解析:从XML标注到深度学习标签生成全指南

简介:面向肺部CT影像分析与Python开发者的LIDC数据说明与标签资源,用于解决LIDC-IDRI数据集中标注文件格式多样、字段含义不清、难以直接用于建模的问题,适合在肺结节检测、分割以及机器学习项目前期做数据梳理和预处理时参考。压缩包内共有1…

2026/10/12 23:34:28 阅读更多 →

最新新闻

VSCode调试Linux coredump:从系统配置到图形化分析实战

VSCode调试Linux coredump:从系统配置到图形化分析实战

服务器半夜收到某个核心服务的崩溃告警,机器上只留了一个几百MB的coredump文件,可执行文件的debug符号也还在。大部分人的第一反应是gdb program core,然后敲bt,凑合看个函数名。但如果你已经习惯在VSCode里写代码,完全…

2026/10/13 2:54:39 阅读更多 →
Windows部署Docker完整指南:WSL2与Docker Desktop实战避坑

Windows部署Docker完整指南:WSL2与Docker Desktop实战避坑

最近在Windows机器上把Docker完整部署了一遍,踩了不少坑,也总结出一些经验。网上关于Windows部署Docker的教程很多,但大多是零散片段,要么只讲安装,要么只讲某个报错,真正把“为什么这样部署”“每一步背后…

2026/10/13 2:54:39 阅读更多 →
5W无线充电发射端方案全解析:IP6825芯片、A11/A11a线圈与FOD调校

5W无线充电发射端方案全解析:IP6825芯片、A11/A11a线圈与FOD调校

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 2:54:39 阅读更多 →
STM32F429移植RT-Thread nano与FinSH实战指南

STM32F429移植RT-Thread nano与FinSH实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 2:54:39 阅读更多 →
告别重复劳动:四款小众插件实测,批量处理与网页自动化让效率翻倍

告别重复劳动:四款小众插件实测,批量处理与网页自动化让效率翻倍

每天打开电脑,总有一堆活儿在等着:给几百个文件改格式、把同一段代码复制到不同位置、在网页里反复填写一模一样的表格、从聊天记录和文档之间来回搬运信息。这些事本身不难,难的是它们又碎又多,一天下来光忙这些,真正…

2026/10/13 2:54:39 阅读更多 →
AnyPS5:用WOL+网页控制面实现PS5远程唤醒与串流调度

AnyPS5:用WOL+网页控制面实现PS5远程唤醒与串流调度

1. 为什么会有 AnyPS5:主机留在客厅,时间却都花在路上先说结论:AnyPS5 不是模拟器,也不是什么越狱工具,它是我给自己家用主机搭的一个远程使用入口——一个网页控制面,加上一套围绕开机、唤醒、状态探测和串…

2026/10/13 2:53:39 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 21:05:09 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 14:48:49 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →