硬件的读取与写入#
使用 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
是一种控制单电机机构的便捷方法,因为它卸载了所有控制工作;但是,由于每个电机都是独立处理的,因此不宜用于多电机机构,尤其是底盘驱动系统。
编码器#
术语
虽然所有 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 或更高版本。更多信息请参见 批量读取 。