Skip to content

游戏手柄(Gamepad)使用指南​

约 3284 个字 248 行代码 预计阅读时间 14 分钟

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 模式)

检查游戏手柄模式开关:确保 Logitech F310 手柄底部的模式开关处于 "X" 位置(即 Xbox 模式)

  • 连接到驾驶站:使用 USB 线将游戏手柄连接到驾驶站设备(通常是一台 Android 手机或平板)

连接到驾驶站:使用 USB 线将游戏手柄连接到驾驶站设备(通常是一台 Android 手机或平板)

  • 指定手柄编号:同时按下 Start 按钮和 A 按钮,将手柄指定为 1 号手柄(用于主驾驶)同时按下 Start 按钮和 B 按钮,将手柄指定为 2 号手柄(用于副驾驶)

指定手柄编号:

  • 同时按下 Start 按钮和 A 按钮,将手柄指定为 1 号手柄(用于主驾驶)

  • 同时按下 Start 按钮和 B 按钮,将手柄指定为 2 号手柄(用于副驾驶)

  • 确认连接成功:正确连接后,驾驶站界面右上角会显示一个手柄图标,标有 "User 1" 或 "User 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 轴前推,但是得到的是负数值。)

  • 左:-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 号手柄(副驾驶)

基本用法示例​

以下是一些常见的游戏手柄控制示例:

控制电机(使用摇杆)​

// 使用左摇杆的垂直方向控制电机
// 注意:通常需要取负值,因为摇杆向上是-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);

控制舵机(使用按钮)​

// 使用A和B按钮控制舵机位置
if (gamepad1.a) {
    servo.setPosition(0.0); // 移动到最小位置
} else if (gamepad1.b) {
    servo.setPosition(1.0); // 移动到最大位置
}

使用扳机控制吸取装置​

// 使用左右扳机控制吸取装置
double intakePower = gamepad1.right_trigger - gamepad1.left_trigger;
intakeMotor.setPower(intakePower);

使用方向键选择自动程序​

// 使用方向键选择不同的自动程序
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";
}

常见编程模式​

基于状态的控制​

这种模式适合控制需要在不同位置之间切换的组件,如手爪、机械臂等:

// 翻转状态变量的值
if (gamepad1.x && !xWasPressed) {
    clawOpen = !clawOpen;
    if (clawOpen) {
        claw.setPosition(CLAW_OPEN_POSITION);
    } else {
        claw.setPosition(CLAW_CLOSED_POSITION);
    }
}
xWasPressed = gamepad1.x;

组合按钮​

有时需要同时按下多个按钮来触发特殊功能:

// 只有同时按下A和B才会激活特殊功能
if (gamepad1.a && gamepad1.b) {
    // 执行特殊功能
    activateSpecialFeature();
}

精确控制(缩放输入)​

有时需要降低摇杆的灵敏度,以便进行精确操作:

// 将摇杆输入缩小到50%,提供更精确的控制
double preciseDrivePower = -gamepad1.left_stick_y * 0.5;
motor.setPower(preciseDrivePower);

完整示例:坦克驱动控制​

下面是一个完整的例子,展示如何使用游戏手柄控制机器人的坦克式驱动系统:

@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();
        }
    }
}

游戏手柄高级功能​

震动反馈​

在 FTC SDK 中,您可以让游戏手柄震动,为驾驶员提供触觉反馈:

// 让1号手柄震动1秒
gamepad1.rumble(1.0, 1.0, 1000);

// 参数解释:左马达强度(0-1),右马达强度(0-1),持续时间(毫秒)

检测游戏手柄事件​

有时您可能需要检测按钮的按下和释放事件,而不仅仅是当前状态:

boolean aWasPressed = false;

// 在循环中使用
if (gamepad1.a && !aWasPressed) {
    // 仅在A按钮刚被按下时执行
    toggleSomething();
}
aWasPressed = gamepad1.a;

游戏手柄属性列表​

以下是完整的游戏手柄属性列表,您可以在代码中访问这些属性:

摇杆​

  • 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)​

这个示例展示了最基本的游戏手柄用法,用于控制一个简单的双轮机器人:

// 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);

代码解析:

  • 首先获取游戏手柄的摇杆值:gamepad1.left_stick_y:左摇杆上下移动的值(-1 到 1)gamepad1.right_stick_x:右摇杆左右移动的值(-1 到 1)

  • gamepad1.left_stick_y:左摇杆上下移动的值(-1 到 1)

  • gamepad1.right_stick_x:右摇杆左右移动的值(-1 到 1)

  • 由于摇杆向前是负值(-1),所以需要取反使其变为正值

  • 通过数学计算,将前进动作和转向动作结合起来:左电机 = 前进值 + 转向值右电机 = 前进值 - 转向值

  • 左电机 = 前进值 + 转向值

  • 右电机 = 前进值 - 转向值

  • 使用Range.clip()方法确保功率值不超出-1 到 1 的范围

  • 最后将计算出的功率值设置给电机

示例 2:POV 控制模式(RobotTeleopPOV_Linear)​

这个示例更加复杂,展示了如何用游戏手柄控制机器人的驱动系统、机械臂和机械爪:

// 驱动控制(与示例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 时,按比例缩小左右电机功率,保持转向比例不变

  • 当计算出的功率值超过 1 时,按比例缩小左右电机功率,保持转向比例不变

  • 机械爪控制使用左右肩部按钮(bumpers):右肩按钮增加爪子偏移量,使爪子打开左肩按钮减小爪子偏移量,使爪子关闭每次按下只调整一点点(CLAW_SPEED=0.02),实现缓慢平滑的控制

  • 右肩按钮增加爪子偏移量,使爪子打开

  • 左肩按钮减小爪子偏移量,使爪子关闭

  • 每次按下只调整一点点(CLAW_SPEED=0.02),实现缓慢平滑的控制

  • 机械臂控制使用 Y 和 A 按钮:Y 按钮使机械臂向上移动A 按钮使机械臂向下移动不按任何按钮时停止机械臂

  • Y 按钮使机械臂向上移动

  • A 按钮使机械臂向下移动

  • 不按任何按钮时停止机械臂

示例 3:检测按钮状态变化(防止重复触发)​

在很多情况下,我们需要检测按钮的按下事件,而不是持续检测按钮的状态。下面的代码展示了如何做到这一点:

// 在类中定义变量来记住上一次的按钮状态
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;

代码解析:

  • 定义变量记录上一次循环中按钮的状态

  • 在每次循环中,先获取当前按钮状态

  • 通过比较当前状态和上一次状态,检测状态变化:当前为true且上一次为false:表示按钮刚刚被按下当前为false且上一次为true:表示按钮刚刚被释放

  • 当前为true且上一次为false:表示按钮刚刚被按下

  • 当前为false且上一次为true:表示按钮刚刚被释放

  • 在循环结束时,更新"上一次"状态变量

这种方法可以防止按钮被按住时重复触发动作,确保每次按下只执行一次操作。

示例 4:使用手柄震动提供反馈​

某些游戏手柄(如 PS4 手柄)支持震动功能,可以提供触觉反馈:

// 让手柄震动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);

代码解析:

  • 基本震动使用rumble()方法,指定左右马达强度和持续时间

  • 自定义震动序列允许创建复杂的震动模式,如摩尔斯电码或特定信号

  • rumbleBlips()提供简单的多次震动功能

注意:并非所有手柄都支持震动功能。罗技 F310 不支持震动,而 PS4 和 Xbox 手柄支持。

实用技巧与进阶方法​

实现精确控制的几种方法​

在实际竞赛中,有时需要更精确的控制。以下是几种常用方法:

1. 非线性控制曲线​

// 应用平方函数,保留原始符号
// 这使得小输入有更精细的控制,大输入仍能达到最大速度
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);

2. 可调节的敏感度控制​

// 使用扳机调节敏感度
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;

多驾驶员协作控制​

在 FTC 比赛中,可以有两个驾驶员,通常使用两个手柄协作控制机器人:

// 驾驶员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. 调试游戏手柄输入​

在开发过程中,通常需要查看游戏手柄的输入值以便调试:

// 在遥测中显示所有重要的游戏手柄输入
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();

2. 避免常见陷阱​

  • 忘记取反 Y 轴:摇杆向前推是负值(-1),如果忘记取反,机器人会向后移动

  • 死区处理不当:小输入可能导致电机随机抖动,应该添加死区逻辑

  • 按钮触发重复:如果在每次循环中检测按钮状态,会导致持续按住按钮时重复触发

  • 驾驶站不显示手柄:确保正确指定手柄编号(按 Start+A 或 Start+B)

3. 高级控制系统设计模式​

随着机器人复杂性增加,可以考虑更先进的控制系统设计:

  • 状态机:将机器人操作分解为不同状态,使用游戏手柄在状态之间切换

  • 命令队列:允许驾驶员通过游戏手柄输入预设一系列动作

  • 辅助功能:开发自动辅助功能,如自动对准目标或自动平衡

总结​

游戏手柄是 FTC 机器人比赛中的核心输入设备,掌握其用法对于开发高效的遥控程序至关重要。从基本的移动控制到复杂的机械操作,游戏手柄提供了丰富的输入方式。通过学习本指南介绍的各种技术和模式,您可以开发出更加精确、可靠和用户友好的机器人控制系统。

记住:

  • 理解摇杆和按钮的数值范围和行为

  • 适当处理输入值,如取反、缩放和应用死区

  • 检测按钮状态变化而不是简单检测当前状态

  • 根据任务的复杂性设计合适的控制模式和驾驶员分工

参考资源​

  • FTC 官方游戏手柄文档

  • FTC SDK 官方游戏手柄 API 文档

  • 游戏手柄使用 - Game Manual 0

颜色主题调整