外观
游戏手柄(Gamepad)使用指南
Gamepad 常用方法
| 方法 | 作用 | 备注与注意事项 |
|---|---|---|
rumble(double rumble1, double rumble2, int durationMs) | 使手柄震动 | 参数:左马达强度(0-1)、右马达强度(0-1)、持续时间(毫秒) |
rumble(int durationMs) | 以 100%功率震动手柄 | 简化版本的震动方法 |
rumbleBlips(int count) | 震动指定次数 | 用于简单的反馈信号 |
stopRumble() | 停止当前震动 | 可用于中断长时间震动 |
isRumbling() | 检查手柄是否正在震动 | 返回布尔值 |
setLedColor(double r, double g, double b, int durationMs) | 设置手柄 LED 灯颜色 | 仅 PS4 和部分第三方手柄支持,颜色值范围 0-1 |
getButtonState(Button button) | 获取指定按钮的状态 | 返回布尔值,表示按钮是否被按下 |
setCourse(double course, double power) | 设定摇杆方向和力度 | 用于创建虚拟摇杆输入,course 为角度(0-360 度) |
atRest() | 检查手柄是否处于静止状态 | 返回布尔值,所有按钮未按下且摇杆在中央位置时为 true |
copy(Gamepad gamepad) | 复制另一个手柄的状态 | 用于保存手柄状态的快照 |
toString() | 返回手柄状态的字符串表示 | 用于调试和记录 |
type() | 返回手柄类型 | 区分不同型号的手柄 |
refreshTimestamp() | 刷新时间戳 | 记录最后一次数据更新的时间 |
什么是游戏手柄 (Gamepad)?
游戏手柄是 FTC 机器人比赛中用来控制机器人的主要输入设备。它就像是电视游戏机的手柄,让操作员可以通过按钮、摇杆和扳机来控制机器人的各种动作。
在 FTC 比赛中:
- 最多可以同时使用 2 个游戏手柄
- 每个手柄都需要通过 USB 线连接到驾驶站 (Driver Station) 设备上
- 官方允许的手柄型号包括 Logitech F310(震动:没有、LED 控制:无)、Sony DualShock 4、Sony DualSense 和 Xbox 360 等

连接游戏手柄
在比赛中正确连接和设置游戏手柄非常重要:
检查游戏手柄模式开关:确保 Logitech F310 手柄底部的模式开关处于 "X" 位置(即 Xbox 模式)
连接到驾驶站:使用 USB 线将游戏手柄连接到驾驶站设备(通常是一台 Android 手机或平板)
指定手柄编号:
- 同时按下 Start 按钮和 A 按钮,将手柄指定为 1 号手柄(用于主驾驶)
- 同时按下 Start 按钮和 B 按钮,将手柄指定为 2 号手柄(用于副驾驶)
确认连接成功:正确连接后,驾驶站界面右上角会显示一个手柄图标,标有 "User 1" 或 "User 2"。当您操作手柄时,对应的图标会显示绿色高亮
游戏手柄的按钮和摇杆
主要控制元件
游戏手柄上的控制元件可以分为以下几类:
- 摇杆(Joysticks):左右两个可以上下左右移动的小杆
- 按钮(Buttons):A、B、X、Y、Back、Start、左右摇杆按钮等
- 方向键(Dpad):上、下、左、右四个方向按键
- 扳机(Triggers):左右两个类似扳机的控制器(可变压力)
- 肩部按钮(Bumpers):左右两个位于手柄上部的按钮

数值范围
不同控制元件的读数范围:
- 摇杆:-1.0 到 +1.0
- 左:-1.0,右:+1.0
- 上:-1.0,下:+1.0(注意:Y 轴是反向的!也就是 Y 轴前推,但是得到的是负数值。)
- 扳机:0.0 到 1.0
- 按钮和肩部按钮:按下为 true,松开为 false
- 方向键:按下为 true,松开为 false
在代码中使用游戏手柄
在 FTC 编程中,游戏手柄是通过 gamepad1 和 gamepad2 两个对象来访问的:
gamepad1对应 1 号手柄(主驾驶)gamepad2对应 2 号手柄(副驾驶)
基本用法示例
以下是一些常见的游戏手柄控制示例:
控制电机(使用摇杆)
java
// 使用左摇杆的垂直方向控制电机
// 注意:通常需要取负值,因为摇杆向上是-1,向下是+1
double motorPower = -gamepad1.left_stick_y;
motor.setPower(motorPower);
// 使用右摇杆的水平方向控制转向
double steeringPower = gamepad1.right_stick_x;
leftMotor.setPower(motorPower + steeringPower);
rightMotor.setPower(motorPower - steeringPower);1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
控制舵机(使用按钮)
java
// 使用A和B按钮控制舵机位置
if (gamepad1.a) {
servo.setPosition(0.0); // 移动到最小位置
} else if (gamepad1.b) {
servo.setPosition(1.0); // 移动到最大位置
}1
2
3
4
5
6
2
3
4
5
6
使用扳机控制吸取装置
java
// 使用左右扳机控制吸取装置
double intakePower = gamepad1.right_trigger - gamepad1.left_trigger;
intakeMotor.setPower(intakePower);1
2
3
2
3
使用方向键选择自动程序
java
// 使用方向键选择不同的自动程序
if (gamepad1.dpad_up) {
selectedAuto = "路径A";
} else if (gamepad1.dpad_right) {
selectedAuto = "路径B";
} else if (gamepad1.dpad_down) {
selectedAuto = "路径C";
} else if (gamepad1.dpad_left) {
selectedAuto = "路径D";
}1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
常见编程模式
基于状态的控制
这种模式适合控制需要在不同位置之间切换的组件,如手爪、机械臂等:
java
// 翻转状态变量的值
if (gamepad1.x && !xWasPressed) {
clawOpen = !clawOpen;
if (clawOpen) {
claw.setPosition(CLAW_OPEN_POSITION);
} else {
claw.setPosition(CLAW_CLOSED_POSITION);
}
}
xWasPressed = gamepad1.x;1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
组合按钮
有时需要同时按下多个按钮来触发特殊功能:
java
// 只有同时按下A和B才会激活特殊功能
if (gamepad1.a && gamepad1.b) {
// 执行特殊功能
activateSpecialFeature();
}1
2
3
4
5
2
3
4
5
精确控制(缩放输入)
有时需要降低摇杆的灵敏度,以便进行精确操作:
java
// 将摇杆输入缩小到50%,提供更精确的控制
double preciseDrivePower = -gamepad1.left_stick_y * 0.5;
motor.setPower(preciseDrivePower);1
2
3
2
3
完整示例:坦克驱动控制
下面是一个完整的例子,展示如何使用游戏手柄控制机器人的坦克式驱动系统:
java
@TeleOp(name="基础坦克驱动", group="示例")
public class BasicTankDrive extends LinearOpMode {
// 声明电机
private DcMotor leftMotor = null;
private DcMotor rightMotor = null;
@Override
public void runOpMode() {
// 初始化电机
leftMotor = hardwareMap.get(DcMotor.class, "left_drive");
rightMotor = hardwareMap.get(DcMotor.class, "right_drive");
// 设置电机方向
leftMotor.setDirection(DcMotor.Direction.FORWARD);
rightMotor.setDirection(DcMotor.Direction.REVERSE);
// 等待开始
telemetry.addData("状态", "初始化完成");
telemetry.update();
waitForStart();
// 主循环
while (opModeIsActive()) {
// 读取游戏手柄输入
double leftPower = -gamepad1.left_stick_y;
double rightPower = -gamepad1.right_stick_y;
// 设置电机功率
leftMotor.setPower(leftPower);
rightMotor.setPower(rightPower);
// 显示当前电机功率
telemetry.addData("左电机", "%.2f", leftPower);
telemetry.addData("右电机", "%.2f", rightPower);
telemetry.update();
}
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
游戏手柄高级功能
震动反馈
在 FTC SDK 中,您可以让游戏手柄震动,为驾驶员提供触觉反馈:
java
// 让1号手柄震动1秒
gamepad1.rumble(1.0, 1.0, 1000);
// 参数解释:左马达强度(0-1),右马达强度(0-1),持续时间(毫秒)1
2
3
4
2
3
4
检测游戏手柄事件
有时您可能需要检测按钮的按下和释放事件,而不仅仅是当前状态:
java
boolean aWasPressed = false;
// 在循环中使用
if (gamepad1.a && !aWasPressed) {
// 仅在A按钮刚被按下时执行
toggleSomething();
}
aWasPressed = gamepad1.a;1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
游戏手柄属性列表
以下是完整的游戏手柄属性列表,您可以在代码中访问这些属性:
摇杆
gamepad1.left_stick_x- 左摇杆水平位置 (-1.0 到 1.0)gamepad1.left_stick_y- 左摇杆垂直位置 (-1.0 到 1.0)gamepad1.right_stick_x- 右摇杆水平位置 (-1.0 到 1.0)gamepad1.right_stick_y- 右摇杆垂直位置 (-1.0 到 1.0)
摇杆按钮
gamepad1.left_stick_button- 左摇杆按钮 (布尔值)gamepad1.right_stick_button- 右摇杆按钮 (布尔值)
主要按钮
gamepad1.a- A 按钮 (布尔值)gamepad1.b- B 按钮 (布尔值)gamepad1.x- X 按钮 (布尔值)gamepad1.y- Y 按钮 (布尔值)
肩部按钮和扳机
gamepad1.left_bumper- 左肩部按钮 (布尔值)gamepad1.right_bumper- 右肩部按钮 (布尔值)gamepad1.left_trigger- 左扳机 (0.0 到 1.0)gamepad1.right_trigger- 右扳机 (0.0 到 1.0)
方向键
gamepad1.dpad_up- 方向键上 (布尔值)gamepad1.dpad_down- 方向键下 (布尔值)gamepad1.dpad_left- 方向键左 (布尔值)gamepad1.dpad_right- 方向键右 (布尔值)
其他按钮
gamepad1.back- 返回按钮 (布尔值)gamepad1.start- 开始按钮 (布尔值)gamepad1.guide- 指南按钮 (布尔值)
官方示例代码解析
下面我们来分析 FTC SDK 中的官方示例代码,看看如何在实际程序中使用游戏手柄。
示例 1:基础操作模式(BasicOpMode_Linear)
这个示例展示了最基本的游戏手柄用法,用于控制一个简单的双轮机器人:
java
// POV模式控制:使用左摇杆前进/后退,右摇杆左右转向
double drive = -gamepad1.left_stick_y; // 获取左摇杆的Y轴值
// 注意:摇杆向前推是负值,所以要取反
double turn = gamepad1.right_stick_x; // 获取右摇杆的X轴值(左右转向)
// 计算左右电机功率
leftPower = Range.clip(drive + turn, -1.0, 1.0); // 将值限制在-1到1之间
rightPower = Range.clip(drive - turn, -1.0, 1.0);
// 坦克模式控制:左摇杆控制左电机,右摇杆控制右电机
// leftPower = -gamepad1.left_stick_y; // 左摇杆Y轴直接控制左电机
// rightPower = -gamepad1.right_stick_y; // 右摇杆Y轴直接控制右电机
// 将计算的功率发送到电机
leftDrive.setPower(leftPower);
rightDrive.setPower(rightPower);1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
代码解析:
- 首先获取游戏手柄的摇杆值:
gamepad1.left_stick_y:左摇杆上下移动的值(-1 到 1)gamepad1.right_stick_x:右摇杆左右移动的值(-1 到 1)
- 由于摇杆向前是负值(-1),所以需要取反使其变为正值
- 通过数学计算,将前进动作和转向动作结合起来:
- 左电机 = 前进值 + 转向值
- 右电机 = 前进值 - 转向值
- 使用
Range.clip()方法确保功率值不超出-1 到 1 的范围 - 最后将计算出的功率值设置给电机
示例 2:POV 控制模式(RobotTeleopPOV_Linear)
这个示例更加复杂,展示了如何用游戏手柄控制机器人的驱动系统、机械臂和机械爪:
java
// 驱动控制(与示例1类似)
drive = -gamepad1.left_stick_y; // 前后移动
turn = gamepad1.right_stick_x; // 左右转向
left = drive + turn; // 左侧电机功率
right = drive - turn; // 右侧电机功率
// 规范化值,确保不超过±1.0
max = Math.max(Math.abs(left), Math.abs(right));
if (max > 1.0) {
left /= max;
right /= max;
}
// 控制机械爪开合
if (gamepad1.right_bumper)
clawOffset += CLAW_SPEED; // 按下右肩键,增加爪子偏移量(开爪)
else if (gamepad1.left_bumper)
clawOffset -= CLAW_SPEED; // 按下左肩键,减小爪子偏移量(闭爪)
// 将爪子偏移量限制在合理范围内
clawOffset = Range.clip(clawOffset, -0.5, 0.5);
// 设置左右爪舵机位置(注意它们是镜像的)
leftClaw.setPosition(MID_SERVO + clawOffset);
rightClaw.setPosition(MID_SERVO - clawOffset);
// 控制机械臂上下移动
if (gamepad1.y)
leftArm.setPower(ARM_UP_POWER); // 按Y键,机械臂向上
else if (gamepad1.a)
leftArm.setPower(ARM_DOWN_POWER); // 按A键,机械臂向下
else
leftArm.setPower(0.0); // 不按键,机械臂停止1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
代码解析:
- 驱动系统控制与基础示例类似,但增加了规范化处理:
- 当计算出的功率值超过 1 时,按比例缩小左右电机功率,保持转向比例不变
- 机械爪控制使用左右肩部按钮(bumpers):
- 右肩按钮增加爪子偏移量,使爪子打开
- 左肩按钮减小爪子偏移量,使爪子关闭
- 每次按下只调整一点点(CLAW_SPEED=0.02),实现缓慢平滑的控制
- 机械臂控制使用 Y 和 A 按钮:
- Y 按钮使机械臂向上移动
- A 按钮使机械臂向下移动
- 不按任何按钮时停止机械臂
示例 3:检测按钮状态变化(防止重复触发)
在很多情况下,我们需要检测按钮的按下事件,而不是持续检测按钮的状态。下面的代码展示了如何做到这一点:
java
// 在类中定义变量来记住上一次的按钮状态
boolean aButtonPrevState = false;
boolean bButtonPrevState = false;
// 在循环中检测按钮状态变化
// 当前按钮状态
boolean aButtonCurrentState = gamepad1.a;
boolean bButtonCurrentState = gamepad1.b;
// 检测A按钮的按下事件(从未按下到按下的变化)
if (aButtonCurrentState && !aButtonPrevState) {
// A按钮刚刚被按下,执行一次性动作
// 例如:切换LED灯的开关状态
ledEnabled = !ledEnabled;
}
// 检测B按钮的释放事件(从按下到未按下的变化)
if (!bButtonCurrentState && bButtonPrevState) {
// B按钮刚刚被释放,执行一次性动作
// 例如:切换到下一个自动程序
selectedAutoMode = (selectedAutoMode + 1) % totalAutoModes;
}
// 更新上一次的按钮状态,为下一次循环做准备
aButtonPrevState = aButtonCurrentState;
bButtonPrevState = bButtonCurrentState;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
代码解析:
- 定义变量记录上一次循环中按钮的状态
- 在每次循环中,先获取当前按钮状态
- 通过比较当前状态和上一次状态,检测状态变化:
当前为true且上一次为false:表示按钮刚刚被按下当前为false且上一次为true:表示按钮刚刚被释放
- 在循环结束时,更新"上一次"状态变量
这种方法可以防止按钮被按住时重复触发动作,确保每次按下只执行一次操作。
示例 4:使用手柄震动提供反馈
某些游戏手柄(如 PS4 手柄)支持震动功能,可以提供触觉反馈:
java
// 让手柄震动1秒钟
gamepad1.rumble(1.0, 1.0, 1000); // 参数:左马达强度,右马达强度,持续时间(毫秒)
// 创建自定义震动序列
Gamepad.RumbleEffect customRumble = new Gamepad.RumbleEffect.Builder()
.addStep(0.0, 1.0, 300) // 右马达震动300毫秒
.addStep(0.0, 0.0, 100) // 暂停100毫秒
.addStep(1.0, 0.0, 300) // 左马达震动300毫秒
.build();
// 运行自定义震动序列
gamepad1.runRumbleEffect(customRumble);
// 简单地震动3次
gamepad1.rumbleBlips(3);1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
代码解析:
- 基本震动使用
rumble()方法,指定左右马达强度和持续时间 - 自定义震动序列允许创建复杂的震动模式,如摩尔斯电码或特定信号
rumbleBlips()提供简单的多次震动功能
注意:并非所有手柄都支持震动功能。罗技 F310 不支持震动,而 PS4 和 Xbox 手柄支持。
实用技巧与进阶方法
实现精确控制的几种方法
在实际竞赛中,有时需要更精确的控制。以下是几种常用方法:
1. 非线性控制曲线
java
// 应用平方函数,保留原始符号
// 这使得小输入有更精细的控制,大输入仍能达到最大速度
double rawInput = -gamepad1.left_stick_y; // 获取原始输入
double sign = Math.signum(rawInput); // 保存符号(1或-1)
double squared = Math.pow(rawInput, 2); // 计算平方值
double scaledInput = sign * squared; // 应用原始符号
motor.setPower(scaledInput);1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
2. 可调节的敏感度控制
java
// 使用扳机调节敏感度
double sensitivity = 0.5 + (gamepad1.right_trigger * 0.5); // 0.5到1.0的范围
double drive = -gamepad1.left_stick_y * sensitivity;
// 或者使用按钮切换不同的速度模式
if (gamepad1.b) {
speedMode = "高速";
speedMultiplier = 1.0;
} else if (gamepad1.x) {
speedMode = "中速";
speedMultiplier = 0.5;
} else if (gamepad1.a) {
speedMode = "低速";
speedMultiplier = 0.25;
}
// 应用速度乘数
leftPower = drive * speedMultiplier;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
多驾驶员协作控制
在 FTC 比赛中,可以有两个驾驶员,通常使用两个手柄协作控制机器人:
java
// 驾驶员1控制移动
double drive = -gamepad1.left_stick_y;
double turn = gamepad1.right_stick_x;
leftDrive.setPower(drive + turn);
rightDrive.setPower(drive - turn);
// 驾驶员2控制机械装置
if (gamepad2.a) {
// 控制收集机构
intakeMotor.setPower(1.0);
} else if (gamepad2.b) {
// 反向运行收集机构
intakeMotor.setPower(-1.0);
} else {
// 停止收集机构
intakeMotor.setPower(0);
}
// 驾驶员2控制发射机构
if (gamepad2.right_trigger > 0.5) {
// 启动发射器
launcherMotor.setPower(1.0);
// 等待发射器达到全速
sleep(500);
// 触发推块机构
pusherServo.setPosition(PUSH_POSITION);
sleep(250);
// 收回推块机构
pusherServo.setPosition(RETRACT_POSITION);
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
这种分工可以让一名驾驶员专注于机器人的移动,另一名驾驶员专注于操作机械装置,从而提高整体效率。
常见问题与经验总结
1. 调试游戏手柄输入
在开发过程中,通常需要查看游戏手柄的输入值以便调试:
java
// 在遥测中显示所有重要的游戏手柄输入
telemetry.addData("左摇杆", "X: %.2f Y: %.2f", gamepad1.left_stick_x, gamepad1.left_stick_y);
telemetry.addData("右摇杆", "X: %.2f Y: %.2f", gamepad1.right_stick_x, gamepad1.right_stick_y);
telemetry.addData("扳机", "左: %.2f 右: %.2f", gamepad1.left_trigger, gamepad1.right_trigger);
telemetry.addData("肩部按钮", "左: %b 右: %b", gamepad1.left_bumper, gamepad1.right_bumper);
telemetry.addData("按钮", "A: %b B: %b X: %b Y: %b", gamepad1.a, gamepad1.b, gamepad1.x, gamepad1.y);
telemetry.update();1
2
3
4
5
6
7
2
3
4
5
6
7
2. 避免常见陷阱
- 忘记取反 Y 轴:摇杆向前推是负值(-1),如果忘记取反,机器人会向后移动
- 死区处理不当:小输入可能导致电机随机抖动,应该添加死区逻辑
- 按钮触发重复:如果在每次循环中检测按钮状态,会导致持续按住按钮时重复触发
- 驾驶站不显示手柄:确保正确指定手柄编号(按 Start+A 或 Start+B)
3. 高级控制系统设计模式
随着机器人复杂性增加,可以考虑更先进的控制系统设计:
- 状态机:将机器人操作分解为不同状态,使用游戏手柄在状态之间切换
- 命令队列:允许驾驶员通过游戏手柄输入预设一系列动作
- 辅助功能:开发自动辅助功能,如自动对准目标或自动平衡
总结
游戏手柄是 FTC 机器人比赛中的核心输入设备,掌握其用法对于开发高效的遥控程序至关重要。从基本的移动控制到复杂的机械操作,游戏手柄提供了丰富的输入方式。通过学习本指南介绍的各种技术和模式,您可以开发出更加精确、可靠和用户友好的机器人控制系统。
记住:
- 理解摇杆和按钮的数值范围和行为
- 适当处理输入值,如取反、缩放和应用死区
- 检测按钮状态变化而不是简单检测当前状态
- 根据任务的复杂性设计合适的控制模式和驾驶员分工
