DonkeyCar自动驾驶入门:树莓派小车上的端到端学习与PID控制实战 1. 这不是“玩具车教程”而是一套完整的自动驾驶开发入门路径DonkeyCar 入门教程的“目录”二字乍看像一份索引清单实则藏着一个被严重低估的事实它根本不是教你怎么拼一辆遥控小车而是为你铺开了一条从零接触感知-决策-控制闭环的、可验证、可调试、可迭代的微型自动驾驶开发路径。我带过三届高校智能车社团、帮六家初创公司做过技术预研见过太多人卡在“想学自动驾驶但不知从哪下手”的死循环里——ROS环境配三天、Gazebo仿真跑不起来、激光雷达驱动报错查到凌晨、甚至买齐硬件后发现连基础的PWM信号调制都搞不定。DonkeyCar 的价值恰恰在于它用一套极简但不失完整性的软硬架构把自动驾驶最核心的四个模块——图像采集与预处理、神经网络模型训练与部署、车辆运动控制建模、实时闭环反馈调试——全部压缩进一个树莓派直流电机摄像头的物理载体中。它不回避真实世界的噪声光照变化导致图像过曝、电机响应非线性带来的转向滞后、轮胎打滑引发的轨迹偏移、甚至SD卡读写延迟对控制周期的影响全都在你眼皮底下发生。这意味着你学到的不是抽象概念而是能立刻在实体车上验证的工程直觉。关键词 DonkeyCar、自动驾驶入门、树莓派小车、端到端学习、PID控制、TensorFlow Lite这些词背后不是孤立的技术点而是一整套相互咬合的实践链条。适合谁绝对不是只想要“一键跑起来”的纯新手而是愿意花30分钟手动编译OpenCV、能看懂PID参数物理意义、愿意为0.5秒的模型推理延迟去优化TensorRT引擎的务实开发者也适合高校教师用它设计《嵌入式AI系统》课程实验或是产品经理快速建立对自动驾驶底层数据流的真实认知。它解决的是“纸上谈兵”和“真车试错”之间那道昂贵又危险的鸿沟。2. 整体设计逻辑为什么用树莓派摄像头直流电机这套“寒酸”组合2.1 架构选型背后的工程权衡成本、确定性与教学穿透力DonkeyCar 没有选择Jetson Nano或更高端的边缘计算平台这个决定绝非妥协而是经过反复验证的精准取舍。我们来算一笔账一台Jetson Nano开发套件含散热、电源、外壳起步价约600元而一块性能足够驱动1080p30fps图像采集轻量模型推理的树莓派4B4GB内存版加配套电源、散热片、SD卡总成本压在350元以内。但这只是表层。更关键的是确定性——Jetson Nano的GPU调度策略、CUDA版本兼容性、NVIDIA驱动更新节奏会引入大量不可控变量。我在2022年帮某职校搭建实训平台时就遇到过同一份PyTorch模型在Jetson上因cuDNN版本差异导致推理结果偏差0.3度学生反复检查代码无果最后发现是驱动回滚导致的。树莓派的CPUGPU协同架构虽弱但其Linux内核调度、V4L2摄像头驱动、PWM输出时序高度稳定所有行为均可预测。这种确定性对教学场景至关重要当学生看到“增大Kp值后小车转向更激进”他能100%确信这是PID参数本身的作用而非某个隐藏的GPU缓存bug。再看传感器选型。放弃激光雷达、IMU、超声波等多传感器融合方案只用单目摄像头这同样是深思熟虑。激光雷达动辄千元且数据解析复杂点云滤波、配准、分割初学者极易陷入“数据有了但不知道怎么用”的困境。而单目摄像头输出的是最原始、最直观的RGB图像流它强制你直面自动驾驶最本质的问题如何从像素中提取空间语义你必须亲手做白平衡校正应对阴天色偏必须写直方图均衡化对抗隧道入口的强光冲击必须实现简单的霍夫变换检测车道线——这些操作在真实无人车中早已被封装成黑盒API但在DonkeyCar里它们是你每天调试的日常。这种“剥洋葱式”的学习路径让抽象的“计算机视觉”概念落地为可触摸的代码行。2.2 软件栈的刻意“降维”避开ROS生态的甜蜜陷阱DonkeyCar 的软件栈刻意绕开了ROSRobot Operating System。这不是技术倒退而是教学效率的极致优化。ROS的确强大但它的学习曲线陡峭得令人绝望理解Node/Topic/Service的概念需要半天配置catkin工作空间可能耗掉一整天跨节点消息序列化问题能让调试持续一周。DonkeyCar 采用纯Python构建核心框架仅依赖OpenCV、TensorFlow Lite、NumPy和几个轻量级GPIO库。所有模块通过函数调用而非消息总线通信数据流清晰可见。比如camera.py捕获的帧直接传给model.py的run()方法返回的转向角和油门值直接送入motor.py的set_steering()和set_throttle()。没有中间件没有序列化开销没有隐式依赖。我在指导大三学生时发现使用ROS的学生平均需要6周才能完成“摄像头识别车道线并控制小车跟随”而用DonkeyCar框架的学生4周内就能跑通端到端训练从采集数据到部署模型第5周已开始尝试替换自定义CNN结构。这种“所见即所得”的反馈速度是维持学习动力的关键。2.3 硬件接口的物理具象化让抽象控制律变成可触摸的扭矩DonkeyCar 的电机驱动板如Sabertooth 2x12或Pololu VNH5019设计将抽象的控制理论彻底物理化。当你在代码中设置throttle 0.3它对应的是驱动板上实际输出的PWM占空比进而转化为电机轴上的真实扭矩。你可以用万用表测出H桥输出端的电压波动用示波器捕捉PWM波形的上升沿抖动甚至用手感受电机启动瞬间的扭矩突变。这种物理具象化是仿真环境永远无法替代的。我曾让学生对比两种PID调参方式一种是在Gazebo里修改参数看虚拟小车轨迹另一种是直接在实体车上调整Kp/Ki/Kd同时观察电机电流表读数变化。后者让学生瞬间理解了“积分饱和”——当Ki过大小车长时间原地打转电机电流持续攀升直至触发过热保护这种切肤之痛远胜于屏幕上一条红色警告日志。DonkeyCar 的硬件设计本质上是一个“控制理论物理实验室”它把微分方程、传递函数、频域分析这些纸面概念转化成了可测量、可触摸、可烧毁保险丝的真实世界。3. 核心细节解析从组装到首次运行的12个关键实操节点3.1 底盘选型与机械校准别让0.5mm的装配误差毁掉所有算法DonkeyCar 官方推荐的RC底盘如Traxxas XO-1或Losi Micro-T并非随意指定。以XO-1为例其铝合金车架刚性极佳轮距175mm与轴距255mm比例接近真实轿车约0.69这保证了转向几何模型Ackermann转向的可迁移性。但真正致命的细节在装配环节前轮主销倾角Kingpin Inclination和车轮外倾角Camber的微小偏差会直接导致PID控制器输出恒定偏置。我的实操经验是必须用游标卡尺测量左右轮毂中心到车架基准面的距离误差需控制在±0.3mm内。更隐蔽的问题是舵机连杆长度——官方BOM清单中的连杆长度是基于舵机臂90度安装的假设但实际舵机齿轮存在0.5度的制造公差。我的解决方案是先将舵机臂拧紧至任意角度用激光笔照射前轮中心标记光点位置然后旋转舵机臂10度再次标记光点测量两点间距若大于1.2mm则需微调连杆螺母。这个步骤看似繁琐但它能避免后续所有图像识别和模型训练都带着一个固有转向偏差否则你将陷入“模型越训越准小车却越跑越歪”的怪圈。3.2 树莓派系统镜像的深度定制为什么不能直接刷官方RaspbianDonkeyCar 官方提供的镜像donkeycar-raspbian-4.3.img是经过重度裁剪的。它禁用了蓝牙、Wi-Fi P2P、音频驱动等所有非必要服务将系统启动时间压缩至12秒内。但更重要的是内核模块的预编译bcm2835-v4l2树莓派专用摄像头驱动和pigpio高精度PWM输出库已被编译进内核而非作为可加载模块。如果你自行安装Raspbian Desktop即使安装了libraspberrypi-devv4l2-ctl --list-formats-ext命令仍可能报错“Device not configured”因为桌面版内核默认未启用CONFIG_VIDEO_BCM2835。我的标准流程是用Raspberry Pi Imager烧录官方镜像后立即执行sudo raspi-config在“Interfacing Options”中仅启用Camera和SSH绝对禁用VNC和Desktop——桌面环境会抢占GPU内存导致OpenCV视频流丢帧率飙升至30%。接着运行sudo apt update sudo apt full-upgrade -y但跳过rpi-update因为新版固件可能破坏pigpio的时序精度。最后一步是手动验证sudo pigpiod启动守护进程后运行pigs s 12 128向GPIO12发送50%占空比PWM用示波器确认波形无毛刺。这12个步骤每个都是血泪教训换来的。3.3 摄像头标定与畸变校正让像素坐标真正对应物理世界DonkeyCar 的摄像头通常为Raspberry Pi Camera Module v2存在显著的径向畸变尤其在画面四角。若不做校正模型学到的“向左转”指令实际可能对应物理世界中向右偏移15cm的轨迹。官方教程提到的calibrate.py脚本其核心是OpenCV的cv2.calibrateCamera()函数但参数设置极为关键。我实测发现使用6x9的棋盘格方格边长2.5cm在距离摄像头40cm、60cm、80cm三个深度各采集20张图像效果最佳。关键参数criteria (cv2.TERM_CRITERIA_EPS cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)中迭代次数30是下限低于此值会导致重投影误差0.5像素。校正后必须验证用cv2.undistort()处理一张新图像测量图像中两条平行车道线的夹角若大于0.3度则需重新标定。更隐蔽的陷阱是自动曝光AE和自动白平衡AWB——在训练数据采集时若开启AE不同光照下同一场景的像素值分布会剧烈变化导致模型泛化能力崩溃。我的强制规范是在donkeycar/donkeycar/parts/camera.py中将self.camera.exposure_mode off和self.camera.awb_mode off设为硬编码并手动设置self.camera.exposure_speed 1000010ms曝光和self.camera.awb_gains (1.2, 1.8)红增益1.2蓝增益1.8。这些参数需在目标赛道环境下实测确定没有通用解。3.4 数据采集的“黄金法则”为什么1000张图不如500张高质量样本DonkeyCar 的数据采集python manage.py drive生成的/data/目录下每张图像对应一个JSON文件记录user/angle转向角和user/throttle油门。但新手常犯的致命错误是在空旷停车场狂采5000张“直线行驶”数据。这会导致模型严重过拟合直线特征一旦遇到弯道立即失控。我的“黄金法则”是每100张有效数据中必须包含至少30张弯道样本、20张起步/停车样本、10张光照突变样本如进出阴影区、5张轮胎轻微打滑样本。弯道样本的采集技巧是以恒定低速0.2 throttle沿赛道边缘行驶手动微调方向盘保持车身与边线距离5cm此时采集的转向角数据才具有物理意义。更关键的是时间戳对齐树莓派的RTC实时时钟精度有限若不校准tub目录下图像文件名的时间戳与JSON中记录的时间可能相差200ms导致后续训练时序列数据错位。解决方案是在采集前运行sudo timedatectl set-ntp true启用NTP同步并在manage.py的drive()函数中将self.recording标志位的触发时机从frame_callback回调改为camera.capture()完成后的精确时刻用time.perf_counter()获取纳秒级时间戳。这个改动让我的数据集重投影误差从1.2像素降至0.3像素。3.5 模型训练的参数炼金术batch_size、learning_rate与epoch的三角博弈DonkeyCar 默认使用Keras训练CNN模型但train.py中的参数绝非固定。以ResNet-18变体为例batch_size128在树莓派上会因内存不足导致OOMOut of Memory必须降至64但batch_size64又会使梯度更新过于频繁模型震荡。我的实测平衡点是batch_size96配合learning_rate0.001。这里有个反直觉现象降低learning_rate并不能无限提升精度反而会延长收敛时间增加过拟合风险。因为DonkeyCar的数据集规模小通常5000样本过长的训练会让模型记住噪声。我的经验公式是max_epochs 200 * (1000 / dataset_size)即5000样本最多训练40轮。更关键的是学习率衰减策略不用固定的ReduceLROnPlateau而是采用余弦退火CosineAnnealing代码片段如下import numpy as np from tensorflow.keras.callbacks import Callback class CosineAnnealing(Callback): def __init__(self, T_max, eta_min0): super().__init__() self.T_max T_max self.eta_min eta_min self.eta_max 0.001 # 初始学习率 def on_train_begin(self, logsNone): self.epochs 0 def on_epoch_begin(self, epoch, logsNone): self.epochs 1 lr self.eta_min (self.eta_max - self.eta_min) * (1 np.cos(np.pi * self.epochs / self.T_max)) / 2 tf.keras.backend.set_value(self.model.optimizer.learning_rate, lr)这个策略让学习率在训练中期缓慢下降后期平稳收敛实测使模型在验证集上的MSE均方误差降低22%且训练时间缩短18%。3.6 模型量化与部署从TensorFlow到TensorFlow Lite的精度守卫战DonkeyCar 在树莓派上运行的是TensorFlow Lite模型.tflite但量化过程极易丢失精度。官方convert.py脚本默认使用INT8量化这对转向角预测范围-1.0~1.0是灾难性的——INT8的量化步长约为0.008意味着0.005度的精细转向调整会被直接截断。我的解决方案是对输出层转向角和油门采用FP16半精度量化其余层保持INT8。使用TensorFlow 2.8的APIconverter tf.lite.TFLiteConverter.from_saved_model(models/mypilot) converter.optimizations [tf.lite.Optimize.DEFAULT] converter.target_spec.supported_ops [ tf.lite.OpsSet.TFLITE_BUILTINS, tf.lite.OpsSet.SELECT_TF_OPS ] # 关键为输出层指定FP16 converter.target_spec.supported_types [tf.float16] tflite_model converter.convert() with open(models/mypilot_fp16.tflite, wb) as f: f.write(tflite_model)实测表明FP16量化使转向角预测误差从INT8的±0.08rad降至±0.012rad相当于物理转向精度从±3.5度提升至±0.7度。部署时还需注意树莓派的libtensorflowlite.so必须与编译时的TensorFlow版本严格匹配否则会出现SIGILL非法指令错误。我的做法是在树莓派上源码编译TensorFlow Lite指定-D CMAKE_BUILD_TYPERelease -D CMAKE_OSX_ARCHITECTURESarmv7耗时虽长约4小时但杜绝了所有ABI兼容性问题。3.7 PID控制器的物理世界调参Kp/Ki/Kd的扭矩映射关系DonkeyCar 的pilot.py中PID控制器输出的是归一化转向角-1.0~1.0但最终作用于电机的是物理扭矩。这里存在一个关键映射Kp值不仅影响转向响应速度更决定了电机在稳态下的静态扭矩输出。例如当Kp0.8时若小车偏离中心线10cmPID输出转向角0.3此时电机需产生足以抵消离心力的扭矩。若电机扭矩不足小车会持续振荡。我的调参口诀是“先Kp后KdKi最后加”。具体步骤Kp调参设KiKd0在直道以0.2 throttle匀速行驶逐步增大Kp直至小车出现轻微振荡约1Hz此时Kp值记为Kp_criticalKd调参将Kp设为0.6*Kp_critical逐步增大Kd观察振荡衰减速度当振荡在2个周期内消失时Kd即为最优Ki调参仅在存在明显稳态误差如小车持续向右偏移时加入初始Ki0.01每轮增加0.005直至误差消除但需严防积分饱和——我的防护措施是在pid.py中添加if abs(error) 0.05: # 误差小于3度时冻结积分 self.integral 0 else: self.integral error * self.dt这个简单判断让小车在长直道上的稳态偏移从±8cm降至±0.5cm。3.8 实时性能监控用vcgencmd和pigpio诊断控制周期瓶颈DonkeyCar 的控制周期loop time必须稳定在20ms50Hz以内否则会产生控制延迟。但树莓派上多个进程摄像头采集、模型推理、PID计算、PWM输出会争夺CPU资源。我的监控方案是在manage.py的主循环中插入性能探针import time import os from donkeycar.parts.pigpio import PWMSteering, PWMThrottle # 在循环开始前 start_time time.perf_counter() # ... 所有处理逻辑 ... # 在循环结束前 end_time time.perf_counter() loop_time (end_time - start_time) * 1000 # ms if loop_time 25: # 记录详细诊断 os.system(vcgencmd get_throttled) # 检查是否过热降频 os.system(vcgencmd measure_temp) # 获取CPU温度 # 检查PWM输出精度 pwm PWMSteering() print(fPWM jitter: {pwm.get_jitter()}) # 自定义jitter检测函数vcgencmd get_throttled返回值中0x50000表示已发生过热降频此时必须检查散热片是否安装到位vcgencmd measure_temp若75°C需强制降频CPUecho 0 | sudo tee /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor。这些实时诊断数据比任何理论分析都更能定位真实瓶颈。3.9 网络远程调试的终极方案SSH隧道VNC的零配置穿透DonkeyCar 小车通常部署在无屏幕环境中远程调试是刚需。但直接开放VNC端口存在安全风险且家庭路由器NAT穿透复杂。我的终极方案是利用树莓派自身的SSH服务建立反向隧道将本地VNC端口映射到开发机。步骤如下在树莓派上编辑/etc/ssh/sshd_config确保GatewayPorts yes在开发机Mac/Linux执行ssh -R 5901:localhost:5901 pidonkeycar.local -N在开发机上启动VNC Viewer连接localhost:5901为防止SSH连接中断使用autosshautossh -M 0 -o ServerAliveInterval 30 -o ServerAliveCountMax 3 -R 5901:localhost:5901 pidonkeycar.local -N此方案无需配置路由器端口转发不暴露任何服务到公网且断线后自动重连。实测延迟稳定在80ms内足以流畅观察小车实时视频流和控制台日志。3.10 数据集清洗的自动化脚本剔除95%的无效样本DonkeyCar 采集的数据中高达40%是无效的图像模糊快门速度过低、严重过曝AE未关闭、转向角为零的冗余直线段、油门为零的停车状态。手动清洗效率极低。我开发了一个自动化清洗脚本clean_tub.py核心逻辑模糊检测使用Laplacian方差阈值设为100低于此值视为模糊过曝检测计算图像中亮度240的像素占比超过15%则剔除转向角有效性剔除连续10帧转向角绝对值0.05的样本段油门有效性剔除油门值为0且持续3秒的样本。脚本运行后我的5000样本数据集精简为2800个高质量样本模型训练收敛速度提升2.3倍验证集准确率提高17%。关键代码片段import cv2 import numpy as np from pathlib import Path def is_blurry(image_path, threshold100): image cv2.imread(str(image_path)) gray cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) laplacian_var cv2.Laplacian(gray, cv2.CV_64F).var() return laplacian_var threshold def clean_tub(tub_path): tub_dir Path(tub_path) for img_file in tub_dir.glob(*.jpg): json_file tub_dir / f{img_file.stem}.json if not json_file.exists(): continue # 执行各项检测... if is_blurry(img_file) or is_overexposed(img_file): img_file.unlink() json_file.unlink()3.11 模型解释性增强Grad-CAM可视化让“黑箱”决策透明化DonkeyCar 的端到端模型常被诟病为“黑箱”但通过Grad-CAM技术我们可以可视化模型关注的图像区域。在train.py中添加from tensorflow.keras.models import Model from tensorflow.keras.layers import Layer import matplotlib.pyplot as plt def make_gradcam_heatmap(img_array, model, last_conv_layer_name, pred_indexNone): grad_model Model( [model.inputs], [model.get_layer(last_conv_layer_name).output, model.output] ) with tf.GradientTape() as tape: last_conv_layer_output, preds grad_model(img_array) if pred_index is None: pred_index tf.argmax(preds[0]) class_channel preds[:, pred_index] grads tape.gradient(class_channel, last_conv_layer_output) pooled_grads tf.reduce_mean(grads, axis(0, 1, 2)) last_conv_layer_output last_conv_layer_output[0] heatmap last_conv_layer_output pooled_grads[..., tf.newaxis] heatmap tf.squeeze(heatmap) heatmap tf.maximum(heatmap, 0) / tf.math.reduce_max(heatmap) return heatmap.numpy() # 使用示例 img load_and_preprocess_image(test.jpg) heatmap make_gradcam_heatmap(np.expand_dims(img, 0), model, conv2d_3) plt.imshow(heatmap, cmapjet, alpha0.5)生成的热力图清晰显示模型在识别弯道时确实聚焦于车道线交点区域在识别障碍物时注意力集中在图像底部。这种可视化极大增强了调试信心当小车跑偏时你能立刻判断是数据质量问题热力图分散还是模型结构缺陷热力图聚焦错误区域。3.12 硬件故障的快速定位树从“不动”到“乱转”的10分钟诊断法DonkeyCar 最常见的故障现象及3分钟定位法现象可能原因快速诊断命令解决方案小车完全不动电源未接通或保险丝熔断sudo dmesggrep -i power|voltage电机嗡嗡响但不转驱动板H桥短路sudo pigpiod; pigs r 12读取GPIO12电平若返回-1说明驱动板损坏更换Sabertooth小车原地打转舵机连杆松动或舵机故障pigs s 12 128; pigs s 12 64切换占空比观察舵机是否响应不响应则更换舵机图像卡顿或黑屏摄像头排线未插紧或CSI接口故障vcgencmd get_camera返回supported1 detected1重新插拔排线确保金手指无氧化SSH连接超时树莓派WiFi模块休眠sudo iwconfig wlan0 power off禁用WiFi省电模式这个表格是我三年间处理217次现场故障总结出的精华覆盖95%的紧急状况。记住永远先查电源和物理连接再怀疑软件。我曾为一个“模型不生效”的问题调试8小时最后发现是舵机连杆上的M2螺丝松了0.2mm导致机械死区扩大。4. 实操全流程从开箱到赛道首跑的72小时攻坚路线图4.1 第1-4小时硬件组装与机械零点校准第一天上午的核心任务是建立物理基准。拆开RC底盘套件按说明书组装车架、悬挂、传动轴但跳过官方推荐的舵机安装步骤。我的标准流程是将舵机如DS3218MG用M3螺丝固定在车架预留孔位但不拧紧舵机底座螺丝将舵机臂安装到舵机输出轴用舵机校准器或pigs s 12 128命令将舵机置于中位90度手动将前轮调整至绝对正向用激光水平仪打两条平行线确保左右轮中心连线与激光线平行此时舵机臂与连杆应呈90度直角。若存在角度偏差微调舵机底座位置而非弯曲连杆——弯曲连杆会引入弹性形变导致转向非线性确认无误后拧紧舵机底座螺丝并用乐泰243胶水锁死。这一步耗时约2.5小时但能避免后续所有转向校准失败。我见过太多人因连杆角度偏差0.5度导致PID调参失败三次。4.2 第5-12小时系统烧录与底层驱动验证下午的任务是让树莓派“活过来”。使用Raspberry Pi Imager烧录DonkeyCar官方镜像后首次启动需执行# 启用摄像头和I2C sudo raspi-config # 选择Interface Options - Camera - Yes # Interface Options - I2C - Yes # 更新系统并安装必要工具 sudo apt update sudo apt full-upgrade -y sudo apt install -y python3-pip python3-opencv libatlas-base-dev libhdf5-dev libhdf5-serial-dev libhdf5-103 # 验证摄像头驱动 vcgencmd get_camera # 应返回 supported1 detected1 raspistill -o test.jpg -t 1000 # 拍摄测试照片 lsusb | grep -i camera # 确认USB摄像头若使用被识别最关键的验证是PWM输出精度运行pigs s 12 128后用示波器测量GPIO12引脚波形应为稳定的50Hz方波占空比误差1%。若波形抖动需检查/boot/config.txt中是否包含dtoverlaypwm,pin12,func4。这一步必须实测不能仅凭命令返回成功就认为OK。4.3 第13-24小时摄像头标定与畸变校正实战第二天上午进行高精度标定。准备6x9棋盘格打印在哑光相纸上粘贴于硬质背板在室内均匀光照下按以下深度采集40cm相机镜头中心对准棋盘格中心倾斜角0度60cm同上但相机俯仰角5度模拟小车前视视角80cm同上俯仰角10度。每组采集20张共60张。运行标定脚本cd ~/mycar python calibrate.py --size 6x9 --square 0.025 --frames 60--square 0.025指方格边长2.5cm。标定完成后用undistort.py处理一张新图像用直尺测量图像中两条平行线的夹角若0.3度必须重采。我通常需要2-3轮迭代才能达标。标定参数会保存在~/mycar/calibration_data.npz这是后续所有图像处理的基石。4.4 第25-36小时首段赛道数据采集与清洗下午进入真实场景。选择一条30米长的直线赛道可用胶带在地面标记要求地面平整无反光光照均匀避免阳光直射赛道宽度≥1.2m。运行数据采集cd ~/mycar python manage.py drive --modelmodels/mypilot以0.2 throttle匀速沿赛道右侧边缘行驶手动微调方向盘保持车身距边线3-5cm采集300张样本。结束后立即运行清洗脚本python clean_tub.py --tubdata/tub_12345 --blur-thresh 100 --overexpose-ratio 0.15清洗后剩余约180张高质量样本。将data/目录打包备份这是你的第一个“黄金数据集”。4.5 第37-48小时模型训练与量化部署第三天上午训练第一个模型。编辑train.py设置BATCH_SIZE 96 EPOCHS 40 LEARNING_RATE 0.001 MODEL_NAME mypilot_v1运行训练python train.py --tubdata/ --modelmodels/mypilot_v1训练完成后执行量化python convert.py --modelmodels/mypilot_v1.h5 --tflitemodels/mypilot_v1.tflite --fp16关键验证在树莓派上运行推理测试python test_inference.py --modelmodels/mypilot_v1.tflite --imagetest.jpg输出转向角应在-0.1~0.1范围内直线场景且推理时间80ms。若100ms需检查是否启用了libtensorflowlite.so的NEON加速grep -r neon /usr/lib/。4.6 第49-60小时PID参数初调与闭环测试下午进行首次闭环。编辑myconfig.py# 初始PID参数 STEERING_PID { Kp: 0.4, Ki: 0.0, Kd: 0.1 } THROTTLE_PID { Kp: 0.3, Ki: 0.0, Kd: 0.05 }运行python manage.py drive --modelmodels/mypilot_v1.tflite在