如何在给定时间内创建从0到1再到0的脉动值?

6
我正在处理一些代码,其中有一个Time对象和一个成员time Time.time 给出了自应用程序启动以来的时间(浮点值)。 现在我想创建一个在0和1之间脉冲的值,然后再从1到0,这将继续执行,直到应用停止。
我想使用sin()函数,但不知道要传递什么参数才能创建这个脉冲值。
如何创建此脉冲值?
顺颂商祺, 波卢克斯

3
你在使用C++、C#编程,还是不知道? - Mark Byers
2
为什么不将你的时间以秒为单位模2,并使用那个时间呢? - Ben Burnett
非常简单,您将大量正弦波相加,直到得到一个很好的方波近似。您给它适当的频率和振幅,并沿y轴稍微偏移一点,就可以了。 :) - stakx - no longer contributing
你不能简单地保存上一次将脉冲从0翻转到1或反之的时间,然后当自那时起已经过去了“period”/2的时间时,再次翻转脉冲,这听起来比正弦函数更简单快捷,唯一的问题是你的周期需要是你时间粒度的倍数。(每个tic的单位值。) - ANeves
像一个可编程的触发器吗? - Phil Gan
5个回答

22

你提到使用sin()函数,所以我猜你想让它在0和1之间持续脉动。

类似以下代码可以实现:

float pulse(float time) {
    const float pi = 3.14;
    const float frequency = 10; // Frequency in Hz
    return 0.5*(1+sin(2 * pi * frequency * time));
}

1/frequency = 0.1 second 是周期,即 1 的时间间隔。


比我的版本干净多了 - 非常好! - corsiKa
Kleist,所以频率将是帧速率? - pollux
哦,那么它会是(1/频率)* Time.time *(TWO_PI)吗? - pollux
它为什么慢?与什么相比较? - Kleist
我修改了我的答案,将 Sin 函数与另一个运行速度大约快两倍的公式进行比较。 - BlueMonkMN

4
如何使用 x = 1 - x? 如果你想让它基于时间,可以使用 Timer % 2。
哦,你也想要值在0到1之间的吗? 如何使用 Math.Abs(100 - (Timer % 200)) / 100 其中 Timer 是类似于 DateTime.Now.TimeOfDay.TotalMilliseconds 的东西。
编辑: 我的测试表明,这种方法比 Sin 方法快两倍以上。对于100万次迭代,Sin 方法需要0.048秒,而 Abs 方法只需要约0.023秒。当然,你会得到不同的波形。Sin 产生正弦波,而 Abs 产生三角波。
static void Main(string[] args)
{
   System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
   sw.Start();
   const int count = 1000000;
   float[] results = new float[count];
   for (int i = 0; i < count; i++)
   {
      results[i] = AbsPulse(i/1000000F);
      //results[i] = SinPulse(i / 1000000F);
   }
   sw.Stop();
   Console.WriteLine("Time Elapsed: {0} seconds", sw.Elapsed.TotalSeconds);
   char[,] graph = new char[80, 20];
   for (int y = 0; y <= graph.GetUpperBound(1); y++)
      for (int x = 0; x <= graph.GetUpperBound(0); x++)
         graph[x, y] = ' ';
   for (int x = 0; x < count; x++)
   {
      int col = x * 80 / count;
      graph[col, (int)(results[x] * graph.GetUpperBound(1))] = 'o';
   }
   for (int y = 0; y <= graph.GetUpperBound(1); y++)
   {
      for (int x = 0; x < graph.GetUpperBound(0); x++)
         Console.Write(graph[x, y]);
      Console.WriteLine();
   }
}

static float AbsPulse(float time)
{
   const int frequency = 10; // Frequency in Hz
   const int resolution = 1000; // How many steps are there between 0 and 1
   return Math.Abs(resolution - ((int)(time * frequency * 2 * resolution) % (resolution * 2))) / (float)resolution;
}

static float SinPulse(float time)
{
   const float pi = 3.14F;
   const float frequency = 10; // Frequency in Hz
   return 0.5F * (1 + (float)Math.Sin(2 * pi * frequency * time));
}

3
一个正弦函数应该是理想的,但你需要调整周期和比例。正弦函数产生的结果在-1到1之间,但你想要在0到1之间。为了正确缩放,你需要使用`(sin(x)+1)/2`。正弦函数从零开始,在pi/2处达到1,在pi处再次为零,在3*pi/2处为-1,并在2*pi处回到零。经过缩放,第一个零将发生在3*pi/2处,之后的第一个最大值将出现在5/2*pi处。因此,上述公式中的x是`(2*time + 3) * pi/2`。将所有内容合并在一起:`(sin((2*time.time + 3) * pi/2) + 1) / 2`。

0
你想让它脉冲的频率是多少?
假设你想在10秒内从0到1。
float pulseValueForTime(int sec) {
    int pulsePoint = sec % 10;
    float pulsePercent = (float)pulsePoint / (float)10;
    float pulseInTermsOfPI = (pulsePercent * 2 * PI) - PI;
    float sinVal = MagicalSinFunction(pulseInTermsOfPI); // what framework you use to compute sin is up to you... I'm sure you can google that!
    return (sinVal + 1) / 2; // sin is between 1 and -1, translate to between 0 and 1
}

glowcoder,谢谢。我现在在做类似的事情,但它不能按设定的时间间隔工作。我希望能够每隔X秒获得一个脉冲。我认为我需要上面提到的Kleist示例。顺便说一下,你的代码中没有使用pulsePoint。 - pollux
它的想法是你会执行 pulseValueForTime(getTimeInSec());,并且 pulsePoint 应该用于第一个浮点数(你会注意到 pulse 从未被声明)。我会修复这个错误,尽管如你已经注意到的那样,Kliest 的版本在概念上更加清晰。 - corsiKa
你可以使用pulsePoint / 10F代替(float)pulsePoint / (float)10:int/float会产生float,而10f是float - 参见http://msdn.microsoft.com/en-us/library/b1e65aza%28VS.71%29.aspx。 - ANeves
我相信它会的。我认为明确地放置浮点转换的可读性是一个优点。我相信任何编译器都能以合理的方式处理其余部分。 - corsiKa

0

看一下缓动函数。它们以各种方式执行此类操作 - 线性、多项式、指数、正弦等。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接