简介这份PDF实验报告面向乐高EV3教育平台的学习者与指导教师围绕RobotC环境下的巡线小车编程实践展开帮助读者从电机控制、传感器读取到循环结构搭建完整的机器人控制逻辑。内容以标准实验报告形式组织涵盖电机转速与差速转向、触碰传感器、光电传感器、超声波传感器的初始化与读值方法并给出巡线小车依靠光电传感器在黑线边沿反复修正方向的实现思路同时涉及电机角度控制与浮动、制动模式的区别。资源包共1个PDF文件约756KB篇幅紧凑适合作为课堂实验或自学参考。目前已有583人学习读者可据此理解传感器阈值判断、while循环与wait1Msec延时配合的写法并对照代码实例完成调试与功能扩展积累硬件控制与编程结合的实践经验。1. 巡线小车的本质为什么两个光电传感器反而更容易翻车很多人第一次做巡线小车直觉是「传感器越多越准」于是左右各装一个光电传感器想着双保险。结果装完发现小车在直道上左右抽搐过弯时干脆冲出赛道。反倒是只留一个传感器、贴着黑线边缘走的方案跑出了 10.03 秒的稳定成绩。这个反直觉的现象背后藏着巡线控制最核心的机制巡线不是「检测黑线在哪」而是「用传感器读数驱动一个负反馈回路」。传感器数量增加带来的是两路信号的阈值不一致、安装高度差异、采样时序错位控制器的状态空间直接翻倍调参难度指数上升。搞懂这件事你才能明白为什么 RobotC 里那几行motor[]赋值和SensorValue读取能撑起一个完整的闭环控制系统也才能理解后面电机转角控制、制动模式、传感器安装位置这些设计取舍到底在解决什么问题。2. 差速转向的底层机制两个轮子的速度差如何变成方向2.1 从motor[]赋值到车体运动学RobotC 里控制电机最直接的方式是给motor[]数组赋值取值范围 -100 到 100正数正转、负数反转、0 停止。这个接口看起来简单但它背后对应的是一个差速驱动模型车体的线速度和角速度由左右两轮的转速共同决定。// 双轮全速前进线速度最大角速度为零 motor[motorA] 100; motor[motorB] 100; // 左轮停、右轮转绕左轮画弧转弯半径等于轮距 motor[motorA] 0; motor[motorB] 100; // 两轮反向绕两轮中点原地旋转转弯半径趋近于零 motor[motorA] -75; motor[motorB] 75;这里的关键参数是两轮的速度差。设左轮速度 $v_L$、右轮速度 $v_R$、轮距 $d$则车体角速度正比于 $(v_R - v_L)/d$。当 $v_L v_R$ 时角速度为零车走直线当 $v_L 0$ 时车绕左轮转转弯半径恰好等于轮距当 $v_L -v_R$ 时转弯半径为零实现原地转向。这三种模式在巡线里各有用途直道用第一种缓弯用第二种做粗调急弯或需要快速纠偏时用第三种。我一般会这么理解motor[]的赋值不是「给轮子一个速度」而是「给车体一个运动约束」。你写的每一个数值都在同时决定线速度和角速度两个自由度这也是为什么巡线代码里左右电机的赋值总是成对出现、且差值才是真正有意义的量。2.2 为什么转向时外侧轮要加速而不是内侧轮减速一个常见的误用是发现车往右偏了就把左轮减速。逻辑上没错但实际跑起来会发现车头摆动幅度很大、恢复慢。原因在于减速会同时降低车体的前进线速度而巡线需要的是「在保持前进的同时修正方向」。更稳的做法是外侧轮加速、内侧轮保持或略降让速度差产生转向力矩同时线速度不塌陷。从能量角度看电机从 100 降到 50 需要克服惯性、经历一个减速过程而从 50 升到 100 是加速过程响应更快。巡线是一个高频纠偏的场景每一次纠偏的响应延迟都会累积成路径误差。所以代码里常见的写法是基础速度给一个中等值比如 60偏转时一侧加到 80、另一侧降到 40差值 40 提供转向平均速度 60 维持前进。这个「基础速度 差速偏置」的结构比「一侧固定、另一侧调节」的结构稳定得多。2.3 制动模式与浮动模式为什么精确控制前必须设bFloatDuringInactiveMotorPWM falseNXT 电机内置了角度编码器360 度精度这让转角控制成为可能。但 RobotC 默认的电机模式是浮动模式float断电后电机可以自由转动制动模式brake下电机会短接绕组产生阻尼。原文里有一句很关键的话制动模式下功率和速度呈良好线性关系浮动模式下要到 40 以上功率才线性。这意味着什么如果你在浮动模式下做低速巡线给 30 的功率电机可能根本不转或者转速极不稳定你的控制回路就建立在一个非线性、有死区的执行器上调参怎么调都是玄学。所以做精确控制前第一件事是把bFloatDuringInactiveMotorPWM设为false切到制动模式让「功率 → 速度」这条映射在低速段也可信。bFloatDuringInactiveMotorPWM false; // 切制动模式低速段线性 nMotorEncoder[motorA] 0; // 编码器清零建立角度基准 nMotorEncoderTarget[motorA] 6; // 设定本次转动增量 6 度 motor[motorA] 50; // 给功率启动否则 target 不生效注意nMotorEncoderTarget[]只是设定目标增量它本身不启动电机后面必须跟一句motor[]赋值。这个设计容易踩坑只写 target 不写 motor程序编译通过、运行没反应新手会以为是传感器或固件问题实际是执行链没闭合。3. 光电传感器巡线的控制回路一个传感器如何撑起闭环3.1 阈值判定与边沿摇摆策略光电传感器读的是反射光强度黑线吸光、白底反光所以读数在黑线上低、在白底上高。原文给的例子是阈值 45大于 45 前进小于等于 45 停止。但巡线不能只做「停/走」二值判断那样车会在黑线边缘反复启停、抖得厉害。真正让车跑起来的是边沿摇摆传感器装在黑线一侧的边缘车偏到黑线上就向另一侧转转到白区又转回来靠高频小幅摆动维持沿黑线前进。const tSensors light1 (tSensors) S1; // 端口1设为光电传感器 task main() { wait1Msec(100); // 上电稳定避免首帧读数漂移 while (true) { int lightVal SensorValue[light1]; if (lightVal 45) { // 读到白区说明车偏向外侧向内修正 motor[motorA] 40; motor[motorB] 70; } else { // 读到黑线说明车偏向内侧向外修正 motor[motorA] 70; motor[motorB] 40; } } }这段代码的结构值得拆开看while(true)是控制回路的采样循环SensorValue是反馈量两个motor[]赋值是执行量阈值 45 是设定值。整个系统是一个 bang-bang 控制器——只有「偏左」和「偏右」两个状态没有中间档。它的优点是实现简单、响应快缺点是稳态误差大、在阈值附近会高频切换。10.03 秒的成绩说明对于一条曲率不大的赛道bang-bang 加合理的机械安装已经够用。3.2 采样周期与wait1Msec的隐藏作用上面的代码没有显式延时循环体执行一遍的时间取决于SensorValue读取和电机赋值的开销通常在毫秒级。但原文的触碰传感器例子里用了wait1Msec(1000)光电例子里用了wait1Msec(100)做上电稳定。这两个延时作用完全不同前者是动作持续时间后者是传感器稳定时间。巡线回路里如果人为加长延时采样周期变长车在两次采样之间走过的距离变大纠偏就会滞后表现为过弯冲出去。所以巡线主循环里一般不加延时让采样尽可能密。但也不能完全不管如果电机赋值过于频繁PWM 占空比还没生效就被下一次赋值覆盖实际输出会偏离预期。常见的做法是在主循环里加一个很小的wait1Msec(5)到wait1Msec(10)给执行器一个响应窗口同时保持足够高的采样率。这个值没有标准答案跟车重、电机型号、赛道曲率都有关属于血泪经验型的参数。3.3 传感器安装位置为什么比代码更影响成绩原文的改进建议里有一条特别实在光电传感器在 y 轴上离轮轴太远转向时传感器旋转半径大、线速度高信号延迟误差大导致转向频繁且卡顿最后把传感器移到离轮轴更近的位置才解决。这条经验背后是刚体运动学传感器到轮轴的距离 $r$ 决定了车体转向时传感器的线速度 $v \omega r$。传感器离轮轴越远同样的角速度下它扫过的弧长越长从「看到黑线」到「车体实际转过去」之间的路径偏差越大。控制回路看到的是一个延迟了 $\Delta t$ 的位置信号而延迟环节是闭环系统里最容易引发振荡的因素。把传感器移近轮轴等于缩短了反馈路径的物理延迟系统稳定性直接提升。这也解释了为什么「两个传感器」方案容易翻车两个传感器的安装高度、前后位置很难做到完全一致两路信号的相位差不同控制器拿到的是一组不同步的反馈调参时顾此失彼。单传感器方案没有这个问题它的反馈是单一、同步的虽然信息量少但控制回路干净。4. 避坑与排查巡线调试里最常见的五个翻车点4.1 现象程序下载后电机不转但编译无报错原因通常是nMotorEncoderTarget[]设了目标却没跟motor[]启动或者传感器初始化用了#pragma config但代码里引用的名称和配置不一致。解决先检查执行链是否闭合——target 后面有没有 motor 赋值再检查const tSensors定义的名字和SensorValue[]里用的名字是否逐字符一致RobotC 对大小写敏感light1和Light1是两个东西。4.2 现象小车在直道上左右抽搐速度上不去原因是阈值卡在黑白过渡区读数在阈值附近抖动控制器高频切换。解决把阈值往黑线一侧偏移让「白区」判定更严格或者给判定加一个小滞回——读数高于阈值加 5 才算白、低于阈值减 5 才算黑。滞回能吃掉边界抖动代价是纠偏精度略降但对稳定性提升明显。4.3 现象过弯时冲出赛道直道正常原因是采样周期太长或基础速度太高车在两次纠偏之间走过的弧长超过了赛道曲率允许的偏差。解决先降基础速度验证是不是速度问题如果降速后能过弯说明是采样率不够去掉主循环里多余的延时如果降速仍冲出去检查传感器安装位置是否离轮轴太远反馈延迟过大。4.4 现象同一份代码换一块场地成绩波动很大原因是光电传感器对环境光敏感场地照明变化会整体抬高或压低读数固定阈值失效。解决每次换场地先跑一段「标定」——让车静止在黑线和白底上各读一次取中间值作为阈值或者用 LCD 显示实时读数人工确认。原文的扩展要求里提到在 LCD 上显示给定值和反馈数据就是为这个场景准备的。4.5 现象低速时电机时转时停高速正常原因是电机处于浮动模式低速段功率和速度非线性30 以下的功率可能不足以克服静摩擦。解决设bFloatDuringInactiveMotorPWM false切制动模式或者把基础速度提到 40 以上避开死区。这个坑在巡线里特别隐蔽因为高速跑得好好的一降速调参就出问题容易误判成传感器故障。5. 从 bang-bang 到 PID控制精度提升的取舍与验证bang-bang 控制器能跑出 10.03 秒说明它对这条赛道是够用的。但它的局限也很清楚稳态误差无法消除车永远在阈值两侧摆动摆动的幅度取决于速度、采样率和机械间隙。想把成绩再往上推就得引入比例项——用「读数偏离阈值的程度」来决定差速的大小而不是固定差速。这就是 PID 里 P 项的雏形。// 比例控制示意偏差越大差速越大 int threshold 45; int baseSpeed 60; int kp 2; // 比例系数需实测调整 while (true) { int error SensorValue[light1] - threshold; // 偏差 int correction kp * error; // 比例修正量 motor[motorA] baseSpeed - correction; motor[motorB] baseSpeed correction; wait1Msec(5); }这段代码和 bang-bang 的本质区别在于执行量是连续的偏差小的时候修正小、偏差大的时候修正大车不会在阈值附近硬切换。但kp的取值是个取舍——太大则系统对噪声敏感、容易振荡太小则纠偏无力、过弯滞后。原文提到小组「研究 PID 思想后」把传感器移近轮轴其实就是在降低反馈延迟让比例控制有更大的稳定裕度去用较大的kp。验证方法很直接固定赛道和基础速度从小到大调kp观察车在直道上的摆动幅度和过弯的跟随性。摆动幅度先减小后增大那个拐点就是当前机械结构下的临界值实际取值留 60% 到 70% 的裕度。这个流程我每次调闭环都强制走一遍因为凭感觉调kp翻车的概率太高而一次系统性的阶跃测试能把参数空间摸清楚。还有一个容易被忽略的取舍传感器数量。单传感器加比例控制信息量只够做一维纠偏双传感器能算出偏差的方向和大致幅度理论上可以做更平滑的控制。但双传感器的阈值一致性、安装对称性要求很高调不好反而引入两路噪声。我的习惯是先用单传感器把控制回路跑通、把机械安装和采样周期这些基础项做扎实再考虑加传感器——因为大多数巡线成绩不理想根因不在控制算法而在反馈延迟和阈值漂移这些底层问题上。希望这些拆解能帮你在下一次调试时少走几个弯路。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?