-
Notifications
You must be signed in to change notification settings - Fork 229
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
TimeSource and support for ROS time #210
Conversation
Putting this in review The description of this PR has been updated to reflect a recent change to the code: Following discussion in ros2/rclcpp#516 I understand now that all clocks managed by a time source should have the same value for The Overall I tried to mirror the implementation in rclcpp, where possible. It diverges in the following ways:
|
rclpy/src/rclpy/_rclpy.c
Outdated
{ | ||
PyObject * pyclock; | ||
PyObject * pyenabled; | ||
if (!PyArg_ParseTuple(args, "OO", &pyclock, &pyenabled)) { |
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.
Could use p
(link here) to convert to bool automatically instead of PyObject_IsTrue()
below.
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.
thanks! 6a9d42b
rclpy/src/rclpy/_rclpy.c
Outdated
return NULL; | ||
} | ||
|
||
bool enabled = PyObject_IsTrue(pyenabled); |
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.
Unlikely to see this happen, but I think this needs to check PyErr_Occurred()
in case pyenabled is an object that raises an exception in it's implementation of __bool__()
.
rclpy/rclpy/time_source.py
Outdated
if not clock.clock_type == ClockType.ROS_TIME: | ||
raise ValueError('Only clocks with type ROS_TIME can be attached.') | ||
# "Cast" to ROSClock subclass so ROS time methods can be used. | ||
clock.__class__ = ROSClock |
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.
Not asking to change anything, just sharing because I think it's interesting. This led me discover that __new__
could be used to return a ROSClock
instance when passed a ROS clock type.
Details
class Clock:
def __new__(cls, *, clock_type="SYSTEM_CLOCK"):
self = None
if clock_type == "ROS_CLOCK":
self = super().__new__(ROSClock)
else:
self = super().__new__(cls)
self._clock_type = clock_type
# create self._clock_handle here too
return self
# Can't have an __init__ because it would get passed clock_type argument
# and ROSCLOCK doesn't wan't one
# def __init__(self):
# pass
def all_clocks_have_this(self):
print("all clocks have this")
class ROSClock(Clock):
# Can't have an __init__ because it would get passed clock_type argument
# and ROSCLOCK doesn't wan't one
# def __init__(self):
# pass
def only_ros_clocks_have_this(self):
print("Only ros clocks have this")
c1 = Clock()
c2 = Clock(clock_type="STEADY_CLOCK")
c3 = Clock(clock_type="ROS_CLOCK")
print(repr(c1))
print(repr(c2))
print(repr(c3))
c1.all_clocks_have_this()
c2.all_clocks_have_this()
c3.all_clocks_have_this()
c3.only_ros_clocks_have_this()
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.
I think this approach might be better! Because there's no extra step for users to get access to ROS clock methods if they have created a ROS clock with Clock(clock_type=ClockType.ROS_TIME)
, they will always get a ROSClock back.
I don't immediately see any disadvantages of this (it's ok in this instance that the Clock class 'knows about' the ROSClock class), so I plan to incorporate it, thank you for the suggestion
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.
# When not using sim time, ROS time should look like system time | ||
now = clock.now() | ||
system_now = Clock(clock_type=ClockType.SYSTEM_TIME).now() | ||
assert (system_now.nanoseconds - now.nanoseconds) < 1e9 |
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.
1e9
seems really small. I think this test could be flaky if the OS happens to context switch between lines 73 and 74.
Also, maybe add abs()
in case there is a future bug where now
is a non-zero time greater than system_now
?
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.
I was thinking 1sec is pretty generous already but it can't hurt to increase.
About the abs, I don't think it makes sense here, since we do expect the value of a later call to now() be greater. I'd rather catch that bug than mask it.
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.
Ooops, I got this completely wrong. For some reason I thought 1e9
meant 1ns. My bad.
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.
LGTM with green CI
Interestingly OS X (only) is failing with |
Back in action! |
Connects to #186
Requires ros2/rcl#274
Gives nodes a clock of type ROS_TIME which will reflect the time received on
/clock
topic if the Time Source has had use of ROS time activated. Since parameters aren't available in python yet, there's no support for theuse_sim_time
parameter, but the Time Source can have ROS time activated manually by callingtime_source.ros_time_is_active = True
.Support for clock jump notifiers not targeted for this PR.