Giter Site home page Giter Site logo

executive_smach's Introduction

SMACH

SMACH is a task-level python execution framework for rapidly composing complex robot behaviors.

travis

executive_smach's People

Contributors

130s avatar ahcorde avatar at-wat avatar bfalacerda avatar bgromov avatar bulwahn avatar cburbridge avatar contradict avatar gerardcanal avatar inigomoreno avatar jbohren avatar jihoonl avatar jorgenfb avatar lesire avatar loyvanbeek avatar matias-pavez-dx avatar mikaelarguedas avatar mikeferguson avatar nandishjpatel avatar rayman avatar sloretz avatar t045t avatar tfoote avatar tydan923 avatar wongyikben avatar yikben-wong-dx avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

executive_smach's Issues

SimpleActionState will hang forever if action node dies

On initialization the SimpleActionState will spin up a thread to wait for the action server to become active. This is the only time the state is waiting and checking for the action server to become available. If this initial check succeeds and the action node dies at a later time, then when the SimpleActionState is executed it will no longer check if the server is active, just send the goal and hang there forever.

preempt_timeout not used in SimpleActionState

Hi,

I wanted to use the preempt_timeout in combination with the SimpleActionState, but it would never work for me. When I was looking through the code I did not really find anything where this value is actually used. Can you please give a hint what is missing to use it?

Thanks

ActionServerWrapper: Feedback of nested state machines is not published

My wrapped SMs usually contain other more complex SMs in order to avoid code duplication. I noticed that the feedback publishing does not work nicely with nested SMs. I.e. for each nested SM its feedback gets only published on time, when that SM finishes. I guess that is due to the feedback being published during the transition callback of the wrapped SM.

SimpleActionState uses inappropriate default outcomes value

In simple_action_state.py, the construction of class SimpleActionState is as follows:

class SimpleActionState(RosState):
    """Simple action client state.

    Use this class to represent an actionlib as a state in a state machine.
    """

    # Meta-states for this action
    WAITING_FOR_SERVER = 0
    INACTIVE = 1
    ACTIVE = 2
    PREEMPTING = 3
    COMPLETED = 4

    def __init__(self,
            node,
            # Action info
            action_name,
            action_spec,
            # Default goal
            goal = None,
            goal_key = None,
            goal_slots = [],
            goal_cb = None,
            goal_cb_args = [],
            goal_cb_kwargs = {},
            # Result modes
            result_key = None,
            result_slots = [],
            result_cb = None,
            result_cb_args = [],
            result_cb_kwargs = {},
            # Keys
            input_keys = [],
            output_keys = [],
            outcomes = [],
            # Timeouts
            exec_timeout = None,
            preempt_timeout = Duration(seconds=60.0),
            server_wait_timeout = Duration(seconds=60.0)
            ):

    RosState.__init__(self, node, outcomes=['succeeded','aborted','preempted'])

This will cause problem when I define outcomes different from ['succeeded','aborted','preempted']. It should look like:

RosState.__init__(self, node, outcomes=outcomes)

is_running() works incorrectly, if execution fails with an exception

If state machine's state fails with an exception, _is_running flag is not lowered, although the state machine obviously is not running.

Example code:

from smach import StateMachine, State

class FailingState(State):
    def execute(self, ud):
        0/0  # exception

s = StateMachine(outcomes=[])
with s:
    StateMachine.add('foo', FailingState())

try:
    s.execute()
except:
    pass

print 'running', s.is_running()

Output:

<...>
ZeroDivisionError: integer division or modulo by zero
<...>
running True

Isn't is an issue?

SimpleActionState.execute will block when "ctrl+c" pressed

1, if "ctrl+c" pressed, "done_cb" will never be called by SimpleActionClient because rospy was shutdown, action client cannot receive action server's state message. So, it will be blocked by the code:"self._done_cond.wait()".

2, in multi-thread mode, run "SimpleActionState.execute()" in child thread, after "ctrl+c" pressed, call "SimpleActionState.request_preempt()" in main thread is not working because that, rospy was shutdown, call SimpleActionClient.cancel_goal() will be failed, So, it will be blocked by the code:"self._done_cond.wait()" again.

Suggestion:
check rospy's state in SimpleAction.request_preempt() function, if rospy is shutdown, call self._done_cond.notify() to notify SimpleAction.execute() function to return "preempted" state.

Publishing SMACH introspection structure message failed.

Hi everyone,

I've been using smach for robotic manipulation and everything works as expected, except SMACH Introspection Server in
combination with smach_viewer.

I'm using this tutorial to initialize SMACH Introspection Server as described.
After creating StateMachine instance and adding all the states I'm creating introspection server as proposed.

After running my state machine, which is mostly consisted of SimpleActionState classes which wrap my ROS action servers. State machine works and sends goals to wrapped action servers which then execute and propagate as commanded in transitions dict.

I'm currently not using or passing any UserData. StateMachine is consistent and I should be able to visualize it.
However, upon running rosrun smach_viewer smach_viewer I'm constantly getting following message:
[ERROR] [1621512586.661582]: Publishing SMACH introspection structure message failed.

However, when inspecting rostopic echo /smach_introspection_server/smach/container_status I get following:

header: 
  seq: 1
  stamp: 
    secs: 1621512682
    nsecs: 499099016
  frame_id: ''
path: "SM_ROOT"
initial_states: [TRIGGER_GO_TO_PREAPPROACH_POSE]
active_states: [TRIGGER_GO_TO_PREAPPROACH_POSE]
local_data: !!binary |
  gAJ9cQAu
info: "HEARTBEAT"

Whereas when switching states, message looks like this:

header: 
  seq: 19
  stamp: 
    secs: 1621512713
    nsecs: 225410938
  frame_id: ''
path: "SM_ROOT"
initial_states: [TRIGGER_GO_TO_PREAPPROACH_POSE]
active_states: [TRIGGER_SERVO_CONTROL]
local_data: !!binary |
  gAJ9cQAu
info: "(<smach.user_data.UserData object at 0x7f0b2040a2d0>, ['TRIGGER_SERVO_CONTROL']),\
  \ {}"

I'm not sure what it is, but this local_data and info when passing between states are quite suspicious. I'm thinking that maybe missing UserData could be cause for: [ERROR] [1621512586.661582]: Publishing SMACH introspection structure message failed.

My question is:
What causes failure when publishing SMACH introspection structure message to smach_viewer?

I'm using Linux 18.04 and ROS Melodic.

IntrospectionServer and PicklingError: explicitly exclude variables from being pickled

I have some variables in a state which should not be pickled by the IntrospectionServer. This is of interest for me, first of all, because I get an PicklingError right now (it seems that SwigPyObjects can't be pickled), but more generally, because they are too big and I don't want them to be serialized.

I guess I'm looking for something like this:

sis = smach_ros.IntrospectionServer(
    'sis', sm, '/SM_ROOT', 
    exclude={
        BlaState: ["variable1", "variable2"],
        FooState: ["variableX"]
    }
)

Is there a way to do this? If not, could we implement something like this? I'd be happy to help!

Porting SMACH to ROS2

Hi guys,
I am porting all my ROS applications to ROS2, and therefore, as I use SMACH to control some part of my autonomous robot mission, I need to port SMACH to ROS2.
I looked at all the GitHub forks of SMACH but found no ROS2 version.

I will start a ROS2 branch on a fork, and try to port everything to ROS2 during the coming days.

First, have I missed something? Is there a ROS2 version under development?
If not, if anybody wants to contribute in the porting, and/or testing of the ROS2 version, let me know!

Best,

SMACH with no ROS

Hi.
Using a Ubuntu with Melodic ROS Distro I'm able to import smach library on python only even not using any ROS functionality.
So, I'm wondering if is it possible to use smach library without ROS on a Windows or Raspbian OS .
Does somebody knows if that's possible?

Problem with rosrun smach_viewer smach_viewer.py

Hi,
I read the smach tutorial there is no problem until I run smach_viewer command.
Smach tutorial
rosrun smach_viewer smach_viewer.py
the terminal shows the below error :
[rospack] Error: package 'smach_viewer' not found
I checked the /opt/ros/noetic/share path there is no smach_viewer package. just smach.
the question is: What should I do to see the graphical representation of my smach code?
I'm using ubuntu focal, Noetic ROS, and python3.
thanks a lot for your support.

improve performance of introspection server

Currently, the SMACH introspection server creates one publisher per SMACH container. For large SMACH machines, this is not very efficient. Instead, all of the status and structure messages should be aggregated and then broadcast at once from a given introspection server.

Constant userdata passing (or constant remapping)

Is it possible to define transitions between states with constant userdata?

I want the input variable var1 of FOO to be set to test every time after executing BAR, without modifying the internals of BAR.
Something along the lines of:

smach.StateMachine.add('BAR', Bar(), 
                       transitions={'outcome1':'FOO'},
                       remapping={'bar_counter_in':'sm_counter'},
                       constants={'var1':'test'})

Or even making the remappings aware that if some key does not exist as an input or output of BAR, it should be treated as a constant:

smach.StateMachine.add('BAR', Bar(), 
                       transitions={'outcome1':'FOO'},
                       remapping={'bar_counter_in':'sm_counter', 'test': 'var1'})

Or even:

smach.StateMachine.add('BAR', Bar(), 
                       transitions={'outcome1':['FOO',{'var1': 'test'}]},
                       remapping={'bar_counter_in':'sm_counter'})

This would be VERY useful to me, as I have different instances of the same state pointing to another state, but depending on which instance I am coming from, I want different data to be passed in a constant manner.

add_auto not behaving as expected

When using the add_auto function, the resulting transitions are not as expected from the documentation. I am not sure if it is a bug or I am misreading the text (if so, many of my colleagues have done the exact mistake)

Using this method, I add connector_outcomes for all the outcomes of the state. I then use transitions with the intent of overwriting some of the outcomes defined by connector_outcomes, following the documentation:

@param transitions: A dictionary mapping state outcomes to other state
        labels. If one of these transitions follows the connector outcome
        specified in the constructor, **the provided transition will override
        the automatically generated connector transition**.

However, when running this, the transition specified do not actually overwrite connector_outcomes and instead are ignored.

Example (modified from the tutorial)

class Foo(smach.State):
    def __init__(self):
        smach.State.__init__(self, outcomes=['succeeded','failed'])
        self.counter = 0

    def execute(self, userdata):
        rospy.loginfo('Executing state FOO')
        if self.counter < 3:
            self.counter += 1
            return 'succeeded'
        else:
            return 'failed'


# define state Bar
class Bar(smach.State):
    def __init__(self):
        smach.State.__init__(self, outcomes=['succeeded'])

    def execute(self, userdata):
        rospy.loginfo('Executing state BAR')
        return 'succeeded'


# main
def main():
    rospy.init_node('smach_example_state_machine')

    # Create a SMACH state machine
    sm = smach.StateMachine(outcomes=['succeeded', 'failed'])

    # Open the container
    with sm:
        # Add states to the container
        smach.StateMachine.add_auto('FOO', Foo(),
                                    connector_outcomes=['succeeded', 'failed'],
                                    transitions={'failed': 'failed'})

        smach.StateMachine.add('BAR', Bar(),
                               transitions={'succeeded':'succeeded'})

    # Execute SMACH plan
    outcome = sm.execute()

if __name__ == '__main__':
    main()

My expectation in this case is the Foo will have transition from succeeded to BAR and from failed to failed. However, visualizing this SM, I get:
image

Using same data in different states of SMACH Concurrent-container

Hey there,
I'm working on a project on which I have concurrent container in which two state machines run concurrently. The states inside those concurrently running state machines need to access and modify the same userdata. A more specific description with code can be found here:
Is there anyway I can use and modify a parent userdata in child-states?

Input keys check on StateMachine doesn't check if the keys are already there

Hi,
I'm not sure if this is actively maintained or not, but worth to ask.

I think there's an issue with the checks of input keys in statemachines. Example:
I define a StateMachine with defined input keys, and then set the input keys manually on the userdata such as:

sm = smach.StateMachine(outcomes=['aborted','succeded'], input_keys=['a'])
sm.userdata.a = 1

This will trigger an error Userdata key 'a' not available. Available keys are: []. And this is not fully true, as the keys are there. This seems to come from

def execute(self, parent_ud = smach.UserData()):

Where the parent_ud is set to empty userdata instead of None. This is only used in the _copy_input/output_keys, where the error is triggered:
if parent_ud is not None:

There may be the need to still check the input_keys are available or pre-written (or maybe not, as I guess the error would trigger when the keys are used later on by one of the States). This could be done inside check_consistency.

Happy to make a PR if this makes sense, but would be good to know if there's anyone there to check it and merge it!

Concurrence race conditions on request_preempt

I believe there are race condition problems with smach containers in general, here demonstrated for Concurrence state.

Scenario:

  1. A preempt request is called on the concurrence state right when the children have completed, but the concurrence state itself is still executing.
  2. The concurrence state will then (sometimes) claim that the preempt was serviced, though none of its children serviced the preempt. This means that the preempt request is sort of lost.

Below a script that demonstrates the problem, it does not require any ROS code running. The script just creates this state machine.

  • Sequence(top)
    • Concurrence(Cc)
      • Delay1(0.3 seconds) (for demonstration purposes: the only child of Concurrence)
    • Delay2(0.5 seconds)

When the script is preempted after approx. 0.3 seconds, I would expect either Delay1 or Delay2 to return 'preempted'.

Test script:

#!/usr/bin/env python

from time import sleep
from smach import State, Concurrence, Sequence
from threading import Timer


class DelayState(State):
    """Delay state for testing purposes"""

    def __init__(self, delay):
        State.__init__(self, outcomes=['succeeded', 'preempted'])
        self.delay = delay

    def execute(self, userdata):
        # A better delay state should be able to preempt during its sleep state
        sleep(self.delay)
        if self.preempt_requested():
            self.service_preempt()
            return 'preempted'
        return 'succeeded'


def test_concurrence_preempt():
    """test demonstrating race condition

    Creates a state machine:

    - Sequence(top)
      - Concurrence(Cc)
        - Delay1(0.3 seconds) (for demonstration purposes: the only child of Concurrence)
      - Delay2(0.5 seconds)

    When preempting the state machine after ~0.3 seconds, the machine
    is expected to return 'preempted'
    """

    def outcome_cb(outcome_map):
        if 'preempted' in outcome_map.values():
            return 'preempted'
        return 'succeeded'

    cc = Concurrence(
        outcomes=['succeeded', 'preempted'],
        default_outcome='succeeded',
        outcome_cb=outcome_cb)
    with cc:
        Concurrence.add('Delay1', DelayState(delay=0.3))

    top = Sequence(outcomes=['succeeded', 'preempted'],
                   connector_outcome='succeeded')

    with top:
        Sequence.add('Cc', cc)
        Sequence.add('Delay2', DelayState(delay=0.5))

    # Execute state machine and try cancel after various milliseconds of delay
    for msDelay in range(290, 330, 2):
        print ('Cancel after delay {}'.format(msDelay))
        t = Timer(msDelay/1000.0, top.request_preempt)
        t.start()
        if top.execute() != 'preempted':
            print ('===== TEST FAILED, delay: %sms =====', msDelay)
            return

    print ('===== TEST OK =====')


test_concurrence_preempt()

The above scenario is one of several cases, where a preempt request is kind of lost. I believe similar problems exist for Sequence container as well.

I will happily work on a pull request, but since this is a quite generic problem for Smach as I see it, it might require some discussion on the right approach.

My setup:

  • Ubuntu 16.04,
  • ROS kinetic
  • ros-kinetic-smach 2.0.1-0xenial-20170608-133042-0800

catkinize hydro smach

It would be nice to be able to maintain a single source tree of SMACH that works in electric, fuerte, groovy, and hydro until the dust has settled on catkin. This would involve supporting both rosbuild and catkin in a single checkout, and should be possible.

verify SMACH python 3.3 compatibility

  • apply all changes suggested by 2to3
  • run all tests on bilingual code on python2.7
  • run all tests on bilingual code on python3.3
  • test on larger integration example

error in concurrence container child termination

Hi,

I've been running some concurrence containers, with 3 or 4 children, and I've noticed that sometimes the child terminates before the parent, which then outputs the following error:

[ERROR] [WallTime: 1384915862.954833] InvalidUserCodeError: Could not execute child termination callback: Traceback (most recent call last):
File "/opt/strands/strands_catkin_ws/src/catkinized_smach/smach/src/smach/concurrence.py", line 348, in _state_runner
preempt_others = self._child_termination_cb(self._child_outcomes)
File "/opt/strands/strands_catkin_ws/src/autonomous_patrolling/waypoint_patroller/src/waypoint_patroller/navigation.py", line 157, in child_term_cb
if ( outcome_map['BUMPER_MONITOR'] == 'invalid' or
KeyError: 'BUMPER_MONITOR'

Exception in thread concurrent_split:BATTERY_MONITOR:
Traceback (most recent call last):
File "/usr/lib/python2.7/threading.py", line 551, in __bootstrap_inner
self.run()
File "/usr/lib/python2.7/threading.py", line 504, in run
self.__target(_self.__args, *_self.__kwargs)
File "/opt/strands/strands_catkin_ws/src/catkinized_smach/smach/src/smach/concurrence.py", line 350, in _state_runner
raise smach.InvalidUserCodeError("Could not execute child termination callback: "+traceback.format_exc())
InvalidUserCodeError: <smach.exceptions.InvalidUserCodeError instance at 0x3af46c8>

In this case, BUMPER_MONITOR terminated before its parent.

This does not affect the execution of the state machine, which continues normally afterwards.

SMACH for multi-agent systems

I have a multi-agent application which is like a big state machine: Parcel sorting.

To deliver parcels, we are currently using Actionlib to provide tasks to the UGVs, and the tasks are defined by a simple state machine.

I just bumped into SMACH and wonder if it is suitable for multi-agent systems like my application. For one UGV, it would be super simple to use SMACH, but I do not see how I could use it for multiple agents.

The state machine for each UGVs looks like:

START
   * INIT
INIT
   * GET_PARCEL
GET_PARCEL
   * when parcel_put: SCAN_PARCEL
SCAN_PARCEL
   * when parcel_scanned DELIVER_PARCEL
DELIVER_PARCEL
   * when parcel_removed: GET_PARCEL
   * if timeout: EMERGENCY_GATE
EMERGENCY_GATE
   * when parcel_removed: GET_PARCEL

TypeError: can't pickle _thread.lock objects

I run into a 'TypeError: can't pickle _thread.lock objects' - error when trying to execute my state machine.
The state-machine consists of a state that executes a command via subprocess.popen and stores the process in a userdata-variable followed by a concurrent container with random content.
When running the code, it throws the following error:

[INFO] [1601065188.496524]: State machine starting in initial state 'ST1' with userdata: 
	['process']
[INFO] [1601065188.501100]: State machine transitioning 'ST1':'succeeded'-->'CON_SUB'
[ERROR] [1601065188.503611]: Could not execute transition callback: Traceback (most recent call last):
  File "/home/faps/catkin_ws/src/executive_smach/smach/src/smach/container.py", line 175, in call_transition_cbs
    cb(self.userdata, self.get_active_states(), *args)
  File "/home/faps/catkin_ws/src/executive_smach/smach_ros/src/smach_ros/introspection.py", line 239, in _transition_cb
    self._publish_status(info_str)
  File "/home/faps/catkin_ws/src/executive_smach/smach_ros/src/smach_ros/introspection.py", line 226, in _publish_status
    base64.b64encode(pickle.dumps(self._container.userdata._data, 2)).decode('utf-8'),
TypeError: can't pickle _thread.lock objects

[INFO] [1601065188.504891]: Concurrence starting with userdata: 
	[]
[INFO] [1601065188.506516]: Concurrent state 'ST2' returned outcome 'succeeded' on termination.
[INFO] [1601065188.508192]: Concurrent Outcomes: {'ST2': 'succeeded'}
[INFO] [1601065188.509342]: State machine terminating 'CON_SUB':'succeeded':'succeeded'
exit-outcome:succeeded

I've tried out a few things and made the following observations:

  • The error only appears when using the smach_ros.IntrospectionServer, without it it runs perfectly fine (you can see that the code runs through and returns succeeded nevertheless)
  • The error doesn't appear when I outcomment the subprocess.Popen
  • The error doesn't appear when I don't store the process in the userdata (for example by outcommenting the line userdata.out_proc = process)
  • The error doesn't appear when ST1 transitions to another simple state ST2 without the concurrence-container (maybe due to the transition-callback like mentioned in the error?)

For you to reproduce, the corresponding code looks as follows:

#!/usr/bin/env python3

import smach
import smach_ros
import subprocess
import rospy
import os


# gets called when ANY child state terminates
def con_term_cb(outcome_map):
    # terminate all running states if SM_1 finished with outcome 'goal_reached'
    if outcome_map['ST2'] == 'succeeded':
        return True
    # terminate all running states if SM_1 finished with outcome 'preempted'
    if outcome_map['ST2'] == 'preempted':
        return True
    # in all other case, just keep running, don't terminate anything
    return False


# gets called when ALL child states are terminated
def con_out_cb(outcome_map):
   if outcome_map['ST2'] == 'succeeded':
      return 'succeeded'
   elif outcome_map['ST2'] == 'preempted':
      return 'preempted'

class st1(smach.State):
    def __init__(self, outcomes=['succeeded', 'preempted']):
        smach.State.__init__(self, outcomes, input_keys=['in_proc'], output_keys=['out_proc'])

    def execute(self, userdata):
        if self.preempt_requested():
            self.service_preempt()
            return 'preempted'

        process = subprocess.Popen('rosbag play -l /home/faps/bags/2020-05-07-11-10-22.bag', stdout=subprocess.PIPE,
                                   shell=True, preexec_fn=os.setsid)
        userdata.out_proc = process
        return 'succeeded'


class st2(smach.State):
    def __init__(self, outcomes=['succeeded', 'preempted']):
        smach.State.__init__(self, outcomes)

    def execute(self, userdata):
        # time.sleep(2)
        if self.preempt_requested():
            self.service_preempt()
            return 'preempted'
        return 'succeeded'


if __name__ == "__main__":
    rospy.init_node('test_state_machine')
    sm_1 = smach.StateMachine(outcomes=['succeeded', 'preempted'])
    sm_1.userdata.process = None
    with sm_1:
        con_sub1 = smach.Concurrence(outcomes=['succeeded', 'preempted'],
                                     default_outcome='preempted',
                                     child_termination_cb=con_term_cb,
                                     outcome_cb=con_out_cb
                                     )

        with con_sub1:
            smach.Concurrence.add('ST2', st2())

        smach.StateMachine.add('ST1', st1(),
                               transitions={'succeeded': 'CON_SUB', 'preempted': 'CON_SUB'},
                               remapping={'out_proc': 'process'})
        smach.StateMachine.add('CON_SUB', con_sub1, transitions={'succeeded': 'succeeded', 'preempted': 'preempted'})

    # Execute SMACH plan
    # Create and start the introspection server
    sis = smach_ros.IntrospectionServer('introspection_server', sm_1, '/SM_ROOT')
    sis.start()
    outcome = sm_1.execute()
    print('exit-outcome:' + outcome)
    # Wait for ctrl-c to stop the application
    rospy.spin()
    sis.stop()

Lunar release

Howdy @jbohren and executive_smach maintainers!

As you may know the next ROS release Lunar Loggerhead is around the corner 🎉
Is it possible to release executive_smach in ROS Lunar? Being part of desktop_full this will be a requirement for Lunar to be released. If you don't have time to make a new release, please consider releasing the current Kinetic version into Lunar asap.

Thanks!

Preempting a concurrence container might not have 'preempted' as outcome

Might be related to #53

But we discovered this in a bigger state-machine where a concurrent monitoring state is trying to preempt another concurrence container. We expected this container then to finalize with outcome 'preempted'.
However, if at that moment the concurrency container already was terminating and one of the states had an outcome defined already, the resulting outcome is not 'preempted' which made our state machine end up in an undesired state.

Inspired by #53 I created a small script as well to demonstrate this:

#!/usr/bin/env python

from time import sleep
from smach import State, Concurrence
from threading import Timer


class DelayState(State):
    """Delay state for testing purposes"""

    def __init__(self, delay):
        State.__init__(self, outcomes=['succeeded', 'preempted'])
        self.delay = delay

    def execute(self, _):
        # A better delay state should be able to preempt during its sleep state
        sleep(self.delay)
        if self.preempt_requested():
            self.service_preempt()
            return 'preempted'
        return 'succeeded'


def test_concurrence_preempt():

    cc1 = Concurrence(
        outcomes=['succeeded', 'preempted'],
        default_outcome='succeeded',
        child_termination_cb=lambda *_: True,  # Always evaluate outcome_map
        outcome_map={'preempted': {'State1': 'preempted',
                                   'State2': 'preempted'}})
    with cc1:
        Concurrence.add('State1', DelayState(0.1))
        Concurrence.add('State2', DelayState(1.0))

    t = Timer(0.3, cc1.request_preempt)
    t.start()
    print(f' Resulting state: {cc1.execute()}')

if __name__ == '__main__':
    test_concurrence_preempt()

In this simple example State1 is already done when the concurrence container is asked to preempt and thus the resulting outcome is succeeded although preempted would be expected.

In code the case where the concurrence is asked to preempt after all states are terminated is caught already:

if self.preempt_requested():
but the resulting outcome is not changed.

I'll sketch-up a PR as well with a potential fix!

ActionServerWrapper: Canceling an goal results in Exception

During the migration of my code from fuerte to hydro I observed this behaviour.
Sending a Cancel with

client = actionlib.SimpleActionClient('bring_object', hobbit_msgs.msg.BringObjectAction)
client.wait_for_server()
client.cancel_all_goals()

results in the following error.

[ERROR] [WallTime: 1389093775.666919] Exception in your execute callback: 0
Traceback (most recent call last):
  File "/opt/ros/hydro/lib/python2.7/dist-packages/actionlib/simple_action_server.py", line 299, in executeLoop
    self.execute_callback(goal)
  File "/opt/ros/hydro/lib/python2.7/dist-packages/smach_ros/action_server_wrapper.py", line 237, in execute_cb
    setattr(result,from_key,self.userdata[to_key])
  File "/opt/ros/hydro/lib/python2.7/dist-packages/smach/user_data.py", line 43, in __getitem__
    return self.__getattr__(key)
  File "/opt/ros/hydro/lib/python2.7/dist-packages/smach/user_data.py", line 56, in __getattr__
    if name[0] == '_':
KeyError: 0

while on fuerte the State machine will simply be preempted.

As far as i can verify it the problem was introduced for #6 by fdf99b1 in file smach_ros/src/smach_ros/action_server_wrapper.py on line 235.

How to use callback arguments

Hello,

I have been working with the smach library I can to use the CBState class.
Now I am not sure how to use the cb_args to pass an argument into the callback function.

Can someone give me an example or explanation?

Hope someone can help me

MonitorState can loose preempt trigger event and block.

When the request_preempt() of the MonitorState is called just before the execute() clears the trigger_event, then the set of the trigger_event will be lost. In this case the execute() has no way to react on the preempt request any more and will block.

SimpleActionState not reusable after preemption if goal not preempted

I am using SimpleActionState to call an action server which does not behave as SimpleActionState expects. The server sets the goal status to succeeded (it does have some results to share, so one could argue this to be valid) when preempted.

The problem is that SimpleActionState only run self.service_preempt() if the state is preempted AND the GoalStatus equals PREEMPTED (at this line). The next time this state is entered, then its just preempts early due to the preempt flag being set (at this line).

The fix is to make sure that self.service_preempt() is called when preempt is requested, regardless of the goal status.

One could also consider changing the outcome of the state is such a situation. Today it results in 'aborted' . I would argue that it should either be 'preempted' since the state is actually preempted, or 'succeeded' since the goal succeeded.

I am happy to provide a PR if we can decide on the behavior.

New release

Can we have a new release of the package? It has been more than two years since v2.0.0 and there are a lot of fixes which would be good to have in the released version (at least for ROS kinetic).

Can't preempt state machine more than once

Hi!

I just came across the following issue. In my state machine I have a bunch of SimpleActionStates that control the behaviour of the robot.

When one of the states is preempted the robot needs to perform a correcting action, that ideally could also be preempted by the user. For simplicity here is the state transition:

A ---preempt---->B---preempt----->C 
                 |
                 |---success---->D

where:
A - SimpleActionState
B - SimpleActionState
C - State
D - State

In my tests I've found out that when I execute the preempt in the state machine, I can only execute it once. Once we are in B, we cannot execute the preempt anymore (I can confirm that the action servers */cancel topics receive the cancel signal only once for the first preempt).

Debugging the code I believe the issue comes from the _preempt_current_state function:

    def _preempt_current_state(self):
        """Preempt the current state (might not be executing yet).
        This also resets the preempt flag on a state that had previously received the preempt, but not serviced it."""
        if self._preempted_state != self._current_state:
            if self._preempted_state is not None:
                # Reset the previously preempted state (that has now terminated)
                self._preempted_state.recall_preempt()

            # Store the label of the currently active state
            self._preempted_state = self._current_state
            self._preempted_label = self._current_label

            # Request the currently active state to preempt
            try:
                self._preempted_state.request_preempt()
            except:
                smach.logerr("Failed to preempt contained state '%s': %s" % (self._preempted_label, traceback.format_exc()))

The first time the above is executed the _preempted_state is None and thus the self._preempted_state != self._current_state check goes through.

However, the second time it's executed both self._preempted_state and the self._current_state point to the same object, that is different from the _current_state we've seen in the previous preempt.

I assume the issue comes from the assignment:

            self._preempted_state = self._current_state

Where self._preempted_state starts being a reference to the self._current_state.

Anyone has any thoughts about it? This point could be highly relevant to the discussion in #52 .

Release into Melodic

It looks like all of the dependencies for this are available in Melodic, so it would be great to get this released. Thanks in advance!

Release ROS1 2.5.1

Some bugfixes are merged so making a release.

Btw I personally haven't used this pkg for awhile, so I refrain from reviewing/merging new feature PRs. Reviewing those PRs is appreciated.

SMACH Usecase Question

Hi! First if all, your State Machine is Awesome!
I'm using it to control my robot and here is my architecture so far,

fsm

As you can see, after Initializing System, it will change to Task_Monitor State that will read user input from rosservice to determine the next task. My question is, is it a good idea to listen input from user using service/msg? Can I use smach for my robot task controller based on user input and it will run until we terminate the system or is it for single task only (opendoor_fsm, go_somewhere_fsm) and I have to call it from other node? Thankyou!

When does the "execute" function of the State class get called?

I'm reading the documents of the generic State class and trying to use it with ROS system. I'm just curious when does the execute function of the State class get called?

class Foo(smach.State):
     def __init__(self, outcomes=['outcome1', 'outcome2']):
       # Your state initialization goes here
     
     # When does this function get called? <---------------
     def execute(self, userdata):
        # Your state execution goes here
        if xxxx:
            return 'outcome1'
        else:
            return 'outcome2'

Build failure for ROS Iron: "Could not find a package configuration file provided by ament_cmake_flake8"

Reported at ros/rosdistro#37815 (comment) thanks to @Yadunund

14:15:20 CMake Error at CMakeLists.txt:10 (find_package):
14:15:20   By not providing "Findament_cmake_flake8.cmake" in CMAKE_MODULE_PATH this
14:15:20   project has asked CMake to find a package configuration file provided by
14:15:20   "ament_cmake_flake8", but CMake did not find one.
14:15:20 
14:15:20   Could not find a package configuration file provided by
14:15:20   "ament_cmake_flake8" with any of the following names:
14:15:20 
14:15:20     ament_cmake_flake8Config.cmake
14:15:20     ament_cmake_flake8-config.cmake
14:15:20 
14:15:20   Add the installation prefix of "ament_cmake_flake8" to CMAKE_PREFIX_PATH or
14:15:20   set "ament_cmake_flake8_DIR" to a directory containing one of the above
14:15:20   files.  If "ament_cmake_flake8" provides a separate development package or
14:15:20   SDK, be sure it has been installed.

State machines cannot be pickled

I was trying to pickle a State machine in order to save it on a file to be executed later and I saw that it cannot be pickled due to the _state_transitioning_lock variable. This could be fixed by adding the following to the state_machine class:

    def __getstate__(self):
        return {k:v for (k, v) in self.__dict__.items() if k is not "_state_transitioning_lock"}

    def __setstate__(self, d):
        self.__dict__ = d
        self._state_transitioning_lock = threading.Lock()

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.