基于Jetson Nano与STM32的嵌入式AI视觉伺服系统实战
2026/9/5 22:43:14 网站建设 项目流程

简介:本资源是一套面向嵌入式AI开发者的端侧智能控制实战项目,聚焦Jetson Nano与STM32协同实现垃圾分类模型推理及舵机物理响应的完整链路,适用于具备基础C语言编程与嵌入式开发经验的进阶学习者。项目覆盖数据集准备(含图像增强与划分)、PyTorch/TensorFlow模型训练与TensorRT优化部署、Jetson Nano端推理服务封装、UART串口通信协议设计、STM32F10x系列C语言固件开发(含TIM/PWM舵机驱动、USART收发逻辑)等关键环节。压缩包共110个文件,含38个C源码(如stm32f10x_usart.c、stm32f10x_tim.c)、40个头文件、8个汇编启动文件,以及Paddle Lite模型文件(.pdmodel/.pdiparams)、Keil工程(.uvprojx)、Python脚本与实操演示mp4视频,整体大小142.82MB。已有1812人学习下载,提供从模型训练到硬件联动的全栈代码、可直接烧录的STM32固件、Jetson端部署脚本及通信调试说明,显著降低边缘AI落地门槛。

1. 项目概述:从数据到动作的嵌入式AI闭环

最近在折腾一个挺有意思的项目,核心目标是把一个深度学习模型从“纸上谈兵”变成“真刀真枪”的物理动作。具体来说,就是让Jetson Nano这个边缘计算小盒子,能够识别摄像头里的目标,然后把识别结果告诉一块STM32单片机,最后由STM32精准地控制舵机转动,指向目标。听起来像是机器人视觉伺服或者智能追踪云台的雏形,对吧?这正是边缘AI落地的典型场景——让智能发生在数据产生的地方,并即时产生物理世界的反馈。

这个项目适合所有对嵌入式AI、物联网硬件交互感兴趣的开发者,无论你是想为机器人增加“眼睛”,还是想做一个自动追踪装置,这个流程都是通用的。整个过程串联了AI模型部署、异构硬件通信、底层电机控制三个关键环节,任何一个环节卡住,整个系统就“瘫痪”了。我把自己从准备数据到最终让舵机动起来的完整过程,包括踩过的坑和验证有效的技巧,都梳理在这里。你会发现,难点往往不在算法本身,而在如何让算法稳定、高效地与硬件“对话”。

2. 项目整体设计与思路拆解

2.1 核心需求与方案选型

这个项目的需求非常明确:实时视觉感知驱动物理执行机构。拆解开来,就是三个子任务:

  1. 视觉感知:在Jetson Nano上运行一个目标检测或分类模型,实时处理摄像头视频流。
  2. 决策与通信:Jetson Nano根据识别结果(如目标类别、位置坐标)生成控制指令。
  3. 执行控制:STM32接收指令,解析并生成相应的PWM信号,驱动舵机转动到指定角度。

为什么选择Jetson Nano + STM32这个组合?这是基于性能和成本的权衡。Jetson Nano拥有128核的GPU,足以在本地实时运行经过优化的轻量级深度学习模型(如YOLOv5s, MobileNet SSD),避免了将所有视频流上传到云端带来的延迟和隐私问题。然而,Jetson Nano的GPIO引脚驱动能力和实时性,对于需要精确时序控制的舵机而言,并非最佳选择。舵机控制要求稳定、无抖动的PWM信号,任何由操作系统调度或后台进程引起的时间抖动都会导致舵机抖动或定位不准。

STM32恰恰弥补了这个短板。作为经典的微控制器,它在处理确定性的、周期性的任务(如生成PWM)方面具有天然优势,中断响应快,几乎没有抖动。因此,分工协作是最优解:Jetson Nano负责“思考”(AI推理),STM32负责“执行”(精准控制)。两者之间通过串口(UART)通信,这是一种简单、可靠、被广泛支持的通信方式。

2.2 技术栈与工具链准备

工欲善其事,必先利其器。以下是整个项目涉及的核心技术栈和工具,我会说明每个选择的理由。

Jetson Nano侧:

  • 操作系统: NVIDIA官方提供的JetPack SDK(基于Ubuntu 18.04/20.04 LTS)。这是必须的,因为它包含了CUDA、cuDNN、TensorRT等核心AI计算库的预配置版本,省去了大量手动配置的麻烦。
  • 深度学习框架PyTorch。虽然TensorFlow也有Jetson版本,但PyTorch在研究和快速原型开发中更流行,其模型转换为TensorRT的流程也相对成熟。我们的模型训练将在PC端完成,然后部署到Nano。
  • 推理加速引擎TensorRT。这是NVIDIA官方的高性能深度学习推理SDK。它能将训练好的模型进行优化(包括层融合、精度校准、内核自动调优),并转换为在Jetson上高效运行的引擎,显著提升推理速度并降低延迟。
  • 通信库: Python的pyserial库。用于实现Jetson Nano通过串口与STM32通信。
  • 摄像头: 官方推荐的Raspberry Pi Camera V2(CSI接口)或通用的USB摄像头。CSI摄像头延迟更低,占用CPU资源少,是首选。

STM32侧:

  • 开发板: 任意一款带有UART和至少一个定时器(用于产生PWM)的STM32系列开发板,如STM32F103C8T6(蓝色药丸板)、STM32F407等。F103系列性价比高,资源足够。
  • 开发环境
    • STM32CubeIDE: ST官方推出的免费集成开发环境,集成了STM32CubeMX图形化配置工具和调试器。它可以通过图形界面配置引脚、时钟、外设(UART, 定时器),自动生成初始化代码,极大提升了开发效率,特别适合初学者和快速开发。
    • STM32CubeMX: 独立的引脚和时钟配置工具,可以生成多种IDE的工程代码。如果习惯Keil或IAR,可以用它生成代码。
  • 编程框架HAL库。相对于标准外设库,HAL库抽象层次更高,代码可移植性更好,虽然效率略有牺牲,但开发速度更快,更不容易出错,对于本项目完全够用。

通信协议设计:这是连接两大核心的“语言”,设计的好坏直接决定系统稳定性。我们需要定义一个简单、高效、容错的串口通信协议。 我设计了一个基于ASCII字符的文本协议,格式如下:[指令头],[参数1],[参数2],...\n例如,控制舵机转到90度:S,90\n

  • S: 指令头,代表“Set Servo angle”。
  • 90: 参数,代表目标角度。
  • \n(换行符): 作为帧结束符,方便接收方解析。

为什么用文本协议而不是二进制?文本协议直观,易于在调试串口助手上查看和模拟,出现问题时排查方便。虽然效率略低于二进制,但对于“角度值”这种低频、小数据量的传输,完全不是瓶颈。关键在于一定要有明确的帧结束符,避免数据粘包问题。

3. 核心环节一:数据集准备与模型训练

3.1 数据采集与标注策略

模型要准,数据先行。如果你的目标是识别特定物体(比如一个红色的球、一个人脸、一种手势),就需要自己采集和标注数据。

  • 采集: 直接在Jetson Nano上连接摄像头,用Python(OpenCV)写个脚本录制视频或抓拍图片。尽可能在最终部署的环境光照、背景、角度变化下采集数据,增强模型的鲁棒性。采集几百到几千张图片作为起点。
  • 标注: 使用标注工具如LabelImgCVATRoboflow。对于目标检测,需要框出物体并打上标签。标注格式通常选择PASCAL VOC XML或更流行的YOLO格式(每个图片对应一个.txt文件,内容为类别id x_center y_center width height,坐标是归一化后的值)。
  • 数据增强: 在数据量不足时,通过旋转、缩放、裁剪、调整亮度对比度、添加噪声等方式人工扩充数据集,能有效防止模型过拟合。Roboflow等在线平台提供了便捷的增强功能。

注意: 标注的一致性至关重要。同一个物体在所有图片中的边界框要紧密度、类别名称必须完全一致。混乱的标注是模型性能不佳的首要原因。

3.2 模型选择、训练与优化

对于Jetson Nano这类边缘设备,模型必须轻量化

  • 模型选择

    • 目标检测YOLOv5n/sSSD-MobileNetV2/V3。YOLOv5n是YOLO系列中最轻量的,速度极快,精度尚可;SSD-MobileNet系列在精度和速度上平衡得很好,且与TensorRT的兼容性非常成熟。
    • 图像分类MobileNetV2/V3EfficientNet-Lite。这些是专为移动和边缘设备设计的网络。 我本次以YOLOv5s为例,它在Nano上配合TensorRT优化后,可以达到接近实时的帧率(15-30 FPS,取决于输入分辨率)。
  • 训练环境: 在拥有NVIDIA GPU的PC或服务器上进行训练。使用PyTorch框架和YOLOv5官方仓库。

    # 克隆YOLOv5仓库 git clone https://github.com/ultralytics/yolov5 cd yolov5 pip install -r requirements.txt # 准备好自己的数据集,按照YOLO格式组织 # 修改data/custom.yaml,定义自己的类别和路径 # 开始训练 python train.py --img 640 --batch 16 --epochs 100 --data data/custom.yaml --weights yolov5s.pt
  • 关键训练参数

    • --img 640: 输入图像尺寸。更小的尺寸(如320)速度更快,但精度可能下降。640是一个较好的平衡点。
    • --batch: 批大小。根据你的GPU显存调整。
    • --epochs: 训练轮数。观察训练损失和验证集精度曲线,在过拟合前停止。
  • 模型导出: 训练完成后,得到的最佳模型是.pt文件。需要将其导出为TensorRT可用的格式。通常先导出为ONNX格式,再转换为TensorRT引擎。

    # 导出为ONNX python export.py --weights runs/train/exp/weights/best.pt --include onnx --img 640 --dynamic

    这里--dynamic参数允许动态的批处理大小,在部署时更灵活。

4. 核心环节二:Jetson Nano上的模型部署与推理

4.1 TensorRT引擎生成与优化

这是提升边缘端推理性能的关键一步。我们将ONNX模型转换为TensorRT引擎。

  1. 安装TensorRT: JetPack已包含TensorRT,但可能需要确认版本。使用dpkg -l | grep tensorrt查看。

  2. 使用trtexec工具转换: TensorRT自带命令行工具trtexec,可以方便地进行转换和性能基准测试。

    # 基本转换命令,生成FP16精度的引擎,显著提升速度且精度损失很小 trtexec --onnx=best.onnx --saveEngine=best_fp16.engine --fp16 --workspace=1024
    • --fp16: 启用半精度浮点数推理,速度大幅提升,是Jetson上的首选。
    • --workspace: 设置GPU内存工作空间大小(MB),复杂模型可能需要更大空间。
    • 还可以添加--int8进行INT8量化,速度最快,但需要校准数据集,过程更复杂,精度损失可能更大。
  3. 编写Python推理脚本: 使用TensorRT的Python API加载引擎并进行推理。

    import tensorrt as trt import pycuda.driver as cuda import pycuda.autoinit import numpy as np import cv2 class TrtModel: def __init__(self, engine_path): # 加载引擎,创建执行上下文 with open(engine_path, 'rb') as f, trt.Runtime(trt.Logger(trt.Logger.WARNING)) as runtime: self.engine = runtime.deserialize_cuda_engine(f.read()) self.context = self.engine.create_execution_context() # 分配输入输出内存(绑定) self.bindings = [] for binding in self.engine: size = trt.volume(self.engine.get_binding_shape(binding)) * self.engine.max_batch_size dtype = trt.nptype(self.engine.get_binding_dtype(binding)) # 分配页锁定内存以提高传输效率 host_mem = cuda.pagelocked_empty(size, dtype) device_mem = cuda.mem_alloc(host_mem.nbytes) self.bindings.append(int(device_mem)) # ... 将输入输出绑定与指针关联 def infer(self, input_image): # 图像预处理(缩放、归一化、BGR2RGB、转换为CHW格式) # 将数据从主机拷贝到设备 cuda.memcpy_htod(self.bindings[0], input_data.ravel()) # 执行推理 self.context.execute_v2(bindings=self.bindings) # 将结果从设备拷贝回主机 cuda.memcpy_dtoh(host_output, self.bindings[1]) # 后处理(解析YOLO输出,应用置信度阈值和NMS) return boxes, scores, class_ids

    实操心得: 图像预处理(如归一化到0-1之间)和后处理(解析检测框、非极大值抑制NMS)的代码必须与模型训练时的设置严格一致,否则结果会完全错误。这部分代码可以借鉴原训练仓库中的dataset.pyutils.general.py

4.2 实时视频流处理与通信逻辑

推理脚本需要嵌入到一个视频捕获循环中,并加入通信逻辑。

import cv2 import serial import time # 初始化串口 # 注意:Jetson Nano的串口设备通常是 /dev/ttyTHS1 或 /dev/ttyUSB0 ser = serial.Serial('/dev/ttyTHS1', 115200, timeout=1) # 波特率需与STM32匹配 # 初始化摄像头 cap = cv2.VideoCapture(0) # 对于CSI摄像头,可能是 `cap = cv2.VideoCapture('nvarguscamerasrc ! ...')` GStreamer管道 cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) # 初始化TensorRT模型 model = TrtModel('best_fp16.engine') while True: ret, frame = cap.read() if not ret: break # 执行推理 detections = model.infer(frame) # 假设我们只追踪一个主要目标(如置信度最高的) if len(detections) > 0: # 获取目标在图像中的中心点x坐标 (0~640) x_center = (detections[0][0] + detections[0][2]) / 2 # (x1 + x2) / 2 # 将像素坐标转换为舵机角度 (例如:0~640像素 映射到 0~180度) # 这里是一个简单的线性映射,你可能需要更复杂的策略(如PID控制) target_angle = int((x_center / 640) * 180) target_angle = max(0, min(180, target_angle)) # 限制在舵机有效范围 # 生成控制指令并发送 command = f"S,{target_angle}\n" ser.write(command.encode('ascii')) print(f"Sent: {command.strip()}") # 显示结果(可选) # draw_boxes(frame, detections) # cv2.imshow('Detection', frame) # if cv2.waitKey(1) & 0xFF == ord('q'): # break cap.release() cv2.destroyAllWindows() ser.close()

关键点解析

  1. 串口初始化: 确保端口号和波特率(这里是115200)与STM32端完全一致。timeout参数设置了读操作的超时时间。
  2. 坐标映射: 将检测框的中心x坐标(像素值)线性映射到舵机的角度范围(0-180度)。这是最简单的“比例控制”。在实际应用中,如果要求云台平滑稳定地跟踪,可能需要引入PID控制器,根据目标位置与当前位置的误差来动态计算控制量,避免舵机抖动和超调。
  3. 指令发送: 务必在指令末尾加上换行符\n,这是STM32端解析帧的结束标志。

5. 核心环节三:STM32的通信与舵机控制

5.1 STM32开发环境搭建与外设配置

  1. 使用STM32CubeMX创建工程

    • 选择你的MCU型号(如STM32F103C8Tx)。
    • 配置时钟树: 将HCLK(系统时钟)设置到最大允许频率(对于F103是72MHz),以获得最佳性能。
    • 配置USART
      • 使能一个USART(如USART1)。
      • 模式选择为“Asynchronous”(异步)。
      • 波特率设置为115200(与Jetson Nano一致)。
      • 字长8位,停止位1,无校验。
      • 开启全局中断(方便使用中断接收数据)。
    • 配置定时器用于PWM
      • 使能一个通用定时器(如TIM2)。
      • 选择一个通道(如Channel 1)为“PWM Generation CH1”。
      • 根据舵机要求配置PWM频率。标准舵机控制信号是周期20ms(50Hz)的PWM波。在72MHz系统时钟下,计算预分频器(PSC)和自动重载值(ARR)以产生50Hz频率。
        • 定时器时钟 = 72MHz / (PSC + 1)
        • 计数频率 = 定时器时钟 / (ARR + 1)
        • 目标: 计数频率 = 50Hz。例如,设置PSC=7199,ARR=199。则定时器时钟=72MHz/7200=10kHz,计数频率=10kHz/200=50Hz。
    • 生成代码: 选择工具链为STM32CubeIDE,生成初始化代码。
  2. 在STM32CubeIDE中编写业务逻辑: 生成的代码已经完成了外设的初始化(HAL_UART_Init,HAL_TIM_PWM_Init)。我们主要需要在main.c中编写串口接收中断回调函数和舵机控制函数。

5.2 串口数据解析与舵机PWM控制实现

串口数据接收(中断方式): 我们使用串口空闲中断(Idle Interrupt)来接收不定长数据。当一帧数据(以\n结尾)接收完毕,总线空闲时,会触发空闲中断,此时我们可以处理已接收的数据。

// 在main.c的USER CODE BEGIN PV区域定义缓冲区 uint8_t uart_rx_buffer[64]; uint8_t uart_rx_len = 0; // 在main函数初始化后,开启串口接收和空闲中断 HAL_UART_Receive_IT(&huart1, uart_rx_buffer, 1); // 先启动接收一个字节 __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); // 使能空闲中断 // 在stm32f1xx_it.c中找到USART1_IRQHandler函数,添加空闲中断处理 void USART1_IRQHandler(void) { if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE) != RESET) { __HAL_UART_CLEAR_IDLEFLAG(&huart1); // 停止本次DMA传输,获取接收到的数据长度 // 注意:如果用的是中断接收而非DMA,处理方式略有不同 HAL_UART_DMAStop(&huart1); uart_rx_len = sizeof(uart_rx_buffer) - __HAL_DMA_GET_COUNTER(&hdma_usart1_rx); // 处理数据 uart_rx_buffer[0...uart_rx_len-1] process_uart_command(uart_rx_buffer, uart_rx_len); // 重新启动接收 memset(uart_rx_buffer, 0, sizeof(uart_rx_buffer)); HAL_UART_Receive_DMA(&huart1, uart_rx_buffer, sizeof(uart_rx_buffer)); } HAL_UART_IRQHandler(&huart1); }

注意: 上述代码示例使用了DMA+空闲中断,这是最高效的方式。如果使用普通中断模式,需要在每次字节接收中断中填充缓冲区,并在收到\n字符时认为一帧结束。

数据解析与舵机控制函数

void process_uart_command(uint8_t* buf, uint16_t len) { // 1. 转换为字符串,并确保以\0结尾 buf[len] = '\0'; char* command = (char*)buf; // 2. 简单的字符串解析 (例如 "S,90") char* token = strtok(command, ","); if(token == NULL) return; if(strcmp(token, "S") == 0) { // 舵机角度设置指令 token = strtok(NULL, ","); if(token != NULL) { int angle = atoi(token); // 将字符串转换为整数 angle = (angle < 0) ? 0 : (angle > 180) ? 180 : angle; // 限幅 set_servo_angle(angle); } } // 可以扩展其他指令,如 "C" 用于校准等 } void set_servo_angle(uint16_t angle) { // 将角度(0-180)映射到PWM占空比对应的比较寄存器值(CCR) // 舵机控制脉宽通常为0.5ms(0度) ~ 2.5ms(180度),周期20ms // 占空比 = 脉宽 / 周期 = (0.5 + angle * 2.0 / 180) / 20 // CCR值 = 占空比 * (ARR + 1) // 根据之前计算的ARR=199,定时器计数200对应20ms // 0度对应脉宽0.5ms -> 计数值 = 0.5 / 20 * 200 = 5 // 180度对应脉宽2.5ms -> 计数值 = 2.5 / 20 * 200 = 25 // 因此,映射公式: ccr = 5 + angle * (20 / 180) = 5 + angle / 9 uint16_t ccr = 5 + angle / 9; // 简化计算,注意整数除法 __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, ccr); // 设置TIM2 Channel1的CCR值 }

关键点解析

  1. PWM计算: 舵机控制的核心是生成一个周期为20ms(50Hz),高电平脉宽在0.5ms到2.5ms之间的PWM信号。CCR(捕获/比较寄存器)的值直接决定了高电平的时间。上述映射公式ccr = 5 + angle / 9是基于ARR=199(即200个计数周期对应20ms)推导出的线性映射。不同的舵机型号或定时器配置,这个公式需要重新计算
  2. 启动PWM输出: 在main函数中,初始化后需要启动PWM输出:HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
  3. 稳定性: 直接映射角度可能导致舵机运动不平滑。在实际应用中,可以加入软件滤波(如移动平均)或更高级的控制算法(如梯形速度曲线),让舵机缓慢加速、减速,运动更平稳,减少机械冲击。

6. 系统联调与性能优化实战

6.1 硬件连接与上电调试步骤

  1. 物理连接

    • Jetson Nano与STM32: 将Jetson Nano的UART TX引脚(如/dev/ttyTHS1对应的引脚,通常是板子上的第8脚)连接到STM32的USART RX引脚(如PA10)。将Jetson Nano的UART RX引脚连接到STM32的USART TX引脚(如PA9)。务必共地(连接两者的GND引脚)。
    • STM32与舵机: STM32的PWM输出引脚(如TIM2_CH1对应的PA0)连接到舵机的信号线(通常是橙色或白色线)。舵机的VCC(红色线)和GND(棕色线)连接到一个独立的5V电源切勿使用STM32开发板的3.3V或5V引脚直接给舵机供电,尤其是大扭矩舵机,启动电流很大,会导致单片机复位或损坏。使用外接电源,并与STM32共地。
    • 摄像头: 将CSI摄像头排线插入Jetson Nano的CSI接口,或者插入USB摄像头。
  2. 上电与调试顺序

    • 先给STM32和舵机的外接电源上电。
    • 再启动Jetson Nano。
    • 这个顺序可以避免通信引脚在未知状态时产生乱码,干扰STM32启动。
  3. 分步调试法

    • 第一步:测试STM32 PWM。先不连接Jetson,在STM32代码中写一个简单的角度循环变化程序,用示波器或逻辑分析仪测量PWM引脚输出,确认脉宽随角度变化正确(0.5ms~2.5ms)。观察舵机是否跟随转动。
    • 第二步:测试串口通信。将STM32通过USB转TTL模块连接到电脑,用串口助手(如Putty, SecureCRT)发送S,90\n格式的指令,观察舵机是否转动到90度。同时,STM32可以回传一些调试信息到串口助手,确认接收解析正常。
    • 第三步:测试Jetson Nano推理。暂时注释掉串口发送代码,运行推理脚本,确保摄像头打开,模型能正确识别并输出目标坐标。
    • 第四步:全系统联调。连接Jetson Nano与STM32的串口,运行完整的Jetson程序。观察舵机是否跟随目标移动。

6.2 通信稳定性与系统性能优化

通信稳定性问题与解决

  • 问题:数据丢包或错乱
    • 排查: 在Jetson端和STM32端都加入打印语句,将发送和接收到的原始数据打印出来(或通过另一个串口回传到电脑)。检查波特率是否绝对一致(115200),检查电平是否匹配(Jetson Nano是3.3V TTL,STM32也是3.3V,可以直接连接)。
    • 解决: 在通信协议中加入校验和。例如,指令改为S,90,99\n,其中99是前面字符的累加和取模。STM32端收到后重新计算校验和,如果不匹配则丢弃该帧。还可以加入应答机制,Jetson发送指令后等待STM32回复“OK”,超时则重发。

系统性能优化

  • Jetson Nano推理速度
    • 模型优化: 使用TensorRT的FP16或INT8量化是最大的性能提升手段。
    • 输入分辨率: 将模型输入分辨率从640降低到320,速度会成倍提升,但精度下降。需要权衡。
    • 视频流解码: 对于CSI摄像头,使用GStreamer管道(nvarguscamerasrc)配合nvv4l2decoder进行硬件解码,比OpenCV的cv2.VideoCapture软件解码效率高得多。
    • Python性能: 避免在推理循环中进行不必要的内存分配和拷贝。使用预分配的缓冲区。
  • 控制响应平滑度
    • 直接映射的抖动问题: 摄像头检测的坐标会有噪声,直接映射会导致舵机高频微抖。
    • 解决方案
      1. 低通滤波: 对Jetson计算出的目标角度进行移动平均滤波。
      2. 死区控制: 设定一个角度死区(如±2度),只有当误差超过死区时才发送新指令,减少不必要的通信和运动。
      3. PID控制: 在STM32端实现一个简单的PID位置控制。Jetson只发送目标角度,STM32以固定频率(如100Hz)读取当前舵机角度(如果舵机带位置反馈)或估算角度,通过PID计算PWM输出,使运动更平滑、精准。这是工业级的做法。

6.3 常见问题排查速查表

问题现象可能原因排查步骤与解决方案
Jetson Nano无法打开摄像头摄像头驱动问题/端口占用1. 检查摄像头连接。2. 对于CSI摄像头,尝试官方GStreamer示例。3. 使用ls /dev/video*查看设备节点。4. 确保没有其他程序占用摄像头。
模型推理速度极慢(<1 FPS)未使用TensorRT/模型未优化1. 确认使用的是.engine文件而非.pt.onnx。2. 使用jetson-statsjtop)工具监控GPU、CPU使用率,确认推理时GPU是否工作。3. 检查是否误用了CPU模式。
STM32舵机不转动电源/PWM信号/代码问题1.首要检查电源:舵机是否独立供电?电压是否足5V?电流是否足够(建议2A以上)?2. 用示波器检查PWM引脚是否有输出,脉宽是否正确。3. 检查代码中定时器PWM输出是否已启动HAL_TIM_PWM_Start。4. 检查舵机信号线是否接反。
舵机抖动或啸叫电源功率不足/PWM信号不稳定1.最常见原因:电源功率不足,换用更大电流的电源。2. PWM信号受到干扰,确保信号线远离电源线。3. STM32的PWM频率不稳定,检查系统时钟和定时器配置,确保生成精确的50Hz。
串口通信无反应接线错误/波特率不匹配/未共地1. 检查TX-RX是否交叉连接。2. 双方波特率、数据位、停止位、校验位设置是否完全一致。3.必须共地!4. 使用USB转TTL工具在电脑上模拟一端,分段测试。
舵机角度不准PWM脉宽计算错误/舵机个体差异1. 用示波器测量0度和180度时的实际脉宽,修正代码中的映射公式。2. 不同品牌舵机的中位脉宽可能不是1.5ms,需要进行校准。可以在代码中增加校准指令,通过串口发送测试脉宽来找到舵机的实际极限和中位值。

最后的个人体会: 这个项目麻雀虽小,五脏俱全。它把AI、嵌入式、控制三个领域串了起来。最大的挑战往往不是某个高深算法,而是跨平台、跨语言的协同调试。我的经验是,一定要分模块、分步骤验证,每个环节都确保独立工作正常,再连起来调。比如,先用串口助手调通STM32,再用简单的Python脚本发数据,最后上完整的AI模型。另外,硬件项目电源是“祖宗”,一大半的诡异问题都源于供电不足或干扰。给舵机一个独立、充沛的电源,能省去后面无数的麻烦。当看到舵机随着你手里的物体平稳转动的那一刻,你会觉得所有这些折腾都是值得的。这个框架可以轻松扩展,比如把舵机换成步进电机做更精确的控制,或者让STM32驱动多个舵机组成机械臂,Jetson Nano则作为视觉大脑,那就能玩出更多花样了。

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询