完整的 Channel 实现(缓存5条数据 + 2度过滤 + 平滑移动)
Kotlin
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.consumeEach
import kotlin.math.abs
data 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? = null
private const val ANGLE_THRESHOLD = 2.0f
private val angleBuffer = ArrayDeque<Float>() // 用于平滑计算
// 电机控制接口
private val motorController = MotorController()
fun start() {
// 启动消费者协程
scope.launch {
processFaceData()
}
// 模拟摄像头数据输入(实际项目中替换为真实的摄像头回调)
simulateCameraInput()
}
fun stop() {
scope.cancel()
faceChannel.close()
}
// 接收人脸数据的入口
fun onFaceDetected(faceData: FaceData) {
// 非阻塞方式发送到 Channel
val result = faceChannel.trySend(faceData)
if (!result.isSuccess) {
println("Channel 已满,丢弃数据: ${faceData.angle}")
}
}
// 主要的数据处理逻辑
private suspend fun processFaceData() {
var processedCount = 0
var skippedCount = 0
faceChannel.consumeEach { faceData ->
processedCount++
// 1. 更新角度缓冲区用于平滑计算
updateAngleBuffer(faceData.angle)
// 2. 计算平滑后的角度
val smoothedAngle = calculateSmoothedAngle()
// 3. 检查角度变化是否超过阈值
if (shouldExecuteMovement(smoothedAngle)) {
// 4. 执行电机控制
motorController.moveToAngle(smoothedAngle)
lastExecutedAngle = smoothedAngle
println("✅ [${processedCount}] 执行移动: ${"%.1f".format(smoothedAngle)}度 " +
"(原始: ${"%.1f".format(faceData.angle)}度)")
} else {
skippedCount++
val diff = lastExecutedAngle?.let { abs(smoothedAngle - it) } ?: 0f
println("⏭️ [${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 = 0
var 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 = 0f
fun 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 配置
Kotlin
private val faceChannel = Channel<FaceData>(
capacity = 5, // 缓存5条数据
onBufferOverflow = BufferOverflow.DROP_OLDEST // 超出时丢弃最旧数据
)
2. 数据处理流程
-
接收数据 →
onFaceDetected()
非阻塞发送到 Channel -
平滑计算 → 使用3帧数据计算移动平均
-
阈值过滤 → 只有角度变化≥2度才执行移动
-
电机控制 → 调用电机控制器
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 帧
实际项目集成
如果要在实际项目中使用,只需要:
-
替换数据输入 :
Kotlin// 在真实的摄像头回调中调用 fun onCameraFrame(cameraFrame: Frame) { val faceData = faceDetector.detect(cameraFrame) faceFollower.onFaceDetected(faceData) }
-
调整参数:
Kotlin// 根据实际需求调整 private const val ANGLE_THRESHOLD = 2.0f // 灵敏度 private val channelCapacity = 5 // 缓存大小 private val bufferSize = 3 // 平滑窗口大小
优势总结
这个 Channel 实现的优势:
-
✅ 内存控制 - 固定5条缓存,不会无限增长
-
✅ 实时性 - 总是处理较新的数据
-
✅ 平滑移动 - 多帧平均减少抖动
-
✅ 性能优化 - 过滤小角度变化,减少电机磨损
-
✅ 简单可靠 - 单消费者模式,逻辑清晰
这个实现完全满足您的需求:缓存几条数据用于平滑移动,超出缓存时自动处理最新数据!