当前位置: 首页 > article >正文

【LWIP】MCU通过ICMP协议实现主动PING检测网络设备状态

1. 为什么你的MCU需要主动PING一个真实的故事大家好我是老张在嵌入式网络这块摸爬滚打了十几年。今天想和大家聊聊一个看似简单但在实际项目中却至关重要的功能让MCU主动去PING网络里的其他设备。你可能已经用LWIP让外部设备能PING通你的MCU了这很棒说明你的网络基础通了。但很多时候这还不够。我遇到过太多这样的场景一个智能家居网关它需要时刻知道它下挂的温湿度传感器、智能开关是不是还“活着”。如果只是等传感器上报数据万一传感器死机了但没重启网关可能半天都发现不了用户就会觉得你的产品“反应迟钝”。又或者一个工业数据采集器它连接着好几个PLC如果某个PLC网络断开了采集器必须立刻知道并尝试重连或上报故障而不是傻傻地继续发送注定失败的数据请求。这时候主动PING检测就从一个“可有可无”的功能变成了保障系统可靠性的“生命线”。它就像你的MCU定期对其他设备喊一声“喂你还在吗”并期待一个“在呢”的回应。这个“喊话”用的协议就是ICMP协议而那个“喊话”的动作就是我们常说的PING。在LWIP协议栈里实现这个功能的核心就是操作一个叫做raw_pcb的东西。你可以把它理解为一个专门处理“原始”网络数据包的控制块。通过它我们就能亲手组装一个ICMP Echo Request包也就是PING请求包发出去并监听和处理返回的ICMP Echo Reply包PING应答包。接下来的内容我会带你一步步拆解这个过程从原理到代码从初始化到定时检测把我踩过的坑和总结的经验都分享给你。即使你之前没怎么接触过网络编程跟着做也能让你的MCU拥有“主动侦察”的能力。2. 动手前的准备理解ICMP与RAW PCB在撸起袖子写代码之前咱们先花几分钟把核心概念搞清楚这样后面调代码的时候心里才有底不至于懵圈。ICMP协议中文叫网际控制报文协议。它不像TCP或UDP那样用于传输你的应用数据比如网页内容、视频流它更像是网络世界的“信使”和“检修工”专门负责传递控制信息和差错报告。我们用的PING功能只是ICMP众多功能中的一种具体来说是ICMP Echo Request和ICMP Echo Reply这两种报文类型。当你执行PING命令时你的设备会发送一个Type8, Code0的ICMP Echo Request包。目标设备收到后如果一切正常就会回一个Type0, Code0的ICMP Echo Reply包。这个过程不涉及复杂的连接建立就是简单的“一问一答”。那么在LWIP中我们怎么才能发送和接收这种“原始”的ICMP包呢答案就是使用RAW API。LWIP提供了几种编程接口Sequential API像socket、NETCONN API以及最底层、最灵活的RAW API。RAW API允许你的应用程序直接与IP层打交道处理原始的数据包。这对于实现像PING、DHCP客户端这类需要构造特定协议报文的场景非常合适。使用RAW API的关键是创建一个struct raw_pcb。这个结构体定义了一个原始协议控制块你需要告诉它“嗨我关心的是IP协议号是IP_PROTO_ICMP的数据包请把这类包都交给我来处理。” 创建好这个raw_pcb后你就可以用它来发送原始数据包并注册一个回调函数来接收匹配的原始数据包。听起来有点抽象别急我们把它和生活做个类比。假设你的MCU是一个小区的保安亭raw_pcb就是你的工作职责登记表。你向物业LWIP内核登记“我只关心所有寄给‘ICMP’这个收件人的快递数据包”。之后每当有ICMP快递送到小区物业就会直接喊你去处理。你可以检查快递内容接收处理也可以自己以“ICMP”的名义往外寄快递发送数据包。这就是raw_pcb的基本工作模式。3. 核心实现四步搭建PING功能理解了原理我们现在开始动手。整个实现过程可以清晰地分为四个步骤我会结合代码和注意事项详细说明。3.1 第一步创建ICMP控制块与初始化这是所有工作的基础我们得先把“保安亭”搭建起来并明确工作目标。这个初始化过程通常放在系统启动时执行一次就够了。#include lwip/raw.h #include lwip/ip_addr.h // 定义两个全局变量用于保存我们的“工作台” static struct raw_pcb *ping_pcb NULL; static struct pbuf *ping_buf NULL; int ping_init(void) { // 1. 创建ICMP类型的原始协议控制块 ping_pcb raw_new(IP_PROTO_ICMP); if (ping_pcb NULL) { printf([PING] Error: Failed to create raw pcb!\n); return -1; // 创建失败可能是内存不足 } // 2. 可选但推荐绑定本地IP和设置目标IP // 明确告诉LWIP我们这个pcb从哪个本地IP发出发给谁可以避免一些多网卡下的歧义。 ip_addr_t local_ip, remote_ip; IP4_ADDR(local_ip, 192, 168, 0, 100); // 你的MCU的IP地址 IP4_ADDR(remote_ip, 192, 168, 0, 200); // 你想要PING的目标设备IP地址 raw_bind(ping_pcb, local_ip); // 绑定本地地址 ping_pcb-remote_ip remote_ip; // 设置默认目标地址方便后续发送 // 3. 预分配一个pbuf用于存放我们要发送的ICMP包 // PBUF_IP表示这个pbuf包含IP层头部我们只需要ICMP回显请求头的大小 ping_buf pbuf_alloc(PBUF_IP, sizeof(struct icmp_echo_hdr), PBUF_RAM); if (ping_buf NULL) { raw_remove(ping_pcb); // 记得清理已创建的pcb ping_pcb NULL; printf([PING] Error: Failed to allocate pbuf!\n); return -2; } printf([PING] Init success. PCB created, buffer allocated.\n); return 0; // 初始化成功 }几个关键点解释raw_new(IP_PROTO_ICMP)这是核心创建了一个专门处理ICMP协议原始数据的控制块。raw_bind将pcb绑定到MCU的本地IP。这在你设备有多个IP地址时特别重要能确保PING包从正确的网口发出。pbuf_allocpbuf是LWIP中管理网络数据包缓冲区的结构。这里我们预先分配一个大小刚好是一个ICMP回显请求头icmp_echo_hdr。使用PBUF_RAM意味着这个缓冲区从内存堆中分配速度快。为什么预分配pbuf在定时PING的场景下我们频繁发送结构完全相同的包。预分配一个pbuf并复用可以避免频繁的内存申请和释放提高效率减少内存碎片。这是嵌入式开发中一个很实用的优化技巧。3.2 第二步组装并发送PING请求包初始化完成后我们就可以随时调用一个函数来发送PING请求了。这个函数负责填充ICMP包头并通过raw_pcb将包发送出去。void ping_send_request(void) { if (ping_pcb NULL || ping_buf NULL) { printf([PING] Error: Not initialized!\n); return; } // 1. 获取pbuf负载区的指针并转换为ICMP头结构 struct icmp_echo_hdr *iecho (struct icmp_echo_hdr *)ping_buf-payload; // 2. 填充ICMP Echo Request包头 iecho-type ICMP_ECHO; // Type 8表示回显请求 iecho-code 0; // Code 0 iecho-chksum 0; // **先置零** 校验和计算需要这一步 iecho-id PP_HTONS(0x1234); // 标识符可以设为固定值或进程ID用于区分不同发送者 iecho-seqno PP_HTONS(ping_seq_num); // 序列号每次发送递增用于匹配请求与应答 // 3. 计算ICMP校验和重要 // 校验和覆盖整个ICMP报文头部数据虽然我们这里数据部分长度为0 iecho-chksum inet_chksum(iecho, sizeof(struct icmp_echo_hdr)); // 4. 发送数据包 err_t err raw_sendto(ping_pcb, ping_buf, (ping_pcb-remote_ip)); if (err ! ERR_OK) { printf([PING] Send failed, err: %d\n, err); } else { printf([PING] Request sent. Seq: %d\n, ping_seq_num); } } // 定义一个全局变量记录序列号 static u16_t ping_seq_num 0;这里有一个我踩过的大坑务必注意校验和的计算时机你必须先填充完type、code、id、seqno等所有字段并将chksum字段先设为0然后再调用inet_chksum计算整个ICMP头部的校验和。如果你先计算了校验和然后又修改了包内容比如在别处改了seqno或者忘了将chksum先置零都会导致校验和错误目标设备会直接丢弃这个非法包你永远也收不到回复。很多新手在这里卡很久。raw_sendto这个函数将我们组装好的ping_buf发送到之前指定的目标IPping_pcb-remote_ip。LWIP内核会自动为我们加上IP头部。3.3 第三步接收并处理PING应答包包发出去了我们还得能“听到”回音。LWIP内核收到IP层传来的ICMP包后会分发给所有注册了的raw_pcb。我们需要告诉内核当收到ICMP包时调用我们自己的处理函数。通常我们不会为PING单独注册一个RAW回调因为LWIP的icmp.c模块已经有一个默认的ICMP输入处理函数icmp_input。更常见的做法是修改这个函数让它能识别出我们关心的PING应答包并给我们一个“信号”。具体操作如下在icmp.c文件或你的应用文件中定义一个全局的标志变量比如叫ping_reply_received。在icmp_input(struct pbuf *p, struct netif *inp)这个函数内部找到处理ICMP_EREcho Reply类型为0的switch-case语句。在对应的case里将我们的标志变量置位。// 在你的应用代码中声明这个外部变量假设它在icmp.c中定义 extern volatile uint8_t ping_reply_received; // 在 icmp.c 文件中的修改示例 volatile uint8_t ping_reply_received 0; // 定义并初始化为0 void icmp_input(struct pbuf *p, struct netif *inp) { // ... 前面的代码解析IP头、获取ICMP头等 switch (icmp_header-type) { case ICMP_ER: // ICMP Echo Reply // 这里可以增加更精细的判断比如检查ID是否匹配我们发出的请求 // if (icmp_header-id PP_HTONS(0x1234)) { ping_reply_received 1; // 关键一步收到应答置位标志 // } // 注意LWIP默认可能会在这里释放pbuf(p)并返回如果你置位后还需要默认处理要小心。 // 更稳妥的做法可能是复制必要信息后仍让原有流程处理这个包。 break; // ... 处理其他ICMP类型如目的不可达 } // ... 后续代码 }重要提醒直接修改icmp.c是侵入性较强的方式但它简单直接。在资源紧张的MCU上这往往是最高效的方法。如果你追求模块化也可以自己创建一个raw_pcb并注册接收回调在回调里过滤出type0的包。但那样会多占用一个raw_pcb资源并且所有ICMP包包括错误报文都会先到你的回调需要自己过滤。两种方式各有优劣根据项目情况选择。3.4 第四步构建状态检测循环与超时机制有了发送和接收的能力我们需要一个“大脑”来调度整个检测流程并做出判断。这个大脑通常是一个定时任务比如每50ms或100ms执行一次。这个任务的工作逻辑是每次执行时先检查ping_reply_received标志。如果为1说明上次发出的PING收到了应答网络是通的。此时我们清除标志重置超时计数器并将连接状态设为正常。如果标志为0说明还没收到应答可能还没到也可能永远到不了。我们增加超时计数器。无论是否收到应答本次任务最后都再次发送一个新的PING请求为下一次检测做准备。当超时计数器超过某个阈值比如1秒内20次检测都没收到回复我们就判定网络连接异常。// 全局状态变量 volatile uint8_t network_connection_status 0; // 0:异常 1:正常 static uint32_t ping_timeout_counter 0; #define PING_TIMEOUT_THRESHOLD (20) // 50ms * 20 1秒超时 void ping_periodic_task(void) { // 1. 检查是否收到应答 if (ping_reply_received) { ping_reply_received 0; // 清除标志 network_connection_status 1; // 连接正常 ping_timeout_counter 0; // 重置超时计数器 printf([PING] Echo received. Connection OK.\n); } else { // 2. 未收到应答计数 if (ping_timeout_counter PING_TIMEOUT_THRESHOLD) { ping_timeout_counter; } else { // 3. 超时判定为连接异常 network_connection_status 0; // 这里可以添加触发重连、报警等操作 printf([PING] Timeout! Connection may be down.\n); // 超时后可以选择暂停计数避免持续打印日志 // ping_timeout_counter PING_TIMEOUT_THRESHOLD; } } // 4. 发送下一次PING请求无论上次结果如何 ping_send_request(); } // 在RTOS的线程或裸机的定时器中断中周期性地调用 ping_periodic_task // 例如在FreeRTOS中 void vPingTask(void *pvParameters) { ping_init(); // 初始化 const TickType_t xDelay pdMS_TO_TICKS(50); // 50ms周期 for(;;) { ping_periodic_task(); vTaskDelay(xDelay); } }定时周期与超时阈值的权衡周期太短如10ms会过于频繁地发送PING包增加网络和MCU的负担。周期太长如500ms网络故障的发现会延迟影响系统实时性。超时阈值通常设置为RTT往返时间的3-5倍。在稳定的局域网内1秒20*50ms是一个比较保守和可靠的值。你可以根据实际网络环境调整。4. 避坑指南与高级技巧代码跑起来只是第一步。想让它在实际产品中稳定可靠还需要注意下面这些我亲身踩过的“坑”。4.1 内存与资源管理嵌入式开发内存是永恒的话题。我们的PING功能虽然小但处理不好也会泄露。pbuf的生命周期我们采用了预分配一个ping_buf并复用的方式。这非常高效。但请确保这个pbuf在程序整个生命周期内都不被释放除非你要彻底关闭PING功能。千万不要在ping_send_request里发送完后调用pbuf_free那样下次发送就崩溃了。raw_pcb的清理如果您的设备有动态切换网络目标或关闭PING功能的需求记得在不再需要时调用raw_remove(ping_pcb)来释放这个控制块并将ping_pcb指针置为NULL。中断上下文ping_reply_received这类在中断网络接收通常是中断和主任务间共享的变量务必使用volatile关键字声明防止编译器优化导致数据不一致。如果是在RTOS中更推荐使用信号量、队列或事件标志组来进行任务间同步这比查询全局变量更安全、更高效。4.2 提升鲁棒性与可调试性匹配请求与应答我们之前的简单实现只检查了收到应答包。更健壮的做法是检查应答包中的id和seqno字段。id可以设置为你MCU的某个独特标识比如任务IDseqno每次发送递增。在icmp_input中只有id和seqno都匹配的应答包才被认为是针对我们本次请求的回复这样可以避免被网络上其他设备的PING应答干扰。添加调试信息在ping_send_request和标志位处理的地方添加条件编译的打印语句。可以打印出每次发送的序列号、目标IP以及收到应答时的序列号。这在联调阶段是定位问题的利器。产品发布时关闭这些调试输出即可。处理网络变化如果设备的IP地址是通过DHCP获取的可能会变化。当本地IP变化时你需要重新调用raw_bind(ping_pcb, new_ip)来更新绑定。可以在DHCP获取到新IP的回调函数里处理这个逻辑。4.3 性能考量与优化CPU占用每50ms执行一次任务包括组包、发送、检查标志、判断超时。这个开销对于主流Cortex-M系列MCU来说微乎其微。但如果你的系统非常繁忙可以考虑将周期放宽到100ms甚至200ms超时阈值相应调整为2-4秒。网络流量一个ICMP Echo Request包很小通常64字节包含IP头。50ms发一个一分钟也就不到8KB的流量对于以太网或Wi-Fi来说完全可以忽略不计。多目标检测如果你需要PING多个设备不要为每个设备都创建一套独立的raw_pcb和任务。更好的做法是使用一个raw_pcb在ping_send_request函数中动态修改ping_pcb-remote_ip并维护一个目标IP列表和对应的超时计数器。定时任务轮流给列表中的每个目标发送PING并独立判断其连接状态。这样可以极大地节省内存和CPU资源。5. 实际项目中的应用变种掌握了基础的单向PING后我们可以玩出更多花样满足更复杂的项目需求。双向心跳检测在物联网设备与云服务器的场景中常常需要双向保活。你的MCU可以定时PING网关或服务器如上述实现。同时服务器也会定时PING你的设备。你需要在MCU端确保LWIP能正常回复Echo Request默认就是支持的。这样任何一端发现超时都可以触发重连报警形成双向监督。网络质量评估PING不仅仅是通断检测。通过记录发送请求的时间戳和收到应答的时间戳可以粗略计算网络往返延迟RTT。虽然精度不如专业工具但对于判断网络是否“拥堵”非常有参考价值。你可以在ping_send_request时记录一个时间戳在icmp_input收到对应序列号的应答时计算时间差。将这个RTT值纳入连接状态的判断比如连续多次RTT超过500ms即使没超时也可以预警网络质量差。作为故障恢复的触发器在我的一个工业网关项目中网络连接状态network_connection_status是一个关键信号。当它从1变为0时不仅会点亮一个故障指示灯还会触发一系列复杂的恢复流程首先尝试重启网络PHY芯片如果无效则复位整个网络接口模块最后还会通过备用通道如4G将故障信息上报。PING功能在这里充当了网络健康的“哨兵”。让MCU主动PING这个功能就像给嵌入式设备装上了“感知”网络环境的触角。它实现起来不复杂但带来的系统可靠性提升是巨大的。希望我分享的这些代码片段和经验能帮你少走弯路。最后再啰嗦一句嵌入式网络调试一定要善用工具比如Wireshark抓包它能让你清晰地看到每一个字节的来龙去脉是解决疑难杂症的终极法宝。

相关文章:

【LWIP】MCU通过ICMP协议实现主动PING检测网络设备状态

1. 为什么你的MCU需要主动PING?一个真实的故事 大家好,我是老张,在嵌入式网络这块摸爬滚打了十几年。今天想和大家聊聊一个看似简单,但在实际项目中却至关重要的功能:让MCU主动去PING网络里的其他设备。 你可能已经用L…...

Flutter 三方库 dart_json_annotations 的鸿蒙化适配指南 - 定义严谨的数据契约、在鸿蒙端实现自动化 JSON 注解实战

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net Flutter 三方库 dart_json_annotations 的鸿蒙化适配指南 - 定义严谨的数据契约、在鸿蒙端实现自动化 JSON 注解实战 前言 在进行 Flutter for OpenHarmony 的全场景应用开发时&#xff0…...

一文看懂AI智能体协议家族:MCP、A2A、ACP全解析,小白程序员必收藏

在AI智能体(Agent)迅猛发展的当下,MCP、A2A、ACP、UTCP、ANP……各种协议层出不穷,几乎每隔一段时间,科技公司就会为“字母家族”增添新成员。归根结底,所有AI智能体协议的目标都是标准化智能体的通信方式&…...

拒绝黑盒!一文看懂大模型底层原理与产品区别,小白程序员必收藏

在当今数字化时代,AI 大模型早已不是陌生词汇 —— 从日常聊天的 ChatGPT,到帮我们处理工作的智能助手,它正悄悄改变着我们的生活与工作节奏。但对大多数人来说,AI 大模型就像个 “黑盒子”:知道它好用,却搞…...

Flutter 三方库 w_transport 的鸿蒙化适配指南 - 构建高可靠网络传输层、实现鸿蒙端复杂协议交互实战

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net Flutter 三方库 w_transport 的鸿蒙化适配指南 - 构建高可靠网络传输层、实现鸿蒙端复杂协议交互实战 前言 在开发 Flutter for OpenHarmony 大型商业应用时,简单的 HTTP 请求…...

Flutter 三方库 codenic_bloc_use_case 的鸿蒙化适配指南 - 践行整洁架构、在 BLoC 中优雅封装鸿蒙业务用例实战

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net Flutter 三方库 codenic_bloc_use_case 的鸿蒙化适配指南 - 践行整洁架构、在 BLoC 中优雅封装鸿蒙业务用例实战 前言 在进行 Flutter for OpenHarmony 的大型项目开发时,复杂…...

Flutter 三方库 kiss_dependencies 的鸿蒙化适配指南 - 践行极简依赖注入、实现鸿蒙跨平台工程的高效解耦

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net Flutter 三方库 kiss_dependencies 的鸿蒙化适配指南 - 践行极简依赖注入、实现鸿蒙跨平台工程的高效解耦 前言 在 Flutter for OpenHarmony 的实际开发中,随着业务逻辑从单一…...

3秒解锁百度网盘资源:零技术门槛的提取码查询工具使用指南

3秒解锁百度网盘资源:零技术门槛的提取码查询工具使用指南 【免费下载链接】baidupankey 项目地址: https://gitcode.com/gh_mirrors/ba/baidupankey 资源获取的隐形墙:你是否也遇到这些困境? 想象这样的场景:设计师小陈…...

200年前的蒸汽机工人,其实早就预言了今天程序员的命运

最近看到一篇很有意思的文章,作者在读 OpenAI 关于“线束工程”(Harness Engineering)的博客时,突然意识到一件事:这个模式他见过,不止一次,而是三次。这三次跨越了两百多年,但本质上…...

告别提取码焦虑:零门槛百度网盘资源解锁工具让你秒级获取文件

告别提取码焦虑:零门槛百度网盘资源解锁工具让你秒级获取文件 【免费下载链接】baidupankey 项目地址: https://gitcode.com/gh_mirrors/ba/baidupankey 一、被提取码困住的三个真实故事 医生王主任的紧急时刻 凌晨两点,急诊科王主任收到同事发…...

Windows Subsystem for Android (WSA) 实战指南:从环境搭建到高效应用

Windows Subsystem for Android (WSA) 实战指南:从环境搭建到高效应用 【免费下载链接】WSA Developer-related issues and feature requests for Windows Subsystem for Android 项目地址: https://gitcode.com/gh_mirrors/ws/WSA 一、WSA技术解析&#xff…...

SpringBoot + 腾讯地图实战:打造全能型地理位置服务平台,开箱即用!

大家好,我是小悟。 什么是腾讯地图 腾讯地图(Tencent Map)是腾讯公司推出的一款数字地图服务,提供丰富的地图展示、定位、搜索、导航等功能。作为国内领先的地图服务提供商,腾讯地图拥有以下特点: 海量数据…...

基于STM32的多屏可编程HID控制键盘设计

1. 项目概述MultiPad 是一款基于 STM32F103VET6 微控制器构建的高自由度桌面控制键盘系统,其设计目标是为开发者、内容创作者及效率追求者提供一套可深度定制、即插即用、软硬协同的物理交互层解决方案。与传统机械键盘或商用宏键盘不同,MultiPad 并非以…...

De Boor算法实战:从理论到B样条曲线点计算的完整实现

1. 从“搭积木”到“画曲线”:为什么你需要De Boor算法? 如果你玩过3D建模、做过动画路径设计,或者搞过机器人轨迹规划,那你肯定遇到过“画一条光滑曲线”这个看似简单、实则让人头疼的问题。直接用直线段连接控制点?太…...

信号与系统 - 从方波到频谱:周期信号傅里叶级数的几何与物理诠释

1. 从方波说起:一个工程直觉的切入点 很多朋友一听到“傅里叶级数”、“频谱”这些词,第一反应可能就是头疼,满眼的积分号和复数,感觉离实际工程应用很远。我刚开始学信号与系统的时候也是这种感觉,直到我遇到了方波这…...

Windows系统下Typora的安装与激活全流程解析

1. 从零开始:为什么选择Typora以及如何获取它 如果你经常需要写点东西,无论是技术文档、学习笔记,还是日常的随笔,那你大概率听说过Markdown。这种用简单符号就能搞定排名的轻量级标记语言,简直是文字工作者的福音。而…...

小学生玩转Arduino---------智能避障小助手

1. 从“倒车指挥员”到“智能避障小助手” 上次我们一起做了一个“倒车指挥员”,用超声波测距器和蜂鸣器模拟了倒车雷达,是不是觉得特别酷?很多小朋友做完之后跑来问我:“老师,这个只能装在‘车’后面吗?能…...

Redis单机多实例部署:从端口隔离到资源优化实战

1. 为什么要在单台机器上跑多个Redis?聊聊我的真实经历 你可能觉得,一台服务器上装一个Redis,让它监听默认的6379端口,这不是天经地义的事情吗?我以前也是这么想的,直到我遇到了下面这些“甜蜜的烦恼”。 最…...

VideoAgentTrek Screen Filter 模型版本管理与回滚策略

VideoAgentTrek Screen Filter 模型版本管理与回滚策略 最近在星图GPU平台上部署VideoAgentTrek Screen Filter模型,遇到了一个挺实际的问题:新版本上线后,效果反而不如老版本稳定,想退回去还挺麻烦。这让我意识到,模…...

Thonny IDE:专为Python初学者设计的轻量级开发环境

1. 为什么说Thonny是Python初学者的“梦中情器”? 如果你刚刚接触编程,面对满屏的代码和复杂的开发工具,是不是感觉有点无从下手?别担心,这种感觉每个程序员都经历过。我刚开始学Python那会儿,光是配置环境…...

基于立创·天猛星MSPM0G3507开发板的电机PID控制实战:编码器测速、定距与曲线显示

基于立创天猛星MSPM0G3507开发板的电机PID控制实战:编码器测速、定距与曲线显示 最近有不少参加电赛或者刚开始学电机控制的朋友问我,PID算法听起来挺复杂,到底怎么在单片机上跑起来,又怎么调参呢?正好,我手…...

突破百度网盘限速壁垒:baidu-wangpan-parse直链解析技术全攻略

突破百度网盘限速壁垒:baidu-wangpan-parse直链解析技术全攻略 【免费下载链接】baidu-wangpan-parse 获取百度网盘分享文件的下载地址 项目地址: https://gitcode.com/gh_mirrors/ba/baidu-wangpan-parse 在数字化协作时代,百度网盘作为国内用户…...

Python flask 大学生运动会管理系统的分析与设计

目录项目技术支持可定制开发之功能创新亮点源码获取详细视频演示 :文章底部获取博主联系方式!同行可合作项目技术支持 前端开发框架:vue.js 数据库 mysql 版本不限 数据库工具:Navicat/SQLyog/ MySQL Workbench等都可以 后端语言框架支持&am…...

Stable Yogi Leather-Dress-Collection实战案例:ACG周边设计师的皮衣风格探索

Stable Yogi Leather-Dress-Collection实战案例:ACG周边设计师的皮衣风格探索 1. 引言:当二次元角色穿上定制皮衣 作为一名ACG周边设计师,你是否曾为笔下角色千篇一律的服装风格而苦恼?或者,在构思新的角色设定时&am…...

突破式重构:GHelper轻量级硬件控制工具的性能优化革命

突破式重构:GHelper轻量级硬件控制工具的性能优化革命 【免费下载链接】g-helper Lightweight Armoury Crate alternative for Asus laptops. Control tool for ROG Zephyrus G14, G15, G16, M16, Flow X13, Flow X16, TUF, Strix, Scar and other models 项目地址…...

自定义字面量实战

1、非修改序列算法这些算法不会改变它们所操作的容器中的元素。1.1 find 和 find_iffind(begin, end, value):查找第一个等于 value 的元素,返回迭代器(未找到返回 end)。find_if(begin, end, predicate):查找第一个满…...

从协议到PCB:PCIe高速硬件设计实战指南

1. 从协议到PCB:为什么PCIe硬件设计是个“瓷器活” 大家好,我是老张,在高速硬件设计这个行当里摸爬滚打了十几年,从早期的PCIe 2.0一路做到现在的PCIe 5.0,踩过的坑比走过的路还多。今天想和大家聊聊一个听起来高大上、…...

从仿真到真机:人形机器人强化学习策略部署实战

1. 从仿真到真机:为什么这一步如此艰难? 在Gazebo里看着自己训练的人形机器人健步如飞,那种成就感别提多爽了。但当你兴冲冲地把模型文件拷出来,准备让实验室那台“铁疙瘩”也动起来时,现实往往会给你当头一棒——机器…...

解析信号构建与瞬时特征提取:希尔伯特变换在Python、C++、MATLAB中的实战

1. 希尔伯特变换:信号处理中的“相位魔法师” 如果你玩过收音机或者调过吉他弦,大概对“频率”和“相位”这两个词不陌生。简单说,频率就是信号抖动的快慢,相位就是抖动起始的“时间点”。在分析一个复杂信号,比如一段…...

Windows系统下Stable Diffusion Web UI的本地部署与远程访问全攻略

1. 为什么要在Windows上自己搭一个AI画室? 如果你最近刷到过那些“一句话生成神图”的视频,心里肯定痒痒的。Midjourney、DALL-E这些在线工具好用是好用,但要么要排队,要么有生成次数限制,最要命的是,你辛辛…...