-
Notifications
You must be signed in to change notification settings - Fork 914
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow saving timestamp-less messages to Cache (using rospy.Time.now() as their timestamp). #806
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -87,10 +87,13 @@ class Cache(SimpleFilter): | |
|
||
Given a stream of messages, the most recent ``cache_size`` messages | ||
are cached in a ring buffer, from which time intervals of the cache | ||
can then be retrieved by the client. | ||
can then be retrieved by the client. The ``allow_headerless`` | ||
option specifies whether to allow storing headerless messages with | ||
current ROS time instead of timestamp. You should avoid this as | ||
much as you can, since the delays are unpredictable. | ||
""" | ||
|
||
def __init__(self, f, cache_size = 1): | ||
def __init__(self, f, cache_size = 1, allow_headerless = False): | ||
SimpleFilter.__init__(self) | ||
self.connectInput(f) | ||
self.cache_size = cache_size | ||
|
@@ -99,18 +102,26 @@ def __init__(self, f, cache_size = 1): | |
# Array to store msgs times, auxiliary structure to facilitate | ||
# sorted insertion | ||
self.cache_times = [] | ||
# Whether to allow storing headerless messages with current ROS | ||
# time instead of timestamp. | ||
self._allow_headerless = allow_headerless | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it would be ok to make the property "public" so that users can check the property later. So I would propose to remove the leading underscore. Same for the other class. |
||
|
||
def connectInput(self, f): | ||
self.incoming_connection = f.registerCallback(self.add) | ||
|
||
def add(self, msg): | ||
# Cannot use message filters with non-stamped messages | ||
if not hasattr(msg, 'header') or not hasattr(msg.header, 'stamp'): | ||
rospy.logwarn("Cannot use message filters with non-stamped messages") | ||
return | ||
if self._allow_headerless: | ||
stamp = rospy.Time.now() | ||
else: | ||
rospy.logwarn("Cannot use message filters with non-stamped messages. " | ||
"Use the 'allow_headerless' constructor option to " | ||
"auto-assign ROS time to headerless messages.") | ||
return | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would suggest to turn around the condition to error / return early:
Same for the other class. |
||
else: | ||
stamp = msg.header.stamp | ||
|
||
# Insert sorted | ||
stamp = msg.header.stamp | ||
self.cache_times.append(stamp) | ||
self.cache_msgs.append(msg) | ||
|
||
|
@@ -125,19 +136,22 @@ def add(self, msg): | |
def getInterval(self, from_stamp, to_stamp): | ||
"""Query the current cache content between from_stamp to to_stamp.""" | ||
assert from_stamp <= to_stamp | ||
return [m for m in self.cache_msgs | ||
if m.header.stamp >= from_stamp and m.header.stamp <= to_stamp] | ||
|
||
return [msg for (msg, time) in zip(self.cache_msgs, self.cache_times) | ||
if from_stamp <= time <= to_stamp] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not valid logic. The first operator results in a boolean which is then used in the second comparison. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If I understand you objection correctly, I think it is incorrect :) Check http://stackoverflow.com/questions/26502775/pycharm-simplify-chained-comparison There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Omg scary syntax, I guess I just learned something new. Thanks for the reference. Please forget my comment. |
||
|
||
def getElemAfterTime(self, stamp): | ||
"""Return the oldest element after or equal the passed time stamp.""" | ||
newer = [m for m in self.cache_msgs if m.header.stamp >= stamp] | ||
newer = [msg for (msg, time) in zip(self.cache_msgs, self.cache_times) | ||
if time >= stamp] | ||
if not newer: | ||
return None | ||
return newer[0] | ||
|
||
def getElemBeforeTime(self, stamp): | ||
"""Return the newest element before or equal the passed time stamp.""" | ||
older = [m for m in self.cache_msgs if m.header.stamp <= stamp] | ||
older = [msg for (msg, time) in zip(self.cache_msgs, self.cache_times) | ||
if time <= stamp] | ||
if not older: | ||
return None | ||
return older[-1] | ||
|
@@ -153,6 +167,11 @@ def getOldestTime(self): | |
if not self.cache_times: | ||
return None | ||
return self.cache_times[0] | ||
|
||
def getLast(self): | ||
if self.getLastestTime() is None: | ||
return None | ||
return self.getElemAfterTime(self.getLastestTime()) | ||
|
||
|
||
class TimeSynchronizer(SimpleFilter): | ||
|
@@ -209,16 +228,30 @@ class ApproximateTimeSynchronizer(TimeSynchronizer): | |
:class:`ApproximateTimeSynchronizer` synchronizes incoming message filters by the | ||
timestamps contained in their messages' headers. The API is the same as TimeSynchronizer | ||
except for an extra `slop` parameter in the constructor that defines the delay (in seconds) | ||
with which messages can be synchronized | ||
with which messages can be synchronized. The ``allow_headerless`` option specifies whether | ||
to allow storing headerless messages with current ROS time instead of timestamp. You should | ||
avoid this as much as you can, since the delays are unpredictable. | ||
""" | ||
|
||
def __init__(self, fs, queue_size, slop): | ||
def __init__(self, fs, queue_size, slop, allow_headerless=False): | ||
TimeSynchronizer.__init__(self, fs, queue_size) | ||
self.slop = rospy.Duration.from_sec(slop) | ||
self._allow_headerless = allow_headerless | ||
|
||
def add(self, msg, my_queue): | ||
if not hasattr(msg, 'header') or not hasattr(msg.header, 'stamp'): | ||
if self._allow_headerless: | ||
stamp = rospy.Time.now() | ||
else: | ||
rospy.logwarn("Cannot use message filters with non-stamped messages. " | ||
"Use the 'allow_headerless' constructor option to " | ||
"auto-assign ROS time to headerless messages.") | ||
return | ||
else: | ||
stamp = msg.header.stamp | ||
|
||
self.lock.acquire() | ||
my_queue[msg.header.stamp] = msg | ||
my_queue[stamp] = msg | ||
while len(my_queue) > self.queue_size: | ||
del my_queue[min(my_queue)] | ||
for vv in itertools.product(*[list(q.keys()) for q in self.queues]): | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you please remove the spaces around the
=
sign (following PEP 8) (also for the other keyword).