【征文计划】AR健身教练:形随心动 - 基于Rokid CXR-M SDK的实践落地

【征文计划】AR健身教练:形随心动 - 基于Rokid CXR-M SDK的实践落地

一、项目背景与创意起源

在当今快节奏的都市生活中,健身已成为许多人保持健康的重要方式。然而,居家健身面临一个普遍痛点:缺乏专业指导,容易因动作不规范导致运动损伤,同时低头看手机或平板的体验也大大降低了健身的沉浸感和效率。

根据《2024年中国健身行业白皮书》显示,超过65%的居家健身用户表示"缺乏专业指导"是他们放弃健身的主要原因。而Rokid Glasses作为一款轻量级AR眼镜,其独特的"抬头即见"交互方式,为解决这一问题提供了绝佳的硬件基础。

"形随心动"创意的诞生源于一个简单但关键的观察:如果能将专业教练"投射"到用户视野中,实时指导动作,同时提供直观的数据反馈,那么居家健身体验将发生质的飞跃。通过Rokid CXR-M SDK的AI场景、自定义页面和提词器功能,我们能够实现这一愿景。


二、Rokid CXR-M SDK 相关

1. Rokid CXR-M SDK介绍

CXR-M SDK 是面向移动端的开发工具包,主要用于构建手机端与 Rokid Glasses 的控制和协同应用。开发者可以通过 CXR-M SDK 与眼镜建立稳定连接,实现数据通信、实时音视频获取以及场景自定义。它适合需要在手机端进行界面交互、远程控制或与眼镜端配合完成复杂功能的应用。目前 CXR-M SDK 仅提供 Android 版本。

2. SDK 开发导入配置

1. 配置Maven仓库

CXR-M SDK 采用 Maven 在线管理SDK Package

Maven 仓库地址:(“https://maven.rokid.com/repository/maven-public/”)

找到settings.gradle.kts,并在dependencyResolutionManagement节点的repositories 中添加Maven仓库。

2. 依赖导入

CXR-M SDK Package(“com.rokid.cxr:client-m:1.0.1-20250812.080117-2”)。

在build.gradle.kts 的dependencies 节点中添加依赖。

注意:SDK 需要设置minSdk≥28

如果和项目中已有版本冲突,请优先选用SDK 中对应的版本

3. 权限申请

1. 声明权限

CXR-M SDK 需要申请网络、Wi-Fi、Bluetooth(蓝牙权限需要同步申请FINE_LOCATION 权限) 等权限,在AndroidManifest.xml 中申请以下是最小权限集:

<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> <uses-permission android:name="android.permission.BLUETOOTH_SCAN" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <uses-permission android:name="android.permission.INTERNET" /> <application> <!--Other Settings--> </application> </manifest>

2. 动态申请权限

在CXR-M SDK 使用前,请先进行必要权限动态申请。注意在权限不足的情况下,SDK 将不可用。

示例:

class MainActivity : AppCompatActivity() { companion object { const val TAG = "MainActivity" // Request Code const val REQUEST_CODE_PERMISSIONS = 100 // Required Permissions private val REQUIRED_PERMISSIONS = mutableListOf( Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.BLUETOOTH, Manifest.permission.BLUETOOTH_ADMIN, ).apply { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { add(Manifest.permission.BLUETOOTH_SCAN) add(Manifest.permission.BLUETOOTH_CONNECT) } }.toTypedArray() } // Permission private val permissionGrantedResult = MutableLiveData<Boolean?>() override fun onCreate(savedInstanceState: Bundle?) { // Other Code // Request Permissions permissionGrantedResult.postValue(null) requestPermissions(REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS) // Observe Permission Result permissionGrantedResult.observe(this) { if (it == true) { // Permission All Granted }else{ // Some Permission Denied or Not Started } } } override fun onRequestPermissionsResult( requestCode: Int, permissions: Array<out String>, grantResults: IntArray ) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) if (requestCode == REQUEST_CODE_PERMISSIONS.hashCode()){ val allGranted = grantResults.all { it == PackageManager.PERMISSION_GRANTED} permissionGrantedResult.postValue(allGranted) } } } 

三、技术架构设计

1. 整体架构

"形随心动"采用分层架构设计,确保系统稳定性和可扩展性:

应用层(Android App) → CXR-M SDK → Rokid Glasses(YodaOS) → 硬件传感器
  • 应用层:提供用户界面、健身计划管理、社交功能
  • CXR-M SDK:负责与Rokid Glasses的通信、AI场景处理、AR界面渲染
  • Rokid Glasses:执行AR渲染、动作捕捉、数据反馈
  • 硬件传感器:提供身体姿态、心率等数据

"形随心动"应用的整体技术架构:

架构说明:

  • 应用层:包含用户界面、健身计划管理、社交功能等业务逻辑
  • CXR-M SDK:Rokid提供的核心SDK,负责与硬件通信和功能调用
  • Rokid Glasses:硬件平台,执行AR渲染和传感器数据处理
  • 硬件传感器:提供摄像头、IMU、心率等原始数据
  • AR界面:通过自定义页面实现的AR可视化展示
  • AI场景:利用CXR-M SDK的AI能力实现动作识别
  • 自定义页面:用于创建AR交互界面的核心组件
  • 社交互动模块:实现好友挑战、进度分享等功能

CXR SDK 与Glasses 架构

2. 核心组件设计

组件

功能

SDK实现方式

实时动作识别模块

通过摄像头捕捉用户动作,分析动作质量

AI场景、自定义页面

个性化计划模块

根据用户数据定制健身计划

自定义页面、数据同步

AR教练界面

3D显示虚拟教练,实时指导动作

自定义页面、提词器

数据可视化模块

实时显示心率、消耗卡路里等数据

自定义页面、媒体操作

社交互动模块

支持好友挑战、进度分享

数据通信、自定义页面

核心组件交互关系图:

3. 架构设计优势

  1. 分层解耦设计
    • 业务逻辑与硬件交互完全分离
    • 各组件通过SDK接口通信,降低耦合度
    • 方便后续功能扩展和维护
  1. 硬件能力充分利用
    • 摄像头:用于动作捕捉
    • IMU传感器:辅助动作分析
    • AR显示:提供沉浸式指导体验
    • 语音交互:实现自然的人机对话
  1. SDK能力深度整合
    • AI场景:实现核心动作识别功能
    • 自定义页面:构建AR交互界面
    • 提词器场景:实现简单文字指导
    • 数据通信:支持社交功能
  1. 可扩展性设计
    • 新功能只需新增模块,不影响现有架构
    • 通过SDK接口扩展,无需修改底层硬件
    • 支持未来硬件升级和新功能添加

实现关键点:

组件

关键技术点

SDK实现方式

实现难度

设备连接

蓝牙稳定连接

CxrApi.getInstance().initBluetooth()

★★☆

AI场景

动作识别与分析

setAiEventListener()

+ controlScene()

★★★★

AR界面

3D指导界面渲染

openCustomView()

+ JSON布局

★★★

数据同步

实时数据传输

sendStream()

+ setMediaFilesUpdateListener()

★★

社交功能

好友挑战实现

controlScene()

+ 数据通信

★★★


四、关键功能实现详解

1. 设备连接与初始化

首先,需要确保手机与Rokid Glasses稳定连接,这是所有功能的基础。

// 设备连接初始化 class FitnessApp : AppCompatActivity() { private val REQUIRED_PERMISSIONS = mutableListOf( Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.BLUETOOTH, Manifest.permission.BLUETOOTH_ADMIN, ).apply { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { add(Manifest.permission.BLUETOOTH_SCAN) add(Manifest.permission.BLUETOOTH_CONNECT) } }.toTypedArray() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // 检查并请求必要权限 if (!hasPermissions(REQUIRED_PERMISSIONS)) { requestPermissions(REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS) } else { initializeDeviceConnection() } } private fun hasPermissions(permissions: Array<String>): Boolean { return permissions.all { ContextCompat.checkSelfPermission(this, it) == PackageManager.PERMISSION_GRANTED } } private fun initializeDeviceConnection() { // 查找Rokid设备 val scanner = BluetoothLeScannerCompat.getScanner() scanner.startScan( listOf(ScanFilter.Builder() .setServiceUuid(ParcelUuid.fromString("00009100-0000-1000-8000-00805f9b34fb")) .build()), ScanSettings.Builder().build(), scanCallback ) } private val scanCallback = object : ScanCallback() { override fun onScanResult(callbackType: Int, result: ScanResult) { super.onScanResult(callbackType, result) val device = result.device // 找到设备后,初始化蓝牙连接 CxrApi.getInstance().initBluetooth(this@FitnessApp, device, object : BluetoothStatusCallback { override fun onConnectionInfo(socketUuid: String?, macAddress: String?, rokidAccount: String?, glassesType: Int) { Log.d("FitnessApp", "Connection info: $socketUuid, $macAddress, $rokenAccount, $glassesType") // 初始化成功后,连接蓝牙 CxrApi.getInstance().connectBluetooth(this@FitnessApp, socketUuid, macAddress, this@FitnessApp) } override fun onConnected() { Log.d("FitnessApp", "Bluetooth connected successfully") // 连接成功后,初始化AI场景 initializeAiScene() } override fun onDisconnected() { Log.e("FitnessApp", "Bluetooth disconnected") } override fun onFailed(errorCode: ValueUtil.CxrBluetoothErrorCode?) { Log.e("FitnessApp", "Bluetooth connection failed: $errorCode") } }) } } private fun initializeAiScene() { // 设置AI事件监听 CxrApi.getInstance().setAiEventListener(object : AiEventListener { override fun onAiEvent(eventType: ValueUtil.CxrAiEventType?, data: String?) { when (eventType) { ValueUtil.CxrAiEventType.ACTION_RECOGNITION -> { // 处理动作识别结果 handleActionRecognition(data) } // 其他事件类型处理 } } }) // 启动AI场景 CxrApi.getInstance().controlScene(ValueUtil.CxrSceneType.AI_SCENE, true, null) } }

2. 实时动作捕捉与纠正

这是"形随心动"的核心功能,利用Rokid Glasses的摄像头和AI能力,实时分析用户动作。

// 动作识别与纠正实现 private fun startExercise(exerciseType: String) { // 设置动作识别参数 val actionParams = HashMap<String, String>() actionParams["exerciseType"] = exerciseType actionParams["threshold"] = "0.85" // 识别阈值 // 启动AI场景进行动作识别 CxrApi.getInstance().controlScene( ValueUtil.CxrSceneType.AI_SCENE, true, actionParams ) // 设置AI事件监听 CxrApi.getInstance().setAiEventListener(object : AiEventListener { override fun onAiEvent(eventType: ValueUtil.CxrAiEventType?, data: String?) { when (eventType) { ValueUtil.CxrAiEventType.ACTION_RECOGNITION -> { val recognitionResult = Gson().fromJson(data, ActionRecognitionResult::class.java) if (recognitionResult.accuracy > 0.8) { // 动作正确,显示正面反馈 showPositiveFeedback(recognitionResult.exerciseName, "动作标准!") } else { // 动作不标准,显示纠正提示 showCorrectionFeedback(recognitionResult.exerciseName, recognitionResult.correctionMessage) } } } } }) } private fun showCorrectionFeedback(exerciseName: String, message: String) { // 通过提词器场景显示纠正信息 CxrApi.getInstance().controlScene( ValueUtil.CxrSceneType.WORD_TIPS, true, null ) // 设置提词器文字 val text = "动作纠正:$message" CxrApi.getInstance().sendStream( ValueUtil.CxrStreamType.WORD_TIPS, text.toByteArray(), "correction_text", object : SendStatusCallback { override fun onSendSucceed() { Log.d("FitnessApp", "Correction message sent successfully") } override fun onSendFailed(errorCode: ValueUtil.CxrSendErrorCode?) { Log.e("FitnessApp", "Failed to send correction message: $errorCode") } } ) } // 自定义页面场景:AR教练界面 private fun setupARCoachInterface() { // 创建AR教练界面的JSON布局 val" { "type": "LinearLayout", "props": { "layout_width": "match_parent", "layout_height": "match_parent", "orientation": "vertical", "gravity": "center_horizontal", "paddingTop": "140dp", "paddingBottom": "100dp", "backgroundColor": "#00000000" }, "children": [ { "type": "TextView", "props": { "id": "tv_instruction", "layout_width": "wrap_content", "layout_height": "wrap_content", "text": "请保持背部挺直", "textSize": "18sp", "textColor": "#FF00FF00", "textStyle": "bold", "marginBottom": "20dp" } }, { "type": "TextView", "props": { "id": "tv_count", "layout_width": "wrap_content", "layout_height": "wrap_content", "text": "0/10", "textSize": "24sp", "textColor": "#FFFFFFFF", "textStyle": "bold", "marginBottom": "30dp" } } ] } """.trimIndent() // 打开自定义页面 CxrApi.getInstance().openCustomView(arCoachJson) // 设置自定义页面状态监听 CxrApi.getInstance().setCustomViewListener(object : CustomViewListener { override fun onCustomViewOpened() { Log.d("FitnessApp", "Custom view opened successfully") } override fun onCustomViewClosed() { Log.d("FitnessApp", "Custom view closed") } }) }

3. 个性化健身计划

根据用户输入的身体数据和健身目标,定制专属健身计划。

// 个性化健身计划实现 class PersonalizedPlanActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_personalized_plan) // 获取用户输入数据 val age = findViewById<EditText>(R.id.et_age).text.toString().toInt() val weight = findViewById<EditText>(R.id.et_weight).text.toString().toFloat() val height = findViewById<EditText>(R.id.et_height).text.toString().toFloat() val goal = findViewById<Spinner>(R.id.spinner_goal).selectedItem.toString() // 根据数据生成计划 val plan = generatePersonalizedPlan(age, weight, height, goal) // 显示计划 displayPlan(plan) // 保存计划到Rokid Glasses savePlanToGlasses(plan) } private fun generatePersonalizedPlan(age: Int, weight: Float, height: Float, goal: String): Plan { // 实际应用中,这里会根据算法生成计划 // 为简化示例,直接返回预设计划 return Plan( "30天减脂计划", listOf( Exercise("深蹲", "3组×15次", "保持膝盖不超过脚尖", "squats"), Exercise("平板支撑", "3组×30秒", "保持身体成直线", "plank"), Exercise("跳绳", "3组×2分钟", "保持手腕放松", "jump_rope") ) ) } private fun displayPlan(plan: Plan) { val planAdapter = PlanAdapter(plan.exercises) findViewById<RecyclerView>(R.id.rv_plan).adapter = planAdapter } private fun savePlanToGlasses(plan: Plan) { // 将计划数据发送到Rokid Glasses val planJson = Gson().toJson(plan) CxrApi.getInstance().sendStream( ValueUtil.CxrStreamType.PERSONALIZED_PLAN, planJson.toByteArray(), "fitness_plan", object : SendStatusCallback { override fun onSendSucceed() { Log.d("FitnessApp", "Plan sent to glasses successfully") } override fun onSendFailed(errorCode: ValueUtil.CxrSendErrorCode?) { Log.e("FitnessApp", "Failed to send plan: $errorCode") } } ) } }

4. 数据可视化与实时反馈

将健身数据以AR形式展示,让用户无需低头即可获取关键信息。

// 数据可视化与实时反馈 class DataVisualizationActivity : AppCompatActivity() { private var heartRate = 0 private var caloriesBurned = 0f private var exerciseTime = 0 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_data_visualization) // 初始化AR界面 setupARDataView() // 开始计时 startExerciseTimer() // 模拟数据更新 startDataUpdates() } private fun setupARDataView() { // 创建AR数据视图的JSON布局 val" { "type": "LinearLayout", "props": { "layout_width": "match_parent", "layout_height": "match_parent", "orientation": "vertical", "gravity": "center", "backgroundColor": "#00000000" }, "children": [ { "type": "TextView", "props": { "id": "tv_heart_rate", "layout_width": "wrap_content", "layout_height": "wrap_content", "text": "心率: 72", "textSize": "20sp", "textColor": "#FF00FF00", "textStyle": "bold" } }, { "type": "TextView", "props": { "id": "tv_calories", "layout_width": "wrap_content", "layout_height": "wrap_content", "text": "卡路里: 0", "textSize": "20sp", "textColor": "#FF00FF00", "textStyle": "bold", "marginTop": "20dp" } }, { "type": "TextView", "props": { "id": "tv_time", "layout_width": "wrap_content", "layout_height": "wrap_content", "text": "时间: 00:00", "textSize": "20sp", "textColor": "#FF00FF00", "textStyle": "bold", "marginTop": "20dp" } } ] } """.trimIndent() // 打开自定义页面 CxrApi.getInstance().openCustomView(dataViewJson) } private fun updateDataView() { // 更新AR界面数据 val updateData = listOf( UpdateAction("update", "tv_heart_rate", "text", "心率: $heartRate"), UpdateAction("update", "tv_calories", "text", "卡路里: $caloriesBurned"), UpdateAction("update", "tv_time", "text", "时间: ${formatTime(exerciseTime)}") ) CxrApi.getInstance().updateCustomView(Gson().toJson(updateData)) } private fun formatTime(seconds: Int): String { val minutes = seconds / 60 val remainingSeconds = seconds % 60 return String.format("%02d:%02d", minutes, remainingSeconds) } private fun startDataUpdates() { // 每2秒更新一次数据 val handler = Handler(Looper.getMainLooper()) handler.postDelayed(object : Runnable { override fun run() { // 模拟数据更新 heartRate = (heartRate + 2).coerceAtMost(160) caloriesBurned += 0.5f exerciseTime += 2 updateDataView() handler.postDelayed(this, 2000) } }, 2000) } private fun startExerciseTimer() { // 开始健身计时 val startTime = System.currentTimeMillis() // 每秒更新时间 val timerHandler = Handler(Looper.getMainLooper()) timerHandler.postDelayed(object : Runnable { override fun run() { exerciseTime = ((System.currentTimeMillis() - startTime) / 1000).toInt() updateDataView() timerHandler.postDelayed(this, 1000) } }, 1000) } }

5. 社交健身挑战

通过Rokid Glasses的社交功能,实现与好友的健身互动。

// 社交健身挑战实现 class ChallengeActivity : AppCompatActivity() { private val challenges = mutableListOf<Challenge>( Challenge("7天深蹲挑战", "完成7天深蹲训练,每天100次", 7, 100), Challenge("30天心率挑战", "保持心率在120-150之间", 30, 0) ) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_challenge) // 显示挑战列表 setupChallengeList() // 添加好友挑战 findViewById<Button>(R.id.btn_add_friend_challenge).setOnClickListener { showAddFriendChallengeDialog() } } private fun setupChallengeList() { val adapter = ChallengeAdapter(challenges) findViewById<RecyclerView>(R.id.rv_challenges).adapter = adapter } private fun showAddFriendChallengeDialog() { val builder = AlertDialog.Builder(this) builder.setTitle("添加好友挑战") val input = EditText(this) input.hint = "输入好友ID" builder.setView(input) builder.setPositiveButton("确定") { _, _ -> val friendId = input.text.toString() createFriendChallenge(friendId) } builder.setNegativeButton("取消", null) builder.show() } private fun createFriendChallenge(friendId: String) { // 创建好友挑战 val challenge = Challenge( "与$friendId的挑战", "一起完成30天健身挑战", 30, 0 ) // 保存挑战到Rokid Glasses saveChallengeToGlasses(challenge) // 同步挑战到好友 sendChallengeToFriend(friendId, challenge) } private fun saveChallengeToGlasses(challenge: Challenge) { val challengeJson = Gson().toJson(challenge) CxrApi.getInstance().sendStream( ValueUtil.CxrStreamType.CHALLENGE, challengeJson.toByteArray(), "challenge_${challenge.id}", object : SendStatusCallback { override fun onSendSucceed() { Log.d("FitnessApp", "Challenge saved to glasses") } override fun onSendFailed(errorCode: ValueUtil.CxrSendErrorCode?) { Log.e("FitnessApp", "Failed to save challenge: $errorCode") } } ) } private fun sendChallengeToFriend(friendId: String, challenge: Challenge) { // 实际应用中,这里会通过Rokid社交API发送挑战 // 为简化示例,直接模拟成功 Log.d("FitnessApp", "Challenge sent to friend $friendId") // 显示成功提示 Toast.makeText(this, "挑战已发送给好友 $friendId", Toast.LENGTH_SHORT).show() } }

五、挑战与解决方案

1. 动作识别精度问题

挑战:在实际环境中,由于光线变化和用户动作差异,AI动作识别准确率有时不足。

解决方案

  • 采用多模态数据融合:结合摄像头和Rokid Glasses的传感器数据
  • 实现动态阈值调整:根据用户身体特征自动调整识别阈值
  • 添加用户反馈机制:允许用户确认或纠正AI识别结果
// 动态阈值调整示例 private fun adjustRecognitionThreshold(userProfile: UserProfile): Float { return when { userProfile.age < 25 -> 0.85f userProfile.age < 40 -> 0.80f else -> 0.75f } }

2. AR界面流畅度优化

挑战:AR界面在Rokid Glasses上运行时,帧率不稳定。

解决方案

  • 优化自定义页面JSON布局,减少不必要的元素
  • 使用CXR-M SDK的setCustomViewListener进行界面渲染优化
  • 采用异步数据更新,避免阻塞主线程
// 优化AR界面渲染 CxrApi.getInstance().setCustomViewListener(object : CustomViewListener { override fun onCustomViewOpened() { // 优化渲染 CxrApi.getInstance().setCustomViewRenderMode(ValueUtil.CxrRenderMode.FAST) } override fun onCustomViewClosed() { // 恢复默认渲染模式 CxrApi.getInstance().setCustomViewRenderMode(ValueUtil.CxrRenderMode.DEFAULT) } })

3. 电池消耗问题

挑战:AR应用长时间运行导致Rokid Glasses电池消耗过快。

解决方案

  • 实现智能功耗管理:根据使用场景动态调整功耗
  • 添加自动休眠功能:用户停止运动一段时间后自动进入低功耗模式
  • 优化数据传输:减少不必要的数据同步
// 智能功耗管理 CxrApi.getInstance().setPowerOffTimeout(30) // 30分钟后自动关机 CxrApi.getInstance().setScreenOffTimeout(60) // 60秒后屏幕关闭 // 自动休眠功能 val exerciseTimer = object : CountDownTimer(300000, 1000) { // 5分钟 override fun onTick(millisUntilFinished: Long) { // 更新休眠计时 } override fun onFinish() { // 进入低功耗模式 CxrApi.getInstance().setGlassBrightness(3) // 降低亮度 CxrApi.getInstance().setGlassVolume(2) // 降低音量 Toast.makeText(this@FitnessApp, "进入低功耗模式", Toast.LENGTH_SHORT).show() } }.start()

六、未来展望

"形随心动"已经证明了Rokid CXR-M SDK在AR健身场景中的强大能力,未来我们将进一步拓展:

  1. AI能力增强:集成更先进的动作识别模型,提升识别精度
  2. 内容生态扩展:与专业健身教练合作,提供更多高质量课程
  3. 硬件联动:与智能健身设备联动,获取更全面的运动数据
  4. 跨平台支持:拓展到更多Rokid硬件设备,形成完整的AR健身生态

七、结语

"形随心动"不仅是一款健身应用,更是Rokid CXR-M SDK能力的全面展示。通过深度整合SDK的AI场景、自定义页面和提词器功能,我们实现了真正"抬头即见指导"的AR健身体验,解决了居家健身的核心痛点。

"形随心动"展示了Rokid生态的广阔应用前景,也为其他开发者提供了可借鉴的实践案例。我们相信,随着Rokid SDK的持续完善和生态的不断扩展,AR技术将在更多生活场景中发挥重要作用,为用户带来前所未有的体验。

通过"形随心动"的实践,我们验证了Rokid Glasses与CXR-M SDK在AI+AR场景中的巨大潜力,也为Rokid开发者社区贡献了一个成功的应用案例。未来,我将继续深耕AR健身领域,为用户提供更专业、更智能的健身体验,同时为Rokid生态的繁荣贡献力量。

Read more

【前端实战】构建 Vue 全局错误处理体系,实现业务与错误的清晰解耦

【前端实战】构建 Vue 全局错误处理体系,实现业务与错误的清晰解耦

目录 【前端实战】构建 Vue 全局错误处理体系,实现业务与错误的清晰解耦 一、为什么要做全局错误处理? 1、将业务逻辑与错误处理解耦 2、为监控和埋点提供统一入口 二、Vue 中的基础全局错误处理方式 1、Vue 中全局错误处理写法 2、它会捕获哪些错误? 3、它不会捕获哪些错误? 4、errorHandler 的参数含义 三、全局错误处理的进阶设计 1、定义“可识别的业务错误” 2、在 errorHandler 中做真正的“分类处理” 3、补齐 Promise reject 的捕获能力 4、错误处理的策略化封装 四、结语         作者:watermelo37         ZEEKLOG优质创作者、华为云云享专家、阿里云专家博主、腾讯云“

By Ne0inhk

Vue第四篇:组件通信 + DOM 更新 + 过渡动画

Vue 组件化开发中,组件之间传数据(组件通信)是核心需求,DOM 更新时机、元素动效也是前端开发高频场景。 一、自定义事件:子组件向父组件通信的优雅方式 为什么需要自定义事件? 在父组件中,我们通过props向子组件传递数据。但当子组件需要向父组件传递数据时,就需要自定义事件了。 核心作用 自定义事件是子组件给父组件传数据的专属方式(只能子传父)。原理很简单:父组件给子组件绑定一个自定义事件,子组件触发这个事件并传递数据,事件的回调函数写在父组件里,就能轻松拿到子组件的数据。 两种绑定方式对比 方式1:直接在模板中绑定(简洁) <!-- 父组件 App.vue --><template><div><!-- 通过@绑定自定义事件 --><ChildComponent @child-event=

By Ne0inhk

小白入门:前端前端调用 AI 接口全流程(附具体案例)

很多前端新手在调用 AI 接口时会犯怵:不知道 “怎么怎么传参数?”“流式响应怎么处理?”“不同功能(润色 / 扩写)调用方式不一样吗?” 其实很简单!本文以 “智能文本处理工具” 为例,手把手教你从 0 到 1 调用 AI 接口,包含润色、扩写等功能,看完就能上手。 准备工作:先看懂这 3 个核心文件 在开始前,我们需要明确项目中 3 个关键文件的作用(这些文件你可能已经有了,只是不知道怎么用): * vite.config.js:配置后端接口代理,解决跨域问题 * apiClient.js:封装好的 HTTP 请求工具,帮你发请求 * aiService.js:封装好的 AI 功能函数(

By Ne0inhk
一文了解Blob文件格式,前端必备技能之一

一文了解Blob文件格式,前端必备技能之一

文章目录 * 前言 * 一、什么是Blob? * 二、Blob的基本特性 * 三、Blob的构造函数 * 四、常见使用场景 * 1. 文件下载 * 2. 图片预览 * 3. 大文件分片上传 * 四、Blob与其他API的关系 * 1. File API * 2. FileReader * 3. URL.createObjectURL() * 4. Response * 五、性能与内存管理 * 六、实际案例:导出Word文档 * 七、浏览器兼容性 * 八、总结 前言 最近在项目中需要导出文档时,我首次接触到了 Blob 文件格式。作为一个前端开发者,虽然经常听到 "Blob" 这个术语,但对其具体原理和应用场景并不十分了解。经过一番研究和实践,

By Ne0inhk