硬件的读取与写入#

使用 FTC® SDK 时,有多种内置硬件类可用于与机器人上的硬件,如直流电机、 舵机 和传感器,进行通信。

创建和实例化硬件对象#

要正确创建对象,首先需要导入对象的类。在 Android Studio 中,如果引用了类却没有导入,可以按 Alt+Enter 键自动导入。导入后,下一步就是创建对象:

private DcMotor liftMotor;

创建对象后,必须对其进行实例化。OpMode 超类的一部分称为 hardwareMap。FTC SDK 使用 hardwareMap 来实例化对象,而不是调用构造函数。

它包含输入到机器人控制器配置中的所有信息,例如硬件名称和插入的端口。下面是实例化我们上面创建的电机的示例:

liftMotor = hardwareMap.get(DcMotor.class, "Lift Motor");

无论使用什么传感器,都要将该类传入 DcMotor.class 所在的位置。例如,如果 liftMotor 是舵机,则应传递 Servo.class 代替。

第二个参数是机器人控制器配置中设备的名称。然后, hardwareMap 将查找该设备所连接的端口,从而访问硬件。

使用通用硬件组件的示例#

直流电机#

DcMotor leftMotor = hardwareMap.get(DcMotor.class, "Left Motor");
DcMotor rightMotor = hardwareMap.get(DcMotor.class, "Right Motor");

DcMotor elevatorMotor = hardware.get(DcMotor.class, "Elevator Motor");
DcMotor intakeMotor = hardware.get(DcMotor.class, "Intake Motor");

DcMotor 实例化之后,你可以设置一些变量来影响直流电机的运行方式。第一个变量是方向:

leftMotor.setDirection(DcMotor.Direction.REVERSE);
rightMotor.setDirection(DcMotor.Direction.FORWARD);

改变电机方向的作用与预期完全一致,即改变方向。如果在正转模式下对电机施加 1 的功率,它将朝一个方向转动。如果处于反转状态,1 的功率将使其向另一个方向旋转。如果将电机轴朝向自己,则正转是逆时针方向(NeveRest 电机除外)。

接下来,有两种零功率行为可以调整:

leftMotor.setZeroPowerBehavior(DcMotor.ZeroPowerBehavior.BRAKE);
rightMotor.setZeroPowerBehavior(DcMotor.ZeroPowerBehavior.FLOAT);

更改该变量会影响直流电机在功率为 0 时的行为。如果电机正在运动, BRAKE 会使电机减速(如果电机尚未运动,则不会使电机保持位置),而 FLOAT 会使电机滑行到停止位置,让摩擦力完成所有工作。

最后,有四种不同的运行模式可用于直流电机:

leftMotor.setMode(DcMotor.RunMode.RUN_WITHOUT_ENCODER);
rightMotor.setMode(DcMotor.RunMode.RUN_USING_ENCODER);

elevatorMotor.setMode(DcMotor.RunMode.RUN_TO_POSITION);
intakeMotor.setMode(DcMotor.RunMode.STOP_AND_RESET_ENCODER);

需要注意的是,只要正确插入编码器,就可以在上述任何模式下读取编码器值。这些模式只是改变电机对这些编码器值的反应方式。 REV Robotics 文档对所有四种运行模式都有解释

警告

RUN_TO_POSITION 是一种控制单电机机构的便捷方法,因为它卸载了所有控制工作;但是,由于每个电机都是独立处理的,因此不宜用于多电机机构,尤其是底盘驱动系统。

编码器#

术语

编码器#

编码器是指记录(通常)围绕轴的旋转运动的设备。

编码器分为绝对编码器和相对编码器。绝对编码器将准确报告轴与其绝对 “零点” 相比的角度。相对编码器将报告 shaft 自开始跟踪以来(例如,自主启动时)旋转了多远。相对编码器通过正交信号输出,而绝对编码器通常通过模拟信号或 i2c 输出。

编码器用于帮助找到机器人或其部分机械结构的位置。

虽然所有 FTC 合规电机都内置有相对正交编码器,但必须单独接线,且并非必须使用。只要使用正交通信协议,就可以使用外部编码器并将其插入编码器端口。

访问编码器需要调用 DcMotor 对象上的一个方法 getCurrentPosition(),该方法可返回插入端口的编码器的当前位置。该数字在 opmode 开始时可以是任意的,除非使用了 STOP_AND_RESET_ENCODERS 或对Expansion Hub进行了电源循环,否则不会重置为 0。

重要

处理正交编码器时没有真正的标准化术语。SDK 默认使用 “CPR”(每转计数)。你也可能看到一些数据表列出了 “PPR”(每转脉冲数)。一个脉冲相当于 1 到 4 个 SDK “计数”。阅读数据表时要小心!

警告

每转计数较高的编码器(如 REV 通孔编码器)如果插入端口 1 或 2,可能会丢失步进。此外,调用 DcMotorEx 对象上的 getVelocity() 时,由于返回的数字仅为 16 位有符号整数,因此每转计数高的编码器可能会溢出。

舵机#

Servo relicServo = hardwareMap.get(Servo.class, "Release Servo");

在实例化 Servo 后,可以调用两个主要函数:setPosition() 和``getPosition()``

releaseServo.setPosition(0.75);
telemetry.addData("Release Servo Target", releaseServo.getPosition());

setPosition() 设置 舵机 的位置。SDK 将使用 舵机 的电位器的内置控制循环,将 舵机 驱动到该位置并保持该位置。setPosition() 接收介于 0 和 1 之间的 double,其中 0 是 舵机 的旋转下限,1 是 舵机 的旋转上限。介于两者之间的是正比例,所以 0.5 是中间值,0.75 是 3/4 上限值,等等。

getPosition() 返回的不是 servo 的 当前位置,而是当前目标位置。如果 servo 的 当前目标位置的变量存储得当,就不需要使用此函数。

连续旋转舵机#

CRServo intakeServo = hardwareMap.get(CRServo.class, "Intake Servo");

CRServo 有一个主要方法: setPower() 。其工作原理与 DcMotor``setPower()``非常相似,即传入 0 会使其停止,传入 1 会使其全速前进,传入 -1 会使其全速后退,以及介于两者之间的所有情况。:

intakeServo.setPower(0.75);

数字IO#

DigitalChannel digitalDevice = hardwareMap.get(DigitalChannel.class, "digital device");

数字C形梁有几个主要方法。setMode() 用于将端口设置为输出或输入端口,getState() 返回端口的当前状态(仅在输入模式下有效),setState() 设置端口的状态(仅在输出模式下有效)。

小技巧

数字端口默认以 INPUT 模式启动

危险

数字端口上拉以防止信号抖动。这意味着在端口和 3.3V 电压之间有一个电阻,因此端口在未连接任何设备时默认为高电平。因此,数字设备在关闭时必须将数字引脚连接到地,而在打开时则保持未连接状态。对于限位开关而言,这意味着将一条引线连接至地线,另一条引线连接至数字端口。连接错误(将 3.3V 电压连接到数字端口)可能导致不稳定,并可能导致 Expansion Hub 崩溃

模拟输入#

AnalogInput analogInput = hardwareMap.get(AnalogInput.class, "analog input");

AnalogInput 有一个主要方法:getVoltage() ,用于获取端口当前的输入电压。

备注

虽然 getMaxVoltage() 返回 3.3v,但Expansion Hub 和 Control Hub模拟输入端口可以安全地处理高达 5v 的电压。

关于硬件调用速度的注意事项#

每次硬件调用(无论是设置电机功率、设置 舵机 位置、读取编码器值等)的执行时间约为 3 毫秒,但 I2C 调用除外,它可能需要 7 毫秒以上。这是因为在幕后,SDK 可能需要进行多次硬件调用才能执行 I2C 操作。

备注

使用 Control Hub 时,你可能会看到硬件调用时间大大缩短,因为 Control Hub 使用直接 UART 连接到 Lynx 板,而不是像使用手机作为RC时那样通过 USB 和中间的 FTDI。

这些时间看似很快,但累积起来也很快。考虑一个控制回路,在使用 IMU 保持航向的同时,向前驱动 N 个编码器计数。这需要 5 次普通硬件调用(4 次设置功率 + 1 次读取编码器)和 1 次 I2C 调用(IMU),这意味着执行循环周期大约需要 22 毫秒,因此运行频率大约为 45 赫兹。

这意味着必须尽量减少硬件调用次数,以保证控制回路快速运行。例如,不要在每个循环中多次读取传感器。相反,只需读取一次,然后将值存储到变量中,以便在同一循环周期的其他时间点再次使用。

使用批量读取硬件调用可以帮助解决这个问题。批量读取的执行时间与任何其他普通硬件调用一样,都是 3 毫秒,但它返回的数据要多得多。要使用批量读取,必须运行 SDK v5.4 或更高版本。更多信息请参见 批量读取