兼容性:ControllerSDK 仅支持山猫 M20 标配的云卓 G20 手柄,不兼容其他型号。
ControllerManager 是 ControllerSDK 核心入口类,采用单例模式管理手柄生命周期。
fun init(application: Application)必须在
Application.onCreate()中调用,且只需一次。init()必须先于connect()执行,否则无法正确识别设备型号。
示例:
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
ControllerManager.init(this)
}
}// 连接手柄(在 Activity.onCreate 中调用)
fun connect(
context: Context,
scope: LifecycleCoroutineScope,
onException: ((Exception) -> Unit)? = null
)
// 断开手柄连接
fun disconnect()
// 恢复摇杆指令发送(Activity.onResume 时调用)
fun onResume()
// 暂停摇杆指令发送(Activity.onPause 时调用)
fun onPause()示例:
// 在 Activity.onCreate 中连接
ControllerManager.connect(this, lifecycleScope) { e ->
Log.e("M20", "手柄连接异常: ${e.message}")
}
// 生命周期管理
override fun onResume() {
super.onResume()
ControllerManager.onResume()
}
override fun onPause() {
super.onPause()
ControllerManager.onPause()
}
override fun onDestroy() {
super.onDestroy()
ControllerManager.disconnect()
}// 当前是否有匹配的手柄实现(G20)
fun hasController(): Boolean
// 软急停按钮是否被触发
fun needSendSoftEmergencyStopMessage(): Boolean
// 设置摇杆死区归一化值(0~0.5),默认 0 无死区
fun setDeadzone(value: Float)| 属性 | 类型 | 说明 |
|---|---|---|
joystickDataLive |
LiveData<JoystickData>? |
摇杆实时数据(归一化值 -1.0~1.0) |
physicalKeyStatesLive |
LiveData<List<PhysicalKeyBase>?>? |
当前按下的物理按键列表 |
d2dConnectStateLive |
LiveData<Boolean>? |
手柄连接状态(true=已连接) |
d2dSignalLive |
LiveData<Int>? |
信号强度(百分比或 RSRP,-1 表示未知) |
softEmergencyStopLive |
LiveData<Boolean>? |
软急停状态(true=软急停中) |
注意:以上 LiveData 在
connect()之前为 null,需确保 connect 后再 observe。
手柄摇杆数据模型,包含原始值和归一化值。
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 轴归一化值
)摇杆数据映射到 RobotSDK AxisData:
| JoystickData 字段 | 映射到 AxisData | 说明 |
|---|---|---|
leftYNorm |
x |
前后移动(向前为正) |
-leftXNorm |
y |
左右移动(向左为正) |
-rightYNorm |
z |
升降(暂不支持) |
-rightXNorm |
yaw |
旋转(逆时针为正) |
注意:当前基础步态(0x1001)和楼梯步态(0x1003)下,仅 x、y、yaw 三个轴生效,z、roll、pitch 暂不支持。
与 RobotSDK 配合使用示例:
ControllerManager.joystickDataLive?.observe(this) { joy ->
RobotSdkManager.axisDataProvider = {
AxisData(
x = joy.leftYNorm,
y = -joy.leftXNorm,
z = -joy.rightYNorm,
yaw = -joy.rightXNorm,
roll = 0f,
pitch = 0f
)
}
}通过 physicalKeyStatesLive 观察当前按下的物理按键列表。
ControllerManager.physicalKeyStatesLive?.observe(this) { keys ->
keys?.forEach { key ->
when (key.name) {
"G20_KEY_STOP" -> RobotSdkManager.sendEnterDampingMode()
"G20_KEY_HOME" -> { /* 自定义 HOME 键逻辑 */ }
else -> { /* 其他按键 */ }
}
}
}云卓 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 |
ControllerManager.connect(this, lifecycleScope) { e ->
Log.e("M20", "手柄连接异常", e)
}if (!ControllerManager.hasController()) {
Toast.makeText(this, "当前设备不支持云卓 G20 手柄", Toast.LENGTH_SHORT).show()
}class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 连接手柄(init 已在 Application 中完成)
ControllerManager.connect(this, lifecycleScope) { e ->
Log.e("M20", "手柄连接异常: ${e.message}")
}
// 观察摇杆数据并映射为 AxisData 提供给 RobotSDK
ControllerManager.joystickDataLive?.observe(this) { joy ->
RobotSdkManager.axisDataProvider = {
AxisData(
x = joy.leftYNorm,
y = -joy.leftXNorm,
z = -joy.rightYNorm,
yaw = -joy.rightXNorm,
roll = 0f,
pitch = 0f
)
}
}
// 观察物理按键
ControllerManager.physicalKeyStatesLive?.observe(this) { keys ->
keys?.forEach { key ->
if (key.name == "G20_KEY_STOP") {
RobotSdkManager.sendEnterDampingMode()
}
}
}
}
override fun onResume() {
super.onResume()
ControllerManager.onResume()
}
override fun onPause() {
super.onPause()
ControllerManager.onPause()
}
override fun onDestroy() {
super.onDestroy()
ControllerManager.disconnect()
}
}