传统提词设备存在明显的使用缺陷:演讲者需频繁注视平板或大屏幕上的文稿,导致视线脱离观众,眼神交流中断,现场氛围显得生硬而不自然。而Rokid AR眼镜则提供了一种更隐蔽、高效的解决方案——将文字直接投射在视野上方,实现“眼不离场”的流畅表达,观众难以察觉其正在读稿,极大提升了演讲的真实感与专业度。
更为关键的是,Rokid CXR-M SDK支持AI驱动的自动跟踪功能。通过集成ASR(语音识别)技术,系统可实时捕捉演讲者的语音内容,并智能匹配到对应文字段落,触发提词器自动滚动。这意味着无需手动翻页,内容推进完全由说话节奏驱动,真正实现了“所说即所见”。这正是AR提词器区别于传统方式的核心优势。
本文将详细讲解如何基于Rokid CXR-M SDK构建一套完整的智能提词系统,涵盖SDK接入流程、提词场景配置、AI模式运行机制、ASR服务整合策略,以及提升识别准确率的演讲稿优化方法。所有实现均依据官方文档,代码具备实际可用性。
Rokid AR开放平台为开发者提供了完善的工具链支持,其中核心组件为CXR-M SDK。该SDK专注于连接手机端应用与Rokid Glasses设备,支持双向数据通信、实时音视频流获取及多种预设交互场景的调用。
截至2025年8月,CXR-M SDK已迭代至v1.0.1版本,具备以下九大核心功能:
上述功能中,“自定义提词器场景”是本文重点探讨的内容,它为实现智能化演讲辅助提供了底层支撑。
根据官方说明,提词器功能主要依赖四个核心接口来完成控制与交互:
// 1. 打开/关闭提词器场景
fun openOrCloseWordTips(toOpen: Boolean): ValueUtil.CxrStatus? {
return CxrApi.getInstance().controlScene(
ValueUtil.CxrSceneType.WORD_TIPS,
toOpen,
null
)
}
// 2. 发送提词器内容
fun setWordTipsText(text: String, fileName: String): ValueUtil.CxrStatus? {
return CxrApi.getInstance().sendStream(
ValueUtil.CxrStreamType.WORD_TIPS,
text.toByteArray(),
fileName,
sendCallback
)
}
// 3. 配置提词器显示参数
fun configWordTipsText(
textSize: Float, // 文字大小
lineSpace: Float, // 行间距
mode: String, // 模式:'normal' 或 'ai'
startPointX: Int, // 起始坐标X
startPointY: Int, // 起始坐标Y
width: Int, // 显示宽度
height: Int // 显示高度
): ValueUtil.CxrStatus? {
return CxrApi.getInstance().configWordTipsText(
textSize, lineSpace, mode,
startPointX, startPointY, width, height
)
}
// 4. 发送ASR结果(AI模式专用)
fun sendWordTipsAsrContent(content: String): ValueUtil.CxrStatus? {
return CxrApi.getInstance().sendAsrContent(content)
}
在这四个接口中,最关键的部分在于第三个接口中的特定参数设置:
mode
该参数决定了提词器的工作模式,具体包括两种:
mode="normal"
mode="ai"
初学者常误以为提词功能仅限于文本显示,忽略了AI模式的重要性。实际上,一旦启用
mode="ai"
参数,提词器即可根据演讲者的实时语句自动定位并滚动至相应位置,彻底摆脱手动干预,这才是真正的“智能提词”所在。
整个智能提词系统的运作依赖三个核心模块的协同配合:
整体工作流程如下:
用户输入主题 → AI生成演讲稿 → 内容推送至眼镜 → 启动AI模式 → 实时语音识别(ASR)→ 自动匹配并滚动文本
从技术角度看,实现过程主要包括以下几个关键环节:
此外,演讲稿本身的格式质量也直接影响ASR匹配的准确性,后续章节将介绍相应的优化技巧。
本项目开发所用环境如下:
SDK的集成过程较为简单,只需按照官方指引添加Maven仓库和依赖项即可。本文不再赘述集成步骤,重点聚焦于提词器功能的具体实现逻辑。
在启动提词功能前,需对显示参数进行合理配置,以确保最佳阅读体验:
class TeleprompterManager(private val context: Context) {
companion object {
private const val TAG = "TeleprompterManager"
}
// 打开提词器场景
fun openTeleprompter(): Boolean {
val status = CxrApi.getInstance().controlScene(
ValueUtil.CxrSceneType.WORD_TIPS,
true,
null
)
if (status == ValueUtil.CxrStatus.REQUEST_SUCCEED) {
Log.d(TAG, "提词器场景打开成功")
return true
} else {
Log.e(TAG, "提词器场景打开失败: $status")
return false
}
}
// 配置AI模式(这是关键)
fun configAIMode() {
val status = CxrApi.getInstance().configWordTipsText(
textSize = 28f, // 字体大小,推荐28
lineSpace = 1.5f, // 行间距,1.5较为合适
mode = "ai", // AI模式,启用ASR自动跟踪
startPointX = 100,
startPointY = 200,
width = 800,
height = 400
)
if (status != ValueUtil.CxrStatus.REQUEST_SUCCEED) {
Log.e(TAG, "配置AI模式失败: $status")
} else {
Log.d(TAG, "AI模式配置成功")
}
}
// 关闭提词器
fun closeTeleprompter() {
CxrApi.getInstance().controlScene(
ValueUtil.CxrSceneType.WORD_TIPS,
false,
null
)
}
}
字体大小与行间距需根据实际佩戴效果调整。默认24号字体偏小,影响辨识;若设置为32号,则单屏显示行数过少(仅两行)。经测试,推荐使用28号字体搭配1.5倍行距,可在视野中稳定呈现5-6行内容,兼顾清晰度与信息密度。
为提升内容生成效率与语言自然度,引入文心一言ERNIE-Bot-Turbo模型协助撰写讲稿:
interface WenxinApi {
@POST("rpc/2.0/ai_custom/v1/wenxinworkshop/chat/ernie-bot-turbo")
suspend fun generateSpeech(@Body request: SpeechRequest): SpeechResponse
}
data class SpeechRequest(
val messages: List<Message>,
val temperature: Float = 0.95f,
val top_p: Float = 0.8f,
val penalty_score: Float = 1.0f,
val system: String? = null
)
data class Message(
val role: String, // "user" 或 "assistant"
val content: String
)
data class SpeechResponse(
val result: String,
val usage: Usage
)
data class Usage(
val total_tokens: Int
)
实际调用示例如下:
class SpeechGenerator(private val wenxinApi: WenxinApi) {
suspend fun generateSpeechScript(
topic: String,
duration: Int,
scene: String
): String {
val prompt = buildPrompt(topic, duration, scene)
val request = SpeechRequest(
messages = listOf(
Message("user", prompt)
),
system = "你是一位演讲稿撰写专家"
)
return try {
val response = wenxinApi.generateSpeech(request)
response.result
} catch (e: Exception) {
Log.e(TAG, "生成演讲稿失败", e)
throw e
}
}
private fun buildPrompt(topic: String, duration: Int, scene: String): String {
return """
你是一位演讲稿撰写专家,请为我生成一篇适合AR眼镜提词器的演讲稿。
主题:$topic
时长:${duration}分钟
场景:$scene
要求:
1. 每段不超过80字,适合AR眼镜小屏幕显示
2. 避免使用生僻字和容易混淆的同音词(如"集成/继承"),方便语音识别
3. 使用自然口语化表达,避免书面语
4. 每段结尾要有明显的停顿词(如"那么"、"接下来"),便于ASR切段
5. 关键术语第一次出现时简短解释
6. 每段单独一行,段间空一行
请直接输出演讲稿,不要额外说明。
""".trimIndent()
}
}
该模型响应速度快,平均耗时1-2秒即可输出一段结构完整、表达口语化的演讲内容,非常适合用于即时生成演讲素材。
生成完成后,需将文本内容通过SDK推送到Rokid眼镜端进行显示:
private val sendCallback = object : SendStatusCallback {
override fun onSendSucceed() {
Log.d(TAG, "演讲稿推送成功")
// 震动反馈,让用户知道推送完成
vibrate()
// 启动ASR监听
startAsrTracking()
}
override fun onSendFailed(errorCode: ValueUtil.CxrSendErrorCode?) {
Log.e(TAG, "推送失败: $errorCode")
showError("演讲稿推送失败,请检查眼镜连接")
}
}
fun pushSpeechToGlasses(content: String) {
val fileName = "speech_${System.currentTimeMillis()}.txt"
val status = CxrApi.getInstance().sendStream(
ValueUtil.CxrStreamType.WORD_TIPS,
content.toByteArray(Charsets.UTF_8),
fileName,
sendCallback
)
if (status == ValueUtil.CxrStatus.REQUEST_SUCCEED) {
Log.d(TAG, "开始推送演讲稿")
} else {
Log.e(TAG, "推送请求失败: $status")
}
}
private fun vibrate() {
val vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
vibrator.vibrate(VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE))
} else {
vibrator.vibrate(100)
}
}
为增强用户体验,推送成功后加入了手机震动反馈。由于推送为异步操作,用户无法直观判断是否完成,震动提示可有效弥补这一感知盲区。
文件命名采用了带时间戳的动态名称(如speech_20250801_1423.txt),避免因使用固定名称(如speech.txt)导致浏览器或设备缓存旧版本的问题。每次更新稿件都能确保眼镜端加载最新内容。
这是AI模式得以运行的核心环节:利用ASR持续监听演讲者语音,识别出完整句子后,调用SDK接口将文本发送给眼镜,由其内部引擎匹配当前进度并自动滚动。
完整流程如下:
sendWordTipsAsrContent()
result.isFinal = true
sendWordTipsAsrContent()
class AsrManager(private val context: Context) {
private var asrService: AliyunAsrService? = null
fun startAsrTracking() {
asrService = AliyunAsrService(context).apply {
setListener { result ->
if (result.isFinal) {
// ASR识别到完整句子
val text = result.text
Log.d(TAG, "ASR识别结果: $text")
// 发送到眼镜,触发自动滚动
val status = CxrApi.getInstance().sendWordTipsAsrContent(text)
if (status == ValueUtil.CxrStatus.REQUEST_SUCCEED) {
Log.d(TAG, "ASR跟踪成功")
} else {
Log.w(TAG, "ASR跟踪失败: $status")
}
}
}
start()
}
}
fun stopAsrTracking() {
asrService?.stop()
asrService = null
}
}
关键实现细节:
必须等待识别结果达到“最终态”才进行发送,即只有确认是一句完整表述时才触发上传动作。这是因为ASR识别过程具有渐进性:
result.isFinal
result.isFinal = true
第1次回调:result.text = "我们",isFinal = false
第2次回调:result.text = "我们今天",isFinal = false
第3次回调:result.text = "我们今天要介绍的是",isFinal = false
第4次回调:result.text = "我们今天要介绍的是Rokid提词器",isFinal = true ?
例如,初始识别可能是“今天我…”,随后逐步修正为“今天我要分享…”、“今天我要分享一个重要的想法”。若在中间阶段就发送,会导致误匹配或频繁跳转。因此,应监听“final result”事件,确保只传递已完成识别的句子,从而保障滚动的精准性和稳定性。
若每次语音识别的中间结果都实时发送,提词器界面会出现频繁跳动现象。只有在接收到最终确认的“final”识别结果后再进行推送,才能确保提词器稳定、准确地同步跟踪演讲进度。
本方案采用阿里云实时语音识别(ASR),其普通话识别准确率在标准发音条件下可达95%以上。此外,也可根据实际需求选择以下替代方案:
在选择ASR服务商时,需重点评估以下几个维度:
延迟:过高的响应延迟会导致提词器显示滞后,影响演讲节奏
准确率:识别错误将直接导致文本匹配失败,中断自动跟踪
成本:长时间或高频使用的演讲场景需考虑整体费用支出
稳定性:在网络条件不佳的情况下是否仍能维持基本功能运行
结合合理的演讲稿撰写规范(如避免使用生僻字和易混淆的同音词),可显著提升提词器的跟踪效果与稳定性。
AI模式中最常见的问题是名称识别不一致。例如,演讲者口述“Rokid眼镜”,但ASR系统可能将其转写为“若鸡的眼镜”。由于提词器中原文为“Rokid”,两者无法匹配,造成跟踪中断。
问题根源:AI生成稿件使用英文品牌名“Rokid”,而实际口语表达习惯使用中文译名“若琪”。
解决方案一:统一使用中文命名
在生成演讲稿的Prompt中加入明确指令,要求全部使用中文名称表述。
技术名词统一用中文表达,比如"若琪眼镜"而不是"Rokid Glasses","安卓"而不是"Android"
解决方案二:配置ASR自定义热词库
以阿里云ASR为例,支持上传自定义热词列表,提前注册关键术语,提升特定词汇的识别优先级。
val hotWords = listOf("Rokid", "CXR-M", "SDK")
asrConfig.setHotWords(hotWords)
除品牌名称外,汉语中的同音或近音词也常导致识别错误,典型案例如:
这些词语在语音上难以区分,一旦识别出错,即可能导致文本定位失败。
解决方式:
实际演讲过程中,语句与原始稿件完全一致的可能性极低,通常会存在增删词语的情况。若仅依赖精确匹配,极易导致跟踪中断。
可在移动端实现增强型容错匹配逻辑(注:此为进阶优化项,SDK已具备基础匹配能力):
fun fuzzyMatch(asrText: String, scriptText: String): Boolean {
// 1. 去除标点符号
val cleanAsr = asrText.replace(Regex("[,。!?;、]"), "")
val cleanScript = scriptText.replace(Regex("[,。!?;、]"), "")
// 2. 计算相似度
val similarity = calculateSimilarity(cleanAsr, cleanScript)
// 3. 70%相似度就认为匹配成功
return similarity > 0.7
}
fun calculateSimilarity(s1: String, s2: String): Double {
val longer = maxOf(s1.length, s2.length)
if (longer == 0) return 1.0
val editDistance = levenshteinDistance(s1, s2)
return (longer - editDistance) / longer.toDouble()
}
// 莱文斯坦距离(编辑距离)
fun levenshteinDistance(s1: String, s2: String): Int {
val dp = Array(s1.length + 1) { IntArray(s2.length + 1) }
for (i in 0..s1.length) dp[i][0] = i
for (j in 0..s2.length) dp[0][j] = j
for (i in 1..s1.length) {
for (j in 1..s2.length) {
val cost = if (s1[i - 1] == s2[j - 1]) 0 else 1
dp[i][j] = minOf(
dp[i - 1][j] + 1, // 删除
dp[i][j - 1] + 1, // 插入
dp[i - 1][j - 1] + cost // 替换
)
}
}
return dp[s1.length][s2.length]
}
容错匹配的核心原理如下:
该机制具有重要实用价值。例如,演讲者口头表达为“那么接下来我们看一下具体实现”,而稿件原文为“接下来看具体实现”,虽多了“那么”和“我们”等连接词,但通过设定70%以上的相似度阈值,仍可成功匹配并继续跟踪。
若未设置textSize参数而使用SDK默认字体,可能导致阅读体验不佳。针对AR眼镜的视觉特性,不同字号的实际呈现效果如下:
建议配置:采用28号字体搭配1.5倍行距,可实现每屏显示5–6行的理想布局。
一次性向设备推送3000字以上的演讲稿,容易引起AR眼镜端明显卡顿。
sendStream
尽管接口返回成功状态,但设备处理大段文本需要时间,期间会影响其他操作的响应速度。
解决方案:分批推送内容
官方文档建议单次推送不超过1000字符。可通过分段发送方式优化性能:
class BatchSpeechManager {
private val BATCH_SIZE = 5 // 每批推送5段
private val paragraphs = LinkedList<String>()
private var currentBatch = 0
fun loadSpeech(fullContent: String) {
paragraphs.clear()
paragraphs.addAll(fullContent.split("\n\n"))
currentBatch = 0
pushNextBatch()
}
private fun pushNextBatch() {
if (paragraphs.isEmpty()) return
val batch = paragraphs.take(BATCH_SIZE)
val content = batch.joinToString("\n\n")
pushSpeechToGlasses(content)
repeat(BATCH_SIZE) {
if (paragraphs.isNotEmpty()) {
paragraphs.removeFirst()
}
}
currentBatch++
}
// 演讲时调用,快到末尾时自动推送下一批
fun onParagraphChanged(currentIndex: Int, totalInCurrentBatch: Int) {
if (totalInCurrentBatch - currentIndex <= 2 && paragraphs.isNotEmpty()) {
Log.d(TAG, "预加载下一批演讲稿")
pushNextBatch()
}
}
}
实施分批推送后,设备运行流畅性显著改善。同时带来额外优势:允许在演讲过程中根据现场情况灵活调整后续内容,无需预先上传全部稿件。
若临近登台才启动演讲稿生成流程,AI接口响应缓慢可能导致内容未能及时准备就绪。
推荐做法:
Rokid AR提词器的核心优势在于“AR显示 + AI自动跟踪”的融合设计,适用于多种专业表达场合:
技术类分享会
AR显示优势:文字悬浮于视野上方,观众难以察觉演讲者正在查看提词,保持自然眼神交流
AI跟踪优势:无需手动翻页,演讲过程更加连贯流畅
适用范围:技术大会、企业内部培训、线下技术沙龙等
产品路演展示
ASR跟踪准确率较高,基本可同步演讲节奏;即使偶尔出现短暂脱节(如临时插入语句),也能快速恢复定位
适用范围:面向投资人的项目推介、新品发布会、商业提案演讲等
在线教学授课
支持长时间讲授内容,减轻记忆负担,降低备课成本
注意事项:在频繁师生互动环节,ASR可能误识学生回答内容,导致提词器异常跳转
应对措施:在提问互动阶段手动暂停ASR功能(调用相应控制接口)
asrManager.stopAsrTracking()
待恢复正常讲解时再重新启用识别服务
基于SDK功能特性和AR眼镜硬件规格,推荐以下最优配置组合:
// 提词器显示配置
textSize = 28f // 字体大小(推荐28,一屏显示5-6行)
lineSpace = 1.5f // 行间距(1.5较为舒适)
mode = "ai" // 必须使用AI模式,启用ASR自动跟踪
startPointX = 100 // 显示起始坐标X
startPointY = 200 // 显示起始坐标Y
width = 800 // 显示区域宽度
height = 400 // 显示区域高度
// 演讲稿生成配置
每段字数:60-80字(适合AR眼镜小屏幕)
AI模型:文心一言 ernie-bot-turbo(响应快,口语化强)
// 推送策略
单批最大字数:500字(约5-6段)
分批推送:长演讲稿建议分批,避免卡顿
Rokid AR眼镜智能提词系统的两大核心价值体现在:
AR显示优势:文本信息呈现在用户视野上方区域,外部观察者无法察觉,保障了演讲者与听众之间的眼神交流自然性
AI自动跟踪能力:通过集成ASR服务实时捕捉语音内容,动态滚动至对应文字段落,真正实现双手解放
实现该方案的关键技术路径包括:
controlScene
sendStream
configWordTipsText
mode="ai"
sendWordTipsAsrContent()
系统架构设计简洁明了:手机端承担AI驱动的演讲稿生成与ASR语音识别功能,眼镜端则专注于内容显示及自动滚动播放,SDK在两者之间实现高效通信。开发者无需关心底层AR渲染与文本同步等复杂技术细节,只需聚焦于核心业务逻辑的实现,相关能力均由SDK统一支持与管理。

以下是项目开发过程中可参考的官方技术资源:
扫码加好友,拉您进群



收藏
