FEATURED · 精选文章

Jetson Nano+STM32边缘AI实战:从数据集到模型部署与舵机控制

发布时间 / 2026/9/1 13:27:02
来源 / 创域科博编辑部
栏目 / 资讯中心
Jetson Nano+STM32边缘AI实战:从数据集到模型部署与舵机控制 简介这是一套面向嵌入式AI初学者与课程设计者的端侧智能控制实战项目资源聚焦Jetson Nano与STM32协同完成图像识别→决策→舵机响应的完整闭环解决边缘设备间通信、模型轻量化部署及硬件联动等典型工程问题适用于毕业设计、大创竞赛、实训项目及深度学习入门实践。压缩包共111个文件含40个头文件h与38个C源码c覆盖STM32底层外设驱动如USART、TIM、ADC、I2C、Keil工程配置uvprojx/uvoptx及Jetson端PyTorch模型导出后的Paddle Lite部署文件model、params、pdparams等另有说明文档md、调试配置dbgconf、自动化脚本bat及演示视频mp4总大小142.82MB。已有1339人学习下载资源经实测可一键复现——无需PCB设计基础支持面包板快速连线固件烧录即运行。 说起边缘AI和嵌入式控制的组合方案Jetson Nano加STM32这套搭配在我接触过的项目里算是出场率相当高的一组。标题里这个“从准备数据集到完成深度学习模型部署”基本把整个项目的技术链路都串起来了。这篇就来完整拆解一下这个项目的落地过程从数据集准备、模型训练到Jetson Nano端部署再到STM32通过串口通信控制舵机转动把每个环节的关键细节和实际踩过的坑都讲清楚。先说一个很多人容易忽略的点这种项目真正的难点往往不在AI模型本身而在“AI计算平台”和“实时控制单元”之间如何协同工作。Jetson Nano跑深度学习推理很强但它不适合直接做毫秒级的舵机PWM控制STM32做实时控制非常稳但跑不了目标检测模型。所以这套双芯片架构的合理性是整篇内容的前提。1. 双芯片架构选型的底层逻辑为什么是Jetson NanoSTM32很多第一次做类似项目的朋友会问为什么不让Jetson Nano直接控制舵机非要中间加一块STM32这个问题问得特别好因为它直接关系到整个系统的稳定性和可维护性。Jetson Nano虽然是一块完整的Linux开发板但它本质上是一台微型电脑。它跑的是Ubuntu系统有完整的文件系统、进程调度、GPU驱动栈。一旦系统负载升高比如模型推理占满GPU、多个进程同时读写文件Linux内核的调度器会介入这就会导致GPIO引脚的输出时序出现不可控的抖动。舵机对PWM信号的脉宽精度要求是微秒级的系统一抖动舵机就会跟着颤抖、啸叫甚至直接卡死。STM32不一样它跑的是裸机程序或者轻量级RTOS中断响应是确定性的PWM输出由定时器硬件直接产生不经过操作系统调度所以脉冲宽度能做到非常稳定的微秒级精度。从功耗和发热的角度看Jetson Nano满载运行时功耗大概在5W到10W之间核心温度轻松冲到七八十度。如果让它同时承担模型推理和舵机控制那还得额外设计复杂的电源隔离和信号调理电路否则PWM信号上的噪声分分钟让舵机乱转。STM32这边功耗极低一块3.3V供电就能跑得很欢两者分工各干各擅长的事系统稳定性才有保障。从实际开发效率来考虑TensorRT推理、图像预处理这些活儿在Jetson Nano上做用的全是Python生态的成熟库而舵机控制、传感器采集、通信协议解析这些底层工作在STM32上做用HAL库加几个定时器就搞定。两边逻辑完全解耦调试的时候可以各自单独测不会出现“改了一行控制代码结果模型推理也崩了”这种鬼问题。这套架构的典型应用场景也很清晰智能云台追踪、垃圾分类机械臂、人脸追踪相机、AGV小车避障转向全都适用。Jetson Nano负责“看”STM32负责“动”中间通过串口通信传指令分工明确。2. 数据集准备阶段这是决定模型上限的隐形瓶颈很多人拿到项目第一步就急着训练模型结果训出来的模型在测试集上看着不错一到真实场景就各种漏检误检。这里我可以负责任地说绝大多数问题都出在数据集上而不是模型结构上。标题里专门提到“从准备数据集开始”说明这一步的分量确实值得单独拿出来讲。2.1 数据采集真实场景数据比公开数据集更关键如果你的项目检测目标比较通用比如人、车、猫狗这类用公开数据集就够了像Aeroscapes这种航拍场景数据集或者COCO的子集都是现成的选择。但如果你要检测的是特定物体比如某型号的电路板元件、某种颜色的管道裂缝那就必须自己采集数据。自己采集数据有几个实操要点。首先是环境覆盖性不要只在光线良好的实验室里拍要模拟实际部署场景比如逆光、暗光、反光、遮挡都要拍到。其次是角度多样性同一物体至少要覆盖俯视、平视、侧视三种角度。再就是距离层次物体在画面中占的比例要从大到小都有样本这样模型才能学会在不同尺度下识别目标。我当时做这个项目的时候采集了大概2000多张原始图片用手机加工业相机混合拍摄分辨率控制在1280x720左右。这里有个小技巧视频抽帧比连拍高效得多拿手机绕着目标物走一圈录段视频然后按每5帧抽1帧的频率提取很快就能攒出一批角度连续的样本。2.2 数据标注边界框的标注质量直接影响mAP数据标注是个体力活但也是最有技术含量的事情之一。标注工具我推荐用LabelImg或者Roboflow前者纯本地运行不需要联网后者有在线协作功能。标注的时候有几个细节直接决定模型效果。边界框要紧贴目标轮廓宁紧勿松特别是目标只占画面很小区域的时候框稍微大一点就会把背景学进去产生大量误检。遮挡目标的标注要把可见部分完整框出来不要试图标出被挡住的那部分轮廓。模糊的极端样本也要保留但可以单独放在验证集里用来测试模型的鲁棒性。如果你用的是YOLOv8标注格式会转成TXT文本每行是一个目标格式是“类别ID x_center y_center width height”注意这四个数值都是相对于图片宽高的比例值范围0到1之间。LabelImg默认输出的是XML格式需要用脚本转一下这一步别手写解析直接用Roboflow导出或者用ultralytics自带的格式转换工具就行。2.3 数据集划分与类别平衡训练集、验证集、测试集的划分比例我习惯用80%、10%、10%。训练集负责让模型学到特征验证集负责调整超参数和早停测试集只用来评估最终效果千万不能混用。类别平衡也是容易被忽视的坑。如果你的检测目标有两种类别其中A类有3000个样本B类只有200个样本训练出来的模型几乎一定会偏向A类B类的召回率会非常难看。解决办法有几种最简单的就是对B类做数据增强比如翻转、旋转、亮度调整把样本量拉上来也可以用Image-Level的过采样让模型在每个batch里都能均衡地看到两类样本。3. 训练YOLOv8模型从配置到收敛的完整链路数据集准备好之后就可以进入模型训练环节。YOLOv8是目前个人项目和工业落地里用得最多的检测模型之一原因很简单精度高、速度快、部署生态成熟。在Jetson Nano这种算力有限的边缘设备上YOLOv8nnano版本或者YOLOv8ssmall版本是性价比最高的选择。3.1 训练环境的搭建与预训练权重选择训练环境我推荐用AutoDL这类云GPU平台没必要在本地笔记本上跑。YOLOv8的显存需求受batch size影响很大以YOLOv8n为例输入分辨率640x640、batch size为16的前提下显存占用大概6GB到8GB一张RTX 3060就能跑。预训练权重这块直接用ultralytics官方发布的YOLOv8n.pt就行。虽然你的检测目标不一定是COCO里的80类但预训练权重在COCO上学到的基础特征边缘、纹理、形状是可以迁移到你的任务上的。从零开始训练不是不行但需要更多的数据才能达到相同精度收敛速度也会慢很多。pip install ultralytics然后准备一个data.yaml文件内容大概是这样的结构train: /path/to/dataset/images/train val: /path/to/dataset/images/val nc: 2 names: [class_a, class_b]3.2 超参数配置的经验值训练命令本身不复杂但有几个超参数是真正决定模型质量的。我贴一份实测下来效果稳定的配置yolo detect train datadata.yaml modelyolov8n.pt epochs100 batch16 imgsz640 patience20 optimizerAdamW lr00.001这里逐项说下理由。imgsz640是速度和精度的平衡点分辨率再高到1280精度确实会涨但推理时间也会接近翻倍在Jetson Nano上这个开销不太值得。patience20是早停参数意思是如果连续20个epoch验证集损失没有下降训练就自动停止这能省下大量等训练的时间。optimizer选AdamW是因为它对学习率的敏感性比SGD低不需要精细调学习率策略也能稳定收敛。训练过程中要盯的指标主要是验证集上的mAP50和mAP50-95。mAP50指的是IoU阈值为0.5时的平均精度这个值一般能到0.9以上就算不错mAP50-95更严格是多个IoU阈值下的平均值对边界框的定位精度要求更高目标比较小的场景要重点看这个指标。3.3 训练完成后的模型导出训练完成后模型文件是weights/best.pt。这一步先别急着部署可以在本地跑一下测试集的推理看看实际的检测效果。如果出现大量误检多半是数据集本身的问题如果检测框位置偏了但类别对那就需要调整标注。等模型效果满意了再导出成适合Jetson Nano部署的格式。导出到TensorRT用的engine文件有两种路径一种是从.pt直接导出.onnx再转.engine另一种是直接利用ultralytics对TensorRT的支持一步到位。我个人的经验是先导出ONNX后续优化空间更大。yolo export modelbest.pt formatonnx imgsz6404. Jetson Nano端部署模型转换、推理与性能调优Jetson Nano的CPU是四核Cortex-A57GPU是128核Maxwell架构说实话算力不算强。如果不做任何优化直接拿PyTorch在上面跑YOLOv8n帧率大概只有2到3 FPS完全没法用。所以要部署到Jetson Nano上TensorRT是必须走的路线。4.1 环境准备JetPack版本与PyTorch安装的坑Jetson Nano的刷机系统是NVIDIA官方提供的JetPack SDK建议至少用JetPack 4.6。这里有个特别容易踩的坑JetPack自带的Python是3.6版本很多新版PyTorch轮子不支持所以安装PyTorch必须用NVIDIA官方预编译的版本不能在Jetson上用pip直接装pip install torch那样装出来的要么是CPU版本要么直接报错。NVIDIA为Jetson平台维护了一套预编译的PyTorch轮子需要到官方论坛对应帖子去找下载链接。安装命令大概是这样的wget https://developer.download.nvidia.com/compute/redist/jp/v46/pytorch/torch-1.12.0a08a1a93a9.nv22.5-cp36-cp36m-linux_aarch64.whl pip install torch-1.12.0a08a1a93a9.nv22.5-cp36-cp36m-linux_aarch64.whlTorchvision也要对应版本否则后面转ONNX的时候各种兼容性报错。核心原则就是不要在Jetson上折腾源码编译PyTorch时间成本太高收益为零。4.2 ONNX转TensorRT Engine精度与批量的权衡ONNX模型转TensorRT engine有两种常见方式一种是用NVIDIA官方提供的trtexec工具另一种是直接用TensorRT的Python API在代码里动态构建。用trtexec的命令大概是这样的/usr/src/tensorrt/bin/trtexec --onnxbest.onnx --saveEnginebest.engine --fp16这里关键参数是--fp16开启FP16精度可以将推理速度提升一到两倍。对于检测任务来说FP16带来的精度损失几乎可以忽略目标检测本身对数值精度不敏感除非你做的是像素级语义分割那种任务才需要考虑FP32。有个细节需要提醒--fp16在个别层上可能会因为动态范围不足导致输出异常如果你发现转完engine后检测框数量明显变少可以尝试加--stronglyTyped或者去掉FP16回归FP32再对比一下精度。engine文件生成之后推理代码可以用TensorRT的Python API写核心流程是加载engine、创建context、分配输入输出显存、执行推理、把结果拷回内存。但要手动处理这些细节确实比较繁琐。更推荐的做法是直接用ultralytics库中已经封装好的TensorRT推理逻辑它内部集成了NMS非极大值抑制可以直接拿到boxes、scores、class_ids三个数组。4.3 推理效率实测与优化方向我自己在Jetson Nano上实测过几次YOLOv8n在FP16下推理一张640x640的图片单帧耗时大概在70到100毫秒之间也就是10到14 FPS左右。这个速度做静态场景检测完全够用但你要是想让舵机跟手跟得很顺滑还得在几个方向上继续优化。第一个优化方向是减小输入分辨率。如果你的检测目标在画面中占比比较大可以把imgsz降到416甚至320推理时间可以缩短到50毫秒以内。第二个方向是TensorRT的workspace设置和profile优化用Python API构建engine的时候指定合适的workspaceSize给足显存空间。第三个方向是把图像预处理也放到GPU上做Jetson Nano上用OpenCV的cv2.dnn.blobFromImage处理效率比numpy数组操作高不少。从实际项目经验来看14 FPS的推理速度配上合理的控制算法舵机追踪的物理延迟已经能控制在200毫秒以内人眼的观感上算是比较顺畅了。5. 上下位机串口通信协议设计与鲁棒性考量Jetson Nano推理出结果之后怎么把“目标在哪、该往哪转”这个指令传给STM32这就是串口通信要解决的问题。别小看这一段通信协议设计得不好后面联调的时候你会被各种乱码、粘包、丢包折磨到怀疑人生。5.1 硬件连接与串口参数Jetson Nano的40Pin GPIO排针上有UART接口默认对应/dev/ttyTHS1这个设备节点。STM32这边选择USART1或者其他空闲串口。两者之间的电平需要注意Jetson Nano的GPIO UART是3.3V TTL电平STM32大多数开发板的串口也是3.3V TTL电平所以可以直接对接不需要MAX232转RS232。但千万别拿这个TTL电平去接RS232接口的设备电压范围完全不一样有烧毁风险。接线方式很简单Jetson的TXD接STM32的RXDJetson的RXD接STM32的TXDGND必须共地。我用的是Jetson Nano的物理引脚8TXD和物理引脚10RXD以及引脚6GND。串口波特率我建议选115200。这个波特率在短距离线缆上差错率很低同时传输速度足够满足指令帧的时效要求。实测一帧16字节的指令在115200波特率下传输时间不到1.5毫秒完全不会成为系统瓶颈。5.2 帧协议设计防粘包、防丢包的关键串口通信最怕的是什么是数据帧边界不清晰。如果每次只发一个字节的控制指令比如发个数字1代表“左转”发个数字2代表“右转”接收端确实很好解析但这完全没有扩展性而且一旦出现干扰字节整个控制序列就乱了。更工程化的做法是设计一帧固定长度的数据报文。我常用的帧格式如下字段长度说明帧头1字节固定0xAA标志一帧开始数据长度1字节有效数据长度目标ID1字节控制哪个舵机目标角度高位1字节角度值高8位目标角度低位1字节角度值低8位校验和1字节除帧头外所有字节累加取低8位简单解释一下为什么需要校验和。串口传输中偶尔会出现一个bit翻转比如0x2A变成0x2B没有校验和的话STM32会把错误的角值当成有效指令去执行舵机就会抽风一样乱跳而且很难排查。校验和是发送端把除帧头外的所有字节累加取结果的低8位附在帧尾接收端做同样计算比对收到的校验和字节不一致就丢弃整帧。这虽然是最简单的校验方式但在室内短帧通信场景下已经足够可靠。发送端的Python代码可以这样写import serial import struct ser serial.Serial(/dev/ttyTHS1, 115200, timeout0.1) def send_servo_command(servo_id, angle): # angle: 0-180 data bytearray() data.append(0xAA) # 帧头 data.append(0x03) # 有效数据长度(servo_id angle_high angle_low) data.append(servo_id) data.append((angle 8) 0xFF) data.append(angle 0xFF) checksum sum(data[1:]) 0xFF data.append(checksum) ser.write(data)5.3 STM32端的中断接收与状态机解析STM32端接收数据我强烈建议用串口空闲中断加DMA的方式或者至少用接收中断配合环形缓冲区。不推荐在主循环里轮询查看有没有数据因为STM32的主循环还要负责PWM更新、传感器读取等任务轮询容易出现数据漏收。状态机解析是处理串口数据最经典的模式。核心逻辑是每次收到一个字节就根据当前状态判断它是不是帧头然后是数据长度、数据内容、校验和。用一个枚举变量记录状态收到完整一帧且校验通过后置一个标志位主循环检测到标志位就去更新舵机PWM。typedef enum { FRAME_STATE_HEADER 0, FRAME_STATE_LENGTH, FRAME_STATE_DATA, FRAME_STATE_CHECKSUM } FrameState; void UART_RxCallback(uint8_t byte) { static FrameState state FRAME_STATE_HEADER; static uint8_t frame_buf[8]; static uint8_t frame_len 0; static uint8_t data_cnt 0; switch (state) { case FRAME_STATE_HEADER: if (byte 0xAA) { state FRAME_STATE_LENGTH; } break; case FRAME_STATE_LENGTH: frame_len byte; data_cnt 0; state FRAME_STATE_DATA; break; case FRAME_STATE_DATA: frame_buf[data_cnt] byte; if (data_cnt frame_len) { state FRAME_STATE_CHECKSUM; } break; case FRAME_STATE_CHECKSUM: uint8_t sum 0; sum frame_len; for (int i 0; i frame_len; i) sum frame_buf[i]; if ((sum 0xFF) byte) { // 解析frame_buf中的指令 servo_id frame_buf[0]; angle (frame_buf[1] 8) | frame_buf[2]; g_servo_target_angle angle; } state FRAME_STATE_HEADER; break; default: state FRAME_STATE_HEADER; break; } }这段代码的一个细节是数据缓冲区的长度定义。如果一帧数据最多是16字节那缓冲区数组至少定义16字节防止溢出。6. STM32控制舵机PWM信号、角度映射与抖动处理STM32收到目标角度之后最后一个环节就是把这个角度值转换成舵机能够理解的PWM脉冲宽度。这个环节做得好不好直接影响舵机转动的平滑度和精确度。6.1 舵机控制的基本原理大多数模拟舵机和数字舵机都接收50Hz的PWM信号也就是周期20毫秒其中高电平脉冲宽度决定了舵机的转角。标准舵机的脉宽范围是0.5ms到2.5ms对应0度到180度。这是一个线性的映射关系但这个线性关系在不同品牌、不同型号的舵机上会有一点差异严谨的做法是用数据手册给的范围而不是想当然地用通用的0.5到2.5。在STM32上生成50Hz的PWM信号常规做法是用一个定时器配置为PWM模式。以STM32F103系列为例使用TIM2的CH1通道预分频器设为72-1自动重载值设为20000-1这样72MHz的时钟分频后得到1MHz的计数频率20000个计数对应20毫秒周期也就是50Hz。然后只需要修改比较寄存器CCR的值就能改变脉冲宽度。TIM_TimeBaseInitTypeDef TIM_InitStructure; TIM_OCInitTypeDef TIM_OCInitStructure; // 72MHz / (72-1) 1MHz TIM_TimeBaseStructure.TIM_Prescaler 72 - 1; TIM_TimeBaseStructure.TIM_Period 20000 - 1; TIM_TimeBaseStructure.TIM_CounterMode TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, TIM_TimeBaseStructure); TIM_OCInitStructure.TIM_OCMode TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse 1500; // 1.5ms 对应中间位置 TIM_OC1Init(TIM2, TIM_OCInitStructure);这里TIM_Pulse1500对应的是1.5ms脉宽也就是90度正好是舵机的中位。6.2 角度到PWM脉宽的线性映射角度映射的计算公式很简单但有个细节值得注意。以0.5ms对应0度、2.5ms对应180度为例脉宽变化范围是2000微秒对应180度所以每度对应的脉宽增量为2000/180约等于11.11微秒。于是目标脉宽可以用这个公式计算uint32_t pulse_width_us 500 (angle * 2000) / 180;这里的运算需要注意数据溢出。angle如果是uint8_t类型最大值是255angle * 2000的结果是510000已经超出了uint16_t的表示范围所以在C语言里要把angle先转为uint32_t再做乘法。还有一些舵机支持270度甚至更大的转角范围对应的脉宽范围也会变宽比如0.5ms到3ms。这种时候公式里的2000要换成对应的数值千万不要套用固定模板。6.3 舵机抖动问题的分析与解决舵机抖动是这类项目里最让人头疼的问题之一。表现形式各有不同有的是舵机在目标位置附近来回颤动有的是启动瞬间猛跳一下再归位有的是转动过程中一顿一顿的。先说目标位置附近来回颤动的问题。这个一般有两个原因一个是PWM信号本身不稳定脉冲宽度在目标值附近波动另一个是舵机的负载过大舵机一直在目标位置附近反复修正。PWM不稳定可以通过示波器检查波形如果没有示波器可以用万用表频率档测一下PWM输出看是否有明显偏移。代码层面比较常见的问题是CCR值被频繁更新比如某个外设回调每毫秒改一次CCR值舵机的响应速度根本跟不上就会表现为抖动。解决方法是在主循环里加一个更新节流比如每20毫秒才允许更新一次目标脉宽。启动瞬间猛跳一下再归位的现象通常出现在系统上电的瞬间。原因是STM32复位之后定时器的寄存器处于未初始化状态CCR值可能为0舵机收到一个极小的脉宽就会猛转到一个极端位置。解决方法是把舵机供电和STM32控制信号分开控制让STM32先完成初始化再给舵机控制信号或者舵机供电。如果你的硬件已经固定了就在STM32初始化代码里先把CCR设置为中位值并且把PWM输出使能放在所有初始化完成之后。转动过程一顿一顿这个多半是因为角度更新的步长太大。比如直接从0度跳到180度舵机需要时间走完整个行程中间如果供电不足就会出现卡顿。解决方法是加一个平滑过渡逻辑让舵机的目标角度经过一个限速函数每一拍只允许增加或者减少一定的角度增量比如每20毫秒最多变化2度。这样舵机的转动看起来就会非常丝滑同时也能减轻舵机内部的齿轮负载。7. 整机联调从串口乱码到舵机跟手的完整调试过程前面各个模块单独都能跑了接下来就是联调阶段。这块内容是实际项目里最耗时、最容易踩坑的部分我把我在这个项目中实际遇到的情况和排查过程完整梳理一遍希望能帮读者少走弯路。7.1 串口通信不通的排查链路联调第一步是确认数据链路通畅。把Jetson Nano的测试代码跑起来往串口发一帧数据看STM32这边的接收标志有没有置位。如果STM32端完全没反应先查硬件连接确认TXD和RXD没接反GND共地没问题。我之前遇到过Jetson Nano引脚定义看错的情况TXD和RXD正好接反了折腾了半小时才反应过来。建议直接用万用表或者示波器量一下Jetson Nano的TXD引脚在发送时有没有电平跳变这一步能快速判断问题出在发送端还是接收端。如果硬件连接没问题但数据就是收不到那就要怀疑Linux系统层面的串口配置。Jetson Nano的/dev/ttyTHS1这个串口默认可能被系统控制台占用或者没有正确启用。检查一下/boot/extlinux/extlinux.conf文件看UBOOT的串口控制台有没有被映射到这个设备上。另外还要确认用户对/dev/ttyTHS1有读写权限否则Python打开串口的时候会报Permission denied。最简单的做法是把当前用户加入dialout组或者在启动命令里用sudo运行脚本。7.2 乱码与校验失败的根因分析如果数据能收到但解析不对最常见的现象是校验和一直失败。排查思路要分层来看。先排除波特率不匹配的问题。Jetson Nano的Python serial库写的是115200STM32的HAL库初始化也是115200两边参数看起来一致但如果STM32的外部晶振不是标准的8MHz比如用了某些开发板上的12MHz晶振那实际波特率就会偏离设定值时间一长累计误差就会导致接收字节错乱。这种情况下的典型现象是短数据偶尔能通长一点的帧就出错。解决方法是调整STM32的时钟配置或者换用误差更小的波特率。再检查信号线干扰。串口线如果和舵机电源线绑在一起走舵机转动时的大电流瞬变会在信号线上感应出噪声导致字节翻转。这在物理层面无解只能从布线上去规避信号线尽量远离电源线必要时用双绞线或者屏蔽线信号回路单独走。代码层面的问题也不可忽视。接收中断服务函数里的逻辑一定要精简如果ISR里做了太多事情比如浮点运算、printf输出会导致下一次串口中断无法及时响应数据漏收之后帧就乱了。我之前在STM32的ISR里加了一个调试用的printf打印结果联调的时候串口数据疯狂出错就是因为printf太耗时间把接收节奏全打乱了。7.3 目标检测到舵机跟手的稳定性打磨通信链路通了之后最后一个难点就是把检测结果和舵机运动结合成一个稳定的闭环。这个阶段要解决一个关键问题舵机的控制频率和模型推理频率不一致怎么办。Jetson Nano的推理速度大概是10到14 FPS也就是说每70到100毫秒能出一个新的检测结果。STM32的PWM更新频率是50Hz每20毫秒一次。如果每次推理结果一到就立刻更新舵机目标角度再加上舵机自身需要时间去物理转动整个系统会非常“慌张”舵机还没走到指定位置新指令又来了表现出来就是来回抖动、永远稳定不下来。我最终采用的方案是加一个简单的低通滤波逻辑。在STM32端维护一个当前角度和目标角度每20毫秒执行一次更新时让当前角度向目标角度逼近一个固定比例比如每次逼近20%。这个比例的平方效果其实更平滑能让舵机在接近目标位置时自动减速很接近工程里常说的S曲线效果但实现成本低得多。还有一点是关于检测丢失的处理。如果某一帧没有检测到目标不要立刻让舵机回到初始位置否则目标短暂出画框时舵机就会疯狂复位。比较稳妥的做法是连续N帧比如5帧都检测不到目标才判定目标真正丢失这时候再让舵机回到中位。这个防抖逻辑在实际演示中非常有效观众不会看到舵机“神经质”地乱动。7.4 数据闭环验证与效果评估整个系统联调完成之后我会习惯性地做一组数据验证确认系统的端到端延迟和精度在可接受范围内。端到端延迟的测量方法很简单在Jetson Nano代码里记录从摄像头获取图像到串口发送指令的时间戳在STM32端记录从收到指令到PWM更新完成的时间戳。两个时间差就是系统的核心延迟。我的实测数据是总延迟大约在150到200毫秒之间其中模型推理占大头串口传输和STM32处理几乎可以忽略不计。这个延迟水平对于舵机云台追踪来说人眼主观感受就是“有点跟手但不算秒跟”在大多数场景下是可以接受的。舵机转动精度的验证方法是给舵机一个固定的目标角度用游标卡尺或者其他角度传感器测量实际转动位置多次测量取平均值和标准差。正常情况下标准舵机的重复定位精度在正负3度以内。如果偏差过大先查脉宽映射公式是不是有线性误差再查舵机的供电电压是否稳定电压偏低时舵机的力矩下降定位会明显变差。8. 功率分配与电气稳定性一套易被忽略却决定成败的细节前面讲的都是软件和算法层面的内容最后这部分要提一个在嵌入式AI项目里很容易被忽视的电气问题——功率分配。Jetson Nano是出了名的“电老虎”启动瞬间的电流尖峰和满载运行时的持续功耗都不小。如果和舵机的电源混在一起很容易出现互相干扰甚至系统重启的问题。8.1 Jetson Nano的供电方案选择Jetson Nano有两种主流供电方式Micro USB供电和DC 5V barrel jack供电。Micro USB接口标称最大只能提供2A电流也就是10W左右这个功率在跑轻量模型时勉强够用但一旦GPU满载电压就会被拉低系统可能会出现降频甚至突然关机。如果条件允许强烈建议用DC电源口供电配一个5V 4A的电源适配器留足功率余量。舵机供电就更有讲究了。舵机转动时瞬时电流可以达到几百毫安甚至1安培以上多路舵机同时动作时电流更大。如果舵机和Jetson Nano共用同一个电源舵机转动时的电压跌落会直接映射到Jetson Nano的供电上轻则导致推理帧率波动重则系统直接重启。我之前确实在这个问题上栽过跟头一开始共用一个5V 3A的电源结果舵机一转Jetson就黑屏重启排查了好久才发现是电压跌落。正确方案是Jetson Nano用一路电源舵机单独用一路电源两者只共地不共用电源正极。舵机的电源可以是一个单独的5V可调BEC或者稳压模块电流裕量至少是舵机峰值电流的两倍。8.2 信号地与电源地的处理单独供电之后会引入一个新的问题信号地和电源地怎么处理。我的建议是信号地Jetson的GND与STM32的GND必须保持连通而舵机电源的地也要和STM32的GND连在一起形成一个统一的参考地。这样做是为了保证PWM信号在STM32和舵机之间有明确的电平参考。如果地之间电位不一致轻则舵机抖动重则损坏舵机的控制芯片。如果实在无法避免地线环路的干扰可以考虑在串口信号线上加一个共模电感或者磁珠这能有效滤除高频干扰但对直流低频干扰效果有限。更稳妥的方式是使用数字隔离器比如ISO7742这类器件把Jetson和STM32的串口信号隔离开但这样会引入额外的插件成本和延迟一般场景下必要性不大。9. 串口消息的调试神技你值得拥有的几种实用工具最后再分享几个关于串口调试的杀手锏技巧这些是文档里很少写到但实际项目中尤其有用的内容。Linux端的串口调试我常用的工具是minicom和Python的serial库直接写脚本。minicom适合快速验证串口是否能通比如在命令行直接输入echo hello /dev/ttyTHS1看STM32那边有没有反应。如果是在Windows端调试STM32XCOM和SSCOM都是比较顺手的工具。但在联调阶段我最推荐的方法是写一个双向的回环测试脚本。Jetson这边发一串带序号的数据STM32收到后原样回传Jetson这边同时开一个线程接收回传数据并检查序号是否连续、数据是否一致。这个脚本能在几秒钟内把串口的丢包率、误码率、最大连续帧数都测出来联调阶段能节省大量排查时间。import serial import time import random ser serial.Serial(/dev/ttyTHS1, 115200, timeout0.1) errors 0 for i in range(1000): seq random.randint(0, 255) payload bytes([seq, i 0xFF]) frame bytes([0xAA, len(payload)]) payload checksum sum(frame[1:]) 0xFF frame bytes([checksum]) ser.write(frame) resp ser.read(4) if len(resp) ! 4 or resp[0] ! 0xAA: errors 1 time.sleep(0.005) print(fround trip test finished, errors{errors}/1000)如果这个脚本跑出来的错误率超过1%那串口链路大概率存在电气问题或者波特率偏移不要往下做控制逻辑了先把链路焊死再说。关于这套架构和这个项目的实际经验我最想强调的就一句话边缘AI项目真正能不能落地拼的不只是模型精度更是整个嵌入式系统的协同能力。Jetson Nano负责视觉、STM32负责运动控制两个芯片各有各的脾气把它们协调好让数据流转起来整个系统才能真正跑得又稳又顺。本文还有配套的精品资源点击获取
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻