本文档面向需要理解 ControllerSDK 内部架构或进行二次开发的开发者。
兼容性:ControllerSDK 仅支持山猫 M20 标配的云卓 G20 手柄,不兼容其他型号。
ControllerSDK 是独立于 RobotSDK 及其底层协议栈的 Android Library:
Your App
↓
ControllerSDK → 通过 rcsdk 直连手柄
↓
rcsdk-v1.9.1.aar (云卓手柄 SDK,需自行获取)
| 模块 | 职责 |
|---|---|
ControllerManager |
单例入口,自动识别设备型号,管理手柄生命周期 |
RemoteControlBase |
手柄抽象基类,定义通用 LiveData 和生命周期方法 |
YzG20ConnectUtils |
云卓 G20 具体实现,摇杆/按键数据解析与归一化 |
JoystickData |
摇杆数据模型,包含原始值与归一化值 |
PhysicalKeyBase |
物理按键基类接口 |
SimpleDemo
└── ControllerSDK (api)
└── rcsdk-v1.9.1.aar (external)
- 设备自动识别:
ControllerManager在connect()时自动判断设备型号,无需手动指定 - 抽象基类:
RemoteControlBase定义通用接口,不同手柄型号继承实现 - 响应式编程:状态通过
LiveData实时推送,UI 被动更新 - 生命周期绑定:
onResume()/onPause()/disconnect()与 Activity 生命周期对齐
统一入口单例,负责根据设备类型自动选择对应的手柄实现。
object ControllerManager {
fun init(application: Application)
fun connect(context: Context, scope: LifecycleCoroutineScope, onException: ((Exception) -> Unit)? = null)
fun disconnect()
fun onResume()
fun onPause()
fun hasController(): Boolean
fun needSendSoftEmergencyStopMessage(): Boolean
fun setDeadzone(value: Float)
val joystickDataLive: LiveData<JoystickData>?
val physicalKeyStatesLive: LiveData<List<PhysicalKeyBase>?>?
val d2dConnectStateLive: LiveData<Boolean>?
val d2dSignalLive: LiveData<Int>?
val softEmergencyStopLive: LiveData<Boolean>?
}手柄遥控抽象基类,定义手柄通用的 LiveData 输出和生命周期方法。
abstract class RemoteControlBase {
var deadzone: Float = 0f // 摇杆死区归一化值(0~0.5)
val physicalKeyStatesLive: FilterMutableLiveData<List<PhysicalKeyBase>?>
val d2dConnectStateLive: FilterMutableLiveData<Boolean>
val d2dSignalLive: FilterMutableLiveData<Int>
val softEmergencyStopLive: FilterMutableLiveData<Boolean>
val joystickDataLive: FilterMutableLiveData<JoystickData>
abstract fun needSendSoftEmergencyStopMessage(): Boolean
abstract fun connect(context: Context, scope: LifecycleCoroutineScope, onException: ((Exception) -> Unit)? = null)
abstract fun disconnect()
fun onResume()
fun onPause()
}摇杆数据模型,包含原始值与归一化值。
data class JoystickData(
val leftX: Int = 0, // 左摇杆 X 轴原始值(范围 1050~1950,中心 1500)
val leftY: Int = 0, // 左摇杆 Y 轴原始值
val rightX: Int = 0, // 右摇杆 X 轴原始值
val rightY: Int = 0, // 右摇杆 Y 轴原始值
val leftXNorm: Float = 0f, // 左摇杆 X 轴归一化值(-1.0 ~ 1.0,应用死区后)
val leftYNorm: Float = 0f, // 左摇杆 Y 轴归一化值
val rightXNorm: Float = 0f,// 右摇杆 X 轴归一化值
val rightYNorm: Float = 0f // 右摇杆 Y 轴归一化值
)物理按键基类接口,各手柄实现类定义具体的物理按键枚举。
interface PhysicalKeyBase {
val name: String
}云卓 G20 按键枚举(YzG20ConnectUtils.PhysicalKey):
| 按键 | name |
|---|---|
| L1 | G20_KEY_L1 |
| L2 | G20_KEY_L2 |
| R1 | G20_KEY_R1 |
| R2 | G20_KEY_R2 |
| HOME | G20_KEY_HOME |
| STOP(急停) | G20_KEY_STOP |
| CAMERA | G20_KEY_CAMERA |
| PUMP | G20_KEY_PUMP |
摇杆原始值范围 [1050, 1950],中心 1500,半量程 450。
normalizeWithDeadzone() 方法将原始值映射为归一化值:
- 死区内(|raw - center| ≤ deadRadius):归一化值 = 0
- 死区外负侧:线性映射到
[-1, 0) - 死区外正侧:线性映射到
(0, 1]
死区半径 = deadzone × 450。例如 deadzone = 0.1 时,原始值在 [1455, 1545] 范围内归一化值均为 0。
ControllerSDK 依赖外部库 rcsdk-v1.9.1.aar(云卓手柄 SDK),该文件不随本项目发布,需从云卓官方获取。