如何保持时间而不产生累积误差?
鉴于您需要以秒为单位的时间定点表示,并且刻度之间的时间无法以该定点格式精确表示,如何在简单的嵌入式系统中跟踪时间?在这些情况下如何避免累积错误。
这个问题是对这篇文章的回应 在斜杠点上。
0.1秒不能简洁地表示为二进制定点数,就像1/3不能简洁地表示为十进制定点数一样。任何二进制定点表示都有一个小误差。例如,如果该点后有 8 个二进制位(即使用按 256 缩放的整数值),则 0.1 乘以 256 为 25.6,将四舍五入为 25 或 26,从而导致约 -2.3% 的误差或分别+1.6%。在该点后添加更多二进制位可以减小此错误的规模,但无法消除它。
随着重复加法,误差逐渐累积。
如何避免这种情况?
How can you keep track of time in a simple embedded system, given that you need a fixed-point representation of the time in seconds, and that your time between ticks is not precisely expressable in that fixed-point format? How do you avoid cumulative errors in those circumstances.
This question is a reaction to this article on slashdot.
0.1 seconds cannot be neatly expressed as a binary fixed-point number, just as 1/3 cannot be neatly expressed as a decimal fixed-point number. Any binary fixed-point representation has a small error. For example, if there are 8 binary bits after the point (ie using an integer value scaled by 256), 0.1 times 256 is 25.6, which will be rounded to either 25 or 26, resulting in an error in the order of -2.3% or +1.6% respectively. Adding more binary bits after the point reduces the scale of this error, but cannot eliminate it.
With repeated addition, the error gradually accumulates.
How can this be avoided?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
我过去见过实现的东西:增量值不能以定点格式精确表示,但可以表示为分数。 (这类似于“跟踪错误值”解决方案。)
实际上,在这种情况下,问题略有不同,但概念上相似 - 问题不是定点表示本身,而是从时钟源不是完美的倍数。我们有一个时钟频率为 32,768 Hz(对于基于手表晶体的低功耗定时器来说很常见)。我们想要一个毫秒计时器。
毫秒计时器应每 32.768 个硬件滴答声递增一次。第一个近似值是每 33 个硬件时钟周期递增一次,名义误差为 0.7%。但是,请注意 0.768 是 768/1000 或 96/125,您可以这样做:
。毫秒计数器上会出现一些短期“抖动”(32 与 33 硬件滴答声),但长期平均值将为 32.768 硬件滴答声。
Something I've seen implemented in the past: the increment value can't be expressed precisely in the fixed-point format, but it can be expressed as a fraction. (This is similar to the "keep track of an error value" solution.)
Actually in this case the problem was slightly different, but conceptually similar—the problem wasn't a fixed-point representation as such, but deriving a timer from a clock source that wasn't a perfect multiple. We had a hardware clock that ticks at 32,768 Hz (common for a watch crystal based low-power timer). We wanted a millisecond timer from it.
The millisecond timer should increment every 32.768 hardware ticks. The first approximation is to increment every 33 hardware ticks, for a nominal 0.7% error. But, noting that 0.768 is 768/1000, or 96/125, you can do this:
There will be some short term "jitter" on the millisecond counter (32 vs 33 hardware ticks) but the long-term average will be 32.768 hardware ticks.
一种方法是不尝试通过重复添加这个 0.1 秒常数来计算时间,而是保留一个简单的整数时钟节拍计数。该滴答计数可以根据需要转换为以秒为单位的定点时间,通常使用乘法和除法。如果中间表示中有足够的位,则此方法允许任何合理的缩放,并且不会累积错误。
例如,如果当前滴答计数为 1024,我们可以通过将其乘以 256,然后除以 10 来获取当前时间(以定点形式表示,点后 8 位),或者等效地,乘以 128,然后除以 5不管怎样,都会有一个误差(除法中的余数),但误差是有限的,因为余数总是小于 5。不存在累积误差。
One approach is not to try to compute the time by repeated addition of this 0.1 seconds constant, but to keep a simple integer clock-tick count. This tick count can be converted to a fixed-point time in seconds as needed, usually using a multiplication followed by a division. Given sufficient bits in the intermediate representations, this approach allows for any rational scaling, and doesn't accumulate errors.
For example, if the current tick count is 1024, we can get the current time (in fixed point with 8 bits after the point) by multiplying that by 256, then dividing by 10 - or equivalently, by multiplying by 128 then dividing by 5. Either way, there is an error (the remainder in the division), but the error is bounded since the remainder is always less than 5. There is no cumulative error.
另一种方法可能在整数乘法和除法被认为成本太高的情况下有用(现在这种情况应该变得非常罕见)。它借鉴了 Bresenhams 画线算法的想法。您将当前时间保留为定点(而不是滴答计数),但也保留了一个误差项。当误差项变得太大时,您可以对时间值进行修正,从而防止误差累积。
在点后 8 位示例中,0.1 秒的表示形式为 25 (256/10),误差项(余数)为 6。在每一步,我们都会向误差累加器添加 6。到目前为止,前两个步骤是...
在第二步中,误差值已溢出 - 超过 10。因此,我们增加时钟并从误差中减去 10。每当误差值达到 10 或更高时,就会发生这种情况。
因此,实际的序列是......
几乎总是存在错误(只有当错误值为零时时钟才是精确正确的),但错误受到一个小常数的限制。时钟值不存在累积误差。
Another approach might be useful in contexts where integer multiplication and division is considered too costly (which should be getting pretty rare these days). It borrows an idea from Bresenhams line drawing algorithm. You keep the current time in fixed point (rather than a tick count), but you also keep an error term. When the error term grows too large, you apply a correction to the time value, thus preventing the error from accumulating.
In the 8-bits-after-the-point example, the representation of 0.1 seconds is 25 (256/10) with an error term (remainder) of 6. At each step, we add 6 to our error accumulator. Based on this so far, the first two steps are...
At the second step, the error value has overflowed - exceeded 10. Therefore, we increment the clock and subtract 10 from the error. This happens every time the error value reaches 10 or higher.
Therefore, the actual sequence is...
There is almost always an error (the clock is precisely correct only when the error value is zero), but the error is bounded by a small constant. There is no cumulative error in the clock value.
纯硬件解决方案是安排硬件时钟滴答运行得非常快 - 足够快以补偿由于重复添加的滴答持续时间值的舍入而造成的累积损失。也就是说,调整硬件时钟节拍速度,以使定点节拍持续时间值精确正确。
仅当时钟仅使用一种定点格式时,此方法才有效。
A hardware-only solution is to arrange for the hardware clock ticks to run very slightly fast - precisely fast enough to compensate for cumulative losses caused by the rounding-down of the repeatedly added tick-duration value. That is, adjust the hardware clock tick speed so that the fixed-point tick-duration value is precisely correct.
This only works if there is only one fixed-point format used for the clock.
为什么不使用 0.1 秒计数器并每十次增加秒计数器,然后将 0.1 计数器回滚到 0?
Why not have 0.1 sec counter and every ten times increment your seconds counter, and wrap the 0.1 counter back to 0?
在这个特定的例子中,我会简单地将时间计数保持在十分之一秒(或毫秒,或任何适合应用程序的时间尺度)。我一直在小型系统或控制系统中这样做。
因此,100 小时的时间值将存储为
3_600_000
刻度 - 零错误(硬件可能引入的错误除外)。这种简单技术带来的问题是:
我还必须使用不“常规”的计时器来玩一些其他技巧。例如,我开发的设备需要每秒进行 300 次数据采集。硬件计时器每毫秒触发一次。无法将毫秒计时器缩放到精确到 1/300 秒单位。因此,我们必须拥有每 3、3 和 4 个刻度执行一次数据采集的逻辑,以防止采集发生漂移。
如果您需要处理硬件时间错误,那么您需要多个时间源并将它们一起使用以保持整体时间同步。根据您的需求,这可以很简单,也可以非常复杂。
In this particular instance, I would have simply kept the time count in tenths of a seconds (or milliseconds, or whatever time scale is appropriate for the application). I do this all the time in small systems or control systems.
So a time value of 100 hours would be stored as
3_600_000
ticks - zero error (other than error that might be introduced by hardware).The problems that are introduced by this simple technique are:
I've also had to play some other tricks this with timers that aren't 'regular'. For example, I worked on a device that required a data acquisition to occur 300 times a second. The hardware timer fired once a millisecond. There's no way to scale the millisecond timer to get exactly 1/300th of a second units. So We had to have logic that would perform the data acquisition on every 3, 3, and 4 ticks to keep the acquisition from drifting.
If you need to deal with hardware time error, then you need more than one time source and use them together to keep the overall time in sync. Depending on your needs this can be simple or pretty complex.