基于旭日X3派与TogetherROS实现人体跟随小车:从算法部署到电机控制全流程解析
1. 项目概述与核心思路一直对AI驱动的智能硬件很着迷尤其是那种能“看懂”你、跟着你走的机器人。这次拿到地平线的旭日X3派开发板就琢磨着能不能用它复现一个经典项目——人体跟随小车。说白了就是让小车通过摄像头“看见”人然后像个小跟班一样自动跟着你移动实现真正的“人走车随”。这不仅是将计算机视觉和机器人控制结合的绝佳实践也是深入理解嵌入式AI应用从感知到决策再到执行全流程的好机会。这个项目的核心逻辑链条非常清晰感知 - 决策 - 执行。旭日X3派搭载的BPUBrain Processing Unit负责运行高效的人体检测算法这就是“眼睛”和“大脑”的感知部分。识别出人体后需要计算出人体在图像中的位置比如是偏左还是偏右距离是近还是远然后根据这个位置信息生成相应的运动控制指令前进、左转、右转或停止这就是“大脑”的决策部分。最后这些控制指令通过GPIO或串口发送给小车底层的电机驱动板控制车轮运动完成“手脚”的执行部分。整个项目的难点不在于算法的从头开发地平线开放的TogetherROS生态和丰富的算法模型库已经为我们提供了强大的“人体检测与跟踪”基础能力。真正的挑战在于如何将官方的算法例程与我们自己搭建的、硬件各异的小车底盘进行“无缝焊接”。官方例程通常输出的是算法层面的结果如人体框坐标我们需要自己编写一个“翻译官”节点将这些坐标数据“翻译”成小车电机能听懂的运动指令cmd_vel话题并确保通信和控制逻辑的稳定可靠。这恰恰是嵌入式AI项目从“Demo演示”走向“实际产品”的关键一步也是最能体现开发者工程化能力的地方。2. 环境准备与硬件选型解析在开始敲代码之前扎实的环境和正确的硬件是项目成功的基石。这一部分我会详细拆解每个环节的要点和背后的考量帮你避开我踩过的那些坑。2.1 旭日X3派基础系统搭建旭日X3派作为核心计算单元其系统状态直接决定了后续所有步骤的可行性。系统镜像烧录地平线官方为旭日X3派提供了适配好的Ubuntu 20.04系统镜像。选择这个版本而非更新的22.04主要原因在于其与TogetherROS及底层驱动经过了充分的兼容性测试稳定性有保障。烧录工具推荐使用BalenaEtcher它跨平台、界面友好几乎不会出错。烧录前务必使用sha256sum校验下载的镜像文件完整性我遇到过因镜像损坏导致系统无法启动排查了半天才发现是源文件问题。注意烧录完成后首次启动系统可能会较慢这是正常现象。务必通过HDMI连接显示器或通过串口登录确认系统正常进入命令行或桌面环境。首次登录的默认用户名和密码通常是sunrise/sunrise建议第一时间修改。网络配置与SSH连接为了便于在PC上进行开发必须让X3派和PC处于同一局域网。有两种主流方式有线连接推荐用于稳定开发用网线直连X3派和路由器在路由器管理后台或使用arp -a、nmap等工具查找X3派的IP地址。无线连接方便移动测试在X3派上连接Wi-Fi。可以通过接显示器键盘操作或者更优雅的方式是先通过有线SSH登录然后使用nmcli或nmtui命令行工具配置Wi-Fi。获取IP后在PC上使用MobaXtermWindows或终端Mac/Linux进行SSH连接。MobaXterm的优势在于集成了SFTP文件浏览器方便后续的文件传输。ssh sunrisex3pi_ip_address # 例如ssh sunrise192.168.1.1002.2 TogetherROS与感知套件安装TogetherROSTROS是地平线基于ROS 2 Humble深度定制优化的机器人操作系统集成了对BPU的调度、算法模型部署和硬件加速驱动。安装TogetherROS按照地平线官方Wiki的指引安装过程通常是几条命令的事。但这里有个关键点务必选择与你的系统镜像匹配的TROS安装包版本。安装完成后一定要执行source /opt/tros/setup.bash来激活环境并且可以将这行命令添加到~/.bashrc中避免每次开新终端都要手动source。F37 Sensor安装与验证F37是地平线推出的一款200万像素双目摄像头深度适配X3派是获取图像数据的关键。物理安装要确保排线插紧。软件上需要安装对应的驱动包。安装后使用官方提供的测试工具验证摄像头是否正常工作# 查看摄像头设备 ls /dev/video* # 使用hobot工具测试摄像头 sudo apt install -y hobot-io hobot_io -t camera -i 0 -s如果能看到图像信息或成功捕获图片说明摄像头驱动和硬件连接正常。一个常见问题是/dev/video0设备不存在这通常需要检查排线、确认驱动安装或者尝试重新加载内核模块sudo modprobe hobot_cim。2.3 小车底盘硬件选型与连接这是个性化最强、也最容易出问题的一环。官方例程可能针对特定小车但我们的底盘可能千差万别。电机驱动方案选择树莓派/通用GPIO控制型驱动板如L298N、TB6612这是最直接的方式。旭日X3派的40Pin GPIO接口与树莓派兼容我们可以直接用Python的Hobot.GPIO库或RPi.GPIO库输出高低电平来控制驱动板的IN1、IN2等引脚从而控制电机正反转。优点是简单直观代码易于理解。缺点是控制精度不高只有开关量无法实现精确的PWM调速且需要自己处理电机供电逻辑。串口通信型驱动板如STM32单片机核心的驱动板更高级和稳定的方案。X3派通过UART如/dev/ttyS3向驱动板发送结构化指令如s,100,100\n表示两个电机速度驱动板上的单片机解析指令并生成精确的PWM信号驱动电机。优点是控制精细可调速、稳定性高、解放了X3派的CPU资源。缺点是需要驱动板固件和上位机协议配合。硬件连接检查清单电源系统务必为X3派和小车驱动电机分别供电电机启动瞬间电流很大会拉低电压导致X3派重启。建议使用两节独立的电池或电源模块。电平匹配X3派GPIO是3.3V电平确保你的驱动板控制端兼容3.3V如果是5V需要加电平转换模块。引脚定义仔细对照X3派和驱动板的引脚图进行连接。例如控制左轮正反转的两个GPIO针脚应接到驱动板对应电机的两个输入引脚上。机械结构确保车轮安装牢固小车重心平衡避免运行时因为机械问题打滑或倾覆。3. 官方人体跟踪例程深度解析与启动环境就绪后我们先让人体检测算法跑起来这是整个项目的“感知源”。3.1 例程代码结构与原理官方的人体跟踪例程通常位于/opt/tros/lib/mono2d_body_detection/或类似路径下。我们通过cp -r命令将其配置文件拷贝到自己的工作目录这保证了我们可以修改配置而不影响原始文件。启动的核心命令是source /opt/tros/setup.bash ros2 launch body_tracking hobot_body_tracking_without_gesture.launch.py这个launch.py文件做了几件关键事启动摄像头节点发布来自/dev/video0的原始图像数据到类似/image_raw的话题。启动人体检测算法节点订阅图像话题利用部署在BPU上的yolov5s-body或其他优化模型进行推理将检测到的人体边框Bounding Box信息发布到类似/body_detection的话题。这里的性能优化是关键地平线已将模型转换为高效的.bin格式在BPU上跑远快于在CPU上。启动跟踪与消息转换节点对连续帧的检测结果进行关联跟踪如使用IOU或Re-ID算法稳定ID。然后根据人体框在图像中的位置x坐标和大小面积计算出一个粗略的“跟随指令”。这个指令会被封装成ROS 2的标准消息类型geometry_msgs/msg/Twist发布到/cmd_vel话题。Twist消息包含linear.x前后速度和angular.z旋转角速度。理解算法输出逻辑人体框中心x坐标 vs 图像中心x坐标决定了小车是该左转angular.z 0还是右转angular.z 0。人体框面积或高度 vs 预设阈值决定了小车是该前进靠近linear.x 0还是停止linear.x 0。面积小意味着人距离远就前进面积大到一定程度意味着人足够近就停止。3.2 启动验证与话题监听在MobaXterm中打开第一个终端执行上述launch命令。如果一切正常你应该能看到一系列节点启动的日志并且如果摄像头前有人日志会打印检测到的人体框信息。为了验证算法是否真的输出了控制指令我们需要打开第二个终端使用ROS 2的工具来“偷听”/cmd_vel话题source /opt/tros/setup.bash ros2 topic echo /cmd_vel当你在摄像头前左右移动时观察这个终端的输出。你应该会看到linear.x和angular.z的数值在动态变化。例如你站在左边angular.z可能是正数要求左转你站远了linear.x变成正数要求前进。这一步至关重要它证明了官方的感知和决策链路是通的。如果这里没有数据说明人体检测或跟踪环节有问题需要回头检查摄像头、模型或配置。4. 定制化小车控制节点开发实战官方例程发出了/cmd_vel指令但我们的自定义小车底盘还听不懂。现在我们需要编写一个“翻译器”节点订阅/cmd_vel并将其翻译成我们小车硬件能执行的具体动作。4.1 控制逻辑设计与代码实现我的小车底盘是通过GPIO控制L298N驱动板的因此代码核心是订阅/cmd_vel话题并根据速度指令设置GPIO高低电平。以下是逐段解析我提供的代码1. 导入与初始化from geometry_msgs.msg import Twist import rclpy from rclpy.node import Node import Hobot.GPIO as GPIO import serial # GPIO引脚定义 (BOARD编码对应物理引脚号) output_pin26 26 # 左轮电机方向A output_pin28 28 # 左轮电机方向B output_pin38 38 # 右轮电机方向A output_pin40 40 # 右轮电机方向B GPIO.setmode(GPIO.BOARD) # 使用物理引脚编号模式 GPIO.setup(output_pin26, GPIO.OUT, initialGPIO.LOW) GPIO.setup(output_pin28, GPIO.OUT, initialGPIO.LOW) GPIO.setup(output_pin38, GPIO.OUT, initialGPIO.LOW) GPIO.setup(output_pin40, GPIO.OUT, initialGPIO.LOW)为什么用BOARD编码因为物理引脚号是固定的不会因主板版本变化而改变代码可移植性更好。初始化为LOW确保节点启动时电机处于停止状态防止上电乱转。2. 串口初始化备用方案uart_dev/dev/ttyS3 baudrate 115200 try: ser serial.Serial(uart_dev, int(baudrate), timeout1) print(open serial sucess) except Exception as e: print(open serial failed!\n)这段代码是为串口控制方案预留的。如果你用GPIO控制可以暂时注释掉。timeout1设置了读写的超时时间避免程序阻塞。3. ROS 2节点与订阅者class CmdNode(Node): def __init__(self,name): super().__init__(name) self.sub_cmd self.create_subscription(Twist, cmd_vel, self.recv_cmd_callback, 6)创建了一个名为CmdNode的ROS 2节点。create_subscription订阅了cmd_vel话题消息类型是Twist。当有新消息时自动调用recv_cmd_callback函数。6是队列深度稍微留点余量。4. 核心回调函数——指令翻译逻辑def recv_cmd_callback(self, cmd): self.cmd_speed cmd.linear.x self.cmd_angular cmd.angular.z if(self.cmd_speed 0): # 需要前进 # 左轮前进IN1HIGH, IN2LOW GPIO.output(output_pin26, GPIO.HIGH) GPIO.output(output_pin28, GPIO.LOW) # 右轮前进IN3HIGH, IN4LOW GPIO.output(output_pin38, GPIO.HIGH) GPIO.output(output_pin40, GPIO.LOW) print(Forward) elif(self.cmd_angular 0): # 需要左转 (原地左转) # 左轮后退IN1LOW, IN2HIGH GPIO.output(output_pin26, GPIO.LOW) GPIO.output(output_pin28, GPIO.HIGH) # 右轮前进IN3HIGH, IN4LOW GPIO.output(output_pin38, GPIO.HIGH) GPIO.output(output_pin40, GPIO.LOW) print(Turn Left) elif(self.cmd_angular 0): # 需要右转 (原地右转) # 左轮前进 GPIO.output(output_pin26, GPIO.HIGH) GPIO.output(output_pin28, GPIO.LOW) # 右轮后退 GPIO.output(output_pin38, GPIO.LOW) GPIO.output(output_pin40, GPIO.HIGH) print(Turn Right) else: # 停止 # 所有电机引脚置低 GPIO.output(output_pin26, GPIO.LOW) GPIO.output(output_pin28, GPIO.LOW) GPIO.output(output_pin38, GPIO.LOW) GPIO.output(output_pin40, GPIO.LOW) print(Stop)逻辑解读这是一个非常基础的“ bang-bang ”控制开关控制。它只判断指令的正负不关心具体大小。linear.x 0前进。两个轮子都向前转。angular.z 0左转。为了让小车原地左转我让左轮后退右轮前进。angular.z 0右转。同理左轮前进右轮后退。其他情况包括指令为0停止。重要改进点实操心得这个简单逻辑在实际中会显得很“楞”小车动作僵硬。一个巨大的改进点是引入死区Dead Zone和比例控制。例如dead_zone_speed 0.05 dead_zone_angular 0.1 if abs(self.cmd_speed) dead_zone_speed and abs(self.cmd_angular) dead_zone_angular: # 停止 elif abs(self.cmd_angular) dead_zone_angular: # 优先处理旋转因为人体横向偏移更敏感 # 可以根据self.cmd_angular的值按比例设置PWM占空比如果是PWM控制 else: # 处理前进/后退这样微小的指令波动不会引起小车抖动控制会更平滑。4.2 节点部署与启动将写好的Python脚本例如my_car_controller.py上传到X3派上比如/home/sunrise/目录下。记得给脚本添加可执行权限chmod x my_car_controller.py。在第三个终端中启动我们自己的控制节点source /opt/tros/setup.bash python3 /home/sunrise/my_car_controller.py如果代码无误你会看到节点初始化的日志然后它就开始静静地等待/cmd_vel话题的消息了。5. 系统联调、问题排查与性能优化当人体检测节点和控制节点都运行起来后激动人心的联调时刻就到了。但往往这也是问题集中爆发的阶段。5.1 联调步骤与预期现象终端1保持运行ros2 launch ...启动人体跟踪。终端2运行ros2 topic echo /cmd_vel观察指令输出是否正常。终端3运行python3 my_car_controller.py启动小车控制器。观察小车站在摄像头前尝试左右移动、前后走动。预期现象当你偏左时小车应开始左转右轮前左轮后或停偏右时右转当你后退远离时小车应前进靠近当你靠近到一定距离时小车应停止。使用ros2 topic list和ros2 node list命令确认所有话题和节点都正常存在。5.2 常见问题排查速查表问题现象可能原因排查步骤与解决方案小车完全不动1. 控制节点未收到/cmd_vel消息。2. GPIO控制逻辑错误或引脚接错。3. 电机驱动板未供电或使能。1. 在终端3的节点中增加print确认回调函数是否被触发。2. 用ros2 topic info /cmd_vel查看发布者和订阅者列表确认控制节点已订阅。3. 使用gpio readall需安装wiringpi或写一个简单的GPIO测试脚本单独测试每个引脚输出是否能点亮LED或驱动电机。4. 检查驱动板的电源和使能ENA, ENB跳线帽。小车动作与预期相反1. 电机线接反。2. 左右轮GPIO定义弄反。3. 算法输出的angular.z符号定义与你的理解相反。1. 交换同一个电机的两根线电机会反转。2. 检查代码中左右轮引脚定义与实际接线是否一致。3. 在终端2观察/cmd_vel记录下你向左/右移动时angular.z的正负然后调整代码中的判断逻辑改成。小车动作剧烈、抖动1. 控制逻辑是“开关式”没有死区和滤波。2. 人体检测框抖动导致指令高频变化。1. 在控制回调函数中加入死区和低通滤波。例如只当速度指令绝对值大于0.1时才响应或者对连续几次的指令取平均。2. 尝试调整人体跟踪算法的参数如置信度阈值score_thr 非极大值抑制阈值nms_thr让检测框更稳定。配置文件通常在config目录下。人体检测时有时无1. 光照条件差。2. 摄像头对焦或曝光问题。3. 模型置信度阈值设得过高。1. 改善环境光照避免背光。2. 检查摄像头是否被遮挡尝试微调摄像头角度。3. 修改算法节点的配置文件降低score_thr如从0.5降到0.3但可能会增加误检。延迟感明显1. 图像传输和处理链路较长。2. 控制周期不稳定。1. 这是嵌入式系统的通病。可以尝试降低摄像头分辨率在launch文件中修改牺牲一些画质换取速度。2. 确保你的控制节点回调函数执行效率高不要有阻塞操作如time.sleep。3. 考虑使用ROS 2的生命周期节点或定时器以固定频率如20Hz去查询最新的/cmd_vel指令并执行而不是每次回调都执行可能很耗时的GPIO操作。启动launch文件报错1. TROS环境未source。2. 依赖包缺失。3. 配置文件路径错误。1. 每个终端都要先source /opt/tros/setup.bash。2. 根据错误信息安装缺失的ROS 2包例如sudo apt install ros-humble-xxx。3. 确认拷贝的config文件夹在当前终端的工作目录下。5.3 进阶优化方向当基础功能跑通后可以从以下几个方面提升小车的性能和体验控制算法升级将简单的开关控制改为PID控制。以人体框中心x坐标与图像中心x坐标的偏差作为PID的输入输出更平滑的angular.z指令小车转向会非常顺滑。速度映射将linear.x和angular.z的指令值范围可能是-1~1线性或非线性地映射到PWM占空比上实现调速而不是全速前进/旋转。安全与交互增加急停开关物理按钮或超声波测距当检测到前方过近障碍物时覆盖/cmd_vel指令强制停车。可以增加一个LED或蜂鸣器用不同的灯光或声音表示当前状态如“搜索中”、“已跟踪”。多模态融合如果小车装有激光雷达或毫米波雷达可以将人体检测的2D信息与雷达的3D点云信息融合更精确地估算人的距离和位置实现更可靠的跟随。容器化部署使用Docker将整个应用TROS环境、算法节点、控制节点打包成一个镜像。这样可以在任何烧录了基础系统的X3派上快速部署和复现极大提升项目的可移植性和交付效率。这个项目从系统烧录到联调完成走通了一个完整的嵌入式AI应用闭环。它最宝贵的价值不在于复现了一个跟随功能而在于让你亲身体验了如何将一个官方的AI算法Demo通过自己的工程化努力与真实的物理世界进行交互和控制。每一次调试、每一个问题的解决都是对“感知-决策-执行”这一机器人核心范式更深刻的理解。当你看到小车第一次稳稳地跟随着你移动时那种成就感就是驱动我们这些开发者不断折腾的最大动力。