布尔输入¶
TeleOp OpModes are generally written in iterative style, with a loop that contains code that is called over and over. Under this paradigm, a simple handling of user input could look like
if (gamepad1.a) {
motor.setPower(1);
}
else {
motor.setPower(0);
}
在这种情况下,这可能会满足驾驶员的需求:当按钮被按住时,电机的功率设置为1,否则功率设置为0。由于多次向电机写入相同的功率对电机的行为没有影响,因此这完全可以正常工作。然而,当按下按钮时,想要做某事时就会出现问题。例如,写这样的东西来按下a或b来调整舵机是很有诱惑力的。
if (gamepad1.a) {
servo.setPosition(servo.getPosition()+0.1);
}
else if (gamepad1.b) {
servo.setPosition(servo.getPosition()-0.1);
}
但是,这将表现出不可预测的行为,因为每次按下按钮时,随着循环频率的变化以及按钮按下长度的变化,将多次调用 setPosition 方法。有一些技术可以避免这种情况,但是它们都需要将游戏手柄状态与上一个循环中的游戏手柄状态进行比较;因此,有必要存储它。
存储游戏手柄状态¶
虽然每个游戏手柄输入的先前状态可以单独存储在一个变量中,例如 boolean previousA ,但这很快就会变得烦人。幸运的是,FTC SDK提供了一种复制游戏手柄状态的方法,带有 gamepad.copy(gamepadToCopy) 。
备注
除了存储循环前一次迭代的gamepad状态之外,还存储循环当前迭代的gamepad状态。这是必要的,因为如果按钮的状态总是从 gamepad1 / gamepad2 读取,它可能会在读取值和存储前一次值之间改变。这是因为 gamepad1 / gamepad2 同时更新 LinearOpMode ,因此可以在循环迭代期间改变。
在基于 LinearOpMode 的TeleOp程序中,存储当前和以前的游戏手柄状态可能如下所示:
public void runOpMode() {
// By setting these values to new Gamepad(), they will default to all
// boolean values as false and all float values as 0
Gamepad currentGamepad1 = new Gamepad();
Gamepad currentGamepad2 = new Gamepad();
Gamepad previousGamepad1 = new Gamepad();
Gamepad previousGamepad2 = new Gamepad();
// other initialization code goes here
while (opModeIsActive()) {
// Store the gamepad values from the previous loop iteration in
// previousGamepad1/2 to be used in this loop iteration.
// This is equivalent to doing this at the end of the previous
// loop iteration, as it will run in the same order except for
// the first/last iteration of the loop.
previousGamepad1.copy(currentGamepad1);
previousGamepad2.copy(currentGamepad2);
// Store the gamepad values from this loop iteration in
// currentGamepad1/2 to be used for the entirety of this loop iteration.
// This prevents the gamepad values from changing between being
// used and stored in previousGamepad1/2.
currentGamepad1.copy(gamepad1);
currentGamepad2.copy(gamepad2);
// Main teleop loop goes here
}
}
上升沿检测器¶
为什么叫上升沿检测器?
信号沿是数字信号中的过渡,在这种情况下,数字信号是游戏手柄输入,不持有时为低,持有时为高。上升沿是从低到高的过渡,下降沿是从高到低的过渡。
最常用的技术是上升沿检测器。它允许代码在最初按下按钮时只运行一次,而不是在按下按钮时运行每个循环。它的工作原理是检查按钮当前是否被按下,但在上一个循环中没有被按下。例如,在TeleOp循环内部:
if (currentGamepad1.a && !previousGamepad1.a) {
servo.setPosition(servo.getPosition() + 0.1);
}
这将使每按 a 一次,舵机位置正好增加0.1。
下降沿检测器¶
一种非常相似的技术是下降沿检测器。它允许代码在按钮释放时只运行一次,而不是在按下按钮时运行每个循环。它的工作原理是检查按钮当前是否未被按下,而是在前一个循环中被按下。例如,在TeleOp循环内部:
if (!currentGamepad1.b && previousGamepad1.b) {
servo.setPosition(servo.getPosition() - 0.1);
}
这将减少舵机位置0.1正好每释放一次 b 。
备注
一个按钮可以在上升沿和下降沿运行不同的代码。这主要适用于相当复杂的交互,因此此处不演示。
切换¶
上升沿检测器的一个常见用例是控制切换。开关可以用来有一个按钮让机器人在状态之间切换;例如,启动和停止吸入拾取装置。这可以针对任意数量的状态进行,但最常见的是在两个状态之间进行。为了在两个状态之间进行切换,上升沿检测器用于将布尔值设置为相反的值,然后该布尔值用于控制动作。
例子¶
在初始化代码中:
boolean intakeToggle = false;
在相应的TeleOp循环内部:
// Rising edge detector
if (currentGamepad1.a && !previousGamepad1.a) {
// This will set intakeToggle to true if it was previously false
// and intakeToggle to false if it was previously true,
// providing a toggling behavior.
intakeToggle = !intakeToggle;
}
// Using the toggle variable to control the robot.
if (intakeToggle) {
intakeMotor.setPower(1);
}
else {
intakeMotor.setPower(0);
}
这将在按下 a 时启动吸入拾取装置,并保持运行状态,直到再次按下。
备注
驾驶员对机器人状态的了解越少,他们就越不可能搞砸。由于切换意味着每次按下按钮都会做不同的事情,请考虑替代解决方案。对于具有两个以上状态的切换尤其如此。