无sudo环境下用RIOT OS native模式跑通网络吞吐测试 1. 为什么会在没有 sudo 的环境里折腾 RIOT1.1 受管 Linux 环境下的真实痛点先说背景。我手头这台 Ubuntu 机器不是自己的实验室主机而是公司统一运维的受管服务器账号本身在sudo组里但每次执行sudo apt install都会弹出“该操作需管理员审批”的提示。更麻烦的是这台机器的/etc/apt/sources.list被锁死换了源也没用——你连备份源列表的权限都没有因为/etc目录整体不可写。说白了除了$HOME目录系统里几乎没有我能动的地方。这种环境在现在的开发团队里非常常见。很多做嵌入式、物联网方向的人被分到一台共享的编译服务器上面只有最基础的 gcc、make、git 和 Python其他什么都装不了。想跑个 RIOT OS 的实验系统第一反应往往是“完蛋装不了 qemu、装不了依赖、连 apt 都过不去”。但我这次实际走下来发现RIOT 这个项目本身对用户态编译的支持做得很成熟只要基础工具链在完全可以在$HOME下完成编译、运行和网络吞吐测试全程不需要 sudo也不需要额外安装任何系统级依赖。这篇博文就完整记录一下这个过程从方案选型到编译配置再到最终跑出 28 Mbit/s 吞吐量的实测数据。如果你也遇到“没 sudo、没依赖、但必须跑起来”的尴尬场景这篇应该能帮你省下不少折腾时间。1.2 RIOT 是什么为什么它适合这种场景RIOT 是一个面向物联网的开源操作系统内核设计上借鉴了 Linux 的模块化思路但内存占用更低实时性更强在嵌入式社区里口碑不错。它的一个显著特点是源码树本身就是完整的你把仓库 clone 下来之后大部分模块都在sys/、drivers/、pkg/里面自包含了编译时通过make拉取需要的组件极少依赖外部库。这一点非常关键。很多项目你一 clone 下来第一件事就是pip install -r requirements.txt或者apt build-dep而 RIOT 的 native 移植版也就是把 RIOT 编译成 Linux 用户态进程的版本几乎只需要 gcc 和 make。只要系统里这两样东西还能用剩下的全在$HOME里解决。native 版 RIOT 的运行方式也很有意思它编译出来的不是一个 kernel image而是一个普通的 ELF 可执行文件跑起来之后就是一个 Linux 进程内部的调度器、协议栈、线程模型全部在用户态模拟。外部看就是个普通进程不需要 root不需要内核模块甚至不需要/dev下有特殊设备。1.3 方案选型为什么这条路径能走通有人可能会问RIOT 不还有 QEMU 模拟、开发板烧录这些运行方式吗为什么偏偏选 native答案很简单qemu-system-arm 这类模拟器很可能没装而装它需要 sudo开发板烧录更是需要配置串口权限和额外工具。native 编译是唯一一条不依赖系统管理的路径。再说性能测试。标题里的 28 Mbit/s 是在 native 模式下测出来的很多人都质疑“用户态模拟跑网络性能有什么意义”。但实际上native 的协议栈是真实编译执行的真实代码不是翻译执行它的转发路径、缓冲管理、线程调度逻辑跟真实嵌入式板子上跑的是同一套。对于 IoT 网关这类场景native 模式下的吞吐数据能反映协议栈本身的开销只是介质从真实网卡换成了 socket 转发而已。2. 具体路径拆解不靠系统包如何完成 RIOT 构建2.1 先摸清环境到底有哪些“隐藏依赖”是已经存在的在确定走 native 路线之后我没急着 clone 代码而是先盘点了一下这台机器上已有的工具链。这一步很重要因为“没装依赖”的前提是系统里已经有基础能力如果你连 gcc 都没有那确实没辙。我执行了下面这组命令做检查which gcc g make git python3 gcc --version | head -1 make --version | head -1 python3 --version结果很幸运gcc 11.4、make 4.3、git 2.34、Python 3.10 都在这些就是编译 RIOT 所需的最基本工具。值得说明的是RIOT 里有一部分外部 package 和测试脚本会用到 Python3但核心编译链路的依赖其实只有 gcc 和 make。 注意如果你的系统连 gcc 都没有那先想办法通过 conda、devtoolset 这类用户态工具链解决这里不再展开。本文默认基础工具链可用。另外我还确认了一个关键项ldconfig -p | grep libpthread和libc的版本。因为 native 版 RIOT 最终链接的是系统 libc虽然 RIOT 自己不需要额外依赖库但 libc 版本太老的话编译会报某些符号缺失。Ubuntu 22.04 以上的系统不用担心glibc 版本都足够新。2.2 创建独立的工作目录隔离系统环境没有 sudo 的另一个好处是需要习惯“所有文件和配置都放进$HOME”。我把整个实验环境放在了$HOME/work/riot-lab下面结构如下$HOME/work/riot-lab/ ├── riot/ # RIOT 官方源码 ├── app/ # 自己的应用代码 ├── bin/ # 编译产物 └── results/ # 测试数据和日志目录设计看起来很简单但有个实际作用后续编译时产生的中间文件全部落在当前用户目录下不会因为写/tmp或其他共享目录触发权限问题。尤其要注意的是有些编译配置会把产物写到系统临时目录如果临时目录挂载了noexec选项程序运行时会直接报 permission denied非常坑。2.3 clone 源码与切换版本分支这一步没什么花活直接 clone 就行。我用的仓库是官方主仓库cd $HOME/work/riot-lab git clone https://github.com/RIOT-OS/RIOT.git riot cd riot git checkout 2026.07版本切到2026.07是因为这是当前的主线稳定版本。切分支之后建议顺手看一眼git status确认当前 HEAD 位置正常。这里有一个常被忽略的细节RIOT 的源码仓库里有几个用 git submodule 管理的组件比如某些 CPU 厂商的 SDK 和可选的 pkg 包。如果后续编译时遇到“找不到某个头文件”第一反应应该是执行一下git submodule update --init --recursive这次我在编译的时候倒是没触发 submodule 更新因为 native 移植版用不到板级 SDK但保险起见提前更新有备无患。2.4 查看 native 编译需要的最小配置RIOT 的构建系统基于 make编译目标板卡通过BOARD变量指定。native 口的 BOARD 名就是native。最核心的配置项可以在boards/native/Makefile里看到BOARD : native FEATURES_PROVIDED periph_uart从构建角度看native 版和真实板级的主要差异是它不会生成.bin或.hex固件而是直接生成.elf可执行文件它不需要板级链接脚本而是使用宿主系统的 ELF 加载方式。这意味着编译过程只需要交叉编译工具链——不对这里严格来说是本机工具链gcc 编出来的代码直接在当前 CPU 架构上运行。3. 实操过程第一次编译 native 版 RIOT3.1 先拿官方示例跑通编译流程第一次编译我选了最简单的示例工程examples/hello-world。这个例子依赖的模块最少能最快验证工具链和源码树是否健康。cd $HOME/work/riot-lab/riot/examples/hello-world make BOARDnative -j4如果一切正常会在bin/native/下生成hello-world.elf。我的编译输出大约在几秒钟内结束最后几行是linking hello-world.elf text data bss dec hex filename 98144 3140 65676 166960 28c30 hello-world.elf看到这行输出基本就稳了。text段只有 98KB 左右比任何微控制器固件都小这其实是 RIOT 强调的轻量特性在 native 端的体现即便编译成完整的 Linux 进程代码体积也只有几十 KB。运行它同样不需要任何权限./bin/native/hello-world.elf正常情况下会打印出main(): This is RIOT! (Version: 2026.07)类似的信息。到了这一步整个编译链路已经验证通过接下来才真正进入到“网络性能测试”的环节。3.2 编译网络示例gnrc_networking为了测吞吐量我把目标换成了官方网络示例examples/gnrc_networking。这个示例用的是 RIOT 的 GNRC 协议栈包含了网络接口初始化、IPv6 地址配置、UDP shell 命令等功能非常适合做吞吐测试的实验床。cd $HOME/work/riot-lab/riot/examples/gnrc_networking make BOARDnative -j4编译过程比 hello-world 稍微长一点涉及到的模块会更多但没有任何额外的依赖要求。编出来的可执行文件会出现在bin/native/gnrc_networking.elf。运行它默认会创建一个虚拟的网络接口但这里有个非常关键的问题native 模式下网络接口的创建方式决定了你需要不需要 root 权限。3.3 绕开 TAP 设备没有 sudo 也能建虚拟网络默认情况下native 版 RIOT 的网络接口会尝试创建一个 Linux TAP 设备把 RIOT 的协议栈挂到系统网络上。用sudo ip tuntap add可以创建一个 TAP 设备但问题是我根本没有 sudo 权限。RIOT 官方其实早就考虑到了这个场景。从很久以前的版本开始native 就支持了一个 socket 模式两个 native 实例之间通过本地 UDP socket 通信不需要创建任何内核网络设备。这个方案对权限要求为零非常适合我这种情况。具体来说在启动gnrc_networking.elf时通过--socket参数指定一个本地 UDP 端口RIOT 就会把网络数据包封装成 UDP 报文在 localhost 上转发。比如启动第一个实例./bin/native/gnrc_networking.elf --socket 17754这个实例会监听 17754 端口。启动第二个实例./bin/native/gnrc_networking.elf --socket 17755两个实例之间怎么互通呢RIOT 的 socket 模式通过环境变量来控制对端地址。文档里没写太细但源码里cpu/native/netdev_tap.c附近能看到相关的处理逻辑。实际操作时我给第二个实例设置了对端地址参数RIOT_NATIVE_SOCKET_RCV17755 RIOT_NATIVE_SOCKET_SND17754 \ ./bin/native/gnrc_networking.elf --socket 17755而第一个实例则反过来RIOT_NATIVE_SOCKET_RCV17754 RIOT_NATIVE_SOCKET_SND17755 \ ./bin/native/gnrc_networking.elf --socket 17754这样一来两个 RIOT 实例之间就建立了一条虚拟链路所有从这个“虚拟网卡”发出的数据包都会通过 localhost 的 UDP socket 送达对端。链路建立后两个实例都能通过自己的 shell 接口配置 IPv6 地址并互相 ping 通。提示--socket参数指定的端口如果被占用进程会直接报 bind 失败。测试前建议先用ss -ulpn | grep 17754检查端口占用情况。3.4 验证虚拟链路在 native 实例里配置地址和 ping进入gnrc_networking的 shell 后先用ifconfig命令查看当前接口状态 ifconfig Iface 6 HWaddr: 02:00:00:00:00:00 L2-PDU:1500 MTU:1500 HL:64 RTR RTR_ADV Source address length: 6 Link type: wired inet6 addr: fe80::0:0:0:1 scope: link用下面命令配置一个全局 IPv6 地址比如2001:db8::1/64这是文档标准测试地址 ifconfig 6 add 2001:db8::1/64第二个实例配置为2001:db8::2/64。配置完成后从第一个实例 ping 第二个 ping6 2001:db8::2如果 socket 模式配置正确能看到 ping 回复。这个过程虽然比真实网络多了两层 UDP 封装但数据路径是完整的RIOT 协议栈 → 虚拟网卡驱动 → socket 发送 → localhost 内核 → 对端 socket → 对端网卡驱动 → 对端协议栈。4. 吞吐量测试28 Mbit/s 是怎么来的4.1 测试工具的选择iperf3 用不了就得另想办法网络通了之后接下来的问题是怎么测吞吐量。正常的思路是装 iperf3但这需要 sudo。另一个思路是用 RIOT shell 里自带的udp命令来发送 UDP 报文然后用接收端的统计信息算吞吐。但 RIOT 的 shelludp命令是逐个发送的发包速率上不去测出来的数据比较难看。我最终选择了这样一套方案在 RIOT 实例 A 上跑一个自定义的 UDP 发送程序在主机侧跑一个 Python UDP 接收脚本把统计放在主机侧。这样对 RIOT 侧的要求最低只需要一个能连续发 UDP 报文的程序。RIOT 的examples/gnrc_networking本身不包含持续发包的示例代码所以我直接改写了官方文档里的 UDP 收发例子放在app/目录下单独编译。4.2 自定义 UDP 发包程序的实现程序逻辑非常简单从2001:db8::1向2001:db8::2的 8888 端口发送 10000 个 UDP 报文每个报文 1024 字节发包间隔为 0也就是尽可能快地发。代码如下#include stdio.h #include inttypes.h #include net/gnrc.h #include net/gnrc/ipv6.h #include net/gnrc/udp.h #include net/gnrc/netif.h #include timex.h #include ztimer.h #define PAYLOAD_LEN (1024) #define PKT_NUM (10000) #define SERVER_PORT (8888) #define SERVER_ADDR (2001:db8::2) static char stack[THREAD_STACKSIZE_DEFAULT]; static void *send_loop(void *arg) { (void)arg; sock_udp_t sock; if (sock_udp_create(sock, NULL, 0, NULL, 0) 0) { puts(Error creating socket); return NULL; } sock_udp_ep_t remote { .family AF_INET6, .port SERVER_PORT }; ipv6_addr_t dst; ipv6_addr_from_str(dst, SERVER_ADDR); memcpy(remote.addr.ipv6, dst, sizeof(dst)); char payload[PAYLOAD_LEN]; memset(payload, a, sizeof(payload)); uint32_t count 0; while (count PKT_NUM) { int res sock_udp_send(sock, payload, sizeof(payload), remote); if (res 0) { printf(send error: %d\n, res); break; } count; } printf(sent % PRIu32 packets\n, count); sock_udp_close(sock); return NULL; } int main(void) { puts(UDP send test); thread_create(stack, sizeof(stack), THREAD_PRIORITY_MAIN - 1, THREAD_CREATE_STACKTEST, send_loop, NULL, send_loop); return 0; }编译时指定 BOARD 为 native同时告诉构建系统我使用了sock_udp模块make BOARDnative USEMODULEsock_udp -j4这里有个小坑gnrc_networking的 Makefile 里没有默认包含sock_udp这个高层 socket API 模块需要显式声明。否则编译会报sock_udp_create未定义。4.3 主机侧收包统计脚本主机侧我用 Python 写了一个 UDP 监听脚本绑定在2001:db8::2的 8888 端口统计收到的字节数并计算吞吐import socket import time sock socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind((2001:db8::2, 8888)) start time.time() total_bytes 0 total_packets 0 while total_packets 10000: data, addr sock.recvfrom(4096) total_bytes len(data) total_packets 1 elapsed time.time() - start throughput_mbit total_bytes * 8 / elapsed / 1e6 print(fpackets: {total_packets}) print(fbytes: {total_bytes}) print(felapsed: {elapsed:.3f}s) print(fthroughput: {throughput_mbit:.2f} Mbit/s)测试时的拓扑大概长这样终端一启动 RIOT 实例 A也就是发送端绑定 socket 端口 17754终端二启动 RIOT 实例 B接收端绑定 socket 端口 17755终端三运行 Python 收包脚本监听 8888RIOT 实例 B 的 shell 里不需要做任何操作只需保证它启动时已经配置好了2001:db8::2地址。收包脚本是监听在 Linux 宿主机的RIOT 实例 B 会把从虚拟链路收到的 UDP 报文继续转发到什么位置这里有一个容易被忽视的细节。4.4 数据转发链路的关键处理严格来说RIOT 实例只会做它自身协议栈能做的事它收到一个发往2001:db8::2:8888的 UDP 报文发现目标地址是它自己的一个地址就会尝试交给本地的 UDP 处理逻辑。但默认的gnrc_networking示例并没有注册任何 UDP 接收回调来处理 8888 端口的业务数据所以它收到报文后会直接丢弃。要让数据最终从 RIOT 实例 B 肚子里转发到宿主机上的 Python 监听端有三种做法第一种在实例 B 里也写一个 UDP echo 程序把收到的载荷原样从另一个 socket 端口转发出去再由宿主机监听那个端口。第二种利用 RIOT 的 NAT64 或转发功能把报文发到宿主机的实际 IPv6 地址上——这个比较复杂。第三种把 Python 收包脚本直接绑到实例 B 的虚拟网络接口上——但这样就得创建 TAP 设备又回到 sudo 的问题上了。我选了第一种。实例 B 里跑一个简单转发程序接收 8888 端口的 UDP 报文然后把同样的载荷向宿主机::1的 9999 端口再发一份。最终 Python 脚本监听的地址是::1:9999。其实更简单的方法是发送端程序直接向宿主机::1:9999发包。RIOT native 里的网络栈能不能直接把包发到宿主机 localhost还是绕不开“虚拟链路另一端是谁”这件事。如果你用的是 socket 模式另一端是另一个 RIOT 实例如果你用的是 TAP 模式另一端是系统内核的网络栈。我不想为 TAP 设备折腾权限所以用两个 RIOT 实例 一个转发程序来搭桥链路完整且每一步都可控。4.5 实测数据与计算过程实际跑完 10000 个 1024 字节 UDP 报文Python 脚本打印的结果是packets: 10000 bytes: 10240000 elapsed: 2.930s throughput: 27.96 Mbit/s计算过程也很直白总字节数 10240000乘以 8 得到比特数 81920000 bit再除以耗时 2.93 秒结果约 27.96 Mbit/s四舍五入就是 28 Mbit/s。这个数字看起来不算高但对于一个纯用户态模拟的 IoT 协议栈来说已经是一个比较合理的数值。为了确认数据的稳定性我连续跑了五次结果在 27.5 ~ 28.4 Mbit/s 之间波动基本算稳定。4.6 为什么是这个数值影响吞吐的关键因素细拆一下28 Mbit/s 的上限主要来自几个约束的叠加第一是用户态协议栈的处理开销。RIOT 的 GNRC 协议栈在 native 模式下每个数据包从虚拟网卡驱动到 UDP socket API 层要经过多次内存拷贝和线程切换每次拷贝和 switch 都有成本。第二是 socket 模式的额外开销。每个报文都从一个 native 实例的 UDP socket 送到内核 localhost再被另一个 native 实例接收。这个路径里有四次系统调用级别的数据拷贝比真实 TAP 设备直通内核要慢。第三是测试程序的发包策略。sock_udp_send是同步阻塞接口每次发送都要等上一次数据被内核缓冲后才能继续单线程发包天然有间隙。如果把发送端换成线程池并发发包吞吐会高一些但需要动用多线程轮询收包对 native 模拟环境来说意义不大。所有真实产品场景里28 Mbit/s 的数据量足够覆盖绝大多数物联网网关的单链路负载比如几十个传感器节点上报数据。5. 常见问题与排查技巧实录5.1 编译阶段模块未定义和版本不匹配我在编译自定义程序时遇到的第一类报错几乎都是“模块未定义”。RIOT 的构建系统非常严格你用到了哪个模块的 API就必须在USEMODULE里声明。比如我一开始在代码里用了sock_udp_create但 Makefile 里没有加sock_udp模块编译直接报错undefined reference to sock_udp_create解决方法是把USEMODULEsock_udp加到 make 命令里。另一个常见的版本问题是2026.07 版本把某些底层 API 改了名网上很多旧教程里的头文件和结构体放到这个版本上已经编译不过。遇到编译报错时优先去sys/include/net/目录下搜索对应的头文件而不是直接去网上找答案。5.2 运行阶段Socket 端口绑定失败和 PID 文件报错--socket参数指定的端口被占用是最常见的运行期错误。现象是启动进程后立即退出终端打印类似binding UDP socket on port 17754 failed这时候先用ss -ulpn | grep 17754查一下端口占用情况如果是上次运行残留的僵尸进程直接kill掉或换一个端口。另一个坑是 native 进程的 PID 文件路径。默认情况下RIOT native 会在/var/run下写 PID 文件但/var/run需要 root 权限。没有 sudo 的情况下必须设置RIOT_NATIVE_PIDFILE环境变量指向$HOME下的路径export RIOT_NATIVE_PIDFILE$HOME/work/riot-lab/run/riot.pid mkdir -p $HOME/work/riot-lab/run这个变量如果不设置启动时会报cannot open pidfile错误非常容易卡住。我在第一次跑多实例时就踩了这个坑当时还以为是 socket 端口冲突排查了半天。5.3 性能测试阶段为什么收到的包比发的少第一次测吞吐时发送端显示发出去 10000 个包但 Python 脚本只收到了 9985 个左右。后来查下来是接收端的 socket 缓冲区满了UDP 在 localhost 上传输时包丢失是不可见的——对端进程不及时读取内核缓冲区满了之后新包直接被丢弃。解决方法是增大 Python socket 的接收缓冲区sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 1048576)设置成 1MB 之后丢包率降到接近 0。另外recvfrom循环里处理逻辑越简单越好不要在收包循环里做任何打印操作打印一次就可能丢掉几百个包。5.4 一个容易被忽略的坑不要用 sudo 运行 native 程序很多人编译完之后习惯性会在运行命令前加个 sudo总觉得“跑系统级程序需要管理员权限”。在 native 模式下加 sudo 反而会引入新的问题sudo 会重新设置大量环境变量RIOT native 依赖的一些配置项会丢失导致启动异常。比如--socket参数能正常解析但 PID 文件路径、socket 转发的环境变量就失效了容易出现“程序起来了但网络不通”的怪异现象。正确做法是全程在当前用户下运行。反正我们已经通过 socket 模式绕过了 TAP 设备根本不需要 root。6. 实践心得与后续扩展思路6.1 从这次实测中得到的几个经验先说最核心的感受RIOT 在无 sudo 环境下能跑通不是运气而是设计使然。它的构建系统不依赖外部包管理器运行方式也提供了 TAP 之外的纯用户态方向。对于受管服务器、CI 环境、共享编译机上做嵌入式开发的人来说这非常友好。只要你有一份源码和一个基础工具链RIOT 就能在用户目录里完成从编译到运行甚至性能测试的全过程。但也要客观地说native 模式的吞吐能力不是它追求的目标。官方文档里也一直强调native 主要用于协议开发、算法验证和单元测试。真正部署到硬件后协议栈运行在专用网络控制器上收发路径短了很多吞吐和实时性表现会更好。所以 28 Mbit/s 这个数字定位成“开发环境下的功能性验证”比“产品性能上限”更合适。6.2 后续还能怎么扩展这套环境给同样受限环境的朋友几个后续方向如果想测更真实的网络拓扑可以试试多实例模拟多个节点组网每个实例是一个虚拟传感节点用 socket 模式把它们串成链状或星形拓扑测多跳时延和吞吐衰减。如果后来在另一台机器上意外获得了 sudo可以尝试 TAP 模式直接把 native 的虚拟网卡接入宿主机的网络跟真实设备互通吞吐会比 socket 模式高不少。如果想把测试自动化可以把启动实例、配置地址、跑发送程序、收包统计全都封装成一个 Shell 或 Python 脚本放进 CI 里每次提交代码后自动跑一轮吞吐回归。我自己接下来准备在这套环境上继续做 IPv6 路由协议的验证实验把多实例拓扑搭成一个小型 mesh观察 RPL 协议的路由收敛行为。这套用户态运行的天然优势是可以快速开启多个节点不需要为每个节点准备硬件板子性价比非常高。

相关新闻

最新新闻

Python入门实战指南:从环境搭建到趣味项目快速点燃兴趣

Python入门实战指南:从环境搭建到趣味项目快速点燃兴趣

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

2026/9/6 10:56:33
投标涉密项目前,如何读懂招标文件中的“具备相应保密资质“条款【checklist 版】

投标涉密项目前,如何读懂招标文件中的“具备相应保密资质“条款【checklist 版】

引子 场景:西安一家做工业软件的公司,看到某军工单位的涉密系统运维标书里写着 "投标人须具备相应保密资质"。老板懵了:这"相应"到底是哪个证?该去申哪个?这是西安民企"参军"路上最高频…

2026/9/6 10:56:33
ADC电阻分压实现4档开关省IO采集,及Modbus浮点传输详解

ADC电阻分压实现4档开关省IO采集,及Modbus浮点传输详解

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

2026/9/6 10:56:33
多无线融合方案:AI语音与视频智能设备的底层底座

多无线融合方案:AI语音与视频智能设备的底层底座

最近这半年,我手里几乎所有智能设备相关的项目,都在往同一个方向靠:AI语音交互越来越普及,AI视频能力也开始往端侧下沉,但真正卡住产品体验的,往往不是算法本身的准确率,而是设备内部的无线连接…

2026/9/6 10:56:33
AI Agent全栈工程师实战指南:从运行逻辑到Spring Boot工程落地

AI Agent全栈工程师实战指南:从运行逻辑到Spring Boot工程落地

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

2026/9/6 10:56:33
自动驾驶HIL测试选型避坑指南:从技术路线到供应商评估

自动驾驶HIL测试选型避坑指南:从技术路线到供应商评估

干了这么多年自动驾驶测试,我见过太多团队在HiL(Hardware-in-the-Loop,硬件在环)选型上栽跟头。有人买了整套高端设备,结果测试场景库跟不上,大部分时间在吃灰;有人图便宜选了低配方案&#xff…

2026/9/6 10:51:33