Pulse
Pulse 是一种在数据集不连续或需要额外插值时创建平滑、基于值动画的强大工具。尤其适用于处理在 实时 提供的值,如陀螺仪、强制触摸或手势。它基于 PID 控制器
- 控制回路反馈机制的概念。
以下动画示例展示了如何将 强制触摸
读取转换为平滑的实时动画。
通过提供合适的配置,您可以控制目标值达到的速度或超调的显著程度。
设置
如果您只需要 核心实现
(PID 控制器的功能),您只需要使用一个单一的、可分离的类 Pulse.swift
。尽管这个存储库附带了一个额外的工具,它可以简化关键的配置部分 - 调整
。
Cocoa Pods
source 'https://github.com/CocoaPods/Specs.git'
pod 'PulseController', '~> 0.1.4'
使用方法
-
导入框架(如果通过软件包管理器安装)
import Pulse
-
添加对应当前值的变量以及
Pulse
控制器的引用private var currentPosition: CGFloat = 0 private var pulseController: Pulse?
-
创建配置(检查
调优
部分以获取更多信息)并初始化Pulse
控制器override func viewDidLoad() { super.viewDidLoad() // Configuration // Note: Providing configuration might be skipped if you want to tunne your controller let configuration = Pulse.Configuration(minimumValueStep: 0.05, Kp: 1.0, Ki: 0.1, Kd: 0.9) // Init PID Controller pulseController = Pulse(configuration: configuration, measureClosure: { [weak self] () -> CGFloat in guard let `self` = self else { return } // This closure returns information about current value return self.currentValue; }, outputClosure: { [weak self] (output) in guard let `self` = self else { return } // Update stored reference to the updated value self.currentValue = output // Here call an update on UI that relies on this value }) }
-
每次它发生变化时设置
setPoint
func setPointChanged(_ newSetPoint: CGFloat) { pidController.setPoint = newSetPoint }
奇迹发生了!每次设置setPoint
时,Pulse
都会逐渐更新outputClosure
中的currentValue
(您应该在其中更新UI)
调优
通过改变以下三个因素的组合
- P(比例)
- I(积分)
- D(微分)
您可以使用仪表板控制函数如何达到期望值或过冲度的大小。每个样本实现都附带一个接口,允许您控制所有这些并查看结果。最好的方法是只调整滚动条,找到最适合的配置。
还有一个附加因素minimumStep
,它说明了测量值
和目标值
之间的距离,以便PID控制器能够达到静止状态。例如,如果您想在非常小的范围<-1, 1>
内设置setPoint
,您可能希望m = 0.005
,而对于范围<-100, 100>
,使用m = 0.5
将得出更好的结果。
调优工具
此存储库附带一个用于调优控制器的实用工具。您应遵循使用说明
部分中描述的步骤,在创建Pulse
时跳过提供配置
。目前它将以默认值初始化。
override func viewDidLoad() {
super.viewDidLoad()
// Init PID Controller
pulseController = Pulse(measureClosure: { [weak self] () -> CGFloat in
guard let `self` = self else { return }
return self.currentValue;
}, outputClosure: { [weak self] (output) in
guard let `self` = self else { return }
self.currentValue = output
})
}
准备就绪后,找到屏幕上显示调优视图
的位置。也许是一个摇晃事件?只需调用Pulse
对象的showTunningView
,并提供有关可能设置为setPoint
的预期值范围
的信息。例如,如果您使用Pulse
来动画化对象旋转,则从0
到360
的值可能是个不错的选择。
override func motionBegan(_ motion: UIEventSubtype, with event: UIEvent?) {
if(motion == .motionShake) {
pulseController?.showTunningView(minimumValue: 0, maximumValue: 360)
}
}
现在您摇晃手机后,应该看到一个漂亮的调优视图
,这样您就可以检查哪些P-I-D
值适用于您。
如果您会发现一个完全实现了解决方案!
PID控制器
本仓库基于PID控制器(比例-积分-微分控制器)的概念——简单高效的反馈控制系统,广泛应用于工业应用。它不断计算误差,即测量值与期望值之间的差异,并根据三个因素的组合应用反向力:P(比例)、I(积分)、D(微分)。随着时间的推移,误差率降低到0
。
有许多优秀的资源解释了这个概念是如何工作的,以及值是如何计算的,如果你对此感兴趣,可以查看
待办事项
项目处于开发初期,有很多需要改进的地方
- 适当的测试覆盖
- 响应方向变化(
调整视图
) - 解决
调整视图
中的内存泄漏 - 限制图上绘制的值(
调整视图
) - 如果有已经在屏幕上的
调整视图
,防止其再次显示
其他平台
尽管这个仓库主要对用户有帮助,但核心实现不使用任何平台特定的依赖项,所以你可以自由地查看代码并将其移植到不同的平台。
致谢
非常感谢我的朋友分享PID控制器的概念并激发了这个工作的灵感
未来
目标是创建一个可以将实时提供的嘈杂数据转化为可用于平滑视觉表示的工具。目前的解决方案完全基于PID控制器
,因为这目前是最佳方案。尽管项目可能会转向不同的概念,如果它能提供更简单的方法来达到相同的结果。目标是拥有非常简单的界面,并需要最少的额外配置/调整。
许可
代码库采用MIT许可证发布。