From a9393e370a39b38bbc9d0c01df5bc9c201ad1e40 Mon Sep 17 00:00:00 2001 From: "Isaac I.Y. Saito" <130s@2000.jukuin.keio.ac.jp> Date: Tue, 6 Jun 2017 16:42:23 -0700 Subject: [PATCH 1/5] Move smach_tutorials (from https://github.com/eacousineau/executive_smach_tutorials.git). Catkinized. According to the repo that currently hosts `executive_smach_tutorials` mentioned above, which is also referenced in the [tutorial set of SMACH](http://wiki.ros.org/smach/Tutorials), the code was evacuated from https://code.ros.org/svn/ros-pkg/stacks/executive_smach_tutorials/trunk fortunately before the website went unaccessible. Since `executive_smach` is part of core ROS (hosted under `ros` org https://github.com/ros/executive_smach), I think it makes sense to move this tutorial package to also `ros` org. The package is also catkinized this time. --- smach_tutorials/CMakeLists.txt | 9 + smach_tutorials/action/Test.action | 3 + smach_tutorials/examples/actionlib2_test.py | 81 +++++++++ smach_tutorials/examples/actionlib_test.py | 82 +++++++++ smach_tutorials/examples/concurrence.py | 75 +++++++++ smach_tutorials/examples/concurrence2.py | 80 +++++++++ smach_tutorials/examples/iterator_tutorial.py | 88 ++++++++++ smach_tutorials/examples/sequence.py | 50 ++++++ smach_tutorials/examples/state_machine.py | 48 ++++++ smach_tutorials/examples/state_machine2.py | 56 +++++++ .../examples/state_machine_nesting.py | 72 ++++++++ .../examples/state_machine_nesting2.py | 80 +++++++++ .../examples/state_machine_simple.py | 56 +++++++ smach_tutorials/examples/user_data.py | 53 ++++++ smach_tutorials/examples/user_data2.py | 66 ++++++++ smach_tutorials/mainpage.dox | 26 +++ smach_tutorials/package.xml | 31 ++++ .../scripts/usecase_01/executive_step_01.py | 42 +++++ .../scripts/usecase_01/executive_step_02.py | 53 ++++++ .../scripts/usecase_01/executive_step_03.py | 59 +++++++ .../scripts/usecase_01/executive_step_04.py | 97 +++++++++++ .../scripts/usecase_01/executive_step_05.py | 107 ++++++++++++ .../scripts/usecase_01/executive_step_06.py | 142 ++++++++++++++++ .../scripts/usecase_01/executive_step_07.py | 156 ++++++++++++++++++ .../scripts/usecase_01/turtle_nodes.launch | 9 + 25 files changed, 1621 insertions(+) create mode 100644 smach_tutorials/CMakeLists.txt create mode 100644 smach_tutorials/action/Test.action create mode 100755 smach_tutorials/examples/actionlib2_test.py create mode 100755 smach_tutorials/examples/actionlib_test.py create mode 100755 smach_tutorials/examples/concurrence.py create mode 100755 smach_tutorials/examples/concurrence2.py create mode 100755 smach_tutorials/examples/iterator_tutorial.py create mode 100755 smach_tutorials/examples/sequence.py create mode 100755 smach_tutorials/examples/state_machine.py create mode 100755 smach_tutorials/examples/state_machine2.py create mode 100755 smach_tutorials/examples/state_machine_nesting.py create mode 100755 smach_tutorials/examples/state_machine_nesting2.py create mode 100755 smach_tutorials/examples/state_machine_simple.py create mode 100755 smach_tutorials/examples/user_data.py create mode 100755 smach_tutorials/examples/user_data2.py create mode 100644 smach_tutorials/mainpage.dox create mode 100644 smach_tutorials/package.xml create mode 100755 smach_tutorials/scripts/usecase_01/executive_step_01.py create mode 100755 smach_tutorials/scripts/usecase_01/executive_step_02.py create mode 100755 smach_tutorials/scripts/usecase_01/executive_step_03.py create mode 100755 smach_tutorials/scripts/usecase_01/executive_step_04.py create mode 100755 smach_tutorials/scripts/usecase_01/executive_step_05.py create mode 100755 smach_tutorials/scripts/usecase_01/executive_step_06.py create mode 100755 smach_tutorials/scripts/usecase_01/executive_step_07.py create mode 100644 smach_tutorials/scripts/usecase_01/turtle_nodes.launch diff --git a/smach_tutorials/CMakeLists.txt b/smach_tutorials/CMakeLists.txt new file mode 100644 index 0000000..2d857e0 --- /dev/null +++ b/smach_tutorials/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 2.8.3) +project(smach_tutorials) + +find_package(catkin REQUIRED COMPONENTS actionlib_msgs message_generation) + +add_action_files(DIRECTORY action) +generate_messages(DEPENDENCIES actionlib_msgs) + +catkin_package(CATKIN_DEPENDS actionlib_msgs message_runtime) diff --git a/smach_tutorials/action/Test.action b/smach_tutorials/action/Test.action new file mode 100644 index 0000000..fb8b0ac --- /dev/null +++ b/smach_tutorials/action/Test.action @@ -0,0 +1,3 @@ +float64 goal +--- +--- diff --git a/smach_tutorials/examples/actionlib2_test.py b/smach_tutorials/examples/actionlib2_test.py new file mode 100755 index 0000000..f102bdd --- /dev/null +++ b/smach_tutorials/examples/actionlib2_test.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python + +import roslib; roslib.load_manifest('smach_tutorials') +import rospy +import smach +import smach_ros + +from smach_tutorials.msg import TestAction, TestGoal +from actionlib import * +from actionlib_msgs.msg import * + + +# Create a trivial action server +class TestServer: + def __init__(self,name): + self._sas = SimpleActionServer(name, + TestAction, + execute_cb=self.execute_cb) + + def execute_cb(self, msg): + if msg.goal == 0: + self._sas.set_succeeded() + elif msg.goal == 1: + self._sas.set_aborted() + elif msg.goal == 2: + self._sas.set_preempted() + +def main(): + rospy.init_node('smach_example_actionlib') + + # Start an action server + server = TestServer('test_action') + + # Create a SMACH state machine + sm0 = smach.StateMachine(outcomes=['succeeded','aborted','preempted']) + + # Open the container + with sm0: + # Add states to the container + + # Add a simple action state. This will use an empty, default goal + # As seen in TestServer above, an empty goal will always return with + # GoalStatus.SUCCEEDED, causing this simple action state to return + # the outcome 'succeeded' + smach.StateMachine.add('GOAL_DEFAULT', + smach_ros.SimpleActionState('test_action', TestAction), + {'succeeded':'GOAL_STATIC'}) + + # Add another simple action state. This will give a goal + # that should abort the action state when it is received, so we + # map 'aborted' for this state onto 'succeeded' for the state machine. + smach.StateMachine.add('GOAL_STATIC', + smach_ros.SimpleActionState('test_action', TestAction, + goal = TestGoal(goal=1)), + {'aborted':'GOAL_CB'}) + + + # Add another simple action state. This will give a goal + # that should abort the action state when it is received, so we + # map 'aborted' for this state onto 'succeeded' for the state machine. + def goal_callback(userdata, default_goal): + goal = TestGoal() + goal.goal = 2 + return goal + + smach.StateMachine.add('GOAL_CB', + smach_ros.SimpleActionState('test_action', TestAction, + goal_cb = goal_callback), + {'aborted':'succeeded'}) + + # For more examples on how to set goals and process results, see + # executive_smach/smach_ros/tests/smach_actionlib.py + + # Execute SMACH plan + outcome = sm0.execute() + + rospy.signal_shutdown('All done.') + + +if __name__ == '__main__': + main() diff --git a/smach_tutorials/examples/actionlib_test.py b/smach_tutorials/examples/actionlib_test.py new file mode 100755 index 0000000..5378f9b --- /dev/null +++ b/smach_tutorials/examples/actionlib_test.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python +""" +Description: + Spawn an actionlib action server, then create a state machine that sends + some goals to the action server that will automatically succeed or abort. + We expect the first goal to succeed, and the second goal to abort, so + when the second goal aborts, we map that onto success of the state + machine. + +Usage: + $> ./actionlib.py + +Output: + [INFO] : State machine starting in initial state 'GOAL_DEFAULT' with userdata: + [] + [WARN] : Still waiting for action server 'test_action' to start... is it running? + [INFO] : State machine transitioning 'GOAL_DEFAULT':'succeeded'-->'GOAL_STATIC' + [INFO] : State machine terminating 'GOAL_STATIC':'aborted':'succeeded' +""" + +import roslib; roslib.load_manifest('smach_tutorials') +import rospy +import smach +import smach_ros + +from actionlib import * +from actionlib_msgs.msg import * + +# Create a trivial action server +class TestServer: + def __init__(self,name): + self._sas = SimpleActionServer(name, + TestAction, + execute_cb=self.execute_cb) + + def execute_cb(self, msg): + if msg.goal == 0: + self._sas.set_succeeded() + elif msg.goal == 1: + self._sas.set_aborted() + elif msg.goal == 2: + self._sas.set_preempted() + +def main(): + rospy.init_node('smach_example_actionlib') + + # Start an action server + server = TestServer('test_action') + + # Create a SMACH state machine + sm0 = smach.StateMachine(outcomes=['succeeded','aborted','preempted']) + + # Open the container + with sm0: + # Add states to the container + + # Add a simple action sttate. This will use an emtpy, default goal + # As seen in TestServer above, an empty goal will always return with + # GoalStatus.SUCCEEDED, causing this simple action state to return + # the outcome 'succeeded' + smach.StateMachine.add('GOAL_DEFAULT', + smach_ros.SimpleActionState('test_action', TestAction), + {'succeeded':'GOAL_STATIC'}) + + # Add another simple action state. This will give a goal + # that should abort the action state when it is received, so we + # map 'aborted' for this state onto 'succeeded' for the state machine. + smach.StateMachine.add('GOAL_STATIC', + smach_ros.SimpleActionState('test_action', TestAction, + goal = TestGoal(goal=1)), + {'aborted':'succeeded'}) + + # For more examples on how to set goals and process results, see + # executive_python/smach/tests/smach_actionlib.py + + # Execute SMACH plan + outcome = sm0.execute() + + rospy.signal_shutdown('All done.') + +if __name__ == '__main__': + main() diff --git a/smach_tutorials/examples/concurrence.py b/smach_tutorials/examples/concurrence.py new file mode 100755 index 0000000..84b0c15 --- /dev/null +++ b/smach_tutorials/examples/concurrence.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python +""" +Description: + This creates a concurrence with two states, one state, 'SET', waits three + seconds before setting something in userdata, while another state 'GET' + blocks while checking for thie userdata key, and only returns once it + has been set. + +Usage: + $> ./concurrence.py + +Output: + [INFO] 1279226335.169182: Concurrence starting with userdata: + [] + [INFO] : >>> Waiting for data... + [INFO] : >>> Waiting for data... + [INFO] : >>> Waiting for data... + [INFO] : >>> Waiting for data... + [INFO] : >>> Waiting for data... + [INFO] : >>> Waiting for data... + [INFO] : >>> Set data: hello + [INFO] : >>> GOT DATA! x = hello + [INFO] : Concurrent Outcomes: {'SET': 'set_it', 'GET': 'got_it'} +""" + +import roslib; roslib.load_manifest('smach_tutorials') +import rospy +import smach +import smach_ros + +# Define a state to set some user data +class Setter(smach.State): + def __init__(self, val): + smach.State.__init__(self, outcomes = ['set_it'], output_keys = ['x']) + self._val = val + def execute(self, ud): + # Delay a bit to make this clear + rospy.sleep(3.0) + # Set the data + ud.x = self._val + rospy.loginfo('>>> Set data: %s' % str(self._val)) + return 'set_it' + +# Define a state to get some user data +class Getter(smach.State): + def __init__(self): + smach.State.__init__(self, outcomes = ['got_it'], input_keys = ['x']) + def execute(self, ud): + # Wait for data to appear + while 'x' not in ud: + rospy.loginfo('>>> Waiting for data...') + rospy.sleep(0.5) + rospy.loginfo('>>> GOT DATA! x = '+str(ud.x)) + return 'got_it' + +def main(): + rospy.init_node('smach_example_concurrence') + + # Create a SMACH state machine + cc0 = smach.Concurrence( + outcomes=['succeeded', 'aborted'], + default_outcome='aborted', + outcome_map = {'succeeded':{'SET':'set_it','GET':'got_it'}}) + + # Open the container + with cc0: + # Add states to the container + smach.Concurrence.add('SET', Setter(val='hello')) + smach.Concurrence.add('GET', Getter()) + + # Execute SMACH plan + outcome = cc0.execute() + +if __name__ == '__main__': + main() diff --git a/smach_tutorials/examples/concurrence2.py b/smach_tutorials/examples/concurrence2.py new file mode 100755 index 0000000..d45204e --- /dev/null +++ b/smach_tutorials/examples/concurrence2.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python + +import roslib; roslib.load_manifest('smach_tutorials') +import rospy +import smach +import smach_ros + +# define state Foo +class Foo(smach.State): + def __init__(self): + smach.State.__init__(self, outcomes=['outcome1','outcome2']) + self.counter = 0 + + def execute(self, userdata): + rospy.loginfo('Executing state FOO') + if self.counter < 3: + self.counter += 1 + return 'outcome1' + else: + return 'outcome2' + + +# define state Bar +class Bar(smach.State): + def __init__(self): + smach.State.__init__(self, outcomes=['outcome1']) + + def execute(self, userdata): + rospy.loginfo('Executing state BAR') + return 'outcome1' + + + +# define state Bas +class Bas(smach.State): + def __init__(self): + smach.State.__init__(self, outcomes=['outcome3']) + + def execute(self, userdata): + rospy.loginfo('Executing state BAS') + return 'outcome3' + + + + +def main(): + rospy.init_node('smach_example_state_machine') + + # Create the top level SMACH state machine + sm_top = smach.StateMachine(outcomes=['outcome6']) + + # Open the container + with sm_top: + + smach.StateMachine.add('BAS', Bas(), + transitions={'outcome3':'CON'}) + + # Create the sub SMACH state machine + sm_con = smach.Concurrence(outcomes=['outcome4','outcome5'], + default_outcome='outcome4', + outcome_map={'outcome5': + { 'FOO':'outcome2', + 'BAR':'outcome1'}}) + + # Open the container + with sm_con: + # Add states to the container + smach.Concurrence.add('FOO', Foo()) + smach.Concurrence.add('BAR', Bar()) + + smach.StateMachine.add('CON', sm_con, + transitions={'outcome4':'CON', + 'outcome5':'outcome6'}) + + # Execute SMACH plan + outcome = sm_top.execute() + + +if __name__ == '__main__': + main() diff --git a/smach_tutorials/examples/iterator_tutorial.py b/smach_tutorials/examples/iterator_tutorial.py new file mode 100755 index 0000000..6b4de06 --- /dev/null +++ b/smach_tutorials/examples/iterator_tutorial.py @@ -0,0 +1,88 @@ +#! /usr/bin/env python +## %Tag(FULLTEXT)% + + +import roslib; roslib.load_manifest('smach') +roslib.load_manifest('smach_ros') +import rospy + +import smach +from smach import Iterator, StateMachine, CBState +from smach_ros import ConditionState, IntrospectionServer + +def construct_sm(): + sm = StateMachine(outcomes = ['succeeded','aborted','preempted']) + sm.userdata.nums = range(25, 88, 3) + sm.userdata.even_nums = [] + sm.userdata.odd_nums = [] + with sm: +## %Tag(ITERATOR)% + tutorial_it = Iterator(outcomes = ['succeeded','preempted','aborted'], + input_keys = ['nums', 'even_nums', 'odd_nums'], + it = lambda: range(0, len(sm.userdata.nums)), + output_keys = ['even_nums', 'odd_nums'], + it_label = 'index', + exhausted_outcome = 'succeeded') +## %EndTag(ITERATOR)% +## %Tag(CONTAINER)% + with tutorial_it: + container_sm = StateMachine(outcomes = ['succeeded','preempted','aborted','continue'], + input_keys = ['nums', 'index', 'even_nums', 'odd_nums'], + output_keys = ['even_nums', 'odd_nums']) + with container_sm: + #test wether even or odd + StateMachine.add('EVEN_OR_ODD', + ConditionState(cond_cb = lambda ud:ud.nums[ud.index]%2, + input_keys=['nums', 'index']), + {'true':'ODD', + 'false':'EVEN' }) + #add even state + @smach.cb_interface(input_keys=['nums', 'index', 'even_nums'], + output_keys=['odd_nums'], + outcomes=['succeeded']) + def even_cb(ud): + ud.even_nums.append(ud.nums[ud.index]) + return 'succeeded' + StateMachine.add('EVEN', CBState(even_cb), + {'succeeded':'continue'}) + #add odd state + @smach.cb_interface(input_keys=['nums', 'index', 'odd_nums'], + output_keys=['odd_nums'], + outcomes=['succeeded']) + def odd_cb(ud): + ud.odd_nums.append(ud.nums[ud.index]) + return 'succeeded' + StateMachine.add('ODD', CBState(odd_cb), + {'succeeded':'continue'}) +## %EndTag(CONTAINER)% +## %Tag(ADDCONTAINER)% + #close container_sm + Iterator.set_contained_state('CONTAINER_STATE', + container_sm, + loop_outcomes=['continue']) +## %EndTag(ADDCONTAINER)% +## %Tag(ADDITERATOR)% + #close the tutorial_it + StateMachine.add('TUTORIAL_IT',tutorial_it, + {'succeeded':'succeeded', + 'aborted':'aborted'}) +## %EndTag(ADDITERATOR)% + return sm + +def main(): + rospy.init_node("iterator_tutorial") + sm_iterator = construct_sm() + + # Run state machine introspection server for smach viewer + intro_server = IntrospectionServer('iterator_tutorial',sm_iterator,'/ITERATOR_TUTORIAL') + intro_server.start() + + + outcome = sm_iterator.execute() + + rospy.spin() + intro_server.stop() + +if __name__ == "__main__": + main() +## %EndTag(FULLTEXT)% diff --git a/smach_tutorials/examples/sequence.py b/smach_tutorials/examples/sequence.py new file mode 100755 index 0000000..62fc865 --- /dev/null +++ b/smach_tutorials/examples/sequence.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python +""" +Description: + Create a simple 3-state state sequence. A Sequence is a StateMachine + that just assumes that states added in some sequence should be + executed in the order in which they were added, but only if they + return the "connector outcome" given in the Sequence constructor. + +Usage: + $> ./sequence.py + +Output: + [INFO] : State machine starting in initial state 'FOO' with userdata: + [] + [INFO] : State machine transitioning 'FOO':'done'-->'BAR' + [INFO] : State machine transitioning 'BAR':'done'-->'BAZ' + [INFO] : State machine terminating 'BAZ':'done':'succeeded' +""" + +import roslib; roslib.load_manifest('smach_tutorials') +import rospy +import smach +import smach_ros + +class ExampleState(smach.State): + def __init__(self): + smach.State.__init__(self, outcomes = ['done']) + def execute(self, ud): + return 'done' + +def main(): + rospy.init_node('smach_example_sequence') + + # Create a SMACH state machine + sq = smach.Sequence( + outcomes = ['succeeded'], + connector_outcome = 'done') + + # Open the container + with sq: + # Add states to the container + smach.Sequence.add('FOO', ExampleState()) + smach.Sequence.add('BAR', ExampleState()) + smach.Sequence.add('BAZ', ExampleState(), {'done':'succeeded'}) + + # Execute SMACH plan + outcome = sq.execute() + +if __name__ == '__main__': + main() diff --git a/smach_tutorials/examples/state_machine.py b/smach_tutorials/examples/state_machine.py new file mode 100755 index 0000000..81af47b --- /dev/null +++ b/smach_tutorials/examples/state_machine.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python +""" +Description: + Create a simple 3-state state machine. + +Usage: + $> ./state_machine.py + +Output: + [INFO] : State machine starting in initial state 'FOO' with userdata: + [] + [INFO] : State machine transitioning 'FOO':'done'-->'BAR' + [INFO] : State machine transitioning 'BAR':'done'-->'BAZ' + [INFO] : State machine terminating 'BAZ':'done':'succeeded' + +""" + +import roslib; roslib.load_manifest('smach_tutorials') +import rospy +import smach +import smach_ros + +class ExampleState(smach.State): + def __init__(self): + smach.State.__init__(self, outcomes = ['done']) + def execute(self, ud): + return 'done' + +def main(): + rospy.init_node('smach_example_state_machine') + + # Create a SMACH state machine + sm = smach.StateMachine(outcomes=['succeeded','aborted']) + + # Open the container + with sm: + # Add states to the container + smach.StateMachine.add('FOO', ExampleState(), {'done':'BAR'}) + smach.StateMachine.add('BAR', ExampleState(), {'done':'BAZ'}) + smach.StateMachine.add('BAZ', + ExampleState(), + {'done':'succeeded'}) + + # Execute SMACH plan + outcome = sm.execute() + +if __name__ == '__main__': + main() diff --git a/smach_tutorials/examples/state_machine2.py b/smach_tutorials/examples/state_machine2.py new file mode 100755 index 0000000..c14d4d7 --- /dev/null +++ b/smach_tutorials/examples/state_machine2.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python + +import roslib; roslib.load_manifest('smach_tutorials') +import rospy +import smach +import smach_ros + +# define state Foo +class Foo(smach.State): + def __init__(self): + smach.State.__init__(self, outcomes=['outcome1','outcome2']) + self.counter = 0 + + def execute(self, userdata): + rospy.loginfo('Executing state FOO') + if self.counter < 3: + self.counter += 1 + return 'outcome1' + else: + return 'outcome2' + + +# define state Bar +class Bar(smach.State): + def __init__(self): + smach.State.__init__(self, outcomes=['outcome1']) + + def execute(self, userdata): + rospy.loginfo('Executing state BAR') + return 'outcome1' + + + + + +def main(): + rospy.init_node('smach_example_state_machine') + + # Create a SMACH state machine + sm = smach.StateMachine(outcomes=['outcome4']) + + # Open the container + with sm: + # Add states to the container + smach.StateMachine.add('FOO', Foo(), + transitions={'outcome1':'BAR', 'outcome2':'outcome4'}) + smach.StateMachine.add('BAR', Bar(), + transitions={'outcome1':'FOO'}) + + # Execute SMACH plan + outcome = sm.execute() + + + +if __name__ == '__main__': + main() diff --git a/smach_tutorials/examples/state_machine_nesting.py b/smach_tutorials/examples/state_machine_nesting.py new file mode 100755 index 0000000..6cd92dc --- /dev/null +++ b/smach_tutorials/examples/state_machine_nesting.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python +""" +Description: + Create one state machine, put a state in it that sets something in the + userdata key 'x'. Put another state machine inside of that state machine, + tell it to fetch the userdata key 'x'. Put a state that reads 'x' into + that nested state machine, and spew some info to rosout. + +Usage: + $> ./state_machine_nesting.py + +Output: + [INFO] : State machine starting in initial state 'SET' with userdata: + [] + [INFO] : State machine transitioning 'SET':'set_it'-->'NESTED' + [INFO] : State machine starting in initial state 'GET' with userdata: + ['x'] + [INFO] : >>> GOT DATA! x = hello + [INFO] : State machine terminating 'GET':'got_it':'done' + [INFO] : State machine terminating 'NESTED':'done':'succeeded' + +""" + +import roslib; roslib.load_manifest('smach_tutorials') +import rospy +import smach +import smach_ros + +# Define a state to set some user data +class Setter(smach.State): + def __init__(self, val): + smach.State.__init__(self, outcomes = ['set_it'], output_keys = ['x']) + self._val = val + def execute(self, ud): + # Set the data + ud.x = self._val + rospy.loginfo('>>> Set data: %s' % str(self._val)) + return 'set_it' + +# Define a state to get some user data +class Getter(smach.State): + def __init__(self): + smach.State.__init__(self, outcomes = ['got_it'], input_keys = ['x']) + def execute(self, ud): + # Wait for data to appear + while 'x' not in ud: + rospy.loginfo('>>> Waiting for data...') + rospy.sleep(0.5) + rospy.loginfo('>>> GOT DATA! x = '+str(ud.x)) + return 'got_it' + +def main(): + rospy.init_node('smach_example_state_machine_nesting') + + # Create a SMACH state machine + sm0 = smach.StateMachine(outcomes=['succeeded']) + + # Open the container + with sm0: + # Add states to the container + smach.StateMachine.add('SET', Setter(val='hello'), {'set_it':'NESTED'}) + + sm1 = smach.StateMachine(outcomes=['done'], input_keys=['x']) + smach.StateMachine.add('NESTED', sm1, {'done':'succeeded'}) + with sm1: + smach.StateMachine.add('GET', Getter(), {'got_it':'done'}) + + # Execute SMACH plan + outcome = sm0.execute() + +if __name__ == '__main__': + main() diff --git a/smach_tutorials/examples/state_machine_nesting2.py b/smach_tutorials/examples/state_machine_nesting2.py new file mode 100755 index 0000000..7d64c11 --- /dev/null +++ b/smach_tutorials/examples/state_machine_nesting2.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python + +import roslib; roslib.load_manifest('smach_tutorials') +import rospy +import smach +import smach_ros + +# define state Foo +class Foo(smach.State): + def __init__(self): + smach.State.__init__(self, outcomes=['outcome1','outcome2']) + self.counter = 0 + + def execute(self, userdata): + rospy.loginfo('Executing state FOO') + if self.counter < 3: + self.counter += 1 + return 'outcome1' + else: + return 'outcome2' + + +# define state Bar +class Bar(smach.State): + def __init__(self): + smach.State.__init__(self, outcomes=['outcome1']) + + def execute(self, userdata): + rospy.loginfo('Executing state BAR') + return 'outcome1' + + + +# define state Bas +class Bas(smach.State): + def __init__(self): + smach.State.__init__(self, outcomes=['outcome3']) + + def execute(self, userdata): + rospy.loginfo('Executing state BAS') + return 'outcome3' + + + + +def main(): + rospy.init_node('smach_example_state_machine') + + # Create the top level SMACH state machine + sm_top = smach.StateMachine(outcomes=['outcome5']) + + # Open the container + with sm_top: + + smach.StateMachine.add('BAS', Bas(), + transitions={'outcome3':'SUB'}) + + # Create the sub SMACH state machine + sm_sub = smach.StateMachine(outcomes=['outcome4']) + + # Open the container + with sm_sub: + + # Add states to the container + smach.StateMachine.add('FOO', Foo(), + transitions={'outcome1':'BAR', + 'outcome2':'outcome4'}) + smach.StateMachine.add('BAR', Bar(), + transitions={'outcome1':'FOO'}) + + smach.StateMachine.add('SUB', sm_sub, + transitions={'outcome4':'outcome5'}) + + # Execute SMACH plan + outcome = sm_top.execute() + + + +if __name__ == '__main__': + main() diff --git a/smach_tutorials/examples/state_machine_simple.py b/smach_tutorials/examples/state_machine_simple.py new file mode 100755 index 0000000..2d87822 --- /dev/null +++ b/smach_tutorials/examples/state_machine_simple.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python + +import roslib; roslib.load_manifest('smach_tutorials') +import rospy +import smach +import smach_ros + +# define state Foo +class Foo(smach.State): + def __init__(self): + smach.State.__init__(self, outcomes=['outcome1','outcome2']) + self.counter = 0 + + def execute(self, userdata): + rospy.loginfo('Executing state FOO') + if self.counter < 3: + self.counter += 1 + return 'outcome1' + else: + return 'outcome2' + + +# define state Bar +class Bar(smach.State): + def __init__(self): + smach.State.__init__(self, outcomes=['outcome2']) + + def execute(self, userdata): + rospy.loginfo('Executing state BAR') + return 'outcome2' + + + + +# main +def main(): + rospy.init_node('smach_example_state_machine') + + # Create a SMACH state machine + sm = smach.StateMachine(outcomes=['outcome4', 'outcome5']) + + # Open the container + with sm: + # Add states to the container + smach.StateMachine.add('FOO', Foo(), + transitions={'outcome1':'BAR', + 'outcome2':'outcome4'}) + smach.StateMachine.add('BAR', Bar(), + transitions={'outcome2':'FOO'}) + + # Execute SMACH plan + outcome = sm.execute() + + +if __name__ == '__main__': + main() diff --git a/smach_tutorials/examples/user_data.py b/smach_tutorials/examples/user_data.py new file mode 100755 index 0000000..322c29c --- /dev/null +++ b/smach_tutorials/examples/user_data.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python +""" +Description: + Create a two-state state machine where one state writes to userdata and + the other state reads from userdata, and spews a message to rosout. + +Usage: + $> ./user_data.py + +Output: + [INFO] : State machine starting in initial state 'SET' with userdata: + [] + [INFO] : State machine transitioning 'SET':'set_it'-->'GET' + [INFO] : >>> GOT DATA! x = True + [INFO] : State machine terminating 'GET':'got_it':'succeeded' +""" + +import roslib; roslib.load_manifest('smach_tutorials') +import rospy +import smach +import smach_ros + +class Setter(smach.State): + def __init__(self): + smach.State.__init__(self, outcomes = ['set_it'], output_keys = ['x']) + def execute(self, ud): + ud.x = True + return 'set_it' + +class Getter(smach.State): + def __init__(self): + smach.State.__init__(self, outcomes = ['got_it'], input_keys = ['x']) + def execute(self, ud): + rospy.loginfo('>>> GOT DATA! x = '+str(ud.x)) + return 'got_it' + +def main(): + rospy.init_node('smach_example_user_data') + + # Create a SMACH state machine + sm = smach.StateMachine(outcomes=['succeeded']) + + # Open the container + with sm: + # Add states to the container + smach.StateMachine.add('SET', Setter(), {'set_it':'GET'}) + smach.StateMachine.add('GET', Getter(), {'got_it':'succeeded'}) + + # Execute SMACH plan + outcome = sm.execute() + +if __name__ == '__main__': + main() diff --git a/smach_tutorials/examples/user_data2.py b/smach_tutorials/examples/user_data2.py new file mode 100755 index 0000000..e61f890 --- /dev/null +++ b/smach_tutorials/examples/user_data2.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python + +import roslib; roslib.load_manifest('smach_tutorials') +import rospy +import smach +import smach_ros + +# define state Foo +class Foo(smach.State): + def __init__(self): + smach.State.__init__(self, + outcomes=['outcome1','outcome2'], + input_keys=['foo_counter_in'], + output_keys=['foo_counter_out']) + + def execute(self, userdata): + rospy.loginfo('Executing state FOO') + if userdata.foo_counter_in < 3: + userdata.foo_counter_out = userdata.foo_counter_in + 1 + return 'outcome1' + else: + return 'outcome2' + + +# define state Bar +class Bar(smach.State): + def __init__(self): + smach.State.__init__(self, + outcomes=['outcome1'], + input_keys=['bar_counter_in']) + + def execute(self, userdata): + rospy.loginfo('Executing state BAR') + rospy.loginfo('Counter = %f'%userdata.bar_counter_in) + return 'outcome1' + + + + + +def main(): + rospy.init_node('smach_example_state_machine') + + # Create a SMACH state machine + sm = smach.StateMachine(outcomes=['outcome4']) + sm.userdata.sm_counter = 0 + + # Open the container + with sm: + # Add states to the container + smach.StateMachine.add('FOO', Foo(), + transitions={'outcome1':'BAR', + 'outcome2':'outcome4'}, + remapping={'foo_counter_in':'sm_counter', + 'foo_counter_out':'sm_counter'}) + smach.StateMachine.add('BAR', Bar(), + transitions={'outcome1':'FOO'}, + remapping={'bar_counter_in':'sm_counter'}) + + + # Execute SMACH plan + outcome = sm.execute() + + +if __name__ == '__main__': + main() diff --git a/smach_tutorials/mainpage.dox b/smach_tutorials/mainpage.dox new file mode 100644 index 0000000..4de1a7a --- /dev/null +++ b/smach_tutorials/mainpage.dox @@ -0,0 +1,26 @@ +/** +\mainpage +\htmlinclude manifest.html + +\b smach_tutorials is ... + + + + +\section codeapi Code API + + + + +*/ diff --git a/smach_tutorials/package.xml b/smach_tutorials/package.xml new file mode 100644 index 0000000..3230f5f --- /dev/null +++ b/smach_tutorials/package.xml @@ -0,0 +1,31 @@ + + smach_tutorials + 0.1.10 + + This package contains numerous examples of how to use SMACH. + See the examples directory. + + Isaac I. Y. Saito + Jonathan Bohren + BSD + + http://wiki.ros.org/smach_tutorials + https://github.com/ros/common_tutorials + https://github.com/ros/common_tutorials/issues + + catkin + + message_generation + message_runtime + actionlib_msgs + actionlib + common_msgs + message_runtime + rospy + ros_tutorials + smach + smach_ros + turtlesim + + + diff --git a/smach_tutorials/scripts/usecase_01/executive_step_01.py b/smach_tutorials/scripts/usecase_01/executive_step_01.py new file mode 100755 index 0000000..20aaaa6 --- /dev/null +++ b/smach_tutorials/scripts/usecase_01/executive_step_01.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python +""" +Description: + +Usage: + $> ./executive_step_01.py + +Output: + [ERROR] : InvalidTransitionError: State machine failed consistency check: + No initial state set. + + Available states: [] + [ERROR] : Container consistency check failed. + [ERROR] : InvalidTransitionError: State machine failed consistency check: + No initial state set. + + Available states: [] + [ERROR] : Container consistency check failed. +""" + +import roslib; roslib.load_manifest('smach_tutorials') +import rospy +import smach + +def main(): + rospy.init_node('smach_usecase_step_01') + + # Create a SMACH state machine + sm0 = smach.StateMachine(outcomes=[]) + + # Open the container + with sm0: + pass + + # Execute SMACH tree + outcome = sm0.execute() + + # Signal ROS shutdown (kill threads in background) + rospy.signal_shutdown('All done.') + +if __name__ == '__main__': + main() diff --git a/smach_tutorials/scripts/usecase_01/executive_step_02.py b/smach_tutorials/scripts/usecase_01/executive_step_02.py new file mode 100755 index 0000000..c96cab8 --- /dev/null +++ b/smach_tutorials/scripts/usecase_01/executive_step_02.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python +""" +Description: + +Usage: + $> roslaunch turtle_nodes.launch + $> ./executive_step_02.py + +Output: + [INFO] : State machine starting in initial state 'RESET' with userdata: + [] + [INFO] : State machine transitioning 'RESET':'succeeded'-->'SPAWN' + [INFO] : State machine terminating 'SPAWN':'succeeded':'succeeded' + +""" + +import roslib; roslib.load_manifest('smach_tutorials') +import rospy + +import threading + +import smach +from smach import StateMachine, ServiceState, SimpleActionState + +import std_srvs.srv +import turtlesim.srv + +def main(): + rospy.init_node('smach_usecase_step_02') + + # Create a SMACH state machine + sm0 = StateMachine(outcomes=['succeeded','aborted','preempted']) + + # Open the container + with sm0: + # Reset turtlesim + StateMachine.add('RESET', + ServiceState('reset', std_srvs.srv.Empty), + {'succeeded':'SPAWN'}) + + # Create a second turtle + StateMachine.add('SPAWN', + ServiceState('spawn', turtlesim.srv.Spawn, + request = turtlesim.srv.SpawnRequest(0.0,0.0,0.0,'turtle2'))) + + # Execute SMACH tree + outcome = sm0.execute() + + # Signal ROS shutdown (kill threads in background) + rospy.signal_shutdown('All done.') + +if __name__ == '__main__': + main() diff --git a/smach_tutorials/scripts/usecase_01/executive_step_03.py b/smach_tutorials/scripts/usecase_01/executive_step_03.py new file mode 100755 index 0000000..da27185 --- /dev/null +++ b/smach_tutorials/scripts/usecase_01/executive_step_03.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python +""" +Description: + +Usage: + $> roslaunch turtle_nodes.launch + $> ./executive_step_02.py + +Output: + [INFO] : State machine starting in initial state 'RESET' with userdata: + [] + [INFO] : State machine transitioning 'RESET':'succeeded'-->'SPAWN' + [INFO] : State machine terminating 'SPAWN':'succeeded':'succeeded' + +""" + +import roslib; roslib.load_manifest('smach_tutorials') +import rospy + +import threading + +import smach +from smach import StateMachine, ServiceState, SimpleActionState, IntrospectionServer + +import std_srvs.srv +import turtlesim.srv + +def main(): + rospy.init_node('smach_usecase_step_02') + + # Create a SMACH state machine + sm0 = StateMachine(outcomes=['succeeded','aborted','preempted']) + + # Open the container + with sm0: + # Reset turtlesim + StateMachine.add('RESET', + ServiceState('reset', std_srvs.srv.Empty), + {'succeeded':'SPAWN'}) + + # Create a second turtle + StateMachine.add('SPAWN', + ServiceState('spawn', turtlesim.srv.Spawn, + request = turtlesim.srv.SpawnRequest(0.0,0.0,0.0,'turtle2'))) + + # Attach a SMACH introspection server + sis = IntrospectionServer('smach_usecase_01', sm0, '/USE_CASE') + sis.start() + + # Execute SMACH tree + outcome = sm0.execute() + + # Signal ROS shutdown (kill threads in background) + rospy.spin() + + sis.stop() + +if __name__ == '__main__': + main() diff --git a/smach_tutorials/scripts/usecase_01/executive_step_04.py b/smach_tutorials/scripts/usecase_01/executive_step_04.py new file mode 100755 index 0000000..f6fe6d9 --- /dev/null +++ b/smach_tutorials/scripts/usecase_01/executive_step_04.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python +""" +Description: + +Usage: + $> roslaunch turtle_nodes.launch + $> ./executive_step_04.py + +Output: + [INFO] : State machine starting in initial state 'RESET' with userdata: + [] + [INFO] : State machine transitioning 'RESET':'succeeded'-->'SPAWN' + [INFO] : State machine transitioning 'SPAWN':'succeeded'-->'TELEPORT1' + [INFO] : State machine transitioning 'TELEPORT1':'succeeded'-->'TELEPORT2' + [INFO] : State machine transitioning 'TELEPORT2':'succeeded'-->'BIG' + [WARN] : Still waiting for action server 'turtle_shape1' to start... is it running? + [INFO] : Connected to action server 'turtle_shape1'. + [INFO] 1279655938.783058: State machine transitioning 'BIG':'succeeded'-->'SMALL' + [INFO] 1279655975.025202: State machine terminating 'SMALL':'succeeded':'succeeded' + +""" + +import roslib; roslib.load_manifest('smach_tutorials') +import rospy + +import threading + +import smach +from smach import StateMachine, ServiceState, SimpleActionState, IntrospectionServer + +import std_srvs.srv +import turtlesim.srv +import turtle_actionlib.msg + + +def main(): + rospy.init_node('smach_usecase_step_04') + + # Construct static goals + polygon_big = turtle_actionlib.msg.ShapeGoal(edges = 11, radius = 4.0) + polygon_small = turtle_actionlib.msg.ShapeGoal(edges = 6, radius = 0.5) + + # Create a SMACH state machine + sm0 = StateMachine(outcomes=['succeeded','aborted','preempted']) + + # Open the container + with sm0: + # Reset turtlesim + StateMachine.add('RESET', + ServiceState('reset', std_srvs.srv.Empty), + {'succeeded':'SPAWN'}) + + # Create a second turtle + StateMachine.add('SPAWN', + ServiceState('spawn', turtlesim.srv.Spawn, + request = turtlesim.srv.SpawnRequest(0.0,0.0,0.0,'turtle2')), + {'succeeded':'TELEPORT1'}) + + # Teleport turtle 1 + StateMachine.add('TELEPORT1', + ServiceState('turtle1/teleport_absolute', turtlesim.srv.TeleportAbsolute, + request = turtlesim.srv.TeleportAbsoluteRequest(5.0,1.0,0.0)), + {'succeeded':'TELEPORT2'}) + + # Teleport turtle 2 + StateMachine.add('TELEPORT2', + ServiceState('turtle2/teleport_absolute', turtlesim.srv.TeleportAbsolute, + request = turtlesim.srv.TeleportAbsoluteRequest(9.0,5.0,0.0)), + {'succeeded':'BIG'}) + + # Draw a large polygon with the first turtle + StateMachine.add('BIG', + SimpleActionState('turtle_shape1',turtle_actionlib.msg.ShapeAction, + goal = polygon_big), + {'succeeded':'SMALL'}) + + # Draw a small polygon with the second turtle + StateMachine.add('SMALL', + SimpleActionState('turtle_shape2',turtle_actionlib.msg.ShapeAction, + goal = polygon_small)) + + # Attach a SMACH introspection server + sis = IntrospectionServer('smach_usecase_01', sm0, '/USE_CASE') + sis.start() + + # Set preempt handler + smach.set_preempt_handler(sm0) + + # Execute SMACH tree in a separate thread so that we can ctrl-c the script + smach_thread = threading.Thread(target = sm0.execute) + smach_thread.start() + + # Signal handler + rospy.spin() + +if __name__ == '__main__': + main() diff --git a/smach_tutorials/scripts/usecase_01/executive_step_05.py b/smach_tutorials/scripts/usecase_01/executive_step_05.py new file mode 100755 index 0000000..97d1ae4 --- /dev/null +++ b/smach_tutorials/scripts/usecase_01/executive_step_05.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python +""" +Description: + +Usage: + $> roslaunch turtle_nodes.launch + $> ./executive_step_06.py + +Output: + [INFO] : State machine starting in initial state 'RESET' with userdata: + [] + [INFO] : State machine transitioning 'RESET':'succeeded'-->'SPAWN' + [INFO] : State machine transitioning 'SPAWN':'succeeded'-->'TELEPORT1' + [INFO] : State machine transitioning 'TELEPORT1':'succeeded'-->'TELEPORT2' + [INFO] : State machine transitioning 'TELEPORT2':'succeeded'-->'DRAW_SHAPES' + [INFO] : Concurrence starting with userdata: + [] + [WARN] : Still waiting for action server 'turtle_shape2' to start... is it running? + [WARN] : Still waiting for action server 'turtle_shape1' to start... is it running? + [INFO] : Connected to action server 'turtle_shape2'. + [INFO] : Connected to action server 'turtle_shape1'. + [INFO] : Concurrent Outcomes: {'SMALL': 'succeeded', 'BIG': 'succeeded'} + [INFO] : State machine terminating 'DRAW_SHAPES':'succeeded':'succeeded' +""" + +import roslib; roslib.load_manifest('smach_tutorials') +import rospy + +import threading + +import smach +from smach import StateMachine, ServiceState, SimpleActionState, IntrospectionServer, Concurrence + +import std_srvs.srv +import turtlesim.srv +import turtle_actionlib.msg + + +def main(): + rospy.init_node('smach_usecase_step_05') + + # Construct static goals + polygon_big = turtle_actionlib.msg.ShapeGoal(edges = 11, radius = 4.0) + polygon_small = turtle_actionlib.msg.ShapeGoal(edges = 6, radius = 0.5) + + # Create a SMACH state machine + sm0 = StateMachine(outcomes=['succeeded','aborted','preempted']) + + # Open the container + with sm0: + # Reset turtlesim + StateMachine.add('RESET', + ServiceState('reset', std_srvs.srv.Empty), + {'succeeded':'SPAWN'}) + + # Create a second turtle + StateMachine.add('SPAWN', + ServiceState('spawn', turtlesim.srv.Spawn, + request = turtlesim.srv.SpawnRequest(0.0,0.0,0.0,'turtle2')), + {'succeeded':'TELEPORT1'}) + + # Teleport turtle 1 + StateMachine.add('TELEPORT1', + ServiceState('turtle1/teleport_absolute', turtlesim.srv.TeleportAbsolute, + request = turtlesim.srv.TeleportAbsoluteRequest(5.0,1.0,0.0)), + {'succeeded':'TELEPORT2'}) + + # Teleport turtle 2 + StateMachine.add('TELEPORT2', + ServiceState('turtle2/teleport_absolute', turtlesim.srv.TeleportAbsolute, + request = turtlesim.srv.TeleportAbsoluteRequest(9.0,5.0,0.0)), + {'succeeded':'DRAW_SHAPES'}) + + # Draw some polygons + shapes_cc = Concurrence( + outcomes=['succeeded','aborted','preempted'], + default_outcome='aborted', + outcome_map = {'succeeded':{'BIG':'succeeded','SMALL':'succeeded'}}) + StateMachine.add('DRAW_SHAPES',shapes_cc) + with shapes_cc: + # Draw a large polygon with the first turtle + Concurrence.add('BIG', + SimpleActionState('turtle_shape1',turtle_actionlib.msg.ShapeAction, + goal = polygon_big)) + + # Draw a small polygon with the second turtle + Concurrence.add('SMALL', + SimpleActionState('turtle_shape2',turtle_actionlib.msg.ShapeAction, + goal = polygon_small)) + + + # Attach a SMACH introspection server + sis = IntrospectionServer('smach_usecase_01', sm0, '/USE_CASE') + sis.start() + + # Set preempt handler + smach.set_preempt_handler(sm0) + + # Execute SMACH tree in a separate thread so that we can ctrl-c the script + smach_thread = threading.Thread(target = sm0.execute) + smach_thread.start() + + # Signal handler + rospy.spin() + +if __name__ == '__main__': + main() diff --git a/smach_tutorials/scripts/usecase_01/executive_step_06.py b/smach_tutorials/scripts/usecase_01/executive_step_06.py new file mode 100755 index 0000000..802f153 --- /dev/null +++ b/smach_tutorials/scripts/usecase_01/executive_step_06.py @@ -0,0 +1,142 @@ +#!/usr/bin/env python +""" +Description: + +Usage: + $> roslaunch turtle_nodes.launch + $> ./executive_step_06.py + +Output: + [INFO] : State machine starting in initial state 'RESET' with userdata: + + [INFO] : State machine transitioning 'RESET':'succeeded'-->'SPAWN' + [INFO] : State machine transitioning 'SPAWN':'succeeded'-->'TELEPORT1' + [INFO] : State machine transitioning 'TELEPORT1':'succeeded'-->'TELEPORT2' + [INFO] : State machine transitioning 'TELEPORT2':'succeeded'-->'DRAW_SHAPES' + [INFO] : Concurrence starting with userdata: + [] + [INFO] : State machine starting in initial state 'DRAW_WITH_MONITOR' with userdata: + [] + [INFO] : Concurrence starting with userdata: + [] + [WARN] : Still waiting for action server 'turtle_shape1' to start... is it running? + [WARN] : Still waiting for action server 'turtle_shape2' to start... is it running? + [INFO] : Connected to action server 'turtle_shape2'. + [INFO] : Connected to action server 'turtle_shape1'. + [INFO] : Preempt requested on action 'turtle_shape2' + [INFO] : Preempt on action 'turtle_shape2' cancelling goal: + edges: 6 + radius: 0.5 + [INFO] : Concurrent Outcomes: {'MONITOR': 'invalid', 'DRAW': 'preempted'} + [INFO] : State machine transitioning 'DRAW_WITH_MONITOR':'interrupted'-->'WAIT_FOR_CLEAR' + [INFO] : State machine transitioning 'WAIT_FOR_CLEAR':'invalid'-->'DRAW_WITH_MONITOR' + [INFO] : Concurrence starting with userdata: + [] + [INFO] : Concurrent Outcomes: {'MONITOR': 'preempted', 'DRAW': 'succeeded'} + [INFO] : State machine terminating 'DRAW_WITH_MONITOR':'succeeded':'succeeded' + [INFO] : Concurrent Outcomes: {'SMALL': 'succeeded', 'BIG': 'succeeded'} + [INFO] : State machine terminating 'DRAW_SHAPES':'succeeded':'succeeded' + +""" + +import roslib; roslib.load_manifest('smach_tutorials') +import rospy + +import threading +from math import sqrt, pow + +import smach +from smach import StateMachine, ServiceState, SimpleActionState, MonitorState, IntrospectionServer, Concurrence + +import std_srvs.srv +import turtlesim.srv +import turtlesim.msg +import turtle_actionlib.msg + + +def main(): + rospy.init_node('smach_usecase_step_06') + + # Construct static goals + polygon_big = turtle_actionlib.msg.ShapeGoal(edges = 11, radius = 4.0) + polygon_small = turtle_actionlib.msg.ShapeGoal(edges = 6, radius = 0.5) + + # Create a SMACH state machine + sm0 = StateMachine(outcomes=['succeeded','aborted','preempted']) + + # Open the container + with sm0: + # Reset turtlesim + StateMachine.add('RESET', + ServiceState('reset', std_srvs.srv.Empty), + {'succeeded':'SPAWN'}) + + # Create a second turtle + StateMachine.add('SPAWN', + ServiceState('spawn', turtlesim.srv.Spawn, + request = turtlesim.srv.SpawnRequest(0.0,0.0,0.0,'turtle2')), + {'succeeded':'TELEPORT1'}) + + # Teleport turtle 1 + StateMachine.add('TELEPORT1', + ServiceState('turtle1/teleport_absolute', turtlesim.srv.TeleportAbsolute, + request = turtlesim.srv.TeleportAbsoluteRequest(5.0,1.0,0.0)), + {'succeeded':'TELEPORT2'}) + + # Teleport turtle 2 + StateMachine.add('TELEPORT2', + ServiceState('turtle2/teleport_absolute', turtlesim.srv.TeleportAbsolute, + request = turtlesim.srv.TeleportAbsoluteRequest(9.0,5.0,0.0)), + {'succeeded':'DRAW_SHAPES'}) + + # Draw some polygons + shapes_cc = Concurrence( + outcomes=['succeeded','aborted','preempted'], + default_outcome='aborted', + outcome_map = {'succeeded':{'BIG':'succeeded','SMALL':'succeeded'}}) + StateMachine.add('DRAW_SHAPES',shapes_cc) + with shapes_cc: + # Draw a large polygon with the first turtle + Concurrence.add('BIG', + SimpleActionState('turtle_shape1',turtle_actionlib.msg.ShapeAction, + goal = polygon_big)) + # Draw a small polygon with the second turtle + draw_monitor_cc = Concurrence( + ['succeeded','aborted','preempted'], + 'aborted', + child_termination_cb = lambda so: True, + outcome_map = { + 'succeeded':{'DRAW':'succeeded'}, + 'preempted':{'DRAW':'preempted','MONITOR':'preempted'}, + 'aborted':{'MONITOR':'invalid'}}) + Concurrence.add('SMALL',draw_monitor_cc) + with draw_monitor_cc: + Concurrence.add('DRAW', + SimpleActionState('turtle_shape2',turtle_actionlib.msg.ShapeAction, + goal = polygon_small)) + + def turtle_far_away(ud, msg): + """Returns True while turtle pose in msg is at least 1 unit away from (9,5)""" + if sqrt(pow(msg.x-9.0,2) + pow(msg.y-5.0,2)) > 2.0: + return True + return False + Concurrence.add('MONITOR', + MonitorState('/turtle1/pose',turtlesim.msg.Pose, + cond_cb = turtle_far_away)) + + # Attach a SMACH introspection server + sis = IntrospectionServer('smach_usecase_01', sm0, '/USE_CASE') + sis.start() + + # Set preempt handler + smach.set_preempt_handler(sm0) + + # Execute SMACH tree in a separate thread so that we can ctrl-c the script + smach_thread = threading.Thread(target = sm0.execute) + smach_thread.start() + + # Signal handler + rospy.spin() + +if __name__ == '__main__': + main() diff --git a/smach_tutorials/scripts/usecase_01/executive_step_07.py b/smach_tutorials/scripts/usecase_01/executive_step_07.py new file mode 100755 index 0000000..2ed6c81 --- /dev/null +++ b/smach_tutorials/scripts/usecase_01/executive_step_07.py @@ -0,0 +1,156 @@ +#!/usr/bin/env python +""" +Description: + +Usage: + $> roslaunch turtle_nodes.launch + $> ./executive_step_06.py + +Output: + [INFO] : State machine starting in initial state 'RESET' with userdata: + + [INFO] : State machine transitioning 'RESET':'succeeded'-->'SPAWN' + [INFO] : State machine transitioning 'SPAWN':'succeeded'-->'TELEPORT1' + [INFO] : State machine transitioning 'TELEPORT1':'succeeded'-->'TELEPORT2' + [INFO] : State machine transitioning 'TELEPORT2':'succeeded'-->'DRAW_SHAPES' + [INFO] : Concurrence starting with userdata: + [] + [INFO] : State machine starting in initial state 'DRAW_WITH_MONITOR' with userdata: + [] + [INFO] : Concurrence starting with userdata: + [] + [WARN] : Still waiting for action server 'turtle_shape1' to start... is it running? + [WARN] : Still waiting for action server 'turtle_shape2' to start... is it running? + [INFO] : Connected to action server 'turtle_shape2'. + [INFO] : Connected to action server 'turtle_shape1'. + [INFO] : Preempt requested on action 'turtle_shape2' + [INFO] : Preempt on action 'turtle_shape2' cancelling goal: + edges: 6 + radius: 0.5 + [INFO] : Concurrent Outcomes: {'MONITOR': 'invalid', 'DRAW': 'preempted'} + [INFO] : State machine transitioning 'DRAW_WITH_MONITOR':'interrupted'-->'WAIT_FOR_CLEAR' + [INFO] : State machine transitioning 'WAIT_FOR_CLEAR':'invalid'-->'DRAW_WITH_MONITOR' + [INFO] : Concurrence starting with userdata: + [] + [INFO] : Concurrent Outcomes: {'MONITOR': 'preempted', 'DRAW': 'succeeded'} + [INFO] : State machine terminating 'DRAW_WITH_MONITOR':'succeeded':'succeeded' + [INFO] : Concurrent Outcomes: {'SMALL': 'succeeded', 'BIG': 'succeeded'} + [INFO] : State machine terminating 'DRAW_SHAPES':'succeeded':'succeeded' + +""" + +import roslib; roslib.load_manifest('smach_tutorials') +import rospy + +import threading +from math import sqrt, pow + +import smach +from smach import StateMachine, ServiceState, SimpleActionState, MonitorState, IntrospectionServer, Concurrence + +import std_srvs.srv +import turtlesim.srv +import turtlesim.msg +import turtle_actionlib.msg + + +def main(): + rospy.init_node('smach_usecase_step_06') + + # Construct static goals + polygon_big = turtle_actionlib.msg.ShapeGoal(edges = 11, radius = 4.0) + polygon_small = turtle_actionlib.msg.ShapeGoal(edges = 6, radius = 0.5) + + # Create a SMACH state machine + sm0 = StateMachine(outcomes=['succeeded','aborted','preempted']) + + # Open the container + with sm0: + # Reset turtlesim + StateMachine.add('RESET', + ServiceState('reset', std_srvs.srv.Empty), + {'succeeded':'SPAWN'}) + + # Create a second turtle + StateMachine.add('SPAWN', + ServiceState('spawn', turtlesim.srv.Spawn, + request = turtlesim.srv.SpawnRequest(0.0,0.0,0.0,'turtle2')), + {'succeeded':'TELEPORT1'}) + + # Teleport turtle 1 + StateMachine.add('TELEPORT1', + ServiceState('turtle1/teleport_absolute', turtlesim.srv.TeleportAbsolute, + request = turtlesim.srv.TeleportAbsoluteRequest(5.0,1.0,0.0)), + {'succeeded':'DRAW_SHAPES'}) + + # Draw some polygons + shapes_cc = Concurrence( + outcomes=['succeeded','aborted','preempted'], + default_outcome='aborted', + outcome_map = {'succeeded':{'BIG':'succeeded','SMALL':'succeeded'}}) + StateMachine.add('DRAW_SHAPES',shapes_cc) + with shapes_cc: + # Draw a large polygon with the first turtle + Concurrence.add('BIG', + SimpleActionState('turtle_shape1',turtle_actionlib.msg.ShapeAction, + goal = polygon_big)) + + # Draw a small polygon with the second turtle + small_shape_sm = StateMachine(outcomes=['succeeded','aborted','preempted']) + Concurrence.add('SMALL',small_shape_sm) + with small_shape_sm: + # Teleport turtle 2 + StateMachine.add('TELEPORT2', + ServiceState('turtle2/teleport_absolute', turtlesim.srv.TeleportAbsolute, + request = turtlesim.srv.TeleportAbsoluteRequest(9.0,5.0,0.0)), + {'succeeded':'DRAW_WITH_MONITOR'}) + + # Construct a concurrence for the shape action and the monitor + draw_monitor_cc = Concurrence( + ['succeeded','aborted','preempted','interrupted'], + 'aborted', + child_termination_cb = lambda so: True, + outcome_map = { + 'succeeded':{'DRAW':'succeeded'}, + 'preempted':{'DRAW':'preempted','MONITOR':'preempted'}, + 'interrupted':{'MONITOR':'invalid'}}) + + StateMachine.add('DRAW_WITH_MONITOR', + draw_monitor_cc, + {'interrupted':'WAIT_FOR_CLEAR'}) + + with draw_monitor_cc: + Concurrence.add('DRAW', + SimpleActionState('turtle_shape2',turtle_actionlib.msg.ShapeAction, + goal = polygon_small)) + def turtle_far_away(ud, msg): + """Returns True while turtle pose in msg is at least 1 unit away from (9,5)""" + if sqrt(pow(msg.x-9.0,2) + pow(msg.y-5.0,2)) > 2.0: + return True + return False + Concurrence.add('MONITOR', + MonitorState('/turtle1/pose',turtlesim.msg.Pose, + cond_cb = turtle_far_away)) + + StateMachine.add('WAIT_FOR_CLEAR', + MonitorState('/turtle1/pose',turtlesim.msg.Pose, + cond_cb = lambda ud,msg: not turtle_far_away(ud,msg)), + {'valid':'WAIT_FOR_CLEAR','invalid':'TELEPORT2'}) + + + # Attach a SMACH introspection server + sis = IntrospectionServer('smach_usecase_01', sm0, '/USE_CASE') + sis.start() + + # Set preempt handler + smach.set_preempt_handler(sm0) + + # Execute SMACH tree in a separate thread so that we can ctrl-c the script + smach_thread = threading.Thread(target = sm0.execute) + smach_thread.start() + + # Signal handler + rospy.spin() + +if __name__ == '__main__': + main() diff --git a/smach_tutorials/scripts/usecase_01/turtle_nodes.launch b/smach_tutorials/scripts/usecase_01/turtle_nodes.launch new file mode 100644 index 0000000..a0ee91d --- /dev/null +++ b/smach_tutorials/scripts/usecase_01/turtle_nodes.launch @@ -0,0 +1,9 @@ + + + + + + + + + From 2c63a763a87b3d389698280720eb1a12a1ec0171 Mon Sep 17 00:00:00 2001 From: "Isaac I.Y. Saito" <130s@2000.jukuin.keio.ac.jp> Date: Thu, 8 Jun 2017 12:25:56 -0700 Subject: [PATCH 2/5] Batch picking newer changes from a forked repo https://github.com/rhaschke/executive_smach_tutorials/commits/indigo-devel. --- smach_tutorials/CMakeLists.txt | 11 +++- smach_tutorials/examples/actionlib2_test.py | 1 - smach_tutorials/examples/actionlib_test.py | 2 +- smach_tutorials/examples/concurrence.py | 1 - smach_tutorials/examples/concurrence2.py | 1 - smach_tutorials/examples/sequence.py | 1 - smach_tutorials/examples/state_machine.py | 1 - .../examples/state_machine_nesting.py | 1 - .../examples/state_machine_nesting2.py | 1 - .../examples/state_machine_simple.py | 2 - .../state_machine_simple_introspection.py | 62 +++++++++++++++++++ smach_tutorials/examples/user_data.py | 1 - smach_tutorials/examples/user_data2.py | 1 - smach_tutorials/package.xml | 3 +- .../scripts/usecase_01/executive_step_01.py | 1 - .../scripts/usecase_01/executive_step_02.py | 1 - .../scripts/usecase_01/executive_step_03.py | 1 - .../scripts/usecase_01/executive_step_04.py | 1 - .../scripts/usecase_01/executive_step_05.py | 1 - .../scripts/usecase_01/executive_step_06.py | 1 - .../scripts/usecase_01/executive_step_07.py | 1 - 21 files changed, 75 insertions(+), 21 deletions(-) create mode 100755 smach_tutorials/examples/state_machine_simple_introspection.py diff --git a/smach_tutorials/CMakeLists.txt b/smach_tutorials/CMakeLists.txt index 2d857e0..e1dc6ce 100644 --- a/smach_tutorials/CMakeLists.txt +++ b/smach_tutorials/CMakeLists.txt @@ -3,7 +3,16 @@ project(smach_tutorials) find_package(catkin REQUIRED COMPONENTS actionlib_msgs message_generation) -add_action_files(DIRECTORY action) +add_action_files( + DIRECTORY action + FILES Test.action) generate_messages(DEPENDENCIES actionlib_msgs) catkin_package(CATKIN_DEPENDS actionlib_msgs message_runtime) + +install( + DIRECTORY examples + DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} + USE_SOURCE_PERMISSIONS + FILES_MATCHING PATTERN "*.py" +) \ No newline at end of file diff --git a/smach_tutorials/examples/actionlib2_test.py b/smach_tutorials/examples/actionlib2_test.py index f102bdd..8a70ef6 100755 --- a/smach_tutorials/examples/actionlib2_test.py +++ b/smach_tutorials/examples/actionlib2_test.py @@ -1,6 +1,5 @@ #!/usr/bin/env python -import roslib; roslib.load_manifest('smach_tutorials') import rospy import smach import smach_ros diff --git a/smach_tutorials/examples/actionlib_test.py b/smach_tutorials/examples/actionlib_test.py index 5378f9b..d476de1 100755 --- a/smach_tutorials/examples/actionlib_test.py +++ b/smach_tutorials/examples/actionlib_test.py @@ -18,11 +18,11 @@ [INFO] : State machine terminating 'GOAL_STATIC':'aborted':'succeeded' """ -import roslib; roslib.load_manifest('smach_tutorials') import rospy import smach import smach_ros +from smach_tutorials.msg import TestAction, TestGoal from actionlib import * from actionlib_msgs.msg import * diff --git a/smach_tutorials/examples/concurrence.py b/smach_tutorials/examples/concurrence.py index 84b0c15..3462445 100755 --- a/smach_tutorials/examples/concurrence.py +++ b/smach_tutorials/examples/concurrence.py @@ -23,7 +23,6 @@ [INFO] : Concurrent Outcomes: {'SET': 'set_it', 'GET': 'got_it'} """ -import roslib; roslib.load_manifest('smach_tutorials') import rospy import smach import smach_ros diff --git a/smach_tutorials/examples/concurrence2.py b/smach_tutorials/examples/concurrence2.py index d45204e..bcf1243 100755 --- a/smach_tutorials/examples/concurrence2.py +++ b/smach_tutorials/examples/concurrence2.py @@ -1,6 +1,5 @@ #!/usr/bin/env python -import roslib; roslib.load_manifest('smach_tutorials') import rospy import smach import smach_ros diff --git a/smach_tutorials/examples/sequence.py b/smach_tutorials/examples/sequence.py index 62fc865..0e73589 100755 --- a/smach_tutorials/examples/sequence.py +++ b/smach_tutorials/examples/sequence.py @@ -17,7 +17,6 @@ [INFO] : State machine terminating 'BAZ':'done':'succeeded' """ -import roslib; roslib.load_manifest('smach_tutorials') import rospy import smach import smach_ros diff --git a/smach_tutorials/examples/state_machine.py b/smach_tutorials/examples/state_machine.py index 81af47b..d5afc95 100755 --- a/smach_tutorials/examples/state_machine.py +++ b/smach_tutorials/examples/state_machine.py @@ -15,7 +15,6 @@ """ -import roslib; roslib.load_manifest('smach_tutorials') import rospy import smach import smach_ros diff --git a/smach_tutorials/examples/state_machine_nesting.py b/smach_tutorials/examples/state_machine_nesting.py index 6cd92dc..55d41a0 100755 --- a/smach_tutorials/examples/state_machine_nesting.py +++ b/smach_tutorials/examples/state_machine_nesting.py @@ -21,7 +21,6 @@ """ -import roslib; roslib.load_manifest('smach_tutorials') import rospy import smach import smach_ros diff --git a/smach_tutorials/examples/state_machine_nesting2.py b/smach_tutorials/examples/state_machine_nesting2.py index 7d64c11..bac37df 100755 --- a/smach_tutorials/examples/state_machine_nesting2.py +++ b/smach_tutorials/examples/state_machine_nesting2.py @@ -1,6 +1,5 @@ #!/usr/bin/env python -import roslib; roslib.load_manifest('smach_tutorials') import rospy import smach import smach_ros diff --git a/smach_tutorials/examples/state_machine_simple.py b/smach_tutorials/examples/state_machine_simple.py index 2d87822..3d40057 100755 --- a/smach_tutorials/examples/state_machine_simple.py +++ b/smach_tutorials/examples/state_machine_simple.py @@ -1,9 +1,7 @@ #!/usr/bin/env python -import roslib; roslib.load_manifest('smach_tutorials') import rospy import smach -import smach_ros # define state Foo class Foo(smach.State): diff --git a/smach_tutorials/examples/state_machine_simple_introspection.py b/smach_tutorials/examples/state_machine_simple_introspection.py new file mode 100755 index 0000000..2b76526 --- /dev/null +++ b/smach_tutorials/examples/state_machine_simple_introspection.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python + +import rospy +import smach +import smach_ros + +# define state Foo +class Foo(smach.State): + def __init__(self): + smach.State.__init__(self, outcomes=['outcome1','outcome2']) + self.counter = 0 + + def execute(self, userdata): + rospy.loginfo('Executing state FOO') + if self.counter < 3: + self.counter += 1 + return 'outcome1' + else: + return 'outcome2' + + +# define state Bar +class Bar(smach.State): + def __init__(self): + smach.State.__init__(self, outcomes=['outcome2']) + + def execute(self, userdata): + rospy.loginfo('Executing state BAR') + return 'outcome2' + + + + +# main +def main(): + rospy.init_node('smach_example_state_machine') + + # Create a SMACH state machine + sm = smach.StateMachine(outcomes=['outcome4', 'outcome5']) + + # Open the container + with sm: + # Add states to the container + smach.StateMachine.add('FOO', Foo(), + transitions={'outcome1':'BAR', + 'outcome2':'outcome4'}) + smach.StateMachine.add('BAR', Bar(), + transitions={'outcome2':'FOO'}) + + # Create and start the introspection server + sis = smach_ros.IntrospectionServer('my_smach_introspection_server', sm, '/SM_ROOT') + sis.start() + + # Execute SMACH plan + outcome = sm.execute() + + # Wait for ctrl-c to stop the application + rospy.spin() + sis.stop() + +if __name__ == '__main__': + main() diff --git a/smach_tutorials/examples/user_data.py b/smach_tutorials/examples/user_data.py index 322c29c..e087982 100755 --- a/smach_tutorials/examples/user_data.py +++ b/smach_tutorials/examples/user_data.py @@ -15,7 +15,6 @@ [INFO] : State machine terminating 'GET':'got_it':'succeeded' """ -import roslib; roslib.load_manifest('smach_tutorials') import rospy import smach import smach_ros diff --git a/smach_tutorials/examples/user_data2.py b/smach_tutorials/examples/user_data2.py index e61f890..056bd6d 100755 --- a/smach_tutorials/examples/user_data2.py +++ b/smach_tutorials/examples/user_data2.py @@ -1,6 +1,5 @@ #!/usr/bin/env python -import roslib; roslib.load_manifest('smach_tutorials') import rospy import smach import smach_ros diff --git a/smach_tutorials/package.xml b/smach_tutorials/package.xml index 3230f5f..23447ad 100644 --- a/smach_tutorials/package.xml +++ b/smach_tutorials/package.xml @@ -5,8 +5,9 @@ This package contains numerous examples of how to use SMACH. See the examples directory. - Isaac I. Y. Saito Jonathan Bohren + Robert Haschke + Isaac I. Y. Saito BSD http://wiki.ros.org/smach_tutorials diff --git a/smach_tutorials/scripts/usecase_01/executive_step_01.py b/smach_tutorials/scripts/usecase_01/executive_step_01.py index 20aaaa6..01c81c2 100755 --- a/smach_tutorials/scripts/usecase_01/executive_step_01.py +++ b/smach_tutorials/scripts/usecase_01/executive_step_01.py @@ -18,7 +18,6 @@ [ERROR] : Container consistency check failed. """ -import roslib; roslib.load_manifest('smach_tutorials') import rospy import smach diff --git a/smach_tutorials/scripts/usecase_01/executive_step_02.py b/smach_tutorials/scripts/usecase_01/executive_step_02.py index c96cab8..7e26ff4 100755 --- a/smach_tutorials/scripts/usecase_01/executive_step_02.py +++ b/smach_tutorials/scripts/usecase_01/executive_step_02.py @@ -14,7 +14,6 @@ """ -import roslib; roslib.load_manifest('smach_tutorials') import rospy import threading diff --git a/smach_tutorials/scripts/usecase_01/executive_step_03.py b/smach_tutorials/scripts/usecase_01/executive_step_03.py index da27185..23c05e3 100755 --- a/smach_tutorials/scripts/usecase_01/executive_step_03.py +++ b/smach_tutorials/scripts/usecase_01/executive_step_03.py @@ -14,7 +14,6 @@ """ -import roslib; roslib.load_manifest('smach_tutorials') import rospy import threading diff --git a/smach_tutorials/scripts/usecase_01/executive_step_04.py b/smach_tutorials/scripts/usecase_01/executive_step_04.py index f6fe6d9..46bdb0e 100755 --- a/smach_tutorials/scripts/usecase_01/executive_step_04.py +++ b/smach_tutorials/scripts/usecase_01/executive_step_04.py @@ -20,7 +20,6 @@ """ -import roslib; roslib.load_manifest('smach_tutorials') import rospy import threading diff --git a/smach_tutorials/scripts/usecase_01/executive_step_05.py b/smach_tutorials/scripts/usecase_01/executive_step_05.py index 97d1ae4..3745514 100755 --- a/smach_tutorials/scripts/usecase_01/executive_step_05.py +++ b/smach_tutorials/scripts/usecase_01/executive_step_05.py @@ -23,7 +23,6 @@ [INFO] : State machine terminating 'DRAW_SHAPES':'succeeded':'succeeded' """ -import roslib; roslib.load_manifest('smach_tutorials') import rospy import threading diff --git a/smach_tutorials/scripts/usecase_01/executive_step_06.py b/smach_tutorials/scripts/usecase_01/executive_step_06.py index 802f153..91a544a 100755 --- a/smach_tutorials/scripts/usecase_01/executive_step_06.py +++ b/smach_tutorials/scripts/usecase_01/executive_step_06.py @@ -39,7 +39,6 @@ """ -import roslib; roslib.load_manifest('smach_tutorials') import rospy import threading diff --git a/smach_tutorials/scripts/usecase_01/executive_step_07.py b/smach_tutorials/scripts/usecase_01/executive_step_07.py index 2ed6c81..471fa5b 100755 --- a/smach_tutorials/scripts/usecase_01/executive_step_07.py +++ b/smach_tutorials/scripts/usecase_01/executive_step_07.py @@ -39,7 +39,6 @@ """ -import roslib; roslib.load_manifest('smach_tutorials') import rospy import threading From 673f4464d1eb25001ccdfcb3b0a829d132f69f19 Mon Sep 17 00:00:00 2001 From: "Isaac I.Y. Saito" <130s@2000.jukuin.keio.ac.jp> Date: Mon, 21 May 2018 09:53:04 -0700 Subject: [PATCH 3/5] [smach_tutorials] Update author, maintainer. --- smach_tutorials/package.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smach_tutorials/package.xml b/smach_tutorials/package.xml index 23447ad..3834b4d 100644 --- a/smach_tutorials/package.xml +++ b/smach_tutorials/package.xml @@ -6,7 +6,7 @@ See the examples directory. Jonathan Bohren - Robert Haschke + Robert Haschke Isaac I. Y. Saito BSD From ddfc8bcd56c2d1dea6f2aaf18d8df61f2280abaa Mon Sep 17 00:00:00 2001 From: "Isaac I.Y. Saito" <130s@2000.jukuin.keio.ac.jp> Date: Mon, 21 May 2018 10:05:03 -0700 Subject: [PATCH 4/5] [smach_tutorials] Install example Python scripts. --- smach_tutorials/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smach_tutorials/CMakeLists.txt b/smach_tutorials/CMakeLists.txt index e1dc6ce..2b9d4cd 100644 --- a/smach_tutorials/CMakeLists.txt +++ b/smach_tutorials/CMakeLists.txt @@ -11,7 +11,7 @@ generate_messages(DEPENDENCIES actionlib_msgs) catkin_package(CATKIN_DEPENDS actionlib_msgs message_runtime) install( - DIRECTORY examples + DIRECTORY examples scripts DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} USE_SOURCE_PERMISSIONS FILES_MATCHING PATTERN "*.py" From f77d02863eefbd491674994e58ed654321e1bd72 Mon Sep 17 00:00:00 2001 From: Daniel Stonier Date: Thu, 2 Apr 2020 09:29:48 -0400 Subject: [PATCH 5/5] trailing newline --- smach_tutorials/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smach_tutorials/CMakeLists.txt b/smach_tutorials/CMakeLists.txt index 2b9d4cd..8ffbcfc 100644 --- a/smach_tutorials/CMakeLists.txt +++ b/smach_tutorials/CMakeLists.txt @@ -15,4 +15,4 @@ install( DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} USE_SOURCE_PERMISSIONS FILES_MATCHING PATTERN "*.py" -) \ No newline at end of file +)