ROS Communication Patterns¶
ROS provides different patterns that can be used to communcate between ROS Nodes:
Topics: are used to send continuous data streams like e.g. sensor data. Data can be published on the topic independent of if there are any subscribers listening. Similarly, Nodes can also subscribe to topics independent of if a publisher exists. Topics allow for many to many connections, meaning that multiple nodes can publish and/or subscribe to the same topic.
Services: are used when it is important that a message is recieved by the recipient and a response on the outcome is wanted. Services should only be used for short procedure calls e. g. changing the state of a system, inverse kinematics calculations or triggering a process.
Actions: are similar as services but are used if the triggered event needs more time and the possebility for recieving updates on the process or being able to cancel the process is wanted. An example could be when sending a navigation command.
Parameters: are not actually a communication pattern but rather is a storage space for variables. It is not designed for high-performance and therefore mostly used for static variables like configuration parameters.
In the Media Gallery of your course page in Canvas are videos, explaining how to use these communication patterns in python. The following sections contain the example code which is explained in the videos.
Topics¶
The video called Simple Topic Implementation is about how to write a simple publisher and subsriber in python. Additional information can also be found on the ROS webpage: creating a ROS package, publisher and subscriber. The following code is an example implementation created and explained in the video:
#! /usr/bin/env python
import rospy
import random
from std_msgs.msg import Int64
from communication_tutorial.msg import rand_num
class simple_publisher():
def __init__(self):
rospy.init_node("simple_publisher", anonymous=False)
self.topic_pub = rospy.Publisher("random_number", rand_num, queue_size=10)
def main(self):
numbers = [1,2,3,4,5,6]
objects = ["Apples","Bananas","Oranges"]
r = rospy.Rate(1)
while not rospy.is_shutdown():
msg = rand_num()
msg.random_number = random.choice(numbers)
msg.object = random.choice(objects)
self.topic_pub.publish(msg)
rospy.loginfo("Published data: "+str(msg))
r.sleep()
if __name__=="__main__":
spub = simple_publisher()
spub.main()
#! /usr/bin/env python
import rospy
import random
from std_msgs.msg import Int64
from communication_tutorial.msg import rand_num
class simple_subscriber():
def __init__(self):
rospy.init_node("simple_subscriber", anonymous=False)
rospy.Subscriber("random_number", rand_num, self.random_number_callback)
def random_number_callback(self, data):
self.number = data
rospy.loginfo("Received random_number: "+str(self.number.random_number))
rospy.loginfo("Received object: "+str(self.number.object))
def main(self):
rospy.spin()
if __name__=="__main__":
spub = simple_subscriber()
spub.main()
Custom Message¶
The video called Create Custom Messages is about how to create a custom message type which can be used by ROS Topics. Additional information can also be found on the ROS webpage: creating a message and service. The following is an example of a message type definition which was created in the tutorial video:
int64 random_number
string object
Services¶
The video called Simple Service Implementation is about how to write a simple service server and client in python. Additional information can also be found on the ROS webpage: service server and client. The following code is an example implementation created and explained in the video:
#! /usr/bin/env python
import rospy
import random
from std_srvs.srv import SetBool, SetBoolResponse
from communication_tutorial.srv import random_sum, random_sumResponse
class simple_server():
def __init__(self):
rospy.init_node("simple_server", anonymous=False)
self.srv = rospy.Service("random_number_service", random_sum, self.random_number_service_callback)
def random_number_service_callback(self, req):
self.req_number = req.random_init
rospy.loginfo("Client request: "+str(self.req_number))
res = random_sumResponse()
numbers = [1,2,3,4,5,6]
rnd = random.choice(numbers)
msg = self.req_number + rnd
rospy.loginfo("Number chosen: "+str(rnd))
rospy.loginfo("Random sum: "+str(msg))
res.success = True
res.random_sum = msg
return res
def main(self):
rospy.spin()
if __name__=="__main__":
service_server = simple_server()
service_server.main()
#! /usr/bin/env python
import rospy
import random
from std_srvs.srv import SetBool, SetBoolResponse
from communication_tutorial.srv import random_sum
class simple_client():
def __init__(self):
rospy.init_node("simple_client", anonymous=False)
self.service_client = rospy.ServiceProxy("random_number_service", random_sum)
rospy.wait_for_service("random_number_service")
def main(self):
numbers = [1,2,3,4,5,6]
r = rospy.Rate(1)
while not rospy.is_shutdown():
msg = random.choice(numbers)
rospy.loginfo("Message sent: "+str(msg))
res = self.service_client(msg)
rospy.loginfo("Response received: "+str(res))
r.sleep()
if __name__=="__main__":
service_server = simple_client()
service_server.main()
Custom Service¶
The video calle Creating Custom Services explains how to create a custom service message type usable by ROS Services. Additional information can also be found on the ROS webpage: creating a message and service. The following is an example of a service message type definition which was created in the tutorial video:
int64 random_init
---
int64 random_sum
bool success
Actions¶
The video called Simple Action Implementation explains how to create a custom action message type usable by ROS Actions. It also shows how to write a simple action server and client in python. Additional information can also be found on the ROS webpage: action server, action client, creating an action message <http://wiki.ros.org/actionlib_tutorials/Tutorials/SimpleActionServer%28GoalCallbackMethod%29>. The following code is an example implementation created and explained in the video:
#! /usr/bin/env python
import rospy
import random
import actionlib
from communication_tutorial.msg import SumAction, SumGoal, SumResult, SumFeedback
class simple_action_server():
def __init__(self):
rospy.init_node("simple_action_server", anonymous=False)
self.aserver = actionlib.SimpleActionServer("random_sum_action", SumAction, execute_cb=self.execute_cb, auto_start = False)
self.aserver.start()
self.result = SumResult()
self.feedback = SumFeedback()
def execute_cb(self, goal):
self.random_init = goal.random_start
self.threshold = goal.goal_number
rospy.loginfo("Goal received: "+str(self.threshold))
rospy.loginfo("Starting number: "+str(self.random_init))
numbers = [1,2,3,4,5,6,7,8,9]
current_value = self.random_init
counter = 0
r = rospy.Rate(1)
while current_value < self.threshold:
if self.aserver.is_preempt_requested():
self.aserver.set_preempted()
rospy.loginfo("Client requested cancelling of the goal")
break
new_number = random.choice(numbers)
current_value += new_number
counter += 1
self.feedback.current_sum = current_value
self.feedback.current_iterations = counter
self.aserver.publish_feedback(self.feedback)
r.sleep()
if not self.aserver.is_preempt_requested():
self.result.final_number = current_value
self.result.final_iterations = counter
self.aserver.set_succeeded(self.result)
def main(self):
rospy.spin()
if __name__=="__main__":
sas = simple_action_server()
sas.main()
#! /usr/bin/env python
import rospy
import random
import actionlib
from communication_tutorial.msg import SumAction, SumGoal, SumResult, SumFeedback
class simple_action_client():
def __init__(self):
rospy.init_node("simple_action_client", anonymous=False)
self.client = actionlib.SimpleActionClient("random_sum_action", SumAction)
self.client.wait_for_server()
def done_cb(self, state, result):
rospy.loginfo("Final number "+str(result.final_number)+" reached after "+str(result.final_iterations)+" iterations")
def active_cb(self):
rospy.loginfo("Action server transitioned into ACTIVE")
def feedback_cb(self, feedback):
rospy.loginfo("Current sum "+str(feedback.current_sum)+" reached after "+str(feedback.current_iterations)+" iterations")
if feedback.current_iterations > 10:
self.client.cancel_goal()
rospy.loginfo("Server is taking too long. Canceling the goal")
def main(self):
numbers = [1,2,3,4,5,6,7,8,9]
rnd = random.choice(numbers)
goal = SumGoal()
goal.random_start = rnd
goal.goal_number = 100
rospy.loginfo("Random start number: "+str(goal.random_start))
rospy.loginfo("Goal number: "+str(goal.goal_number))
self.client.send_goal(goal, done_cb=self.done_cb, active_cb=self.active_cb, feedback_cb=self.feedback_cb)
self.client.wait_for_result()
#result = self.client.get_result()
if __name__=="__main__":
sac = simple_action_client()
sac.main()
Parameters¶
The video called Working with Parameters shows how to write parameters to the parameterserver and launch a python script from a launch file as well as read and write parameters in a python script. Additional information can also be found on the ROS webpage: writing parameters from launch file, using parameters in python. The following code is an example implementation of a launch file and python script working with parameters:
<launch>
<param name="starting_pos_x" type="double" value="9.5" />
<param name="starting_pos_y" type="double" value="-4.0" />
<node pkg="communication_tutorial" name="simple_param" type="simple_param.py" output="screen" />
</launch>
#! /usr/bin/env python
import rospy
class simple_param():
def __init__(self):
rospy.init_node("simple_param", anonymous=False)
self.init_x = rospy.get_param("starting_pos_x")
self.init_y = rospy.get_param("starting_pos_y")
rospy.loginfo("initial x position: "+str(self.init_x))
rospy.loginfo("initial y position: "+str(self.init_y))
rospy.set_param("starting_pos_x", 18.2)
self.init_x = rospy.get_param("starting_pos_x")
rospy.loginfo("initial x position: "+str(self.init_x))
if __name__=="__main__":
sp = simple_param()