18 KiB
Temi 终端控制应用交接文档
本文档面向后续接手本项目的技术人员,重点说明项目定位、当前架构、配置约定、关键业务流程、排障思路,以及与旧版实现相比已经发生的变化。建议先通读本文,再结合 technique.md 和代码深入。
1. 项目定位与当前状态
- 项目类型:基于 Temi SDK 的 Android 应用(Kotlin)。
- Android 模块:
app - 包名:
com.example.lzwcai_terminal_temi - 核心能力:
- 通过 MQTT 指令控制 Temi 机器人导航、巡逻、接待、通知播报和基础动作。
- 通过 LiveKit 进行音视频接入,并把房间内收到的 ASR/文本数据转发到 MQTT。
- 通过 HTTP 接口做设备激活、运行时配置拉取、门禁工作流调用。
- 通过 Telemetry 上报机器人状态心跳、事件、电量和位姿。
- 当前版本和早期代码相比,最大的变化是:
- MQTT 用户名/密码不再写死在代码中,而是在激活后从服务端拉取并存入本地。
- 设置页主配置已经从单纯的
network_ip扩展成base_url + 登录账号 + 激活码 + 设备名。 - 主流程增加了激活态控制、自动回充、门禁工作流、接待返航工作流、ASR 转发等能力。
推荐阅读顺序:
- 本文档:快速建立维护视角。
technique.md:查看协议和流程图。MainActivity.kt、SettingsActivity.kt、MqttManager.kt、TaskController.kt:理解当前真实行为。
2. 核心模块一览
代码主要集中在 app/src/main/java/com/example/lzwcai_terminal_temi:
MainActivity- 主界面与机器人事件中心。
- 管理 MQTT / LiveKit 生命周期、TTS 状态、人体检测、到站处理、UI 状态、激活提示。
SettingsActivity- 配置页。
- 负责
base_url、登录账号密码、激活码、设备名、LiveKit 参数、当前位置、特殊状态、agent_demp_id等配置。
ConnectionService- 连接层编排。
- 根据当前配置和激活状态创建/更新
MqttManager与LiveKitManager。
MqttManager- MQTT 连接、重连、订阅、发布。
- 解析
robot/cmd与soul2user,管理流式播报、TTS 队列和任务启动。
TaskController- 任务状态机。
- 管理
patrol、reception、notification、speech等任务,以及任务超时、巡逻推进、接待确认按钮。
MainTaskPolicy- 统一定义空闲态 / 特殊状态下的行为决策。
- 控制是否允许自动回充、门禁工作流、到站播报、闲时问候。
NavController- Temi 导航与动作封装。
- 包含
goTo、recharge、repose、turnBy、tiltAngle、randomPatrol等。
AutoRechargeScheduler- 机器人空闲到站后的延时自动回充。
- 当前策略是空闲到站后 10 秒且不在
home base时触发recharge()。
WorkflowService- 工作流调用编排。
- 负责开门、关门、接待返航工作流,并在缺配置时尝试刷新服务端配置。
HttpManager- HTTP 接口封装。
- 负责登录、激活、拉取运行时配置、执行工作流。
LiveKitManager- LiveKit 房间连接、事件监听、自动重连、麦克风开关控制。
- 麦克风开启条件依赖人体检测稳定状态和 TTS 静音状态。
TelemetryManager- 周期性心跳与事件上报。
- 包含电量、位置、运动状态、任务状态、MQTT/LiveKit 连接状态。
RobotEventHandler- 机器人事件辅助逻辑。
- 用于状态归类、地点归一化、角度归一化。
UiState- 主界面上网络异常横幅、激活提示横幅、连接指示灯颜色更新。
PermissionManager- Temi 权限检查与申请。
AnimatedEmojiView- 自绘表情组件,受任务状态和 TTS 状态驱动。
LogManager- Logcat 监听与订阅。
3. 运行环境与依赖
3.1 开发环境
- Android Studio:近期版本即可,需支持:
- AGP
8.10.1 - Kotlin
2.0.21
- AGP
- JDK:17
- Android SDK:
compileSdk = 36minSdk = 23targetSdk = 36
- 运行设备:Temi 真机优先;模拟器仅适合 UI 预览。
3.2 主要依赖
见 app/build.gradle.kts 与 libs.versions.toml:
- Temi SDK:
com.robotemi:sdk:1.137.1 - MQTT:
org.eclipse.paho.android.service:1.1.1org.eclipse.paho.client.mqttv3:1.2.5
- LiveKit Android:
2.23.5 - AndroidX / Material / Emoji2 等基础 UI 依赖。
3.3 权限
- Temi 权限:
MAPSEQUENCEFACE_RECOGNITIONSETTINGS
- Android 权限:
RECORD_AUDIOCAMERA
说明:
MainActivity.onStart()会注册机器人监听器并按激活状态决定是否启动 MQTT / LiveKit / Telemetry。onRobotReady()中会触发PermissionManager.checkAndRequestPermissions()。- LiveKit 权限未授予时不会建立连接。
4. 配置与持久化约定
应用配置统一存放在 SharedPreferences("app_prefs")。
4.1 关键配置项
- 服务与激活相关:
base_url:后端服务基础地址,例如http://192.168.11.24:8088login_username/login_password:调用登录接口用的账号密码activation_code:激活码device_name:设备名称device_id:本机生成并持久化的设备标识is_activated:是否已激活
- 运行时下发配置:
mqtt_username/mqtt_passwordod_wfid/od_wf_key:开门工作流配置cd_wfid/cd_wf_key:关门工作流配置vr_wfid/vr_wf_key:接待返航工作流配置
- 任务与状态:
current_location:当前位置special_state:特殊状态开关agent_demp_id:soul2user过滤字段
- LiveKit:
livekit_urllivekit_roomlivekit_tokenlivekit_enabled
- 兼容字段:
network_ip:仍会被保存,但现在是由base_url自动解析 host 后回填,主要给旧逻辑或排查时参考。
4.2 设置页实际能力
当前 SettingsActivity 不只是“填 IP”,而是包含以下能力:
- 保存
base_url、agent_demp_id、登录用户名、登录密码。 - 触发设备激活:
- 调
HttpManager.activateDevice() - 成功后拉取 MQTT 与工作流配置
- 将
is_activated设为true
- 调
- 保存 LiveKit URL / 房间 / Token / 自动连接开关。
- 选择当前位置。
- 开关特殊状态。
- 清除当前任务。
- 查看 About 信息。
- 长按 3 秒重启应用。
4.3 一个重要行为
当 base_url 发生变化时,设置页会主动清空以下信息并重置激活状态:
activation_codedevice_nameis_activated- MQTT 用户名/密码
- 所有工作流 ID / API Key
这意味着切环境后必须重新激活,文档和运维流程都要按这个行为来理解。
5. 通信协议与主题约定
5.1 MQTT Broker 与主题
- Broker 地址:
tcp://<base_url_host>:1883- 实际 host 由
ConnectionService.resolveBrokerHostFromBaseUrl()从base_url提取。
- 实际 host 由
- MQTT 账号:
- 从本地
mqtt_username/mqtt_password读取。 - 这些值通常由激活成功后从服务端拉取。
- 从本地
- 订阅主题:
robot/cmd:控制指令soul2user:流式播报文本
- 发布主题:
robot/status:状态心跳robot/event:事件上报,例如battery_lowrobot/asr:LiveKit 收到的文本/ASR 转发结果
5.2 指令映射
指令解析统一在 MqttManager.handleJsonCommand():
- 基础控制:
rechargegotoreposeturntiltstopterminatecontinuestatus
- 语音:
speakstream
- 任务:
patrolnotificationreception
5.3 当前实现里的几个“非直觉行为”
这些点很容易和旧文档或接口约定不一致,接手时必须知道:
reception指令里的location目前没有真正使用。MqttManager里接待地点被硬编码为前台。destination默认值是会议室。text默认值是“你是我要接待的贵宾吗?”
goto若收到未知地点,不会报错退出,而是会回退到会议室。- 这是
NavController.goTo()的当前保护逻辑。
- 这是
stream不只是简单播报全文。- 文本会进入
speechBuffer - 根据中英文标点和换行拆句
is_final=true时会刷新剩余缓存
- 文本会进入
soul2user消息支持通过agent_demp_id过滤demp_id。- 当
session_id或message_id变化时,当前流式 TTS 会被打断并切换到新的会话上下文。
5.4 常见指令示例
{"action":"goto","location":"前台"}
{"action":"speak","text":"欢迎光临","lang":"zh"}
{"action":"patrol","flag":false,"locations":["前台","会议室"],"times":2,"waiting":5}
{"action":"notification","location":"大厅","text":"会议即将开始,请尽快入场"}
6. 关键业务流程说明
6.1 激活态控制
MainActivity 已经把“是否激活”纳入主流程:
- 未激活时:
- 主界面显示“请先激活应用”横幅
- MQTT 断开
- LiveKit 断开
- Telemetry 停止
- 不再进入正常联网工作态
- 已激活时:
- 恢复 MQTT / LiveKit 连接流程
- 开始心跳上报
这部分逻辑是当前版本的重要门槛,所有“为什么没连上 MQTT / LiveKit”的排查都先确认 is_activated。
6.2 任务状态机
任务逻辑集中在 TaskController.kt:
currentTask主要值:- 空字符串:空闲
speechpatrolreceptionnotification
- 巡逻:
- 支持指定路线和随机路线
- 支持
times、waiting、nonStop - 通过
handlePatrolArrival()推进路线索引和剩余圈数
- 接待:
- 到达接待点后开启等待
- 稳定检测到人时显示确认按钮并播报接待文案
- 点击按钮后前往目的地
- 通知:
- 到达目标地点即播报一次并结束任务
- 纯播报:
- 由 MQTT 流式/非流式语音触发
- TTS 队列清空后自动清回空任务
- 任务超时:
- 接待任务在等待阶段会启动 15 分钟超时
- 超时后播报“任务超时了,任务被取消,我先回充电桩了”并执行回充
6.3 到站、播报与自动回充
onGoToLocationStatusChanged() 是到站处理的关键入口:
- 到站后会:
- 更新
lastArrivalLocation - 回写
current_location - 处理巡逻到站推进
- 处理接待任务到站
- 处理通知任务到站
- 根据
MainTaskPolicy决定是否播报“已到达xxx”
- 更新
- 自动回充当前规则:
- 仅在空闲任务或
speech任务下允许 - 特殊状态下禁用
- 仅在不在
home base时生效 - 到站 10 秒后自动执行
recharge()
- 仅在空闲任务或
6.4 人体检测、门禁与问候
人体检测在 MainActivity 中做去抖:
DETECTED:约 0.8 秒稳定确认IDLE:约 5 秒稳定确认
稳定状态变化后:
- 会调用
LiveKitManager.setDetectionActive()更新麦克风逻辑。 - 会优先交给
TaskController.handleDetectionStateChanged()处理任务内行为。 - 如果当前是空闲态且允许门禁逻辑:
- 在
home base检测到人时执行开门工作流 - 回到
IDLE时执行关门工作流
- 在
- 如果当前是空闲态且不在
home base:- 检测到人时会按当前时段播报“早上好 / 中午好 / 下午好 / 晚上好”
6.5 LiveKit 与语音互斥
LiveKit 麦克风并不是“连上就开”,而是由三项共同决定:
- LiveKit 连接时传入的
enableMic detectionActive == truettsMuted == false
也就是说:
- 机器人开始 TTS 时会自动关闭麦克风。
- TTS 结束后才允许再次开麦。
- 没有人时不会持续开麦。
6.6 工作流调用
当前工作流主要有三类:
- 开门:
od_wfid/od_wf_key - 关门:
cd_wfid/cd_wf_key - 接待返航:
vr_wfid/vr_wf_key
调用路径:
WorkflowService.executeDoorWorkflow():处理开门/关门。WorkflowService.markReceptionReturnPending():- 在接待确认按钮点击后设置“返航待触发”。
WorkflowService.triggerReceptionReturnWorkflowIfNeeded():- 当机器人后续回到
home base时触发返航工作流。
- 当机器人后续回到
补充说明:
- 如果本地没有 workflow 配置,
WorkflowService会尝试重新调用fetchRuntimeConfigs()刷新一次。 - 工作流执行失败时,主界面会展示短暂的“网络异常”横幅。
6.7 Telemetry 上报
TelemetryManager 负责状态上报:
- 心跳频率:每 15 秒一次
- 状态快照字段包括:
tasklocationmqttConnectedliveKitConnectedbatterymovementposition
- 低电量策略:
- 电量
<= 20%且未充电 - 10 分钟内只重复提醒一次
- 本地播报“电量低,请及时充电。”
- 向
robot/event发布battery_low
- 电量
7. 日志与排障
7.1 主要日志来源
- Logcat Tag:
MainActivityMqttManagerLiveKitManagerSettingsActivityConnectionServiceTaskControllerTelemetryManagerHttpManager
LogManager.startLogcatListener()会在应用启动时开始监听。
7.2 排障建议
- MQTT 连不上:
- 先确认是否已激活
- 再看
base_url是否能正确解析出 host - 再看本地是否已有
mqtt_username/mqtt_password - 查看
MqttManager日志中的MQTT connect skipped、Initial connection failed
- 激活失败:
- 检查
base_url是否正确 - 检查登录账号密码是否正确
- 检查激活码和设备名是否填写完整
- 查看
HttpManager的Login failed、Activation failed
- 检查
- 工作流不触发:
- 检查本地是否已保存
od_*/cd_*/vr_* - 确认
home base地点名称归一化后能匹配 - 查看是否处于特殊状态或非空闲任务,导致门禁逻辑被策略层禁用
- 检查本地是否已保存
- 接待任务不工作:
- 先注意当前实现里接待地点默认就是
前台 - 检查是否稳定检测到人,而不是瞬时检测
- 检查按钮是否显示、任务是否超时被取消
- 先注意当前实现里接待地点默认就是
- 导航到错误地点:
- 当前未知地点会自动回退到
会议室 - 先检查 Temi 侧地点名称是否与消息一致
- 当前未知地点会自动回退到
- LiveKit 不连或没声音:
- 检查 URL / Room / Token / 自动连接开关
- 检查麦克风和摄像头权限
- 注意“已连接但没人时不开麦”是当前设计,不是异常
monitor.py调试失败:- 它仍然写死了
API_KEY/API_SECRET/ URL / 房间名 - 需要和当前 LiveKit 环境保持一致
- 它仍然写死了
8. 常见改动场景指引
8.1 新增 MQTT 指令
推荐修改顺序:
- 在
MqttManager.handleJsonCommand()增加action分支。 - 如果涉及持续态流程,在
TaskController增加任务类型,而不是把状态散落在MainActivity。 - 如果需要策略控制,同时补
MainTaskPolicy。 - 如果需要状态上报,扩展
TelemetryManager。
8.2 修正接待逻辑
如果你希望接待任务真正使用 MQTT 中的 location:
- 修改
MqttManager中reception分支,不要再写死前台。 - 联调
TaskController.startReceptionMode()与现场 Temi 地点名称。 - 回归测试接待确认按钮、接待返航工作流、朝向恢复逻辑。
8.3 调整自动回充或门禁策略
优先改这里:
MainTaskPolicy:决定什么情况下允许自动回充、门禁、问候AutoRechargeScheduler:决定延时多久回充MainActivity.handleStableDetectionStateChanged():决定何时触发开门/关门
8.4 更换服务环境
新环境切换步骤建议如下:
- 在设置页改
base_url - 确认激活状态已被重置
- 重新填写登录信息和激活码
- 重新激活,拉取 MQTT 与工作流配置
- 再检查 LiveKit 参数是否也需要同步更新
8.5 扩展 HTTP / 工作流集成
建议在 HttpManager 中新增原子接口,在 WorkflowService 中编排业务流程,避免把 HTTP 细节直接散落到 MainActivity。
9. 安全与后续优化建议
- 敏感信息:
- 登录账号密码目前明文保存在
SharedPreferences - LiveKit 默认
API_KEY/API_SECRET仍写在代码中 monitor.py里也写死了 LiveKit 凭据和 URL
- 登录账号密码目前明文保存在
- 配置管理:
- 现在已经有“激活后拉运行配置”的雏形,可以继续把更多环境配置收敛到服务端
- 协议一致性:
reception.location未实际生效goto未知地点会静默降级到会议室- 这些行为最好和上游协议重新对齐
- 文案资源化:
- 业务提示语很多还写在 Kotlin 代码里,后续可以逐步迁移到
strings.xml
- 业务提示语很多还写在 Kotlin 代码里,后续可以逐步迁移到
- 稳定性:
- 工作流失败现在只是展示网络异常横幅,可考虑补充错误事件上报和重试机制
10. 建议接手路径
如果你是第一次接手,建议按下面顺序熟悉:
- 先在真机上跑通激活流程。
- 在设置页填好
base_url、登录账号、激活码、设备名,确认 MQTT 能连上。 - 用
goto、speak、patrol、notification四类指令验证基础链路。 - 再测试空闲态门禁、接待任务、自动回充、低电量事件。
- 最后再看
technique.md里的流程图,对照MainActivity和TaskController理解策略差异。
如需继续维护,建议同步更新 README.md 与 technique.md,因为它们目前还有一部分内容停留在旧版实现描述。