[Documentation] [TitleIndex] [WordIndex

Note: This tutorial assumes that you have completed the previous tutorials: Chatter Concert.
(!) Please ask about problems and questions regarding this tutorial on answers.ros.org. Don't forget to include in your question the link to this page, the versions of your OS & ROS, and also add appropriate tags.

Turtle Concert

Description: An example concert demonstration, turtlesim style.

Keywords: concert

Tutorial Level: BEGINNER

Next Tutorial: Turtle Teleop Concert

Overview

This tutorial is a glorified version of ros' turtlesim tutorial. We up the game here by launching each turtle on their own master and provide two concert services - turtlesim (the simulation backend that spawns the turtles) and turtle_stroll, an orchestrated service that requests turtles and starts turtle_stroll apps on each turtle.

Turtle Concert

Preparation

Follow the rocon installation instructions.

Demo

Source your setup.bash and rocon_launch the chatter concert, e.g. for a source install:

> source ~/rocon/devel/setup.bash
> rocon_launch turtle_concert turtle.concert --screen

This launches the concert solution, robot apps and immediately links everything once all the components are discovered.

Screenshot

Turtle Concert

The Turtle Pond Service

You can look around in much the same way as outlined in the Chatter Concert Tutorial.

One further step that is interesting to pursue - this tutorial shows how you can set up a very basic roslaunch'able service. The service package itself consists of four files:

The launcher consists of a single node (python script). Service configurable parameters should get configured in the .parameters file - here we customise the number of turtles we'd like to start. The interesting part though is the actual script:

   1 #!/usr/bin/env python
   2 #
   3 # License: BSD
   4 #   https://raw.github.com/robotics-in-concert/rocon_services/license/LICENSE
   5 #
   6 ##############################################################################
   7 # About
   8 ##############################################################################
   9 
  10 # Simple script to request a pre-configured number of strolling turtles.
  11 # This could also be done with link graphs, but chatter concert does that.
  12 
  13 ##############################################################################
  14 # Imports
  15 ##############################################################################
  16 
  17 import rospy
  18 import rocon_python_comms
  19 import concert_service_utilities
  20 import rocon_scheduler_requests
  21 import unique_id
  22 import scheduler_msgs.msg as scheduler_msgs
  23 
  24 ##############################################################################
  25 # Classes
  26 ##############################################################################
  27 
  28 
  29 class TurtlePond:
  30     '''
  31       Requets handling for getting its mittens on some turtle_stroll'ing
  32       turtles.
  33     '''
  34     __slots__ = [
  35         'service_name',
  36         'service_description',
  37         'service_id',
  38         'requester',
  39         'pending_requests',   # [uuid.UUID] list of request id's pending feedback from the scheduler
  40         'allocated_requests'  # [uuid.UUID] moved here from pending requests once granted
  41     ]
  42 
  43     def __init__(self):
  44         ####################
  45         # Discovery
  46         ####################
  47         (self.service_name, self.service_description, self.service_id) = concert_service_utilities.get_service_info()
  48 
  49         ####################
  50         # Setup
  51         ####################
  52         self.requester = self.setup_requester(self.service_id)
  53         self.pending_requests = []
  54         self.allocated_requests = []
  55         number_of_turtles = rospy.get_param("turtles", default=1)
  56         rospy.loginfo("TurtlePond : requesting %s turtles" % number_of_turtles)
  57         for unused_i in range(0, number_of_turtles):
  58             self.request_turtles()
  59 
  60     def setup_requester(self, uuid):
  61         try:
  62             scheduler_requests_topic_name = concert_service_utilities.find_scheduler_requests_topic()
  63             #rospy.loginfo("Service : found scheduler [%s][%s]" % (topic_name))
  64         except rocon_python_comms.NotFoundException as e:
  65             rospy.logerr("TurtlePond : %s" % (str(e)))
  66             return  # raise an exception here?
  67         frequency = rocon_scheduler_requests.common.HEARTBEAT_HZ
  68         return rocon_scheduler_requests.Requester(self.requester_feedback, uuid, 0, scheduler_requests_topic_name, frequency)
  69 
  70     def request_turtles(self):
  71         '''
  72          Request a turtle.
  73         '''
  74         resource = scheduler_msgs.Resource()
  75         resource.id = unique_id.toMsg(unique_id.fromRandom())
  76         resource.rapp = 'turtle_concert/turtle_stroll'
  77         resource.uri = 'rocon:/'
  78         resource_request_id = self.requester.new_request([resource])
  79         self.pending_requests.append(resource_request_id)
  80         self.requester.send_requests()
  81 
  82     def requester_feedback(self, request_set):
  83         '''
  84           Keep an eye on our pending requests and see if they get allocated here.
  85           Once they do, kick them out of the pending requests list so _ros_capture_teleop_callback
  86           can process and reply to the interaction.
  87 
  88           @param request_set : the modified requests
  89           @type dic { uuid.UUID : scheduler_msgs.ResourceRequest }
  90         '''
  91         for request_id, request in request_set.requests.iteritems():
  92             if request_id in self.pending_requests:
  93                 if request.msg.status == scheduler_msgs.Request.GRANTED:
  94                     self.pending_requests.remove(request_id)
  95                     self.allocated_requests.append(request_id)
  96         # Need to add logic here for when the request gets released or preempted
  97 
  98     def cancel_all_requests(self):
  99         '''
 100           Exactly as it says! Used typically when shutting down or when
 101           it's lost more allocated resources than the minimum required (in which case it
 102           cancels everything and starts reissuing new requests).
 103         '''
 104         self.requester.cancel_all()
 105         self.requester.send_requests()
 106 
 107 ##############################################################################
 108 # Launch point
 109 ##############################################################################
 110 
 111 if __name__ == '__main__':
 112 
 113     rospy.init_node('turtle_pond')
 114     turtle_pond = TurtlePond()
 115     rospy.spin()
 116     if not rospy.is_shutdown():
 117         turtle_pond.cancel_all_requests()

This is a very simple service - it requests two resources (robots) that have the turtle_stroll rapp and releases them when done.


2019-07-13 13:16