我正在尝试以编程方式生成Android振动模式,其中“微脉冲”打开和关闭,以控制振动对最终用户的强烈感受。这是我在一些类似主题中推荐的解决方案,API的问题是没有提供控制振动强度的接口(因为硬件的功能如我所知)。
然而,生成这些模式的算法似乎只是暗示,但没有发布实际的算法。
我想做的是,如果输入强度介于0.0f和1.0f之间,则按照以下模式生成数组:
(zero intensity)
[20,0]
[9,1,9,1]
...
[3,1,3,1,3,1,3,1,3,1]
[2,1,2,1,2,1,2,1,2,1,2,1,2]
(half intensity)
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
[1,2,1,2,1,2,1,2,1,2,1,2,1,1]
[1,3,1,3,1,3,1,3,1,3]
...
[1,9,1,9]
(full intensity)
[0,20]
编写此类算法的任何帮助(或建议更好的策略来实现相同的目标)?
编辑:我已经添加了100个声望的奖金:)
答案 0 :(得分:11)
在看了一会儿的问题,而不是在数学上不是很有天赋之后,我想出了一个过于简化的算法(与我在Dithermaster指向那个方向后发现的一些PWM公式相比)。我做的几个假设首先是短脉冲宽度始终为1,而长脉冲宽度是1和振动持续时间之间的整数。我还假设长脉冲宽度是振动强度的线性函数。特别是,后一种假设并不准确。我猜这个函数应该更像是一个分贝计算(振动的“强度”类似于声音的“响度”)。
发布我的简化解决方案,以防其他人在此处结束。这对于我正在使用它的应用程序足够接近,但我仍然想要更好的东西。如果有人发布替代答案,我会测试并接受它,如果它更好。
public long[] genVibratorPattern( float intensity, long duration )
{
float dutyCycle = Math.abs( ( intensity * 2.0f ) - 1.0f );
long hWidth = (long) ( dutyCycle * ( duration - 1 ) ) + 1;
long lWidth = dutyCycle == 1.0f ? 0 : 1;
int pulseCount = (int) ( 2.0f * ( (float) duration / (float) ( hWidth + lWidth ) ) );
long[] pattern = new long[ pulseCount ];
for( int i = 0; i < pulseCount; i++ )
{
pattern[i] = intensity < 0.5f ? ( i % 2 == 0 ? hWidth : lWidth ) : ( i % 2 == 0 ? lWidth : hWidth );
}
return pattern;
}
答案 1 :(得分:7)
假设总持续时间为n
,而不是20.您的函数执行两项强度i
更改:
k(i)
,周期数会发生变化。它从k(0) = 1
开始,在k(0.5) = n/2
处达到峰值,然后降至k(1) = 1
。r(i)
发生变化。如果我们有一个周期[a, b]
,时间为a
且时间为b
,则为r(i)*a = b
。按照您的示例,我们有r(0) = 0
,r(0.5) = 1
,然后是渐近r(1) = infinity
有许多功能可以匹配k(i)
和r(i)
,但让我们坚持使用简单的功能:
k(i) = (int) (n/2 - (n-2)*|i - 0.5|) r(i) = 1 / (1.000001 - i) - 1
其中|x|
表示x
的绝对值。我还在1
的分母中用1.000001
代替r
,这样我们就不必处理被零除错误。
现在,如果周期需要总和到n
,那么任何一个周期[a, b]
的长度都是n/k(i)
。由于我们也有r(i)*a = b
,因此
a = n/(k*(1+r)) b = r*a
要形成强度为i
的数组,我们只需重复[a, b]
k
次。以下是n = 20
的输出示例:
Intensity: 0.00, Timings: 20.0, 0.0
Intensity: 0.05, Timings: 9.5, 0.5, 9.5, 0.5
Intensity: 0.10, Timings: 6.0, 0.7, 6.0, 0.7, 6.0, 0.7
Intensity: 0.15, Timings: 4.3, 0.7, 4.3, 0.7, 4.3, 0.7, 4.3, 0.7
Intensity: 0.20, Timings: 3.2, 0.8, 3.2, 0.8, 3.2, 0.8, 3.2, 0.8, 3.2, 0.8
Intensity: 0.25, Timings: 2.5, 0.8, 2.5, 0.8, 2.5, 0.8, 2.5, 0.8, 2.5, 0.8, 2.5, 0.8
Intensity: 0.30, Timings: 2.0, 0.9, 2.0, 0.9, 2.0, 0.9, 2.0, 0.9, 2.0, 0.9, 2.0, 0.9, 2.0, 0.9
Intensity: 0.35, Timings: 1.6, 0.9, 1.6, 0.9, 1.6, 0.9, 1.6, 0.9, 1.6, 0.9, 1.6, 0.9, 1.6, 0.9, 1.6, 0.9
Intensity: 0.40, Timings: 1.3, 0.9, 1.3, 0.9, 1.3, 0.9, 1.3, 0.9, 1.3, 0.9, 1.3, 0.9, 1.3, 0.9, 1.3, 0.9, 1.3, 0.9
Intensity: 0.45, Timings: 1.1, 0.9, 1.1, 0.9, 1.1, 0.9, 1.1, 0.9, 1.1, 0.9, 1.1, 0.9, 1.1, 0.9, 1.1, 0.9, 1.1, 0.9, 1.1, 0.9
Intensity: 0.50, Timings: 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0
Intensity: 0.55, Timings: 0.9, 1.1, 0.9, 1.1, 0.9, 1.1, 0.9, 1.1, 0.9, 1.1, 0.9, 1.1, 0.9, 1.1, 0.9, 1.1, 0.9, 1.1, 0.9, 1.1
Intensity: 0.60, Timings: 0.9, 1.3, 0.9, 1.3, 0.9, 1.3, 0.9, 1.3, 0.9, 1.3, 0.9, 1.3, 0.9, 1.3, 0.9, 1.3, 0.9, 1.3
Intensity: 0.65, Timings: 0.9, 1.6, 0.9, 1.6, 0.9, 1.6, 0.9, 1.6, 0.9, 1.6, 0.9, 1.6, 0.9, 1.6, 0.9, 1.6
Intensity: 0.70, Timings: 0.9, 2.0, 0.9, 2.0, 0.9, 2.0, 0.9, 2.0, 0.9, 2.0, 0.9, 2.0, 0.9, 2.0
Intensity: 0.75, Timings: 0.8, 2.5, 0.8, 2.5, 0.8, 2.5, 0.8, 2.5, 0.8, 2.5, 0.8, 2.5
Intensity: 0.80, Timings: 0.8, 3.2, 0.8, 3.2, 0.8, 3.2, 0.8, 3.2, 0.8, 3.2
Intensity: 0.85, Timings: 0.8, 4.2, 0.8, 4.2, 0.8, 4.2, 0.8, 4.2
Intensity: 0.90, Timings: 0.7, 6.0, 0.7, 6.0, 0.7, 6.0
Intensity: 0.95, Timings: 0.5, 9.5, 0.5, 9.5
Intensity: 1.00, Timings: 0.0, 20.0
这是伪劣的代码:
public void Test()
{
foreach (var intensity in Enumerable.Range(0, 20 + 1).Select(i => i/20f))
{
var cycle = new List<float> {a(intensity), b(intensity)};
var timings = Enumerable.Repeat(cycle, k(intensity)).SelectMany(timing => timing).ToArray();
SDebug.WriteLine(
String.Format("Intensity: {0,2:N2}, Timings: ", intensity) +
String.Join(", ", timings.Select(timing => String.Format("{0,2:N1}", timing))));
}
}
private static float r(float i)
{
return 1f/(1.000001f - i) - 1f;
}
private static int k(float i)
{
return Mathf.CeilToInt(10 - 18*Mathf.Abs(i - 0.5f));
}
private static float a(float i)
{
return 20/(k(i)*(1 + r(i)));
}
private static float b(float i)
{
return r(i)*a(i);
}
从这里做的最好的事情是功能r(i)
。但是,如果可以,请先将第一个和最后一个时间放宽为[n, 1]
和[1, n]
,这样可以避免使用渐近线。
答案 2 :(得分:3)
三个想法:
这是一种PWM。随着强度上升,“关闭”变小,“开”变大。
这似乎是一种抖动,就像Ordered Dither一样。但不是2D,而只是1D。
这看起来像数字微分分析仪或Bresenham's line algorithm。
这些想法的某些组合应该可以解决问题。