Search
During this lab, you should learn how to start some additional features of ROS work such as timers and services.
Do you have any questions regarding the last week's lab or the homework?
Slides used in this lab are available here: https://docs.google.com/presentation/d/1CHjWHuDJ3mwQ-lRneVF0dozPUXE1AtJmxgp8sSfzqYA/edit?usp=sharing .
There are several new key aspects/buzzwords to know from today's lab:
ROS has built-in types for Time and Duration and their arithmetics.
Time can be used to get the current time of your ROS system.
time_now = self.get_clock().now() # current time with int64 nanoseconds seconds = time_now.nanoseconds * 1e-9 # time in floating point format
Duration can be created, e.g., equal to a certain number of seconds and then used, e.g., to wait in your code for such a duration.
d = rclpy.duration.Duration(seconds=42.1) self.get_clock().sleep(d)
The result of subtracting two ROS Time instances is also an instance of ROS Duration.
time_start = self.get_clock().now() d = rclpy.duration.Duration(seconds=3) self.get_clock().sleep_for(d) diff = self.get_clock().now() - time_start
Rate is a very convenient way to enforce approximately constant frequency of, e.g., iterating inside a loop. The rate.sleep() will try to keep the given frequency independent of the duration of other operations during the loop.
rate.sleep()
rate = self.create_rate(10) # create rate with 10 Hz frequency while rclpy.ok(): # do some calculation rate.sleep()
Timer object can be used to periodically call some function, e.g., to calculate something and publish it. In most cases, the timer can be used instead of a custom multithreading of your application.
def my_callback(event): # calculate something to msg publisher.publish(msg) self.timer = self.create_timer(2.0, my_callback) # calls function (callback) my_callback every two seconds
Services are similar to topics, but they are used for synchronous communication with request + response model. They are useful for not so frequent messaging and in systems without communication dropouts.
In terminal you can use commands:
ros2 interface list
ros2 interface show
ros2 interface package
ros2 service list
ros2 service info <service_name>
ros2 service call <service_name> <service_type> <request data>
In python code you can create your own service definitions or you can use existing ones. The services, similarly to topics, have Service server part (similar to topic subscriber) and Service client part (similar to topic publisher). The service client sends service requests (e.g., for SetBool service it is SetBool_Request) and the service server accepts the requests, process them using some callback and generates a response (e.g., SetBool_Response), which is sent back to service client.
SetBool
SetBool_Request
SetBool_Response
The service server part of code would look like:
def activate_cb(request, response): # react to the service request of type SetBool return response # response of type SetBool s = self.create_service('/activate', SetBool, activate_cb)
The service client code can look like:
service_client = self.create_client('/activate', SetBool) # creates the service client while not service_client.wait_for_service(timeout_sec=1.0): # waits for the service to be alive pass req = std_srvs.srv.SetBool_Request() # creates the service request req.data = True # fill the request future = service_client.call_async(req) # call the service and get the response rclpy.spin_until_future_complete(self, future) response = future.result()
XML files that automatize the start-up of nodes. The launch files enable functionalities such as:
The xml file can include tag elements:
<launch>
<node>
<arg>
<include>
<param>
<set_param>
<group>
You can pass parameters to ROS nodes (and thus change the node behaviour) in different ways:
ros2 run <package> <node> –ros-args -p param1:=value1 -p param2:=value2
<param name=“arg1” value=“value1”/>
<node …> </node>
<param file=“$(find-pkg-share my_package)/config/my_params.yaml” />
Once you have your python node you can load your private/global params using:
self.declare_parameter("is_cool", False) is_cool = self.get_parameter("is_cool").get_parameter_value().bool_value
TF is a ROS library for working with transformations between frames of reference. TF allows you to get, e.g., a position of a robot from any time by linear interpolation between measurements of the position. Moreover, it allows to create even complicated chains of transformations and expressing the position in any of the reference frames in the chain. For example, it allows you to get position in GPS coordinate frame of some object detected in camera measurement.
Once you run the turtlebot simulation try to:
ros2 topic echo /tf
rqt
ros2 run rqt_tf_tree rqt_tf_tree
ros2 run tf2_ros tf_echo base_link base_footprint
ros2 run tf2_ros tf_monitor
Library ros_numpy includes tools for a convenient conversion of ROS messages to and from numpy arrays. The library contains two main functions numpify and msgify:
ros_numpy
numpify
msgify
arr = numpify(msg, ...) # tries to get a numpy object from a ROS message msg = msgify(MessageType, arr, ...) # tries to convert a numpy object to a ROS message
Currently, the supported type of messages are OccupancyGrid, Vector3, Point, Quaternion, Transform, Pose from geometry_msgs package, and PointCloud2 and Image from sensor_msgs package. Notice, that you can also directly use the internal functions for particular types such as ros_numpy.geometry.point_to_numpy to get numpy vector from geometry_msgs/Point.
geometry_msgs
sensor_msgs
ros_numpy.geometry.point_to_numpy
Implement a simple service client node and service server node in Python ROS tutorial. Both can be placed inside the aro_reactive_control/scripts of the student-packages.
server.py
client.py
Follow the assignment of the homework HW2.