当前位置: 首页 > news >正文

人脸跟随 ( Channel 实现(缓存5条数据 + 2度过滤 + 平滑移动))

完整的 Channel 实现(缓存5条数据 + 2度过滤 + 平滑移动)

import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.consumeEach
import kotlin.math.absdata class FaceData(val angle: Float,val position: FacePosition,val timestamp: Long = System.currentTimeMillis()
)data class FacePosition(val x: Float,val y: Float,val width: Float,val height: Float
)class FaceFollower {// Channel 配置:容量5,超出时丢弃最旧数据private val faceChannel = Channel<FaceData>(capacity = 5,onBufferOverflow = BufferOverflow.DROP_OLDEST)private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default)// 状态变量private var lastExecutedAngle: Float? = nullprivate const val ANGLE_THRESHOLD = 2.0fprivate val angleBuffer = ArrayDeque<Float>()  // 用于平滑计算// 电机控制接口private val motorController = MotorController()fun start() {// 启动消费者协程scope.launch {processFaceData()}// 模拟摄像头数据输入(实际项目中替换为真实的摄像头回调)simulateCameraInput()}fun stop() {scope.cancel()faceChannel.close()}// 接收人脸数据的入口fun onFaceDetected(faceData: FaceData) {// 非阻塞方式发送到 Channelval result = faceChannel.trySend(faceData)if (!result.isSuccess) {println("Channel 已满,丢弃数据: ${faceData.angle}")}}// 主要的数据处理逻辑private suspend fun processFaceData() {var processedCount = 0var skippedCount = 0faceChannel.consumeEach { faceData ->processedCount++// 1. 更新角度缓冲区用于平滑计算updateAngleBuffer(faceData.angle)// 2. 计算平滑后的角度val smoothedAngle = calculateSmoothedAngle()// 3. 检查角度变化是否超过阈值if (shouldExecuteMovement(smoothedAngle)) {// 4. 执行电机控制motorController.moveToAngle(smoothedAngle)lastExecutedAngle = smoothedAngleprintln("✅ [${processedCount}] 执行移动: ${"%.1f".format(smoothedAngle)}度 " +"(原始: ${"%.1f".format(faceData.angle)}度)")} else {skippedCount++val diff = lastExecutedAngle?.let { abs(smoothedAngle - it) } ?: 0fprintln("⏭️ [${processedCount}] 跳过移动: 变化 ${"%.1f".format(diff)}度 < ${ANGLE_THRESHOLD}度阈值")}// 定期打印统计信息if (processedCount % 20 == 0) {println("📊 统计: 处理 $processedCount 帧, 跳过 $skippedCount 帧")}}}private fun updateAngleBuffer(newAngle: Float) {angleBuffer.addLast(newAngle)// 保持缓冲区大小为3(可根据需要调整)if (angleBuffer.size > 3) {angleBuffer.removeFirst()}}private fun calculateSmoothedAngle(): Float {return if (angleBuffer.isNotEmpty()) {// 简单移动平均angleBuffer.average().toFloat()} else {0f}}private fun shouldExecuteMovement(newAngle: Float): Boolean {return when {lastExecutedAngle == null -> true  // 第一次执行abs(newAngle - lastExecutedAngle!!) >= ANGLE_THRESHOLD -> true  // 变化超过阈值else -> false  // 变化太小,跳过}}// 模拟摄像头输入(测试用)private fun simulateCameraInput() {scope.launch {var frameCount = 0var currentAngle = 0f// 模拟人脸角度变化while (isActive) {frameCount++// 模拟角度变化:大部分时间小变化,偶尔大变化val angleChange = when {frameCount % 30 == 0 -> 5f + (Math.random() * 10).toFloat()  // 偶尔大跳跃frameCount % 10 == 0 -> -3f + (Math.random() * 6).toFloat()  // 中等变化else -> -1f + (Math.random() * 2).toFloat()  // 小变化}currentAngle += angleChange// 限制角度范围currentAngle = currentAngle.coerceIn(-45f, 45f)val faceData = FaceData(angle = currentAngle,position = FacePosition(x = 0.5f,y = 0.5f,width = 0.3f,height = 0.4f))onFaceDetected(faceData)delay(33) // 模拟30fps}}}
}// 电机控制器(模拟)
class MotorController {private var currentAngle: Float = 0ffun moveToAngle(targetAngle: Float) {val moveTime = abs(targetAngle - currentAngle) * 10 // 模拟移动时间currentAngle = targetAngle// 在实际项目中这里会调用硬件API控制电机println("    🎯 电机移动到: ${"%.1f".format(targetAngle)}度 (耗时: ${"%.1f".format(moveTime)}ms)")}
}// 使用示例
fun main() = runBlocking {println("🚀 启动人脸跟随系统...")println("📝 配置: Channel容量=5, 角度阈值=${FaceFollower().ANGLE_THRESHOLD}度")println("---")val faceFollower = FaceFollower()faceFollower.start()// 运行10秒后停止delay(10000)faceFollower.stop()println("---")println("🛑 系统已停止")
}

关键特性说明

1. Channel 配置

private val faceChannel = Channel<FaceData>(capacity = 5,  // 缓存5条数据onBufferOverflow = BufferOverflow.DROP_OLDEST  // 超出时丢弃最旧数据
)

2. 数据处理流程

  1. 接收数据 → onFaceDetected() 非阻塞发送到 Channel

  2. 平滑计算 → 使用3帧数据计算移动平均

  3. 阈值过滤 → 只有角度变化≥2度才执行移动

  4. 电机控制 → 调用电机控制器

3. 运行效果示例

🚀 启动人脸跟随系统...
📝 配置: Channel容量=5, 角度阈值=2.0度
---
✅ [1] 执行移动: 0.5度 (原始: 0.5度)
🎯 电机移动到: 0.5度 (耗时: 5.0ms)
⏭️ [2] 跳过移动: 变化 0.3度 < 2.0度阈值
⏭️ [3] 跳过移动: 变化 0.8度 < 2.0度阈值
✅ [4] 执行移动: 3.2度 (原始: 3.2度)
🎯 电机移动到: 3.2度 (耗时: 27.0ms)
✅ [30] 执行移动: 15.8度 (原始: 15.8度)
🎯 电机移动到: 15.8度 (耗时: 126.0ms)
📊 统计: 处理 20 帧, 跳过 15 帧

实际项目集成

如果要在实际项目中使用,只需要:

  1. 替换数据输入
    // 在真实的摄像头回调中调用
    fun onCameraFrame(cameraFrame: Frame) {val faceData = faceDetector.detect(cameraFrame)faceFollower.onFaceDetected(faceData)
    }
  2. 调整参数

    // 根据实际需求调整
    private const val ANGLE_THRESHOLD = 2.0f  // 灵敏度
    private val channelCapacity = 5          // 缓存大小
    private val bufferSize = 3               // 平滑窗口大小

优势总结

这个 Channel 实现的优势:

  • ✅ 内存控制 - 固定5条缓存,不会无限增长

  • ✅ 实时性 - 总是处理较新的数据

  • ✅ 平滑移动 - 多帧平均减少抖动

  • ✅ 性能优化 - 过滤小角度变化,减少电机磨损

  • ✅ 简单可靠 - 单消费者模式,逻辑清晰

这个实现完全满足您的需求:缓存几条数据用于平滑移动,超出缓存时自动处理最新数据!

http://www.dtcms.com/a/453482.html

相关文章:

  • 线性表——数组描述
  • 帮客户做网站 没签合同咋办重视网站阵地建设
  • Raven2
  • 面试陷阱:SQL 子查询 vs JOIN 的性能差异
  • 做蛋糕视频的网站非法网站开发
  • 设备管理平台项目部署文档(含自动化脚本)
  • 毕业设计除了网站还可以做啥外贸网站关键词
  • 前端内存泄漏:从原理到实践的全方位解析
  • app手机网站建设黄消防网站模板
  • 成品网站建设咨询怀化最新消息今天
  • 第八章 FPGA 片内 FIFO 读写测试实验
  • 2025年--Lc170--H289. 生命游戏(矩阵)--Java版
  • FC和SFC的原版说明书(扫描的PDF)
  • 网站建设方案文库我们是设计师 网站建设专家
  • 做网站自己买服务器好还是用别人的柳州企业网站建设
  • 震荡市中MACD-KDJ组合的动态参数优化思路
  • 一文了解解耦 Prefilling 与 Decoding(PD 分离)以及典型的 PD 分离方案
  • petri网自学(四)
  • 海思Hi3516CV610/Hi3516CV608开发笔记之环境搭建和固件编译
  • 学生化残差(Studentized Residual):概念、计算与应用
  • 网站建设杭州做rap的网站
  • 华为交换机实战配置案例:从基础接入到核心网络
  • OpenCV(四):视频采集与保存
  • 证券业智能化投研与分布式交易系统架构:全球发展现状、技术创新与未来趋势研究
  • AI Agent竞争进入下半场:模型只是入场券,系统架构决定胜负
  • 图书商城网站开发的目的网页设计实训报告总结1500字
  • 做俄语网站做网站傻瓜软件
  • 兼具本地式与分布式优势、针对大类通用型Web漏洞、插件外部动态化导入的轻量级主被动扫描器
  • 第4章 文件管理
  • JavaScript初识及基本语法讲解