You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

FTC开发中Java ArrayList与接口问题及替代方案咨询

FTC通用TeleOp开发问题解答

先解释DcMotor.class的用法

DcMotor.class是Java的类字面量,用来获取DcMotor这个接口对应的Class对象。FTC的hardwareMap.get()方法需要这个参数定位硬件映射中的对应设备——因为实际硬件是实现了DcMotor接口的具体类实例,通过Class对象可以匹配到对应类型的硬件。

为什么ArrayList<HardwareDevice>无法添加DcMotor对象?

理论上DcMotor继承自HardwareDevice接口,实现DcMotor的实例必然是HardwareDevice的子类,应该能正常添加。大概率是以下问题导致:

  • 硬件初始化失败:hardwareMap.get()返回了null(比如硬件名称写错、Driver Station中未配置对应硬件),添加null虽允许,但后续逻辑会报错。
  • 泛型类型不匹配:比如声明的是ArrayList<某个具体类>而非ArrayList<HardwareDevice>,强制转换时出错。
  • 代码逻辑疏漏:比如未初始化ArrayList就调用add(),导致空指针异常。

替代ArrayList的方案(解决索引同步隐患+更健壮)

原方案用两个ArrayList分别存设备和灵敏度,最大问题是索引同步风险(删改设备时两个列表容易错位),推荐以下几种替代方案:

方案1:自定义封装类(最推荐)

创建一个类把设备和灵敏度绑定,彻底避免索引不一致问题:

// 新建HardwareWithSensitivity类
public class HardwareWithSensitivity {
    private final HardwareDevice device;
    private final double sensitivity;

    public HardwareWithSensitivity(HardwareDevice device, double sensitivity) {
        this.device = device;
        this.sensitivity = sensitivity;
    }

    // Getter方法
    public HardwareDevice getDevice() { return device; }
    public double getSensitivity() { return sensitivity; }
}

修改Commands类的实现:

public class Commands {
    private final List<HardwareWithSensitivity> deviceList;

    public Commands(List<HardwareWithSensitivity> deviceList) {
        this.deviceList = deviceList;
    }

    // 遍历所有设备处理(也可以保留单设备run方法)
    public void runAll(Gamepad gamepad, Telemetry telemetry) {
        for (HardwareWithSensitivity hw : deviceList) {
            HardwareDevice device = hw.getDevice();
            double sensitivity = hw.getSensitivity();
            // 这里根据设备类型获取对应游戏手柄输入,示例:
            double power = 0;
            if (device instanceof DcMotor) {
                power = gamepad.left_stick_y;
            } else if (device instanceof Servo) {
                power = gamepad.right_trigger;
            }

            if (isInRange(power, 1 - sensitivity)) {
                executeDeviceAction(device, power, sensitivity, telemetry, gamepad);
            }
        }
    }

    private void executeDeviceAction(HardwareDevice device, double power, double sensitivity, Telemetry telemetry, Gamepad gamepad) {
        if (device instanceof DcMotor) {
            ((DcMotor) device).setPower(power);
            telemetry.addData("Motor Power", ((DcMotor) device).getPower());
        } else if (device instanceof Servo) {
            double newPos = ((Servo) device).getPosition() + power * sensitivity;
            // 限制位置在0-1之间
            newPos = Math.max(0, Math.min(1, newPos));
            ((Servo) device).setPosition(newPos);
            telemetry.addData("Servo Position", newPos);
        } else if (device instanceof CRServo) {
            ((CRServo) device).setPower(power);
            telemetry.addData("CRServo Power", ((CRServo) device).getPower());
        } else if (device instanceof Intake) {
            ((Intake) device).runIntake(gamepad);
        }
    }

    // 保留原isInRange方法
    public static boolean isInRange(double val, double sensitivity){
        return val < -sensitivity || val > sensitivity;
    }
    public static boolean isInRange(double val){
        return isInRange(val, 0.1);
    }
}

使用时直接封装设备和灵敏度:

// 初始化硬件后
List<HardwareWithSensitivity> deviceList = new ArrayList<>();
deviceList.add(new HardwareWithSensitivity(fl, 0.5));
deviceList.add(new HardwareWithSensitivity(servoClaw, 0.3));
// ...添加其他设备
Commands commands = new Commands(deviceList);

方案2:使用Map<HardwareDevice, Double>

用Map直接存储设备与灵敏度的键值对,无需维护索引:

public class Commands {
    private final Map<HardwareDevice, Double> deviceSensitivityMap;

    public Commands(Map<HardwareDevice, Double> deviceMap) {
        this.deviceSensitivityMap = deviceMap;
    }

    public void run(HardwareDevice device, Gamepad gamepad, double power, Telemetry telemetry) {
        Double sensitivity = deviceSensitivityMap.get(device);
        if (sensitivity == null) {
            telemetry.addData("Error", "未配置该设备的灵敏度");
            return;
        }

        if (isInRange(power, 1 - sensitivity)) {
            // 原设备操作逻辑...
        }
    }
}

使用时:

Map<HardwareDevice, Double> deviceMap = new HashMap<>();
deviceMap.put(fl, 0.5);
deviceMap.put(servoClaw, 0.3);
Commands commands = new Commands(deviceMap);

注意:FTC硬件设备类一般已正确重写equals()和hashCode(),可以正常作为Map的键。

方案3:按设备类型分类存储

如果不同类型设备的操作逻辑差异大,可分开存储不同类型的列表,避免频繁instanceof判断:

public class Commands {
    private final List<DcMotor> motors;
    private final List<Double> motorSensitivities;
    private final List<Servo> servos;
    private final List<Double> servoSensitivities;
    // ...其他设备类型

    public Commands(List<DcMotor> motors, List<Double> motorSens, List<Servo> servos, List<Double> servoSens) {
        this.motors = motors;
        this.motorSensitivities = motorSens;
        this.servos = servos;
        this.servoSensitivities = servoSens;
    }

    public void runMotors(Gamepad gamepad, Telemetry telemetry) {
        for (int i = 0; i < motors.size(); i++) {
            DcMotor motor = motors.get(i);
            double sensitivity = motorSensitivities.get(i);
            double power = gamepad.left_stick_y;
            if (isInRange(power, 1 - sensitivity)) {
                motor.setPower(power);
                telemetry.addData("Motor Power", motor.getPower());
            }
        }
    }

    // 同理实现runServos等方法
}

这种方式代码更清晰,适合设备类型区分明确的场景。


内容的提问来源于stack exchange,提问作者getrithekd

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 15:40:02