超视距遥操(Teleop)指南 · Android
超视距遥操(Teleop)指南 · Android
⚠️ 尚未上线:本能力为 v1.5.0-SNAPSHOT 开发中特性,接口与协议可能调整,正式发布前请勿用于生产环境。
v1.5.0 新增。Teleop = 超视距遥操:远端操作员通过 SDK 在线接管机器人的语音交互。Android 侧的常见形态是「操作员 App」——通过 AgentSDK 把操作员语音上行给机器人,通常还会另行集成第三方 WebRTC 拉流方案拿到机器人现场画面。本页只讲 AgentSDK 的公开用法;视频拉流仅在文末给出与灵心开放平台相关的拉流授权接口。接口签名请参阅 Teleop API。
Android SDK 使用与 Java 完全一致的 com.agibot.aiem.sdk.teleop.* 类。API 语义参考 Java v1.5.0 · 超视距遥操指南,本页只补充 Android 独有差异:
- Android SDK
TeleopRequest.appendAudioDelta(...)会在发送前把 base64 解码一次得到audioLen字段一并下发(服务端便于校验,业务侧无感); TeleopRequest.enter/exit内部会调用AgentSdkTeleopMgr.trackEvent(eventId, this),即使 App 侧不显式跟踪也能被 SDK 的 dispatcher 找回对应回调;- Android 端的麦克风采集、PCM/Opus 编码、base64 分片均由业务侧实现,SDK 只接收 base64 字符串。
调用流程
严格顺序与 Java 一致:enter → 任意次 startAudio / appendAudioDelta / doneAudio → exit。
遥操存续期间可任意频次调用 keepalive 探活(推荐每 10 秒一次,见下文 机器人在线检测(keepalive))。
enter exit
┌──────────────────────┐ ┌──────────────────────┐
│ TeleopRequest.enter │ │ TeleopRequest.exit │
│ (onEnterAck) │ │ (onExitAck) │
└──────────┬───────────┘ └──────────┬───────────┘
│ 任意次 (fire-and-forget) │
▼ │
┌──────────────────────┐ │
│ startAudio(param) │ │
│ appendAudioDelta(*) │───────────────────┘
│ doneAudio │
└──────────────────────┘
┌──────────────────────────┐
│ TeleopRequest.keepalive │ ← 建议每 10 秒一次;code != 0 视为机器人离线
│ (onKeepaliveAck) │
└──────────────────────────┘
enter/keepalive/exit都有 ACK 回调;on*Ack收到code == 0视为成功;startAudio/appendAudioDelta/doneAudio是 fire-and-forget,本地不排队等 ACK;- 若发送瞬间 SDK 连接已关闭,
enter/keepalive/exit回调会在原线程同步以code == 1000+msg == "Agentsdk connection is closed"触发一次失败回执。
回调码定义同 Java:0 成功、-1 服务端拒绝、1000 SDK 侧连接已 closed。
最小 Android 集成
1. AndroidManifest.xml 权限
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
RECORD_AUDIO 是危险权限,Activity 首次调用 startAudio 前必须 ActivityCompat.requestPermissions。
2. 创建 SDK 实例 & 注册遥操会话
AgentSdk agentSdk = AgentSdk.create(
"wss://open.agibot.com/api/V1/open-portal/app/wss/agent-sdk",
appId, appKey, appSecret);
agentSdk.registerAuth(new AgentAuthCallback() {
@Override
public void onAuthState(String appId, int code, String msg) {
// code == 0 后才可以调用 enter
}
});
agentSdk.initialize();
String teleopId = IdGenerator.generateTeleopId();
TeleopRequest teleopRequest = new TeleopRequest(agentSdk, agentId, teleopId);
agentSdk.registerTeleop(teleopRequest);
3. 进入遥操
AgentParam enterParam = AgentParam.create()
.setString("role", "male")
.setInteger("threshold", 0);
teleopRequest.enter(enterParam, new TeleopEnterCallback() {
@Override
public void onEnterAck(String teleopId, String eventId, int code, String msg) {
// 主线程 UI 操作请切回 Handler,避免在 SDK 线程直接操作 View
}
}, 2000);
4. 推麦克风音频
Android 侧建议用 AudioRecord(16kHz 单声道 s16le,20ms 一片)采集,base64 后 appendAudioDelta:
String eventId = teleopRequest.startAudio(startParam);
// 麦克风回调里:
byte[] pcm20ms = ... // 640 bytes @16kHz mono s16le
String base64 = Base64.encodeToString(pcm20ms, Base64.NO_WRAP);
teleopRequest.appendAudioDelta(eventId, base64);
// 停止说话时:
teleopRequest.doneAudio(eventId);
5. 退出
teleopRequest.exit(new TeleopExitCallback() {
@Override
public void onExitAck(String teleopId, String eventId, int code, String msg) {
// 主线程 UI 提示 / 关闭 Activity
}
}, 2000);
agentSdk.unregisterTeleop(teleopId);
机器人在线检测(keepalive)
遥操会话通常持续较长,期间机器人可能因网络中断 / 电量耗尽 / 现场断电等原因掉线。SDK 提供 TeleopRequest.keepalive(...) 一次性探活方法,业务侧应定时调用(建议每 10 秒一次)判断机器人是否仍在线。
接口与回调
public String keepalive(TeleopKeepaliveCallback keepaliveCallback, long timeout);
public abstract class TeleopKeepaliveCallback {
public abstract void onKeepaliveAck(String teleopId, String eventId, int code, String msg);
}
timeout:本地占位超时(< 50clamp 到 50,语义同enter/exit);- 返回本次消息的
eventId; - 每次调用发送一条
agentsdk.teleop.keepalive,网关按teleopId转发到目标机器人;机器人在线则回success == true。
code 含义(与 enter / exit 一致)
code | 含义 |
|---|---|
0 | 机器人在线且响应正常 |
-1 | 服务端返回失败,msg 携带 errorMsg——表示机器人已离线,业务侧必须处理该异常 |
1000 | SDK 端 WebSocket 已关闭,本次未真正下发 |
Android 定时探活(Handler + postDelayed)
Android 侧推荐用 Handler(Looper.getMainLooper()) + postDelayed 方式实现定时探活——对 Android 开发者更自然,且在 Activity onDestroy 中一行 removeCallbacks 即可停止。
注意:
onKeepaliveAck不在 UI 线程回调,更新界面需要切回主线程。
任何一次 code != 0,都应视为机器人已离线——立即停止定时任务、exit 退出遥操、unregisterTeleop 释放资源,避免后续 appendAudioDelta 继续下发无效音频。
private final Handler mainHandler = new Handler(Looper.getMainLooper());
private final AtomicBoolean robotOnline = new AtomicBoolean(true);
private Runnable keepaliveTick;
@Override
protected void onCreate(Bundle savedInstanceState) {
// ... enter 成功后启动定时
keepaliveTick = new Runnable() {
@Override
public void run() {
if (!robotOnline.get()) return;
teleopRequest.keepalive(new TeleopKeepaliveCallback() {
@Override
public void onKeepaliveAck(String teleopId, String eventId, int code, String msg) {
if (code == 0) {
// 继续下一轮
mainHandler.postDelayed(keepaliveTick, 10_000);
return;
}
if (!robotOnline.compareAndSet(true, false)) return;
Log.w(TAG, "robot offline => code=" + code + ", msg=" + msg);
mainHandler.removeCallbacks(keepaliveTick);
// 掉线自动退出
teleopRequest.exit(new TeleopExitCallback() {
@Override
public void onExitAck(String teleopId, String eventId, int code, String msg) {
agentSdk.unregisterTeleop(teleopId);
}
}, 2000);
}
}, 2000);
}
};
mainHandler.postDelayed(keepaliveTick, 10_000);
}
@Override
protected void onDestroy() {
super.onDestroy();
mainHandler.removeCallbacks(keepaliveTick);
// 保险起见也 exit 一下
}
调用 exit 后应主动 mainHandler.removeCallbacks(keepaliveTick) 停止发送探活消息。
视频拉流与拉流授权
遥操场景下操作员通常需要同时看到机器人现场画面。视频拉流不属于 AgentSDK 的能力:AgentSDK 只负责把操作员语音上行到机器人;现场视频由 App 自行集成第三方 WebRTC 拉流方案(如腾讯 TRRO 的 Android SDK 或其他标准 WebRTC 客户端)完成。两条链路在业务层通过同一台机器人关联。
本节只描述与灵心开放平台相关的拉流授权接口(下发 WebRTC 拉流所需的
password与authCode)。第三方 WebRTC SDK 自身的初始化、连接、渲染与音频播放请参阅其官方文档,不在本页范围。
拉流授权:authCode / password 获取接口
一次 WebRTC 拉流会话需要两个由灵心开放平台签发的凭证:
password:初始化 WebRTC 拉流 SDK 时使用;authCode:建立拉流会话时作为会话权限令牌使用(如 TRRO 的setSessionPermissionToken)。
接口
POST {authBaseUrl}/api/V1/interaction/app-credential/gw/webrtc/authCode
Header: Authorization: <登录后拿到的 bearer token>
Content-Type: application/json
authBaseUrl 按环境取值:
| 环境 | authBaseUrl |
|---|---|
| 生产(PRD) | https://open.agibot.com |
| 准生产(UAT) | https://open.uat.agibot.com |
入参(请求体)
{
"sn": "<机器人硬件序列号>",
"pullDid": 123456789
}
| 字段 | 类型 | 说明 |
|---|---|---|
sn | string | 目标机器人的硬件序列号。与 agentId 是两个概念:agentId 是灵心平台颁发的智能体标识(遥操寻址用),sn 是硬件序列号(WebRTC 网关寻址用)。 |
pullDid | number(int64) | 拉流方(操作员设备)ID,需在进程内稳定;由 App 自行生成。 |
出参(响应体)
{
"code": 0,
"message": "ok",
"data": {
"password": "<拉流密码>",
"authCode": "<会话授权码>"
}
}
| 字段 | 说明 |
|---|---|
code | 0 为成功,非 0 为失败(原因见 message) |
data.password | 传给 WebRTC 拉流 SDK 初始化 |
data.authCode | 建立拉流会话时作为权限令牌下发 |
拿到 password / authCode 后,按第三方 WebRTC SDK 的文档完成初始化与连接即可。
与遥操会话的协同建议
- 建议在
enter拿到onEnterAck(code == 0)之后再发起拉流授权与拉流连接,避免对着未就绪的画面开麦; - 退出时建议先断开 / 销毁视频拉流,再
exit遥操,避免画面残留; - 回声消除(AEC)由 App 自行处理:当设备同时播放机器人下行音、又采集操作员上行音时会产生回声。AgentSDK 不介入任何音频处理,仅接收 base64 PCM;是否做 AEC / 降噪、如何做,完全由 App 决定。
常见问题
onEnterAck收到code == 1000:底层 WebSocket 未建立或已断开。等AgentAuthCallback.onAuthState(code == 0)之后再enter;断连后 SDK 会自动重连,重连成功再次enter即可。enter之后能否立即startAudio:可以。若同时集成了视频拉流,更稳的做法是等画面出图后再开麦,避免操作员对着黑屏说话。- 每片 audio delta 建议多长:20 ms ~ 40 ms 的 PCM/Opus 编码为 base64 后
appendAudioDelta;太大易堵塞 WS 单帧,太小则消息数过多。 onKeepaliveAck在哪个线程回调:非 UI 线程。更新界面请runOnUiThread或Handler.post。