ROS与STM32串口通信实战:从硬件接线到协议解析
做机器人项目的时候很多朋友第一次把ROS主控和STM32放在一起遇到的第一道坎就是这两块东西怎么“说话”。ROS跑在Ubuntu的Linux环境里底层是一整套分布式通信机制负责建图、导航、视觉这类重负载任务而STM32跑的是C语言裸机或者FreeRTOS负责电机控制、编码器采集、IO操作这些对时间要求极严的活。两边架构完全不同光用一根杜邦线连起来根本不够必须把硬件接线、通信协议、两端的解析逻辑、调试手段全打通这套系统才能真正跑起来。这篇内容我会从最底层的硬件接线开始一路讲到STM32和ROS端的代码怎么写中间穿插我自己调试时踩过的坑最后给一份可以直接照着排查的问题速查表。适合正在从零搭建机器人、或者已经把ROS和STM32分头调通但始终连不到一起的开发者参考。1. 架构思路为什么是“ROS主控STM32”这套组合1.1 上位机和下位机的分工先搞清楚为什么几乎所有的机器人项目都要分成上下位机而不是一块板子干到底。Linux系统不是实时系统进程调度、DDS消息传递都会带来毫秒级的不确定性延迟。这种延迟对SLAM、路径规划没有影响但放到电机PID控制里就会要命——电流环和速度环需要固定的控制周期中断响应得做到微秒级。所以常见的分层方式是ROS主控端负责传感器融合、路径规划、运动学解算、人机交互把“去哪个点”“走多快”这类目标指令发给下位机。STM32端负责执行层接收目标速度后解算成左右轮各自的速度再通过定时器中断周期性地跑PID输出PWM驱动电机。同时采集编码器、IMU、电池电压等数据回传给上位机。这种架构的本质是“决策与执行分离”。上位机只关心“我想让机器人以0.5m/s的速度直线前进”下位机负责“怎么让两个轮子精确转起来”。两边通过通信链路交换的是目标值和状态反馈而不是底层的PWM波形或编码器脉冲细节。1.2 通信方案怎么选为什么大多数项目最终选了串口ROS主控和STM32之间可以走很多种物理链路常见的有UART串口、RS485、CAN总线、以太网。我个人的经验是入门和中型项目优先选UART TTL串口如果对可靠性要求高、通信距离超过1米再升级到RS485或者CAN。通信方式成本距离速率ROS端接入难度适用场景UART TTL最低0.5m内可靠115200~921600低USB转TTL即可板间通信、桌面机器人RS485/RS422中几十米115200~2M中需转接模块工业底盘、长距离布线CAN总线中高几十米最高1Mbps较高需USB-CAN适配器多节点、实时性要求高以太网高百米级高中需协议栈对接高性能机器人、多传感器很多人一上来就想上CAN觉得串口“low”。但从实际项目看串口在短距离板间通信里完全够用而且调试手段最丰富——随便一个USB转TTL模块、一条杜邦线、一个串口助手就能把链路测通。CAN虽然抗干扰和实时性更好但你需要额外买适配器在ROS端写驱动遇到问题排查起来也麻烦。先把串口这套逻辑彻底跑通后期要迁移到CAN也非常容易因为通信协议和分层架构是可以平移的。1.3 通信内容的数据边界设计通信时最忌讳的是把底层细节全部搬到上层。我见过有初学者试图通过上位机直接控制每个PWM引脚的占空比这在架构上是完全错误的方向。上下位机之间应该交换的是下行指令线速度、角速度、目标点、模式切换、急停。上行状态里程计数据、IMU姿态、电机电流、电池电压、系统运行状态。同时要明确控制周期的归属——STM32负责周期性的速度闭环和位置闭环即使上位机通信延迟甚至断开下位机也要能维持当前状态或安全停车这是基本的系统健壮性要求。2. 硬件链路搭建接线、电平与供电避坑2.1 串口引脚映射与接线交叉以最常见的STM32F103系列为例USART1的引脚是PA9TX和PA10RX。用STM32CubeMX配置时勾选USART1选择异步模式这两个引脚就会自动配置为复用功能AF。这里有个很容易犯的错误只打开了USART外设但没有配置GPIO的复用模式导致引脚默认是普通输入输出串口自然无法工作。接线时有个基础原则串口必须交叉连接。发射端连接对方的接收端接收端连接对方的发射端。如果是USB转TTL模块和STM32板连接参考接线USB转TTL模块STM32板TXPA10RXRXPA9TXGNDGND3.3V可选的VCC视模块是否带输出上位机如果是普通PC或者ARM开发板一般通过USB口扩展出串口。此时“上位机端”实际上是USB转TTL模块暴露出来的串口接线方式完全相同。2.2 电平匹配是新手最容易踩的坑STM32的GPIO一般是3.3V TTL电平而市面上不少USB转TTL模块默认是5V电平或者支持3.3V/5V跳线切换。如果直接把5V模块的TX接到STM32的RX上长期使用大概率会损伤引脚甚至导致通信时好时坏因为高电平阈值判断会处于临界区。我的建议是买模块时直接选支持3.3V电平的或者带有跳线能切到3.3V。如果不确定模块电平用万用表量一下TX引脚空闲时的电压3.3V左右就是3.3V电平接近5V就要处理。实在只有5V模块可以在串口线上串一个1kΩ电阻分压或者用一个小型电平转换模块淘宝几块钱一个不要省这个钱。另外一条铁律是通信双方必须共地。串口信号是相对于地线来判定电平的如果不共地两边参考电位不同轻则偶尔乱码重则完全无法通信。接好TX、RX之后GND是绝对不能省的那根线。2.3 供电与抗干扰加固很多机器人底盘在电机启动瞬间会有很大的电流冲击如果STM32板和电机驱动共用一个电源或者电源走线很细很容易出现电源电压跌落和纹波串口就会开始随机乱码。我的处理习惯是STM32控制板单独用一路稳压供电电机驱动再单独供电两路电只在电池端汇总。控制板电源入口并联一个100uF电解电容和0.1uF陶瓷电容吸收低频和高频纹波。串口线尽量短不要用超过20cm的杜邦线飞线。如果必须拉长降低波特率到9600或者换RS485方案。还有一点调试时如果同时插着USB转TTL模块和给STM32供电的电源要注意USB模块的供电方向和板载电源是否冲突。不少STM32开发板有电源选择跳线默认用USB供电时再往3.3V引脚灌入外部电压可能烧掉板载稳压器。3. 通信协议设计让两边照着一套规则对话3.1 一帧数据长什么样物理链路通了两边能收到字节接下来要解决的是“字节流里的语义”问题。串口是流式传输没有天然的消息边界所以必须自己定义帧格式让接收方知道“从哪开始、到哪结束、中间是什么”。我用的最多的一套协议结构是字段长度说明帧头11字节固定0xAA帧头21字节固定0x55命令字1字节标识这条帧的类型数据长度1字节后面数据区的字节数数据区N字节实际载荷CRC校验2字节从命令字到数据区结尾的CRC16帧尾2字节固定0x0D 0x0A帧头的作用是让接收方找到消息的起点帧尾是辅助校验边界。命令字用来区分“这是设置速度的指令”还是“这是回传里程计的数据”。长度字段告诉接收方要收多少字节才算一条完整消息。你可能会问既然有了帧头和长度为什么还要CRC因为串口在恶劣电磁环境下会出现位翻转如果只靠帧头帧尾判断可能把坏帧当成好帧用尤其在发送速度指令时一个字节错误可能导致机器人行为异常。CRC就是一个把关的守门员数据不对就整帧丢弃。3.2 CRC16选型与实现CRC比累加和可靠得多。累加和只需要做加法但两个错位字节可能相互抵消漏检率高。CRC16对每一位数据做多项式除法检测能力要强很多。机器人控制场景我推荐用CRC16-Modbus参数为多项式0x8005、初始值0xFFFF、输入输出不反转。代码很短可以直接移植到STM32和ROS两端uint16_t crc16_modbus(const uint8_t *data, uint16_t len) { uint16_t crc 0xFFFF; for (uint16_t i 0; i len; i) { crc ^ data[i]; for (uint8_t j 0; j 8; j) { if (crc 0x0001) crc (crc 1) ^ 0xA001; else crc 1; } } return crc; }两端使用这个函数时必须保证计算范围完全一致。比如我约定“从命令字开始到数据区最后一个字节为止”那么发送端调用crc16_modbus(frame[2], 2 data_len)接收端也必须对相同的字节段做校验不能把帧头也圈进去。这个细节不对称的话会出现“明明很简单的逻辑却怎么也调不通”的情况。3.3 协议设计里的隐藏细节字节序和状态解析嵌入式领域默认是小端序但为了保险还是建议在协议文档里明确定义多字节字段的字节序。比如一个16位的速度值是低字节在前还是高字节在前两端要写成一致。对于浮点数最稳妥的做法是通过memcpy按字节打包和解析不要试图把float类型的指针直接转换成uint8_t*之后逐字节发送——虽然经常能工作但涉及对齐和编译器优化时容易出诡异问题。解析字节流时我的推荐写法是状态机而不是memcmp找帧头。状态机的核心思路是每收到一个字节就推进一次状态只有当按顺序满足帧头、长度、数据、校验全部条件时才算一帧完整消息。typedef enum { ST_WAIT_HEAD1, ST_WAIT_HEAD2, ST_READ_CMD, ST_READ_LEN, ST_READ_DATA, ST_READ_CRC_H, ST_READ_CRC_L } parser_state_t; void parse_byte(uint8_t byte) { static parser_state_t state ST_WAIT_HEAD1; static uint8_t frame[128]; static uint16_t index 0; static uint8_t len 0; static uint16_t crc_calc 0; switch (state) { case ST_WAIT_HEAD1: if (byte 0xAA) state ST_WAIT_HEAD2; break; case ST_WAIT_HEAD2: state (byte 0x55) ? ST_READ_CMD : ST_WAIT_HEAD1; break; case ST_READ_CMD: frame[index] byte; state ST_READ_LEN; break; case ST_READ_LEN: frame[index] byte; len byte; index 0; state ST_READ_DATA; break; case ST_READ_DATA: frame[index] byte; if (index len) state ST_READ_CRC_H; break; case ST_READ_CRC_H: frame[index] byte; state ST_READ_CRC_L; break; case ST_READ_CRC_L: frame[index] byte; // 此时 frame 里是 cmd data索引为 1 len crc_calc crc16_modbus(frame, 1 len); if (crc_calc ((frame[1 len 1] 8) | frame[1 len])) on_frame_received(frame[0], frame[1], len); state ST_WAIT_HEAD1; index 0; break; } }初始化状态时要注意一帧结束后即使CRC校验失败也要把状态和索引全部复位不要带着残留状态去解析下一帧。另外数据区里如果出现了0xAA 0x55状态机确实可能误判帧头但由于长度和CRC在后续会校验失败接收端丢弃该帧即可。如果要求更高的可靠性可以同时约定最大帧长和接收超时半截帧超时后强制复位状态机。协议里加一个1字节的序列号字段也很有用。发送端每帧递增接收端可以据此判断是否丢帧、重放和乱序。排查通信问题的时候这个字段几乎能把“偶发丢包”变成可量化的问题。4. STM32端实现把协议落地成可跑的收发逻辑4.1 接收层串口中断加环形缓冲区在STM32端接收数据最忌讳的做法是在主循环里调用阻塞式接收等待数据。机器人主循环通常还要跑控制算法、状态机、看门狗喂狗一旦阻塞在串口上整个系统就会卡住。我的做法是串口中断只负责把收到的字节扔进一个环形缓冲区主循环里再按时机去解析。环形缓冲区是一个固定大小的数组配合两个指针head 写指针、tail 读指针使用。当写指针追上读指针时就认为缓冲区满新的数据可以选择丢弃或覆盖旧的需要根据系统容忍度取舍。#define RING_SIZE 256 typedef struct { uint8_t buffer[RING_SIZE]; volatile uint16_t head; volatile uint16_t tail; } ring_buffer_t; uint8_t ring_write(ring_buffer_t *rb, uint8_t byte) { uint16_t next (rb-head 1) (RING_SIZE - 1); if (next rb-tail) return 0; // 满 rb-buffer[rb-head] byte; rb-head next; return 1; } int ring_read(ring_buffer_t *rb, uint8_t *byte) { if (rb-head rb-tail) return 0; *byte rb-buffer[rb-tail]; rb-tail (rb-tail 1) (RING_SIZE - 1); return 1; }环形缓冲区大小取2的幂是为了用按位与代替取模效率更高。在单生产者中断写单消费者主循环读的场景下这个结构不需要加锁。注意head和tail都要用volatile修饰避免编译器优化导致中断里更新的值在主循环里读不到。中断接收的代码长这样以HAL库为例void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart-Instance USART1) { ring_write(uart_rb, rx_byte); HAL_UART_Receive_IT(huart1, rx_byte, 1); } }这里使能的是单字节中断接收每次收到一个字节就进入回调写入缓冲区然后重新使能下一次接收。这种方式比用固定长度的HAL_UART_Receive_IT(huart1, buf, N)灵活得多因为帧长度在运行时才知道不适合定长接收。如果串口中断很频繁可以考虑DMA加IDLE中断的方案减少CPU占用但入门阶段用单字节中断没有任何问题。4.2 解析与回调分发主循环或者一个独立的任务里不断从环形缓冲区读字节喂给前面写好的状态机解析函数void uart_parse_task(void) { uint8_t byte; while (ring_read(uart_rb, byte)) { parse_byte(byte); } }解析到一帧完整且校验通过的消息后调用on_frame_received。这个回调函数里根据命令字把数据区的内容拷贝到对应的全局结构体同时设置一个标志位让主循环在合适的时机去处理。一个重点设计原则是不要在串口中断或者解析函数里直接执行复杂的业务逻辑比如直接开始跑PID、直接更新PWM占空比。应该只做数据拷贝和标志位置位具体控制在主循环或者定时器中断里处理。这样可以最大限度减少通信对控制实时性的干扰。4.3 上报帧的组帧与DMA发送STM32向ROS端上报数据推荐的做法是独立写一个组帧函数传入命令字、数据指针和数据长度自动计算CRC然后统一发送void uart_send_frame(uint8_t cmd, const uint8_t *data, uint8_t len) { uint8_t frame[128]; frame[0] 0xAA; frame[1] 0x55; frame[2] cmd; frame[3] len; memcpy(frame[4], data, len); uint16_t crc crc16_modbus(frame[2], len 2); frame[4 len] crc 0xFF; frame[5 len] crc 8; frame[6 len] 0x0D; frame[7 len] 0x0A; HAL_UART_Transmit(huart1, frame, len 8, 100); }如果是低速上报10~50Hz用阻塞式HAL_UART_Transmit就够了。如果上报频率很高或者数据量大建议换成DMA传输并注意一个坑DMA发送过程中不能修改正在发送的缓冲区。做法是准备两个发送缓冲区交替使用或者在DMA发送完成中断里置一个标志确认上一帧发完后再填写下一帧。4.4 从数据到控制一个速度指令的完整闭环假设上位机发来一帧“设置速度”指令命令字为CMD_SET_SPEED数据区里是两个float分别是线速度v和角速度w。STM32端解析后的处理逻辑是用memcpy把数据区的前4字节还原成float v后4字节还原成float w。根据机器人底盘运动学模型把v、w换算成左右轮的目标转速。把目标转速写入电机控制的全局结构体由定时器中断里的PID计算环节去执行。这里要注意目标速度最好设置一个合理的上下限防止上位机数据异常导致电机飞转。同时建议实现一个“看门狗”逻辑如果超过500ms没有收到任何来自上位机的新指令自动将目标速度清零并停车避免通信中断后机器人还在继续跑。5. ROS端实现把串口数据变成话题5.1 第一步串口设备权限和固定端口名在Linux下访问串口首先要解决权限问题。把当前用户加入dialout组后重新登录就可以直接读写串口设备了sudo usermod -aG dialout $USER重启或者重新登录后执行ls -l /dev/ttyUSB0确认当前用户有读写权限。USB转串口设备插入后设备名可能会漂移今天是ttyUSB0明天接另一个口就变成ttyUSB1对ROS节点的稳定性影响很大。我的做法是写一个udev规则根据设备的USB物理位置固定成软链接sudo vim /etc/udev/rules.d/99-robot-uart.rules内容类似KERNELttyUSB*, ATTRS{idVendor}1a86, ATTRS{idProduct}7523, SYMLINKrobot_uartidVendor和idProduct可以通过lsusb查到。注意不同USB转串口芯片的VID/PID不一样不要用一条规则硬套所有模块。设置完成后插拔一次USB设备检查/dev/robot_uart是否出现。这样ROS节点里只需固定写/dev/robot_uart换USB口也不会影响。5.2 用termios直接读写串口ROS端读写串口可以借助现成的serial库也可以直接用Linux的termios接口。我倾向于直接写POSIX代码不引入额外依赖控制感更强#include termios.h #include fcntl.h #include unistd.h int fd open(/dev/robot_uart, O_RDWR | O_NOCTTY); if (fd 0) { // 处理打开失败 } struct termios options; tcgetattr(fd, old_options); cfmakeraw(options); cfsetispeed(options, B115200); cfsetospeed(options, B115200); options.c_cc[VMIN] 0; options.c_cc[VTIME] 1; tcsetattr(fd, TCSANOW, options);cfmakeraw会把串口设置为原始模式不对输入做任何行处理这是收发二进制协议帧的前提。VMIN0, VTIME1的意思是如果没有数据可读read最多阻塞100ms后返回0如果有数据read立刻返回当前可读的字节。这个组合非常适合在非中断线程里做轮询读取既不占满CPU也不会永久阻塞。写入时需要注意write返回的字节数可能小于请求长度所以要么用完整长度循环写要么干脆每次调用前检查返回值。串口是流式接口不能假设一次write就一定把数据全部发完。5.3 节点逻辑一个“串口桥”的整体结构ROS端的核心节点就是一个“串口桥”。往下要解析STM32上报的帧发布成ROS话题往上要订阅速度指令打包成协议帧写入串口。ROS1和ROS2在API上有差异但整体思路完全一致。以ROS2为例节点的大致骨架是class UartBridge : public rclcpp::Node { public: UartBridge() : Node(uart_bridge) { odom_pub_ create_publishernav_msgs::msg::Odometry(odom, 10); cmd_vel_sub_ create_subscriptiongeometry_msgs::msg::Twist( cmd_vel, 10, [this](geometry_msgs::msg::Twist::SharedPtr msg) { // 将线速度、角速度打包成帧写入串口 uint8_t data[8]; memcpy(data[0], msg-linear.x, 4); memcpy(data[4], msg-angular.z, 4); uart_send_frame(CMD_SET_SPEED, data, 8); }); read_thread_ std::thread([this] { read_loop(); }); } private: void read_loop() { while (rclcpp::ok()) { uint8_t buf[256]; int n read(fd_, buf, sizeof(buf)); if (n 0) { for (int i 0; i n; i) parse_byte(buf[i]); } } } rclcpp::Publishernav_msgs::msg::Odometry::SharedPtr odom_pub_; rclcpp::Subscriptiongeometry_msgs::msg::Twist::SharedPtr cmd_vel_sub_; std::thread read_thread_; int fd_; };读取和解析都在独立的线程里做避免阻塞主循环。解析到里程计帧后填充nav_msgs::msg::Odometry消息发布出去。时间戳可以取ROS当前时间但如果你对数据时间同步要求比较严格最好在协议里加一个STM32本地时间戳字段由上位机统一校准。订阅cmd_vel这个操作看起来简单但有一个使用经验值得分享ROS的cmd_vel话题频率通常不高大概10~20Hz但如果你直接在上层话题回调里写串口再用串口发送中间没有缓冲底盘控制会很“生硬”。要做到平滑最好在STM32端做速度斜坡滤波或者上位机再包一层速度平滑器。6. 调试心得与常见问题排查实录6.1 先单独把每一层都测通刚把硬件焊完、代码写完就急着连ROS是我见过最多失败案例的调试方式。通信链路分成好几个独立层必须分层验证第一步STM32回环测试。把STM32的PA9和PA10直接用杜邦线短接然后在代码里写一个简单的自收发测试串口收到字节后原样发回。如果自己发自己收都不通问题在单片机端。第二步USB转TTL模块自测。把模块的TX和RX短接用串口助手发送数据看是否原样返回。如果返回异常确认模块的电平和驱动是否正常。第三步STM32通过USB转TTL模块连接电脑。在串口助手里手动发送协议帧观察STM32是否能正确执行有没有正确的回传数据。第四步ROS端用脚本或者节点直接向串口发送帧观察STM32是否有反应。每一层都验证过再进入正式联调。不要妄想一次跳过前三步直接调通整体。6.2 用逻辑分析仪和脚本验证数据如果通信时好时坏硬件层面的问题需要用示波器或者逻辑分析仪去看波形。重点看TX和RX引脚的波形幅值是否正常有没有严重过冲或欠压。波特率是否准确一帧波形的时间宽度是不是符合理论值。干扰是否严重比如电机启动时数据线上出现毛刺。软件层面推荐用Python的pyserial写一个小脚本既可以用作模拟端也可以用来抓包分析import serial ser serial.Serial(/dev/ttyUSB0, 115200, timeout0.1) # 抓包 while True: data ser.read(64) if data: print(data.hex())把收到的数据打印成十六进制一眼就能看出协议帧是否完整、CRC字节是否异常。6.3 常见问题速查表现象可能原因排查手段解决办法串口全是乱码波特率不一致、电平不匹配、共地不良串口助手回环测试、万用表量电平统一波特率、确认3.3V/5V、接GND偶发丢帧干扰、线太长、波特率过高降低波特率、换屏蔽线、看波形加CRC重传、降低速率、改RS485ROS端读不到数据串口权限、设备名错误、节点卡死ls -l /dev/ttyUSB0、检查日志加入dialout组、固定udev软链接只有上行或只有下行通TX、RX没有交叉、单方向接线错误用示波器量引脚波形检查交叉接线CRC总是不对两端CRC计算范围不一致、字节序相反两端打印CRC中间值对比统一计算范围、统一字节序电机一启动就乱码电源跌落、共模干扰观察电源波形、串口波形控制板和驱动分开供电、加滤波电容6.4 通信跑通之后的健壮性加固通信“能通”和“稳定”是两回事真正的调试工作量往往在后半段。我做完基础联调后会检查几个细节把日志打印关掉或者移到独立的调试串口。很多人喜欢在STM32的开发板上通过同一个串口打印调试信息这会把正常的协议字节流冲散导致上位机频繁解析失败。调试日志和通信协议应当分属不同的串口或者关闭日志。确认控制任务不受通信影响。串口中断和PID定时器中断要划分清楚优先级。如果PID定时中断被串口中断频繁打断速度环会抖动。一般把PID定时器中断优先级设得比串口高串口中断只做数据搬运。增加通信丢失保护。上位机通过心跳帧和STM32保持联系如果下位机在设定时间内没有收到心跳或任何合法帧立即执行安全停车。这个逻辑听起来简单但在真机上救过我好几次——USB线意外松动、ROS节点崩溃、上位机关机这些都可能导致下位机收到不完整协议流没有保护机制的话机器人会一直维持最后的指令状态。最后分享一个我自己的调试习惯协议里始终带一个递增的序列号字段。不管是排查丢包率、确认帧是否重放、还是判断两端的解析状态是否对齐这个字段都是最快的信息来源。刚开始做通信方案的时候我觉得序列号是多余的开销后来在真机上排查过一次“时好时坏”的问题靠的就是序列号统计出来大约有2%的丢包率最终定位到串口线过长加电机电磁干扰。协议里的冗余字段在调试阶段远比看似“省下来的几个字节”更有价值。

相关新闻

DMA读旧数据?三招搞定Cache一致性难题

DMA读旧数据?三招搞定Cache一致性难题

/* 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 6:36:54 阅读更多 →
STM32寄存器操作实战:从GPIO到DMA的底层开发指南

STM32寄存器操作实战:从GPIO到DMA的底层开发指南

/* 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 6:36:54 阅读更多 →
STM32寄存器级编程实战:从GPIO到串口的中文手册

STM32寄存器级编程实战:从GPIO到串口的中文手册

/* 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 6:36:54 阅读更多 →

最新新闻

从零了解 Langfuse 6:Agent 与 Tool 调用观测

从零了解 Langfuse 6:Agent 与 Tool 调用观测

内容核验:2026-10-15,参照当日 Langfuse 官方 v4 文档。本文以概念和设计示意为主,不是完整可运行项目;示例金额、分数、通过率和耗时均非实测数据。AgentScope/MCP 节点名称是业务埋点示例,需要显式配置语义映射。版本…

2026/10/19 7:17:12 阅读更多 →
让 AI 重构旧代码,怎样把“行为不变”变成可检查的要求?

让 AI 重构旧代码,怎样把“行为不变”变成可检查的要求?

“行为不变”,要先说清楚哪些行为 旧代码功能正常,只是函数太长、命名难懂、重复判断越来越多。这时让 AI 帮忙整理,很容易得到一份看起来更漂亮的代码。但代码变短了、分支变少了,并不能说明重构成功。 真正需要验收的是&#…

2026/10/19 7:17:12 阅读更多 →
基于Python与face_recognition的人脸识别考勤系统:从摄像头到SQLite完整实现

基于Python与face_recognition的人脸识别考勤系统:从摄像头到SQLite完整实现

简介:面向计算机相关专业毕业设计与课程设计需求,这份基于深度学习的人脸识别考勤系统源码,提供了人脸检测、特征提取、打卡记录等完整流程。项目经导师指导并通过评审,得分98分,适合正在做大作业、期末项目或毕设开发…

2026/10/19 7:17:12 阅读更多 →
手作复现困境:用记录系统把创作流程变成可复用资产

手作复现困境:用记录系统把创作流程变成可复用资产

最近在整理一个叫 artcraft 的个人创作项目,核心就一句话:把“艺术设计”和“手工制作”这两段本来经常脱节的流程,拧成一条可以复用的流水线。以前我做热缩片挂件、小木牌、桌面摆件,基本是兴致来了就动手,做完往柜子…

2026/10/19 7:17:12 阅读更多 →
深度强化学习交易策略实战:PPO、A2C与DDPG联合回测解析

深度强化学习交易策略实战:PPO、A2C与DDPG联合回测解析

简介:基于深度强化学习的自动化股票交易策略设计源码,面向金融工程、量化交易研究者和具备一定Python基础的开发者,解决传统人工交易易受情绪干扰、策略难以自适应市场变化的问题。项目完整实现了PPO、A2C与DDPG三类Actor-Critic算法的训练与…

2026/10/19 7:17:12 阅读更多 →
普通个人电脑跑大模型:显存、量化与CPU+GPU混合推理实战指南

普通个人电脑跑大模型:显存、量化与CPU+GPU混合推理实战指南

前阵子有个朋友问我:“普通个人电脑到底能跑多大的大模型?”我当时没直接甩个数字给他,因为这个问题本身就是一个“多变量博弈”:同样叫“7B”的模型,float16 版本和 int4 量化版本,显存需求能差出三倍&…

2026/10/19 7:16:12 阅读更多 →

日新闻

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

简介:本资源为电源技术方向的学术研究文档,聚焦介质阻挡放电型臭氧发生器电源的频率跟踪型PWM控制策略,面向电力电子、高压电源及臭氧应用领域的科研人员与工程技术人员。针对负载呈容性、等效电容随外加电压升高而增大、谐振频率随之漂移的难…

2026/10/19 0:00:47 阅读更多 →
Vibe Coding 遇上 TaoToken:把开发者思维圣殿的灵感流接入统一 Key 通道

Vibe Coding 遇上 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 0:00:47 阅读更多 →
HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

【免费下载链接】HundredCV-Chat 项目地址: https://ai.gitcode.com/hf_mirrors/Jax-dan/HundredCV-Chat 点击查看 免费下载 HundredCV-Chat 是一个由 100 位青年简历数据开发而来的中文多轮对话数据集,共包含 24,750 组日常闲聊对话和 4,871 个话题标签…

2026/10/19 0:00:47 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/19 1:30:52 阅读更多 →

月新闻

我发现了一个新思路:用 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/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练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 阅读更多 →