1. 项目缘起为什么选择这个组合来造一台“跟屁虫”小车几年前我在一个机器人展会上看到一台能自动跟随人移动的展示车当时就觉得这玩意儿太酷了它不像传统的遥控车而是像一个智能的“小跟班”。回来后我就琢磨着自己也搞一台。市面上有现成的跟随机器人套件但价格不菲而且“黑盒”操作居多可玩性不高。作为一个喜欢折腾的硬件爱好者我决定从零开始用更开放、更具性价比的方案来实现它。经过一番调研和选型我最终敲定了“树莓派 Mediapipe 麦克纳姆轮”这个黄金组合。为什么是它们这背后有非常实际的考量。首先树莓派作为一款功能强大的微型计算机它不仅能运行完整的操作系统如Debian处理复杂的视觉和逻辑运算还提供了丰富的GPIO接口可以轻松连接和控制各种电机驱动板、传感器是项目当之无愧的“大脑”。其次Mediapipe是谷歌开源的一个跨平台机器学习解决方案框架它内置了现成的、经过高度优化的姿态估计模型。这意味着我不需要从零开始训练一个复杂的人体检测模型也不用在树莓派上部署庞大的深度学习框架如TensorFlow/PyTorch来推理Mediapipe提供了非常轻量级的Python API能直接在树莓派上以不错的帧率实时检测出人体的关键点如鼻子、肩膀、髋部。最后麦克纳姆轮是一种特殊的全向轮它由许多小辊子以特定角度倾斜排列而成。通过控制四个麦克纳姆轮的不同转速和转向组合小车可以实现平面内任意方向的平移、旋转即“全向移动”。这对于跟随场景至关重要——想象一下如果小车只能前进后退、左右转弯那么当目标横向移动时小车需要先转向再前进动作笨拙且不连贯。而麦克纳姆轮能让小车像螃蟹一样直接横移平滑地跟上目标的侧向步伐体验感直接拉满。这个项目的核心逻辑就是让树莓派驱动摄像头捕捉图像通过Mediapipe识别画面中的人体并计算出目标相对于画面中心的位置再根据这个位置偏差通过PID等控制算法计算出四个麦克纳姆轮各自应有的转速最终通过电机驱动板控制小车移动形成一个“感知-决策-控制”的闭环。整个过程充满了硬件组装、软件调试、算法调参的乐趣最终当你走动时一个小车静静地跟在你身后那种成就感是无与伦比的。下面我就把这套方案的完整实现过程、踩过的坑以及调优心得毫无保留地分享出来。2. 硬件选型与组装打造小车的“躯体”与“神经”一台能稳定运行的跟随车硬件是基石。这部分工作看似是拧螺丝、接电线但每一个选择都直接影响着后续软件开发的难度和小车的最终性能。我的硬件清单和考量如下2.1 核心控制器树莓派4B/5的抉择树莓派是项目的大脑其算力决定了视觉处理的帧率和整体响应速度。树莓派4B (4GB/8GB RAM)这是经过市场长期检验的“老兵”。对于运行Mediapipe的BlazePose模型轻量级来说其性能完全足够。在优化良好的情况下处理640x480分辨率的图像帧率能达到10-15 FPS这对于跟随应用是可用的。其GPIO、USB、CSI摄像头接口等资源都非常成熟稳定。优点是社区支持极其广泛任何问题几乎都能找到解决方案性价比高。树莓派5这是新一代的“猛将”CPU和GPU性能均有显著提升并且原生支持PCIe接口。对于这个项目它的最大优势在于能轻松驱动更高分辨率的摄像头如1080p并在更高帧率下运行Mediapipe使得目标跟踪更平滑、延迟更低。缺点是发布初期一些外围驱动如某些特定的摄像头模块驱动的稳定性可能还在完善中且功耗和发热相对4B更高。我的选择与建议如果你是第一次尝试追求极致的稳定性和丰富的参考资料树莓派4B 4GB版本是稳妥且性价比最高的选择。如果你对性能有更高要求不介意处理一些新硬件可能遇到的兼容性问题并且希望小车反应更敏捷那么树莓派5是更面向未来的选择。我本次项目使用的是树莓派4B 4GB事实证明其性能完全胜任。2.2 “眼睛”摄像头模块的选择与安装摄像头负责“看”世界其画质、帧率和安装位置至关重要。官方CSI摄像头 vs USB摄像头树莓派官方CSI摄像头通过排线直接连接到主板上的CSI接口这种连接方式带宽高、延迟极低、CPU占用小是首选方案。USB摄像头虽然即插即用但在树莓派上需要占用USB带宽并由CPU进行图像数据搬运和处理会增加系统负载在高帧率下可能成为瓶颈。分辨率与帧率Mediapipe处理图像需要时间更高的分辨率意味着更多的像素需要计算。对于跟随应用我们不需要看清人的面部细节只需要定位躯干。因此640x480 (480p) 或 800x600 的分辨率是完全足够的。这样可以在树莓派上获得更高的处理帧率FPS。在raspistill或libcamera命令中我们可以轻松设置分辨率。安装位置摄像头应安装在小车前方镜头轴线尽量与地面平行且高度适中。如果镜头朝上或朝下会导致透视畸变影响Mediapipe检测的准确性进而影响位置计算的精度。我使用了一个可调节角度的摄像头支架方便微调。实操步骤连接与测试CSI摄像头确保树莓派已关机轻轻抬起CSI接口的卡扣。将摄像头排线的蓝色一面背对以太网口树莓派4B/5金色触点朝向卡扣内侧插入接口并按下卡扣锁紧。启动树莓派打开终端首先启用摄像头接口如果未启用sudo raspi-config选择Interface Options-Legacy Camera(树莓派Bullseye及之前) 或Camera(新版Bookworm系统)启用它。测试摄像头使用libcamera命令新系统推荐libcamera-hello -t 0这会实时预览摄像头画面。按CtrlC退出。拍摄一张测试照片libcamera-jpeg -o test.jpg用文件管理器查看test.jpg确认图像清晰无误。2.3 “腿脚”麦克纳姆轮、电机与驱动板这是实现全向移动的核心选型错误会导致小车行动怪异、无力甚至损坏。麦克纳姆轮购买时务必注意四个轮的安装方向麦克纳姆轮有左旋和右旋之分必须按照“X”形或“O”形布局来安装才能产生正确的合力。通常卖家会提供安装示意图。我选择的是60mm直径的橡胶轮兼顾了速度与抓地力。直流减速电机轮子需要电机来驱动。我选用的是TT马达双轴配套的减速箱提供了较大的扭矩使得小车有足够的动力启动和克服轻微不平的地面。关键参数是额定电压如3-6V和空载转速。需要确保电机驱动板能提供足够的电流。电机驱动板树莓派的GPIO引脚无法直接驱动电机需要驱动板作为“肌肉”。我选用的是TB6612FNG双电机驱动模块。一块TB6612可以驱动两个电机因此我们需要两块来驱动四个轮子。选择它的原因① 效率高发热远小于传统的L298N② 支持PWM调速控制精细③ 内置保护电路。接线时需要仔细连接树莓派的GPIO引脚到驱动板的控制端PWMA, AIN1, AIN2等以及驱动板的输出端到电机。硬件组装流程与注意事项车架可以使用亚克力板、3D打印件或现成的机器人底盘。确保结构牢固有足够空间安装树莓派、电池和驱动板。安装电机与轮子将四个电机固定在车架底板的四个角上。严格按照卖家提供的麦克纳姆轮布局图安装轮子确保左前轮和右后轮的辊子倾斜方向一致右前轮和左后轮的方向一致。这是全向移动的基础装错了小车会“鬼畜”。电路连接将一块TB6612的VM电机电源和VCC逻辑电源连接到电池如7.4V锂电池的正极GND连接到电池负极。将两块TB6612的GND也与树莓派的GND引脚相连共地。将树莓派的3.3V或5V引脚连接到TB6612的VCC如果模块支持3.3V逻辑电平则接3.3V更安全。连接控制信号线例如将树莓派的GPIO17、GPIO27、GPIO22连接到第一块TB6612的PWMA,AIN1,AIN2分别控制一个电机的速度和方向。同理连接其他GPIO控制其余三个电机。务必绘制一张接线表记录每个GPIO引脚对应控制哪个轮子后续编程全靠它。供电强烈建议电机驱动板和树莓派使用独立的电源供电并通过共地连接。因为电机启停时会产生很大的电流波动和电压尖峰可能引起树莓派重启或损坏。我使用一块7.4V锂电池通过降压模块给驱动板供电另一块移动电源或5V电池组给树莓派供电。3. 软件环境搭建为树莓派注入“灵魂”硬件组装完毕接下来是让树莓派“活”起来安装必要的操作系统和软件库。3.1 系统安装与基础配置烧录系统我选择Raspberry Pi OS (64-bit) Lite版本。因为跟随车作为独立设备运行不需要图形桌面Lite版本更轻量资源占用少。使用Raspberry Pi Imager工具烧录到MicroSD卡在烧录前即可设置主机名、开启SSH、配置Wi-Fi非常方便。无头启动与SSH连接烧录完成后将SD卡插入树莓派上电启动。在路由器管理界面找到树莓派的IP地址使用SSH客户端如PuTTY连接。用户名为pi密码为你设置的密码。系统更新与换源为了获得更快的软件下载速度建议更换为国内镜像源如清华源、中科大源。sudo nano /etc/apt/sources.list sudo nano /etc/apt/sources.list.d/raspi.list将文件中的archive.raspberrypi.org和raspbian.org域名替换为镜像源地址然后更新sudo apt update sudo apt upgrade -y安装必要工具python3-pip,git,vim等。3.2 Mediapipe及其依赖的安装这是软件部分的核心也是最容易踩坑的地方。Mediapipe的官方安装命令在树莓派上可能因为架构和依赖问题失败。避坑指南分步安装法安装系统依赖sudo apt install -y python3-opencv libatlas3-base libgomp1 libqtgui4 libqt4-testpython3-opencv是必须的因为Mediapipe处理图像需要OpenCV的后端。使用预编译的Wheel文件推荐直接pip install mediapipe在树莓派上很可能编译失败。幸运的是社区有爱好者为ARM架构树莓派编译好了Wheel文件。我们可以直接安装。访问https://github.com/helgeerbe/mediapipe-arm64或类似仓库找到适用于你Python版本如3.9和系统bullseye/bookworm的.whl文件。使用wget下载到树莓派然后用pip安装wget https://github.com/helgeerbe/mediapipe-arm64/releases/download/v0.10.3/mediapipe-0.10.3-cp39-cp39-linux_aarch64.whl pip3 install mediapipe-0.10.3-cp39-cp39-linux_aarch64.whl如果没有找到完全匹配的可以尝试寻找其他来源或者使用pip install mediapipe --no-deps尝试不安装依赖然后手动安装其依赖numpy,opencv-python,protobuf等但这比较繁琐。验证安装创建一个简单的Python脚本test_mediapipe.pyimport cv2 import mediapipe as mp mp_pose mp.solutions.pose print(MediaPipe Pose module imported successfully!)运行python3 test_mediapipe.py如果没有报错说明安装成功。3.3 电机控制库与项目结构我们需要一个库来方便地控制GPIO从而驱动TB6612模块。RPi.GPIO是经典选择但这里我推荐gpiozero它的API更现代、易用。sudo apt install -y python3-gpiozero建立项目目录结构如下follow_car/ ├── car_controller.py # 电机控制类封装麦克纳姆轮运动逻辑 ├── pose_tracker.py # Mediapipe姿态检测与目标定位类 ├── main.py # 主程序整合跟踪与控制逻辑 ├── config.py # 配置文件GPIO引脚定义、PID参数等 └── requirements.txt # Python依赖列表4. 核心代码实现从“看见”到“跟随”一切准备就绪现在开始编写让小车动起来的核心代码。我们将逻辑分层便于调试和维护。4.1 底层驱动麦克纳姆轮运动学与控制封装首先我们需要理解麦克纳姆轮的运动学。小车的运动可以分解为三个分量沿X轴平移左右、沿Y轴平移前后、绕Z轴旋转自转。每个轮子的转速是这三个分量的线性组合。在car_controller.py中我们创建一个MecanumCar类import time from gpiozero import PWMOutputDevice, DigitalOutputDevice class MecanumCar: def __init__(self, config): # 根据config.py中的定义初始化四个电机的PWM和方向控制引脚 # 例如motor_fl_pwm PWMOutputDevice(pinconfig.MOTOR_FL_PWM) # motor_fl_in1 DigitalOutputDevice(pinconfig.MOTOR_FL_IN1) # motor_fl_in2 DigitalOutputDevice(pinconfig.MOTOR_FL_IN2) self.motors self._init_motors(config) # 电机转向校准系数因为安装或接线可能导致实际转向与理论相反 self.calibration config.MOTOR_CALIBRATION def _init_motors(self, config): # 初始化四个电机的PWM和方向控制对象返回一个字典方便管理 motors {} # ... 初始化代码 ... return motors def move(self, vx, vy, omega): 根据速度向量控制小车运动。 :param vx: X轴速度右为正范围[-1.0, 1.0] :param vy: Y轴速度前为正范围[-1.0, 1.0] :param omega: 旋转角速度逆时针为正范围[-1.0, 1.0] # 麦克纳姆轮运动学公式简化版假设轮子位于正方形四角 # 每个轮子的速度是vx, vy, omega的线性组合 wheel_speeds [ vy vx omega, # 左前轮 vy - vx - omega, # 右前轮 vy - vx omega, # 左后轮 vy vx - omega, # 右后轮 ] # 将速度归一化到[-1, 1]区间防止超过PWM最大占空比 max_speed max(abs(s) for s in wheel_speeds) if max_speed 1.0: wheel_speeds [s / max_speed for s in wheel_speeds] # 应用校准系数并设置电机 for i, speed in enumerate(wheel_speeds): calibrated_speed speed * self.calibration[i] self._set_motor_speed(i, calibrated_speed) def _set_motor_speed(self, motor_index, speed): 根据速度值-1到1设置指定电机的PWM和方向。 motor self.motors[motor_index] if speed 0: motor[in1].on() motor[in2].off() else: motor[in1].off() motor[in2].on() # 取绝对值作为PWM占空比 motor[pwm].value abs(speed) def stop(self): 停止所有电机。 for motor in self.motors.values(): motor[pwm].value 0关键点与避坑运动学公式上述公式是一个基础模型。实际中因为轮子摩擦、重心等因素可能需要微调系数。这是一个需要实际测试和调整的地方。速度归一化这一步至关重要。当计算出的某个轮子速度超过1.0时如果不归一化PWM值会超限导致电机控制异常小车运动失真。校准系数config.MOTOR_CALIBRATION是一个包含4个数字的列表如[1, 1, -1, 1]。如果发现某个轮子转向反了可以通过修改对应位置的符号1或-1来纠正而无需重新焊接电线。4.2 视觉感知Mediapipe人体检测与目标定位接下来在pose_tracker.py中我们编写从摄像头画面中找出人并计算其位置的代码。import cv2 import mediapipe as mp import numpy as np class PoseTracker: def __init__(self, camera_index0, target_height480): self.cap cv2.VideoCapture(camera_index) self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, target_height) # 初始化MediaPipe Pose模型选择轻量级模型以提升速度 self.mp_pose mp.solutions.pose self.pose self.mp_pose.Pose( static_image_modeFalse, model_complexity1, # 0:轻量1:中等2:重度。选1平衡精度速度。 smooth_landmarksTrue, enable_segmentationFalse, min_detection_confidence0.5, min_tracking_confidence0.5 ) self.mp_draw mp.solutions.drawing_utils self.frame_center_x 320 # 初始化会在第一帧更新 self.frame_center_y target_height // 2 def get_target_offset(self): 从摄像头捕获一帧检测人体返回目标中心点相对于画面中心的偏移。 :return: (dx, dy, detected) dx, dy为归一化偏移-1到1detected为是否检测到人 success, frame self.cap.read() if not success: return 0, 0, False # MediaPipe处理需要RGB图像 frame_rgb cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) results self.pose.process(frame_rgb) dx, dy 0, 0 detected False if results.pose_landmarks: detected True # 获取画面尺寸 h, w, _ frame.shape self.frame_center_x, self.frame_center_y w // 2, h // 2 # 选择用于跟踪的关键点。鼻尖(0)或躯干中点左右髋部中心比较稳定。 # 这里使用鼻尖 landmark results.pose_landmarks.landmark[self.mp_pose.PoseLandmark.NOSE] # 将关键点坐标从归一化坐标转换为像素坐标 target_px int(landmark.x * w) target_py int(landmark.y * h) # 计算归一化偏移 (目标坐标 - 中心坐标) / (半宽或半高) # 这样偏移范围在[-1, 1]左右方便后续控制 dx (target_px - self.frame_center_x) / (w / 2) dy (target_py - self.frame_center_y) / (h / 2) # 可选在画面上绘制关键点和连线用于调试 # self.mp_draw.draw_landmarks(frame, results.pose_landmarks, self.mp_pose.POSE_CONNECTIONS) # cv2.circle(frame, (target_px, target_py), 10, (0, 255, 0), -1) # cv2.imshow(Pose Tracking, frame) # cv2.waitKey(1) return dx, dy, detected def release(self): self.cap.release() cv2.destroyAllWindows()关键点与调优关键点选择跟踪鼻尖(NOSE)是最直观的但人转头时鼻尖位置变化大。跟踪左右髋部(LEFT_HIP,RIGHT_HIP)的中点作为“躯干中心”会更稳定因为人走路时躯干相对稳定。你可以根据实际效果选择或融合多个点。置信度阈值min_detection_confidence和min_tracking_confidence很重要。调高它们可以减少误检但可能在人被部分遮挡时丢失目标调低则更敏感但也更容易引入噪声。需要在实际场景中权衡。图像分辨率在VideoCapture中设置分辨率。分辨率越低Mediapipe处理越快但目标定位的像素精度会下降。480p是一个很好的起点。归一化偏移将偏移量dx,dy归一化到[-1, 1]区间是为了与上层控制器的输出范围匹配方便设计控制律。4.3 控制大脑PID控制器与主循环现在我们需要一个“大脑”将视觉感知的偏移量转化为电机控制的速度指令。一个简单有效的方法是使用PID控制器。PID通过比例(P)、积分(I)、微分(D)三个环节计算出一个控制量来消除误差这里是目标偏移量。在main.py中我们整合所有模块import time from car_controller import MecanumCar from pose_tracker import PoseTracker import config class PIDController: def __init__(self, kp, ki, kd, max_output1.0): self.kp, self.ki, self.kd kp, ki, kd self.max_output max_output self.integral_x 0 self.integral_y 0 self.previous_error_x 0 self.previous_error_y 0 def compute(self, error_x, error_y, dt): # 比例项 p_x self.kp * error_x p_y self.kp * error_y # 积分项防止积分饱和 self.integral_x error_x * dt self.integral_x max(min(self.integral_x, 2.0), -2.0) # 积分限幅 i_x self.ki * self.integral_x self.integral_y error_y * dt self.integral_y max(min(self.integral_y, 2.0), -2.0) i_y self.ki * self.integral_y # 微分项 d_x self.kd * (error_x - self.previous_error_x) / dt if dt 0 else 0 d_y self.kd * (error_y - self.previous_error_y) / dt if dt 0 else 0 self.previous_error_x error_x self.previous_error_y error_y # 合成输出并限幅 output_x p_x i_x d_x output_y p_y i_y d_y output_x max(min(output_x, self.max_output), -self.max_output) output_y max(min(output_y, self.max_output), -self.max_output) return output_x, output_y def main(): car MecanumCar(config) tracker PoseTracker(target_heightconfig.CAMERA_HEIGHT) pid_x PIDController(kp0.8, ki0.05, kd0.2, max_output0.6) # 控制左右偏移 pid_y PIDController(kp0.6, ki0.03, kd0.15, max_output0.5) # 控制前后偏移 last_time time.time() lost_target_count 0 MAX_LOST_COUNT 10 # 连续丢失目标N帧后小车停止 try: while True: current_time time.time() dt current_time - last_time last_time current_time # 1. 获取目标偏移 dx, dy, detected tracker.get_target_offset() if detected: lost_target_count 0 # 2. PID计算控制量 # 注意dx为正表示目标在画面右侧我们需要小车右移正vx来对准所以误差取反或直接在PID中处理符号 control_vx, control_vy pid_x.compute(-dx, -dy, dt) # 这里简单起见用同一个PID计算实际最好分开 # control_vy pid_y.compute(-dy, dt) # 更精细的做法是XY独立PID # 3. 发送速度指令给小车 # 这里将控制量直接映射为vx, vy。omega旋转可以设为0或根据身体朝向计算。 car.move(vxcontrol_vx, vycontrol_vy, omega0) print(fTarget detected. dx{dx:.2f}, dy{dy:.2f}, vx{control_vx:.2f}, vy{control_vy:.2f}) else: lost_target_count 1 print(fTarget lost. Count: {lost_target_count}) if lost_target_count MAX_LOST_COUNT: car.stop() # 长时间丢失目标停止小车 # 可选进入搜索模式让小车缓慢旋转寻找目标 # 控制循环频率避免CPU占用过高 time.sleep(0.05) # 约20Hz except KeyboardInterrupt: print(\nProgram stopped by user.) finally: car.stop() tracker.release() if __name__ __main__: main()PID参数整定心得 这是整个项目调试中最“玄学”但也最有趣的部分。参数没有标准答案完全取决于你的小车物理特性、摄像头视野、期望的跟随速度和平滑度。Kp (比例)决定了系统对当前误差的反应速度。Kp太大小车会对着目标位置剧烈振荡像“醉汉”一样Kp太小小车反应迟钝慢悠悠地挪过去。Ki (积分)用来消除静态误差。如果小车总是停在离目标中心差一点的位置可以适当增加Ki。但Ki太大会引起超调和振荡甚至系统不稳定。Kd (微分)预测误差变化趋势起到阻尼作用能抑制振荡让运动更平滑。但对噪声敏感如果Mediapipe检测的坐标有抖动Kd大了反而会放大抖动。调参步骤归零法先将Ki和Kd设为0只调Kp。逐渐增大Kp直到小车开始出现明显的、持续的振荡。然后取这个Kp值的50%-70%作为初始值。加积分引入一个很小的Ki如0.01观察小车能否更准确地停在中心。如果出现缓慢振荡或超调说明Ki大了要减小。加微分最后引入Kd从0.1开始尝试观察振荡是否被抑制运动是否更平滑。注意观察对检测抖动的敏感性。分开调X轴左右和Y轴前后的响应特性可能不同最好有两套独立的PID参数。5. 调试、优化与功能扩展代码跑通只是第一步要让小车稳定可靠地工作还需要大量的调试和优化。5.1 基础调试与问题排查电机不转或转向错误检查供电用万用表测量驱动板VM和VCC电压是否正常。电机电源是否独立且功率足够检查接线反复核对GPIO引脚编号是否与代码中config.py的定义一致。TB6612的AIN1/AIN2电平组合是否正确01正转10反转00刹车11刹车。检查校准系数在car_controller.py的move函数里打印计算出的wheel_speeds看正负号是否符合预期。通过修改config.MOTOR_CALIBRATION来纠正转向。Mediapipe检测不到人或抖动严重光照确保环境光线充足、均匀避免强逆光或昏暗环境。背景尽量选择背景简洁、与人体对比度高的环境进行初始测试。置信度阈值适当降低min_detection_confidence如0.4看看。摄像头用libcamera-hello确认摄像头画面本身是否清晰、有无畸变。性能在终端运行主程序时观察输出帧率。如果远低于5 FPS考虑降低摄像头分辨率或使用Mediapipe的model_complexity0最轻量级。小车运动振荡或“画圈”PID参数这是最常见原因。回到上一节的PID整定步骤优先降低Kp。机械结构检查轮子是否安装牢固车架有无松动。松动的结构会引入无法用软件弥补的抖动。延迟从检测到控制的总延迟过高。可以尝试在main.py循环中减少time.sleep的时间或优化代码如不在每一帧都绘制图像预览。5.2 进阶优化策略目标丢失处理与搜索当前代码在丢失目标后只是停止。可以改进为“搜索模式”例如让小车缓慢自转设置一个小的omega同时持续进行检测一旦重新发现目标立即切换回跟随模式。滤波Mediapipe返回的关键点坐标可能存在抖动。可以在pose_tracker.py中对计算出的dx, dy进行低通滤波或移动平均滤波平滑数据后再送给PID控制器能显著提升小车运动的平滑度。# 简单的移动平均滤波示例 class MovingAverageFilter: def __init__(self, window_size5): self.window_size window_size self.values [] def update(self, new_value): self.values.append(new_value) if len(self.values) self.window_size: self.values.pop(0) return sum(self.values) / len(self.values) # 在PoseTracker类中使用 self.filter_x MovingAverageFilter(5) self.filter_y MovingAverageFilter(5) # ... 计算dx, dy后 ... dx_filtered self.filter_x.update(dx) dy_filtered self.filter_y.update(dy)距离估计与速度规划目前我们只控制了方向让目标处于画面中心但没有控制距离。可以通过人体关键点如肩宽在画面中的像素大小粗略估计人与车的距离。然后设计一个分层控制器外环控制距离通过vy内环控制对准通过vx和omega。这样小车就能在跟随的同时保持一个你设定的理想距离。多目标与目标选择Mediapipe可以检测多个人。可以修改逻辑选择距离画面中心最近的人或者通过某种交互如挥手来指定跟随目标。5.3 扩展想法从跟随到交互基础跟随实现后这个平台还有巨大的扩展空间增加激光雷达结合二维激光雷达如RPLidar A1可以实现更精确的避障和地图构建让小车在跟随的同时绕开障碍物。语音交互接入语音识别如Vosk离线库和语音合成实现“过来”、“停下”、“跟我走”等语音指令控制。手势控制利用Mediapipe的手部关键点检测实现手势控制例如手掌张开表示停止握拳表示前进向左挥手表示左转等。远程监控与控制在树莓派上搭建一个简单的Web服务器使用Flask通过网页可以实时查看摄像头画面并手动控制小车运动。这个项目就像一把钥匙打开了嵌入式AI机器人世界的大门。它融合了硬件集成、计算机视觉、自动控制等多个领域的知识。调试过程中你会遇到各种各样的问题但每一个问题的解决都会让你对这套系统的理解加深一分。当小车第一次稳稳地跟在你身后时所有的折腾都值了。希望这份详细的指南能帮助你少走弯路顺利打造出属于自己的智能跟随车。