CANUAV C源码解析:UAVCAN协议栈实现与嵌入式避坑指南

简介:CANUAV是一套基于C语言实现的UAVCAN协议栈源码,面向在STM32F407平台上开发无人机及自动化设备通信节点的嵌入式工程师。它依托轻量级Canard库完成UAVCAN核心功能,将传感器数据、控制指令、状态报告等消息映射到CAN帧,并实现心跳与节点管理,帮助开发者快速构建可靠的分布式通信网络。资源包共36个文件,约103KB,以h头文件与c、cpp源文件为主体,辅以md说明文档、txt配置、py脚本及yml持续集成配置,涵盖canard核心、stm32与socketcan驱动、测试用例等模块,目录结构清晰,便于按平台裁剪移植。目前已有949人学习下载。读者可从中获得完整的协议栈实现、消息编解码与内存分配参考、CRC及浮点编码测试样例,以及STM32 CAN控制器配置与Canard库编译链接的实践思路,适合具备一定嵌入式与CAN总线基础的中高级开发者参考复用。

1. 从一份 CANUAV 的 C 源码说起:为什么飞控通信层值得单独拆一遍

如果你正在做无人机、机器人或者任何多节点嵌入式系统,大概率绕不开 CAN 总线。而 UAVCAN(现在官方叫 Cyphal,但老工程师还是习惯叫 UAVCAN)就是架在 CAN 之上的一套高层协议,解决的是“多个节点怎么自动分配 ID、怎么交换遥测、怎么做固件升级”这类问题。我手里这份 CANUAV 的 C 源码,就是一个把 UAVCAN 协议栈用纯 C 实现的 git 仓库,没有 RTOS 绑定,没有 HAL 依赖,核心就是协议编解码、传输层状态机和节点管理。它适合谁?适合那些不想直接啃 libcanard 源码、想先拿一个能编译能跑的最小实现来理解 UAVCAN 帧格式和状态流转的人。我第一次拿到它的时候,最直接的感受是:终于不用在几千行的协议栈里找“心跳包到底怎么发”了。

2. 拆开 CANUAV 的 C 源码:目录结构、编译链路与最小可跑节点

2.1 仓库里到底放了什么:从 include 到 examples 的职责划分

这份源码的目录结构很典型,没有花哨的构建系统,就是 Makefile 加几个文件夹。我拿到手先跑了一遍tree,核心目录大概是这样的:

# 假设你已经把仓库克隆到本地 cd canuav-c tree -L 2 -I '.git'

输出里你会看到几个关键目录:include/放的是协议头文件,比如uavcan_protocol.h、can_frame.h;src/是协议栈实现,包括can_driver.c(抽象驱动层)、transfer.c(传输层)、node.c(节点状态机);examples/下面通常有一个simple_node.c和一个heartbeat_demo.c。我一般会先看examples/里的 Makefile,因为它决定了你需不需要改编译器选项。

# examples/Makefile 片段(我根据常见实现补全的典型写法) CC ?= gcc CFLAGS += -I../include -I../src -Wall -Wextra -std=c99 LDFLAGS += -lm TARGET = simple_node SRCS = simple_node.c ../src/can_driver.c ../src/transfer.c ../src/node.c $(TARGET): $(SRCS) $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) clean: rm -f $(TARGET)

这段 Makefile 的逻辑很直白:把src/下的协议实现和示例代码一起编译,不依赖外部库。参数上要注意-std=c99,因为源码里用了不少stdint.h的定长类型和复合字面量,用 gnu89 可能会报错。-lm是给 CRC 校验里可能用到的数学函数留的,虽然大多数情况用不上,但加上不亏。

2.2 编译前必须确认的三件事:CAN 驱动、时钟与内存对齐

在make之前,我血泪经验是先把这三件事确认一遍,否则编译过了也跑不起来。

第一,CAN 驱动抽象层。can_driver.c里通常定义了一组函数指针,比如can_send()、can_recv()、can_filter_set()。源码里给的是空实现或者 Linux SocketCAN 的示例。如果你用的是 STM32 的 bxCAN 或者 MCP2515,需要自己填这三个函数。我一般会先写一个回环测试,不接真实总线,只让can_send把数据打印出来。

// 一个最小的回环驱动实现,用于验证协议栈逻辑 static int loopback_send(const CanFrame *frame) { // 参数说明:frame->id 是 29 位扩展 ID,frame->data 是 8 字节负载 printf("TX ID=0x%08X DLC=%d DATA=", frame->id, frame->dlc); for (int i = 0; i < frame->dlc; i++) { printf("%02X ", frame->data[i]); } printf("\n"); return 0; // 返回 0 表示发送成功 }

第二,系统时钟。UAVCAN 的传输层依赖时间戳做超时重传和状态切换。源码里通常有一个get_monotonic_ms()的弱符号或者宏,默认返回 0。如果你不实现它,节点会一直认为时间没走,心跳包永远不发。常见做法是用SysTick或者clock_gettime(CLOCK_MONOTONIC)填上。

第三,内存对齐。CAN 帧结构体里如果有uint8_t data[8]和uint32_t id,在 32 位 MCU 上默认对齐是 4 字节,但 CAN 控制器 DMA 可能要求 1 字节对齐。我踩过的坑是:结构体大小从 12 字节变成 16 字节,DMA 搬运时多读了 4 个字节的垃圾。解决办法是用__attribute__((packed))或者手动序列化。

2.3 跑通第一个心跳包:从 main 到总线上的实际波形

编译通过后,先别急着接电机。我一般会跑heartbeat_demo,因为它只做一件事:让节点以 1Hz 频率往总线上发心跳。代码大概长这样:

// heartbeat_demo.c 的核心逻辑 #include "uavcan_protocol.h" #include "node.h" int main(void) { // 初始化节点,参数:节点 ID、优先级、回调表 UavcanNode node; uavcan_node_init(&node, 42, 16, NULL); // 42 是临时节点 ID // 注册心跳发送任务,周期 1000ms uavcan_node_set_heartbeat_interval(&node, 1000); while (1) { // 驱动协议栈状态机,内部会处理时间戳和发送 uavcan_node_spin_once(&node); // 模拟 1ms 延时,实际项目里用 RTOS 延时或定时器 delay_ms(1); } }

逻辑说明:uavcan_node_init的第三个参数是回调表,心跳 demo 不需要接收,所以传 NULL。uavcan_node_spin_once是核心,它检查当前时间是否超过下一次心跳的截止时间,如果是就调用can_send发一帧。参数上,节点 ID 42 是随意选的,但注意 UAVCAN 规定 1~127 是有效动态 ID 范围,0 是匿名,128 以上保留。如果你接上真实 CAN 分析仪,应该能看到 ID 为0x1F0A2A00之类的扩展帧,具体取决于节点 ID 和消息类型。

提示:如果你用 Linux SocketCAN 跑,记得先ip link set can0 up type can bitrate 1000000,UAVCAN 默认 1Mbps,但很多工控现场用 500k,改can_driver.c里的波特率配置就行。

3. 传输层状态机与消息编解码:把 CAN 帧拼成完整报文

3.1 单帧与多帧传输:什么时候用 TA 什么时候用 TS

UAVCAN 的传输层有两种模式:单帧传输(Single Frame)和多帧传输(Multi Frame)。单帧就是所有数据塞进一个 CAN 帧的 8 字节里,适合心跳、状态这种小消息。多帧需要拆分,用首帧(Start of Transfer)和后续帧(Toggle)拼起来。源码里transfer.c的核心是一个状态机,我把它简化成下面这张表:

传输类型CAN ID 标志位数据长度典型消息
单帧bit 7 = 0≤ 7 字节心跳、节点状态
多帧首帧bit 7 = 1, bit 6 = 18 字节固件升级、日志
多帧后续帧bit 7 = 1, bit 6 = 08 字节同上
匿名帧特殊 ID8 字节动态节点 ID 分配

我一开始没注意 bit 7 和 bit 6 的区别,直接把所有帧都当单帧处理,结果发固件升级包的时候,接收端只收到第一个字节。后来翻transfer.c才发现,多帧的 CRC 校验和序列号都在首帧里,后续帧只带数据。常见做法是:发送前先算好消息长度,如果超过 7 字节就自动切多帧模式,不要手动拼。

3.2 编解码实战:用 C 结构体映射 UAVCAN 消息

UAVCAN 的消息定义是 DSL 描述的,但 C 源码里通常用结构体加位域来映射。比如一个NodeStatus消息,源码里可能是这样:

// 根据 UAVCAN 规范补全的典型结构体 typedef struct { uint32_t uptime_sec; // 运行时间,秒 uint8_t health; // 健康状态:0=OK, 1=Warning, 2=Error, 3=Critical uint8_t mode; // 模式:0=Operational, 1=Initialization, 2=Maintenance uint16_t vendor_specific; // 厂商自定义状态码 } NodeStatus; // 编码函数:把结构体序列化成 CAN 负载 int encode_node_status(const NodeStatus *msg, uint8_t *out_buf) { // 参数说明:out_buf 至少 7 字节,返回实际写入长度 out_buf[0] = msg->uptime_sec & 0xFF; out_buf[1] = (msg->uptime_sec >> 8) & 0xFF; out_buf[2] = (msg->uptime_sec >> 16) & 0xFF; out_buf[3] = (msg->uptime_sec >> 24) & 0xFF; out_buf[4] = msg->health & 0x03; out_buf[5] = msg->mode & 0x07; out_buf[6] = msg->vendor_specific & 0xFF; out_buf[7] = (msg->vendor_specific >> 8) & 0xFF; return 8; // 实际 UAVCAN 里 NodeStatus 是 7 字节,这里演示用 8 }

逻辑说明:UAVCAN 是小端字节序,所以uptime_sec先放低字节。health和mode是位域,只占几个 bit,但为了简单,我直接按字节存。参数上要注意:out_buf的长度必须至少等于消息的序列化长度,否则会越界。我一般会在函数开头加assert(out_buf != NULL),但嵌入式里 assert 可能被关掉,所以更稳妥的是返回错误码。

3.3 动态节点 ID 分配:匿名帧与冲突解决

UAVCAN 最实用的功能之一是动态节点 ID 分配。新节点上电后没有 ID,它会发一个匿名帧,里面带一个 128 位的唯一 ID(通常是 MCU 的 UID)。分配器收到后,回一个包含分配结果的帧。源码里node.c的uavcan_node_allocate_id()就是干这个的。我实测时发现,如果两个节点同时上电,匿名帧会冲突,这时候协议栈会随机退避重试。常见做法是:在can_driver.c里开启接收过滤,只收匿名帧和广播帧,减少 CPU 占用。

// 动态 ID 分配的回调示例 void on_id_allocated(uint8_t new_id, void *user_data) { UavcanNode *node = (UavcanNode *)user_data; node->node_id = new_id; printf("Node ID allocated: %d\n", new_id); // 参数说明:new_id 范围 1~127,0 表示失败 if (new_id == 0) { // 分配失败,通常是因为总线太忙或 UID 冲突 // 常见做法是延时 1 秒后重新发起分配 } }

注意:动态分配需要总线上的分配器节点在线,如果你只有一个节点,直接写死 ID 更省事。我见过有人为了动态分配专门跑一个树莓派当分配器,结果树莓派一挂,整个总线都瘫了。

4. 避坑与排查:CANUAV 源码在真实硬件上的五个翻车现场

4.1 现象:编译通过但总线无波形;原因:驱动函数没注册;解决:检查函数指针表

我第一次把源码移植到某款 Cortex-M4 开发板时,make成功,烧录后示波器上一点波形都没有。查了半天发现can_driver.c里有一个static const CanDriverOps ops = { .send = NULL, .recv = NULL };,我忘了在main里调用can_driver_register(&ops)。源码默认是空实现,不会报错,但也不会发数据。解决办法:在初始化节点之前,先注册驱动,并确保send函数返回 0。

4.2 现象:心跳包周期忽快忽慢;原因:时间戳溢出;解决:用 64 位单调时钟

UAVCAN 的心跳周期依赖get_monotonic_ms()。我一开始用HAL_GetTick(),它返回 32 位毫秒,大概 49 天溢出。溢出后时间戳回绕,协议栈会认为超时,疯狂发心跳。现象就是总线负载突然飙升。解决办法:把时间戳改成 64 位,或者用SysTick的周期计数自己累加。源码里如果用的是uint32_t,记得在比较时处理回绕,比如(now - last) > interval这种写法。

4.3 现象:多帧传输丢包;原因:接收缓冲区太小;解决:调整CAN_RX_BUFFER_SIZE

transfer.c里通常有一个接收缓冲区,默认可能是 8 或者 16 帧。如果你发一个 256 字节的固件包,需要 32 个后续帧,缓冲区不够就会丢。我踩过的坑是:发送端说发完了,接收端只收到一半。解决办法:在uavcan_protocol.h里把CAN_RX_BUFFER_SIZE改成 64 或者更大,同时确保你的 CAN 控制器接收 FIFO 也够深。常见做法是:接收中断里只做搬运,协议栈解析放到主循环。

4.4 现象:节点 ID 冲突导致总线仲裁失败;原因:两个节点用了同一个 ID;解决:开启动态分配或加冲突检测

有一次实验室里两个节点都写死了 ID 10,上电后总线一直报错。CAN 的仲裁机制会让 ID 小的优先,但两个相同 ID 的节点同时发数据,就会互相干扰。现象是:接收端收到一堆错误帧,或者数据错乱。解决办法:要么用动态分配,要么在代码里加一个“ID 占用检测”——上电后先监听总线,如果发现自己的 ID 已经在用,就换一个。源码里node.c有uavcan_node_check_id_conflict()的弱实现,需要自己填。

4.5 现象:CRC 校验总是失败;原因:字节序搞反了;解决:统一小端并检查位域打包

UAVCAN 的 CRC 是 CRC-16-CCITT,计算范围包括消息头和负载。我一开始把uint16_t直接 memcpy 到缓冲区,结果在大端 MCU 上 CRC 对不上。现象是:接收端能收到帧,但校验失败,直接丢弃。解决办法:所有多字节字段手动按小端写入,不要依赖编译器。另外,位域结构体在不同编译器下布局可能不同,我一般用#pragma pack(1)或者手动移位。

5. 进阶技巧:用 CANUAV 源码做协议模糊测试与总线负载估算

5.1 把源码改造成模糊测试目标:随机帧注入与状态机覆盖

这份源码的另一个价值是:它足够小,可以拿来做模糊测试。我一般会把can_driver.c的recv函数替换成一个随机帧生成器,然后跑几万次,看协议栈会不会崩溃或者进入死循环。具体做法是:写一个fuzz_driver.c,在recv里返回随机 ID 和随机数据,然后编译一个带-fsanitize=address的版本。

// fuzz_driver.c 片段:随机注入 CAN 帧 static int fuzz_recv(CanFrame *frame) { static uint32_t seed = 12345; seed = seed * 1103515245 + 12345; frame->id = (seed >> 16) & 0x1FFFFFFF; // 29 位扩展 ID frame->dlc = (seed >> 8) & 0x0F; if (frame->dlc > 8) frame->dlc = 8; for (int i = 0; i < frame->dlc; i++) { frame->data[i] = (seed >> (i * 4)) & 0xFF; } return 0; }

逻辑说明:这个函数每次被调用都生成一个伪随机帧,ID 和 DLC 都在合法范围内。参数上,seed用线性同余,保证可复现。跑的时候用while(1) uavcan_node_spin_once(&node);,如果协议栈有数组越界或者空指针,AddressSanitizer 会直接报出来。我实测跑过 10 万次,发现了一个transfer.c里对dlc > 8没做检查的 bug,后来在recv入口加了if (frame->dlc > 8) return -1;。

5.2 总线负载估算:用源码里的计数器算理论占用率

UAVCAN 总线负载率是无人机现场调试的常见指标。源码里通常有一个uavcan_node_get_stats()函数,返回发送和接收的帧数。我一般会在spin_once里加一个计数器,每秒统计一次,然后按下面的公式算:

参数含义典型值
frames_per_sec每秒帧数心跳 1 + 状态 10 + 日志 5 = 16
bits_per_frame每帧位数扩展帧约 128 位(含填充)
bus_bitrate总线波特率1000000 bps
load负载率frames_per_sec * bits_per_frame / bus_bitrate

按上表算,16 帧/秒 × 128 位 = 2048 位/秒,除以 1M 就是 0.2%。但如果加上固件升级,负载会飙到 50% 以上。我习惯在代码里加一个软限制:如果负载超过 70%,就降低日志发送频率。源码里没有现成的限流逻辑,需要自己在spin_once里加。

5.3 从源码到产品:我一般会保留的三个调试钩子

最后说一个习惯:不管项目多赶,我都会在 CANUAV 源码里保留三个钩子。第一个是can_driver.c里的can_send入口加一行if (debug_mask & DEBUG_TX) printf(...),方便看实际发出的帧。第二个是transfer.c里的 CRC 校验失败处加计数器,超过阈值就报警。第三个是node.c里的状态切换处加日志,记录节点从 Initialization 到 Operational 的耗时。这三个钩子占不了多少 Flash,但能省下大量示波器和 CAN 分析仪的时间。从那以后我每次移植新的 CAN 协议栈,都强制走一遍回环测试、动态 ID 分配、多帧传输和负载统计,缺一个都不上真实总线。希望帮到你。

本文还有配套的精品资源,点击获取

相关新闻

MIMO通信系统Matlab仿真:Alamouti编码与BER曲线实现

MIMO通信系统Matlab仿真:Alamouti编码与BER曲线实现

简介&#xff1a;这是一套面向通信工程学生与无线通信初学者的MIMO通信系统Matlab仿真实现&#xff0c;基于多天线收发、空间复用/分集与波束赋形构建立可运行链路&#xff0c;帮助理解信道容量提升、抗衰落机制及不同参数对系统性能的影响。压缩包共3个文件&#xff0c;以2个m…

2026/10/20 8:02:22 阅读更多 →
分时电价省钱实操:把家电搬到谷段,一年省几百元

分时电价省钱实操:把家电搬到谷段,一年省几百元

“早上八点的电费涨到一块二了&#xff0c;家里的洗衣机还在嗡嗡转。”这句话如今已经成了我家的自嘲梗。说真的&#xff0c;分时电价这玩意儿&#xff0c;第一次认真刷电费账单的人都会愣一下&#xff1a;一天之内电价居然能上蹿下跳好几回&#xff0c;早上出门前和深夜睡觉前…

2026/10/20 8:01:21 阅读更多 →
MATLAB疑难杂症诊疗手册:高频报错与性能问题的排查思路

MATLAB疑难杂症诊疗手册:高频报错与性能问题的排查思路

1. 开篇&#xff1a;为什么MATLAB总在关键时刻掉链子要说起来&#xff0c;MATLAB这套工具&#xff0c;用了十几年的人不敢说完全摸透&#xff0c;刚入门的人又被各种报错劝退。我见过太多人卡在奇奇怪怪的问题上——明明代码逻辑没问题&#xff0c;矩阵维度也对得上&#xff0c…

2026/10/20 8:01:21 阅读更多 →

最新新闻

银行模拟程序:数据结构选型与离散事件仿真实战

银行模拟程序:数据结构选型与离散事件仿真实战

1. 项目概述&#xff1a;为什么一个银行模拟程序能成为数据结构教学的“试金石”“数据结构-银行模拟程序”这个标题乍看平平无奇&#xff0c;但在我带过的十几届学生项目实践中&#xff0c;它几乎稳居“最能暴露基础漏洞”的榜首。不是因为它有多复杂&#xff0c;恰恰相反——…

2026/10/20 8:55:28 阅读更多 →
AI推理加速器设计实战:从算子映射到INT8量化落地全解析

AI推理加速器设计实战:从算子映射到INT8量化落地全解析

做硬件加速这几年来&#xff0c;被问得最多的一个问题就是&#xff1a;既然 CPU 和 GPU 都能跑模型&#xff0c;为什么还要自己折腾 AI 推理加速器&#xff1f;一开始我也觉得这是个伪需求&#xff0c;直到真在边缘侧设备上部署过几个模型才明白——通用处理器要么算力不够&…

2026/10/20 8:55:28 阅读更多 →
前后端分离AI客服系统:会话缓存优先的多级查询策略与DeepSeek流式接入

前后端分离AI客服系统:会话缓存优先的多级查询策略与DeepSeek流式接入

简介&#xff1a;这份资源是一套基于SpringBoot与Vue.js前后端分离架构的AI智能客服系统完整源码&#xff0c;面向具备Java与前端基础的开发者、课程设计或毕业设计人群&#xff0c;解决自然语言对话与高并发响应场景下的系统搭建问题。压缩包共63个文件、约23.76MB&#xff0c…

2026/10/20 8:55:28 阅读更多 →
用workbuddy把官方AI教程改造成能讲的课:实操与避坑

用workbuddy把官方AI教程改造成能讲的课:实操与避坑

2. 官方教程的"三座大山"&#xff1a;为什么直接啃原版很难受在讲我怎么用workbuddy之前&#xff0c;先聊聊我起初面对官方AI认证教程的真实感受。很多朋友拿到官方文档的第一反应是"权威、全面、跟着学就行了"&#xff0c;但实际翻上两章就会发现&#xf…

2026/10/20 8:55:28 阅读更多 →
OpenClaw从零部署指南:环境准备到安全加固运维

OpenClaw从零部署指南:环境准备到安全加固运维

OpenClaw这个开源项目&#xff0c;在个人自动化与任务调度圈子里热度一直在线。它把任务编排、插件管理、多终端数据同步整合成一个轻量级服务&#xff0c;安装边界清晰&#xff0c;部署完就能感受到它的调度与插件生态比裸写脚本省心太多。但很多朋友卡在第一步&#xff1a;不…

2026/10/20 8:55:28 阅读更多 →
Agentic RAG 实战指南:从直线管道到多步决策闭环

Agentic RAG 实战指南:从直线管道到多步决策闭环

1. 检索管道的天花板&#xff1a;一次查询解决不了连锁问题做了一段时间 RAG 应用的人&#xff0c;多半都会遇到同一个尴尬时刻&#xff1a;知识库文档放得挺全&#xff0c;向量检索的召回率调得也不算差&#xff0c;但面对稍微复杂一点的问题&#xff0c;系统就开始"答非…

2026/10/20 8:54:27 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

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

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

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

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

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

2026/10/20 1:01:30 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/19 10:02:04 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 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/19 2:54:50 阅读更多 →
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/19 9:10:22 阅读更多 →
黑夜航拍船只数据集训练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/18 12:16:16 阅读更多 →