filters icon indicating copy to clipboard operation
filters copied to clipboard

`set_capacity` not defined for `realtime_circular_buffer`?

Open roncapat opened this issue 1 year ago • 12 comments
trafficstars

Am I right or set_capacity() member is not implemented? I searched in the whole repo, only one result for this symbol, and it's the line at the link below.

https://github.com/ros/filters/blob/e33463aebca78471c0e52f45eaf31585b781af61/include/filters/realtime_circular_buffer.hpp#L83C8-L83C20

In this case, would you like me to provide a PR?

roncapat avatar Mar 25 '24 09:03 roncapat

Yep, doesn't seem to be implemented. A PR would be appreciated, thanks!

jonbinney avatar Mar 25 '24 21:03 jonbinney

Will do :)

roncapat avatar Mar 25 '24 21:03 roncapat

@jonbinney The seems to be a bit of ambiguity about the original meaning of this function.

In Boost:

Given the signature of the missing set_capacity() implementation here, I believe that it should call resize() internally. Since we would not break any API here, we could think about renaming this method to avoid any confusion.

roncapat avatar Mar 26 '24 13:03 roncapat

From the boost documentation of resize(): "If the new size is greater than the current size, copies of item will be inserted at the back of the of the circular_buffer in order to achieve the desired size."

That doesn't sound like the behavior I'd usually want. What use case do you have in mind? I wouldn't worry too much about keeping the signature of set_capacity() the same here.

jonbinney avatar Mar 26 '24 19:03 jonbinney

@jonbinney IMHO new items should be somehow initialized, so the user has to provide a value.

My use case is that I implemented an average filter on top of this buffer, by averaging latest N samples of an incoming measure. I would not change size of the buffer at runtime, but it would come handy to change the size of a default-constructed one during for example initialization of a controller - like, on_init()/on_configure() stages.

That doesn't sound like the behavior I'd usually want.

Could you elaborate a bit? Let's discuss what is your ideal behaviour, maybe we can implement it inside this class here.

roncapat avatar Mar 27 '24 09:03 roncapat

I may misunderstand, but wouldn't your rolling window average be wrong at the start? For example, assume N=10 for your case. You resize() the buffer to a size of 10 , fill it with some default value, and start collecting samples. When you have collected 5 samples so far, how would you know that the other 5 samples aren't yet actual samples? The size would be 10 and the capacity would be 10, so they're effectively storing the same information twice.

If we have a set_capacity() method, in the same case capacity would be 10 and size would be 5, so you'd have more info - you'd know exactly how many valid samples you've got, right?

jonbinney avatar Mar 27 '24 23:03 jonbinney

The point is: let's say we construct a RealtimeCircularBuffer(5,0). If we only do set_capacity(10) on the internal buffer later on, the user will not be able to add a sixth element at all.

About the averaging in my use case: in the first few cycles I may be able to accept a transient with "wrong values" (a sort of ramp effect), but truly I use a counter to keep track of the number of real measures obtained up to that moment (i.e. something that grows like 1 2 3 4 5 5 5 5 5 5...)

roncapat avatar Mar 28 '24 04:03 roncapat

You're right - I misunderstood how push_back() worked! Also the constructor of RealtimeCircular buffer already requires initializing the elements, so having the resize method do the same makes sense. I agree now that having a resize() method makes more sense.

jonbinney avatar Mar 28 '24 23:03 jonbinney

There is still a thing to notice:

  • since we expose both push_front() and push_back() the user is free to use the circular buffer in both directions - i.e. when pushing back an element, the buffer might drop the front one (if it's already full), when pushing front an element, it might drop the back one instead.

Given this, the problem here is to choose between resize and rresize (notice the double R). Because people might want to reside extending the left-most side, or the right-most one, depending on the direction they are using the buffer (front->back or back->front).

@jonbinney do you have any opinion or more background on this?

roncapat avatar Apr 03 '24 09:04 roncapat

It looks like both the mean and median filters in this repo use push_back(); I'd guess that is the most common case.

jonbinney avatar Apr 03 '24 13:04 jonbinney

I would propose to drop RealtimeCircularBuffer::push_front() and expose boost::circular_buffer::resize(). This way users are forced to use the circular buffer in one direction only.

But I can imagine breaking some possible packages in the wild doing so...

roncapat avatar Apr 03 '24 13:04 roncapat

I should have asked this earlier, but at some point is it worth encouraging people to just use boost::circular_buffer directly instead of exposing more and more of its member functions in RealtimCircularBuffer?

jonbinney avatar Apr 05 '24 17:04 jonbinney