1. 从竞速到落地这个项目到底在做什么先把话说在前面全国大学生智能车竞赛早就不是单纯拼速度的赛道游戏了。第二十一届、第二十二届的赛题方向越来越偏向智能载体 行业场景而地瓜机器人RDK X3系列凭借内置BPU贝叶斯加速单元和完整的ROS 2生态成了很多队伍做创意赛、视觉赛的首选平台。我这次做的东西简单说就是——让一台地瓜机器人小车承担起智慧医疗巡检助手的角色。它有两个核心任务人脸皮肤健康初筛小车通过摄像头识别患者面部区域提取肤色特征判断是否存在痤疮、色斑、泛红等常见皮肤问题的倾向输出健康评估提示。药品自动配送导航在模拟病房区域内小车根据指令自动导航到指定床位送达指定药品用色块模拟送达后语音/屏幕提示。这个项目我当时断断续续做了三周踩了不少坑尤其是模型量化那一步差点把我劝退。本文会把整个方案、代码逻辑、调试经验和踩坑记录全部放出来适合正在备赛、或者想用RDK X3做视觉SLAM/目标检测项目的同学参考。先给一个整体的软硬件选型表后面所有内容都围绕这套配置展开部件型号/方案用途主控地瓜机器人 RDK X34GB版本图像处理、模型推理、运动控制摄像头USB免驱广角摄像头720p/30fps人脸采集、巡线识别底盘差速两轮驱动 编码器运动控制与里程计电机驱动STM32作为下位机 或 直接PWM驱动速度环控制显示器树莓派7寸触摸屏可选患者交互界面电源12V锂电池 降压模块整机供电如果你手里的车模是现成的比如之前竞速赛留下来的那完全可以沿用底盘和电机把主控换成RDK X3就行。2. 为什么选地瓜机器人而不是树莓派或Jetson这个选择我相信很多队伍纠结过。我先说结论再解释原因如果追求开发效率与AI推理性能的平衡RDK X3是这几个平台里最稳的选择。对比一下三个常用平台维度树莓派4BJetson Nano地瓜机器人 RDK X3AI算力无专用NPU472 GFLOPS5 TOPSBPU功耗约5W5-10W约5W开发语言Python为主Python/CPython/CROS 2支持可用但性能受限完整原生适配开箱即用模型部署只能跑轻量模型TensorRT地平线工具链一键转换官方文档通用通用提供大量机器人场景案例Jetson Nano其实也很强但对我们学生队伍来说有两个很头疼的问题一是TensorRT的部署调试周期长二是板子散热是个麻烦事装在小车上跑赛题算法温度一高就降频帧率浮动很厉害。树莓派就更不用说了跑个YOLO都要用CPU硬扛实时性很难保证。而RDK X3的BPU对常见CNN模型支持度很好地平线的工具链能直接把PyTorch/ONNX模型转换成board格式在板端推理并且流程非常顺。加上官方提供了大量机器人开发套件级别的Demo手势识别、人体关键点检测、巡线导航等我们做二次开发能省下很多时间。另外一点RDK X3上预装了Ubuntu ROS 2 Foxy的系统镜像这个对智能车竞赛尤其关键——赛题里经常要求自主导航 任务调度ROS 2的节点通信机制可以很优雅地把感知、决策、控制拆成独立模块后面调试单项功能时不用反复烧录整个系统。3. 整个系统的架构与数据流设计在动代码之前我想先讲讲系统的整体架构。这部分的规划直接决定了后面写代码、调参的难易程度。整个系统的运行逻辑可以概括为三句话感知层负责看摄像头采集图像经过人脸检测和肤色分析生成健康评估结果同时识别地面引导线和障碍物。决策层负责想根据当前任务初筛任务还是配送任务决定小车的行为状态巡线、停靠、避障、语音播报。执行层负责动将决策结果转换为电机PWM信号通过PID控制实现精确的速度与转向。在代码层面我用了ROS 2节点架构来组织整个程序。这样做的好处是每个功能模块独立运行任何一个节点崩溃了不会拖垮整台车。先看一下我定义的Topic和节点结构/face_detector —— 人脸检测与肤色分析节点感知 /obstacle_laser —— 激光/超声波避障数据节点感知 /navigation —— 导航决策节点决策 /motor_controller —— 电机控制节点执行 /voice_service —— 语音播报服务节点交互消息流是这样走的摄像头画面 →/face_detector节点分析完成后把皮肤状态分数和是否检测到人脸发布到ROS 2的Topic →/navigation节点接收结果结合当前任务状态决定下一步动作 → 如果是在配送模式就发指令给/motor_controller执行巡线动作如果是初筛模式就在原地完成分析把结果送往/voice_service做语音播报。这里有个容易被新手忽略的点摄像头和电机驱动的实时性问题。如果你用Python直接在主循环里同时处理图像和电机控制帧率一高就会出现电机响应不及时的问题。我的解决办法是让图像处理和电机驱动跑在不同进程里通过ROS 2的Topic异步通信。这样图像处理哪怕偶尔掉帧电机控制也不受影响。4. 智慧医疗核心功能一皮肤健康初筛算法4.1 数据来源与预处理皮肤健康初筛是整个项目里最有医疗AI味道的部分。它能通过摄像头拍摄人脸分析皮肤表面特征给出一个健康评分和诊断提示。先说数据。很多人一听到皮肤病检测就头皮发麻觉得要收集一堆医学影像。实际上我这里的做法是基于肤色特征分析而非深度医学诊断。它判断的是皮肤表面的颜色分布是否均匀、是否有异常色块这用传统的图像处理算法就能实现不需要训练深度模型。实时视频帧输入后第一步是人脸检测。我直接调用了OpenCV的DNN模块加载了一个轻量的SSD人脸检测模型res10_300x300_ssd_iter_140000.caffemodel这个模型在CPU上也能跑得动性能足够。人脸检测的代码实现import cv2 import numpy as np class FaceDetector: def __init__(self, proto_path, model_path, conf_threshold0.7): # 加载Caffe格式的人脸检测模型 self.net cv2.dnn.readNetFromCaffe(proto_path, model_path) self.conf_threshold conf_threshold # 输入图像尺寸固定为300x300 self.input_width 300 self.input_height 300 def detect_faces(self, frame): h, w frame.shape[:2] # 构造blob并送入网络 blob cv2.dnn.blobFromImage( cv2.resize(frame, (self.input_width, self.input_height)), 1.0, (self.input_width, self.input_height), (104.0, 177.0, 123.0), swapRBFalse ) self.net.setInput(blob) detections self.net.forward() faces [] for i in range(detections.shape[2]): confidence detections[0, 0, i, 2] if confidence self.conf_threshold: box detections[0, 0, i, 3:7] * np.array([w, h, w, h]) x1, y1, x2, y2 box.astype(int) # 边界保护 x1, y1 max(0, x1), max(0, y1) x2, y2 min(w, x2), min(h, y2) faces.append((x1, y1, x2, y2)) return faces提示这里的模型文件在OpenCV的官方GitHub仓库里就有搜索res10_300x300_ssd_iter_140000.caffemodel和deploy.prototxt下载后放到项目的models目录即可。4.2 肤色分析与健康评分检测到人脸之后我们需要进一步分析皮肤状态。这一步用到了HSV颜色空间转换和色块统计分析。我在赛道调试时发现一个很关键的问题很多人对于肤色检测的第一反应是用RGB阈值但RGB对光照变化太敏感了同一个人的脸在室内灯和自然光下颜色的数值差异非常大。而HSV色调、饱和度、亮度空间把颜色和亮度分离了所以用H和S通道做肤色分割更稳定。下面是肤色分析的核心代码def analyze_skin_health(face_roi): 输入人脸ROI区域输出健康评分指标 # 转换到HSV颜色空间 hsv cv2.cvtColor(face_roi, cv2.COLOR_BGR2HSV) # 肤色范围HSV空间的典型阈值可根据实际场景微调 lower_skin np.array([0, 30, 60], dtypenp.uint8) upper_skin np.array([25, 150, 240], dtypenp.uint8) skin_mask cv2.inRange(hsv, lower_skin, upper_skin) # 形态学操作去除噪点和填充孔洞 kernel cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)) skin_mask cv2.morphologyEx(skin_mask, cv2.MORPH_OPEN, kernel) skin_mask cv2.morphologyEx(skin_mask, cv2.MORPH_CLOSE, kernel) # 计算肤色区域占比 total_pixels face_roi.shape[0] * face_roi.shape[1] skin_pixels np.count_nonzero(skin_mask) skin_ratio skin_pixels / total_pixels # 找异常色块色斑、泛红区域 # 在HSV中红色/褐色区域的色调与正常肤色存在差异 lower_red np.array([0, 100, 100], dtypenp.uint8) upper_red np.array([10, 255, 255], dtypenp.uint8) red_mask cv2.inRange(hsv, lower_red, upper_red) red_ratio np.count_nonzero(red_mask) / total_pixels # 纹理粗糙度评估用Laplacian方差近似 gray cv2.cvtColor(face_roi, cv2.COLOR_BGR2GRAY) laplacian_var cv2.Laplacian(gray, cv2.CV_64F).var() # 综合评分满分100 score max(0, 100 - red_ratio * 500 - abs(laplacian_var - 500) / 20) return { skin_ratio: skin_ratio, red_ratio: red_ratio, texture_score: laplacian_var, health_score: min(100, score) }这个评分逻辑说实话不够医学严谨但在竞赛场景里足够展示完整的采集-分析-反馈链路。如果想做得更专业可以把这里的传统图像特征换成训练好的分类模型比如MobileNetV2微调后区分痤疮/正常皮肤后面我会讲模型量化部署的方法。4.3 在RDK X3上跑肤色分析的速度实测很多做这个项目的同学会担心——OpenCV的DNN模型在RDK X3上跑人脸检测会不会很卡我实测的数据是这样的人脸检测OpenCV DNNSSD模型单帧约35msHSV转换 肤色分析单帧约8ms整体端到端延迟约50ms相当于20 FPS这个速度做实时分析完全没问题。而且要注意我们的分析逻辑只是在对齐好的人脸ROI上做的不是每帧全图分析所以CPU占用其实很低。5. 智慧医疗核心功能二药品自动配送导航5.1 巡线与路口识别不靠视觉SLAM也能稳定送药药品配送的导航方案我们最终没有上激光雷达SLAM因为赛题场景是结构化环境有固定床位、固定过道用巡线 路口标志识别是最稳、最不容易翻车的方案。巡线的实现非常经典把摄像头画面转成二值图后提取每一行的中心线位置用加权平均得出小车相对引导线的横向偏移再用PID控制转向。def get_line_center(frame): 从底部图像区域提取引导线中心位置 h, w frame.shape[:2] # 只取画面底部三分之二作为巡线区域 roi frame[int(h*0.3):, :] gray cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY) # 引导线是黑色背景上的白色胶带或白色背景上的黑色胶带 _, binary cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU) # 逐行扫描计算每行白色像素的中心 centers [] for row in range(binary.shape[0]): row_data binary[row, :] indices np.where(row_data 0)[0] if len(indices) 0: centers.append(np.mean(indices)) if len(centers) 0: return w // 2, False # 没找到线 # 对中心点取平均并映射回原图坐标 offset int(h * 0.3) line_center int(np.mean(centers)) return line_center, True这一步的细节在于巡线区域不是全图而是只取底部2/3。因为摄像头安装在小车前方近距离的线最可靠远处的线会因为视角关系产生较大的透视误差。5.2 路口识别与定点停靠配送药品需要小车在指定的床位前停下来。我用了一个非常实用的策略——在床位的正前方地面贴一张ArUco码小车每次经过路口时检测画面中是否存在ArUco码识别到对应的ID就停车。ArUco码的方案比RFID地标或者色卡都更稳定因为即使摄像头视角有偏移OpenCV的ArUco模块也能准确还原出码的位置和ID。import cv2 from cv2 import aruco def detect_aruco_id(frame): 检测画面中的ArUco码返回ID列表 gray cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) aruco_dict aruco.getPredefinedDictionary(aruco.DICT_ARUCO_ORIGINAL) parameters aruco.DetectorParameters() detector aruco.ArucoDetector(aruco_dict, parameters) corners, ids, rejected detector.detectMarkers(gray) return ids, corners在停车逻辑上我用的是距离阈值 方向角判断双条件。当ArUco码在画面中的面积超过一定像素说明已经足够近且码的中心位于画面中线偏下时触发停车。这样可以避免在侧向经过场景下误停。5.3 配送调度状态机整个配送任务里的行为切换我写成了一个简单的状态机运行在/navigation节点中状态触发条件执行动作IDLE任务开始前原地等待接收指令SEARCH_LINE初始位置不在线上旋转180度寻找引导线FOLLOW_LINE检测到引导线PID巡线前进STOP_AT_BED检测到目标ArUco码且距离足够近减速停车播报到站通知DELIVER_DONE停留2秒后语音提示药品已送达任务结束有了状态机之后逻辑就特别清晰了。每一步只需要处理当前状态下的事件不用在各种if-else之间乱跳。6. 模型量化与部署把PyTorch模型塞进BPU前面提到如果不想用传统图像处理而是想上真正的深度学习分类模型RDK X3对PyTorch/ONNX模型有完整转换链路。这里我把流程走一遍。6.1 训练与导出ONNX假设我们要训练一个痤疮/正常皮肤的二分类模型选MobileNetV2作为backbone分类网络用MobileNetV2在嵌入式设备上推理速度快、模型小。训练完成后需要先导出为ONNX格式。import torch import torch.nn as nn from torchvision import models, transforms # 以MobileNetV2为例 model models.mobilenet_v2(pretrainedTrue) # 修改最后一层为二分类 model.classifier[1] nn.Linear(model.classifier[1].in_features, 2) # 加载训练好的权重 model.load_state_dict(torch.load(skin_classifier.pth)) model.eval() # 导出ONNX dummy_input torch.randn(1, 3, 224, 224) torch.onnx.export(model, dummy_input, skin_classifier.onnx, opset_version11, input_names[input], output_names[output]) print(ONNX导出完成)6.2 地平线工具链转换流程在RDK X3上部署模型走的是地平线的OE工具链在PC端完成转换命令大致如下# 安装地平线模型转换工具在PC上执行 pip install horizon_onnx2onnx horizon_model_toolkit # 转换ONNX为horizon模型 hb_mapper makertbin --model-type onnx \ --model skin_classifier.onnx \ --output model_output \ --input-shape input:1,3,224,224 \ --calibration-data calib_images/ \ --calibration-type kl \ --quantize True这里有个很重要的概念量化校准。BPU推理是int8精度的所以需要准备一批校准图片大概20-50张工具会统计这些图片在网络各层的激活值分布从而找到最合适的量化参数。校准图片最好和真实场景分布一致——我一开始随便找了些网图做校准结果模型在实物上推理时准确率掉了好几个点。6.3 板端推理转换完成后会得到一个.board后缀的模型文件。在RDK X3上使用horizon_python_sdk加载模型进行推理from horizon_tc_ui import HB_ONNXRuntime import cv2 import numpy as np # 加载horizon模型 sess HB_ONNXRuntime(model_fileskin_classifier.board) input_name sess.input_names[0] def predict_skin(img_bgr): # 预处理resize到224x224归一化 img cv2.resize(img_bgr, (224, 224)) img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) img img.astype(np.float32) / 255.0 img img.transpose(2, 0, 1)[np.newaxis, ...] output sess.run([], {input_name: img})[0] cls_id np.argmax(output) confidence np.max(output) return cls_id, confidence整个转换链路做熟之后从PyTorch训练到板端推理大约2小时能跑通。对竞赛而言这个周期完全可接受。7. 完整代码整合主控节点的设计与实现上面各功能模块都说明白了接下来我把所有代码整合成一个可运行的项目并解释主控节点的实现。项目目录结构smart_medical_car/ ├── models/ │ ├── deploy.prototxt │ └── res10_300x300_ssd_iter_140000.caffemodel ├── src/ │ ├── face_detector.py │ ├── skin_analyzer.py │ ├── line_follower.py │ ├── aruco_navigator.py │ └── main.py ├── scripts/ │ ├── calibrate_camera.py │ └── generate_aruco.py └── requirements.txt主控节点main.py的代码思路是创建各个功能模块的实例然后在ROS 2的定时回调里依次执行采集、感知、决策、控制。如果是单机直跑不跑ROS 2主循环也差不多。import cv2 import time from face_detector import FaceDetector from skin_analyzer import analyze_skin_health from line_follower import LineFollower from aruco_navigator import ArucoNavigator import RPi.GPIO as GPIO # RDK X3可通过GPIO控制 class SmartMedicalCar: def __init__(self): # 初始化各模块 self.face_detector FaceDetector( proto_pathmodels/deploy.prototxt, model_pathmodels/res10_300x300_ssd_iter_140000.caffemodel ) self.line_follower LineFollower() self.aruco_navigator ArucoNavigator() # 任务配置 self.current_mode HEALTH_SCAN # 或 DELIVERY self.target_bed_id 1 self.health_result None # 摄像头 self.cap cv2.VideoCapture(0) self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) def run_health_scan(self): 健康初筛模式采集10帧进行分析求平均分数 scores [] for _ in range(10): ret, frame self.cap.read() if not ret: continue faces self.face_detector.detect_faces(frame) if len(faces) 0: # 取最大的人脸区域 x1, y1, x2, y2 max(faces, keylambda f: (f[2]-f[0])*(f[3]-f[1])) face_roi frame[y1:y2, x1:x2] result analyze_skin_health(face_roi) scores.append(result[health_score]) time.sleep(0.1) if scores: avg_score sum(scores) / len(scores) print(f皮肤健康评分: {avg_score:.1f} / 100) if avg_score 80: print(状态健康皮肤状态良好) elif avg_score 60: print(状态注意建议保持作息规律) else: print(状态警告建议前往皮肤科咨询) return avg_score else: print(未检测到人脸请调整位置) return None def run_delivery_task(self): 药品配送模式巡线ArUco停靠 self.aruco_navigator.reset() while True: ret, frame self.cap.read() if not ret: continue line_center, found self.line_follower.get_line_center(frame) ids, _ self.aruco_navigator.detect_aruco_id(frame) # 检查是否到达目标床位 if ids is not None and self.target_bed_id in [int(i) for i in ids.flatten()]: self.line_follower.stop() print(f已到达 {self.target_bed_id} 号床位药品送达) break # 巡线控制 if found: error line_center - frame.shape[1] // 2 self.line_follower.set_pid(error) else: self.line_follower.slow_down() time.sleep(0.05) def main_loop(self): try: while True: print(\n选择任务1健康初筛 2药品配送 q退出) cmd input() if cmd 1: self.current_mode HEALTH_SCAN self.run_health_scan() elif cmd 2: self.current_mode DELIVERY self.run_delivery_task() elif cmd q: break finally: self.cap.release() cv2.destroyAllWindows() if __name__ __main__: car SmartMedicalCar() car.main_loop()注意RDK X3上的GPIO引脚定义与树莓派不完全一样如果用GPIO控制电机参考RDK X3的40Pin针脚定义做对应调整。我实际项目中因为使用了带串口的电机驱动板所以代码里没有直接操作GPIO。8. 调试中最容易踩的5个坑8.1 摄像头视角与画面延迟的平衡第一版我用的摄像头是垂直向下装在车顶的角度倒是能完整看到引导线但人脸检测几乎用不了看不到正脸。后来我换成了30度左右的俯仰角安装兼顾了巡线区域和人脸采集。如果你也需要同时做这两个任务建议还是给摄像头加一个舵机云台按需调整角度。另外延时问题也很重要。RDK X3跑720p30fps时如果所有节点都在同一个Python进程里跑CPU占用会到80%以上。这时候画面会卡顿导致巡线PID响应变慢。我最终把分辨率降到640x480并开启了JPEG硬件编码传输流畅度立刻上来了。8.2 PID参数不能只靠理论计算巡线PID的初始参数我一开始参考网上的教程设的结果车开起来蛇形走位一圈都跑不完就偏了。后来用了工程上常用的调参顺序先把积分项I设为0只取比例项P从0.3开始逐步增加直到车辆在直线段能稳定居中但允许在弯道轻微偏移。加入微分项D从1.0开始增加直到过弯时车身姿态稳定、不甩尾。最后加一点积分项I用于消除巡线时的静态偏差如电机转速不一致导致的长期偏航。最终我这边的参数是P0.55I0.02D1.5。但这个参数换一辆车就不一定适用了核心还是理解每个参数对响应的影响。8.3 ArUco码检测的远小近大误判问题ArUco码在小车5米外就能被识别但此刻触发停车显然不合理。我做了一个简单的面积过滤只有当目标ID对应的码角点围成的面积超过画面总面积的2%时才认为到了停车点。这个阈值需要根据你实际场地中贴码的大小来调整。8.4 肤色检测在赛场灯光下失效我第一次测试肤色分析是在实验室白炽灯下效果很好。结果到了赛场棚顶的强光灯一照整个画面的色温偏蓝肤色分割的mask变得支离破碎。解决办法是在肤色分析前加一步白平衡预处理。最简单的方式是OpenCV的灰度世界假设白平衡算法def white_balance(img): result cv2.cvtColor(img, cv2.COLOR_BGR2LAB) avg_a np.average(result[:, :, 1]) avg_b np.average(result[:, :, 2]) result[:, :, 1] result[:, :, 1] - ((avg_a - 128) * (result[:, :, 0] / 255.0) * 1.1) result[:, :, 2] result[:, :, 2] - ((avg_b - 128) * (result[:, :, 0] / 255.0) * 1.1) return cv2.cvtColor(result, cv2.COLOR_LAB2BGR)在进入肤色分析前对ROI做一次这个处理肤色掩膜的质量会稳定很多。当然这还是会消耗一点点CPU时间实测大概2ms可以接受。8.5 电池电压对电机转速的隐藏干扰编码器测速和控制如果不闭环电池电压从满电12.6V降到11V的过程中同一PWM下的电机会明显变慢。我们调试时遇到过上午跑得好好的下午跑歪了的问题。解决的办法是在电机控制中加基于编码器的闭环速度控制PID控制不能用开环PWM。如果项目时间紧没有编码器退而求其次的方法是在代码里定时检测电池电压做PWM的线性补偿。9. 竞赛演示技巧与答辩加分项项目代码和功能做扎实只是第一步现场演示和答辩表现同样重要。根据我的参赛体会这几个细节能帮你多拿不少分。9.1 演示脚本的讲故事设计评委看演示通常会关注这个项目到底解决什么问题。我当时把演示流程设计为开场阶段让小车在场地中自主巡线一周屏幕实时显示摄像头画面和识别框突出感知-决策-执行的完整性。健康初筛环节请一位观众坐小车前方小车自动识别并播报皮肤健康评分屏幕上同步展示人脸检测框和肤色分布图。送药环节在手机/电脑上输入1号床配送小车自动行驶到1号床位置播报药品已送达并显示ArUco码ID。这套流程下来评委能看到的东西非常直观人脸检测、图像处理、决策调度、精准停靠每一个技术点都对应一个明确的看得见的结果。9.2 数据可视化工装能让答辩更有说服力我专门做了一个小的可视化面板把摄像头画面、健康评分曲线、小车当前状态巡线中/停靠中实时显示在一块副屏上。评委走过去不用问就能看懂系统的工作步骤。这比临时打开终端解释一坨日志强得多。9.3 如何应对你这个方案有什么短板的提问诚实地讲这个方案肯定有不足。比如肤色分析受光照影响、导航依赖地面标志、无法处理动态障碍等。如果被问到不用慌这样组织回答思路先承认局限当前方案在设计时优先考虑了赛题场景下的稳定性所以选择了传统图像处理和固定路标方案。再谈演进方向如果要落地到真实医院环境可以替换为更鲁棒的深度学习皮肤分析模型并将巡线导航升级为基于激光雷达的SLAM自主导航。这样的回答既展示了你对技术边界的清醒认知也体现了你思考过后续的工程化路径。10. 对准备参加智能车竞赛的同学说几句实在话最后说点项目之外的体会。地瓜机器人这套平台现在很适合做创意赛项目因为它把智能的门槛降低了——你不用花大量时间调内核、适配驱动可以把精力集中在行业应用场景的算法与交互设计上。但对参加智能车竞赛的同学来说我还是想强调三件事软件架构比功能堆叠更重要。如果你准备用ROS 2一开始就设计好节点划分和消息接口后期扩展新功能会快很多。不要等代码写到两千行再回头重构那会非常痛苦。算法要分优先级。竞赛现场突发情况很多与其把精力放在模型精度从90%提升到92%不如把时间花在异常处理上没检测到线怎么办、连续几帧没看到ArUco码怎么办。系统稳定性和鲁棒性对最后成绩的影响远大于那2%的精度。多留余量现场一定会有意外。电池没充满、场地光线不对、贴纸反光甚至工作人员走位挡住摄像头这些事我都遇到过。准备一套快速恢复操作的流程比临时改代码靠谱。我现在回过头看这次项目最大的收获不是奖项或代码量而是搞明白了从一个想法到一台能稳定跑起来的机器之间到底隔着多少细节。地瓜机器人也好其他平台也罢真正难的从来不是某个单点技术而是把感知、决策、控制、交互这些环节像齿轮一样咬合起来的综合能力。如果你也在做类似的项目或者在调试中卡住了欢迎在评论区带上你的具体问题来聊。代码和思路都不难但每个人踩坑的位置不一样把自己的经验在社区里摊开来分享进步会快得多。