Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rclpy action examples #222

Merged
merged 25 commits into from
Mar 5, 2019
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
a93e25c
Copy rclpy action examples from #216
jacobperron Jan 8, 2019
4194f2c
Bump version of examples_rclpy_action to match other packages
jacobperron Jan 8, 2019
8c7c564
Consolidate composable action client example
jacobperron Jan 29, 2019
7226b87
Add action client example that is not composable
jacobperron Jan 29, 2019
0333797
Wait for action server
jacobperron Jan 29, 2019
c55a413
Restructure into separate packages for action client and action serve…
jacobperron Jan 29, 2019
a5b067d
Update API in action server examples
jacobperron Jan 29, 2019
6f5246e
Rename action server examples
jacobperron Jan 29, 2019
0e48b86
Add action server example that is not composable
jacobperron Jan 29, 2019
32a5caf
Update setup.py
jacobperron Jan 29, 2019
0173b2c
Fix syntax
jacobperron Jan 29, 2019
0a41589
Update action client examples to use goal handle API for the result
jacobperron Feb 1, 2019
6095a0e
Improve action client output and result handling
jacobperron Feb 1, 2019
32ea3b6
Update action server examples
jacobperron Feb 7, 2019
e7917a3
Move action examples into Python packages
jacobperron Feb 7, 2019
5216ba9
Add action client cancel example
jacobperron Feb 8, 2019
502a8c6
Address review comments
jacobperron Feb 13, 2019
45bde23
GoalResponse.ACCEPT_AND_EXECUTE -> GoalResponse.ACCEPT
jacobperron Feb 13, 2019
ce7bb67
Fix client_cancel example
jacobperron Feb 13, 2019
b0c739a
Remove race from server_single_goal example
jacobperron Feb 13, 2019
e68a577
Add action server example that defers the execution of an accepted goal
jacobperron Feb 14, 2019
0ea84a0
Reduce the timer period for the client cancel example
jacobperron Feb 14, 2019
240e7ab
Support canceling goals with non-composable action server example
jacobperron Feb 14, 2019
eec2b79
Add action server example that queues goals
jacobperron Mar 4, 2019
dfd71ad
Address review
jacobperron Mar 5, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Copyright 2018 Open Source Robotics Foundation, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from action_msgs.msg import GoalStatus
from example_interfaces.action import Fibonacci

import rclpy
from rclpy.action import ActionClient
from rclpy.node import Node


class MinimalActionClient(Node):

def __init__(self):
super().__init__('minimal_action_client')
self._action_client = ActionClient(self, Fibonacci, 'fibonacci')

def goal_response_callback(self, future):
goal_handle = future.result()
if not goal_handle.accepted:
self.get_logger().info('Goal rejected :(')
return

self.get_logger().info('Goal accepted :)')

self._get_result_future = goal_handle.get_result_async()
self._get_result_future.add_done_callback(self.get_result_callback)

def feedback_callback(self, feedback):
self.get_logger().info('Received feedback: {0}'.format(feedback.sequence))

def get_result_callback(self, future):
status = future.result().action_status
if status == GoalStatus.STATUS_SUCCEEDED:
self.get_logger().info('Goal succeeded! Result: {0}'.format(future.result().sequence))
jacobperron marked this conversation as resolved.
Show resolved Hide resolved
else:
self.get_logger().info('Goal failed with status: {0}'.format(status))

# Shutdown after receiving a result
rclpy.shutdown()

def send_goal(self):
self.get_logger().info('Waiting for action server...')
self._action_client.wait_for_server()

goal_msg = Fibonacci.Goal()
goal_msg.order = 10

self.get_logger().info('Sending goal request...')

self._send_goal_future = self._action_client.send_goal_async(
goal_msg,
feedback_callback=self.feedback_callback)

self._send_goal_future.add_done_callback(self.goal_response_callback)


def main(args=None):
rclpy.init(args=args)

action_client = MinimalActionClient()

action_client.send_goal()

rclpy.spin(action_client)

action_client.destroy_node()


if __name__ == '__main__':
main()
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Copyright 2019 Open Source Robotics Foundation, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from action_msgs.msg import GoalStatus
from example_interfaces.action import Fibonacci

import rclpy
from rclpy.action import ActionClient
from rclpy.callback_groups import ReentrantCallbackGroup
from rclpy.node import Node
from rclpy.timer import WallTimer


class MinimalActionClient(Node):

def __init__(self):
super().__init__('minimal_action_client')
self._action_client = ActionClient(self, Fibonacci, 'fibonacci')

def cancel_done(self, future):
cancel_response = future.result()
if len(cancel_response.goals_canceling) > 0:
self.get_logger().info('Goal successfully canceled')
else:
self.get_logger().info('Goal failed to cancel')

rclpy.shutdown()

def goal_response_callback(self, future):
goal_handle = future.result()
if not goal_handle.accepted:
self.get_logger().info('Goal rejected :(')
return

self._goal_handle = goal_handle

self.get_logger().info('Goal accepted :)')

# Start a 3 second timer
jacobperron marked this conversation as resolved.
Show resolved Hide resolved
self._timer = self.create_timer(2.0, self.timer_callback)

def feedback_callback(self, feedback):
self.get_logger().info('Received feedback: {0}'.format(feedback.sequence))

def timer_callback(self):
self.get_logger().info('Canceling goal')
# Cancel the goal
future = self._goal_handle.cancel_goal_async()
future.add_done_callback(self.cancel_done)

# Cancel the timer
self._timer.cancel()

def send_goal(self):
self.get_logger().info('Waiting for action server...')
self._action_client.wait_for_server()

goal_msg = Fibonacci.Goal()
goal_msg.order = 10

self.get_logger().info('Sending goal request...')

self._send_goal_future = self._action_client.send_goal_async(
goal_msg,
feedback_callback=self.feedback_callback)

self._send_goal_future.add_done_callback(self.goal_response_callback)


def main(args=None):
rclpy.init(args=args)

action_client = MinimalActionClient()

action_client.send_goal()

rclpy.spin(action_client)

action_client.destroy_node()


if __name__ == '__main__':
main()
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Copyright 2019 Open Source Robotics Foundation, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from action_msgs.msg import GoalStatus

from example_interfaces.action import Fibonacci

import rclpy
from rclpy.action import ActionClient
from rclpy.node import Node


def feedback_cb(logger, feedback):
logger.info('Received feedback: {0}'.format(feedback.sequence))


def main(args=None):
rclpy.init(args=args)

node = rclpy.create_node('minimal_action_client')

action_client = ActionClient(node, Fibonacci, 'fibonacci')

node.get_logger().info('Waiting for action server...')

action_client.wait_for_server()

goal_msg = Fibonacci.Goal()
goal_msg.order = 10

node.get_logger().info('Sending goal request...')

send_goal_future = action_client.send_goal_async(
goal_msg, feedback_callback=lambda feedback: feedback_cb(node.get_logger(), feedback))

rclpy.spin_until_future_complete(node, send_goal_future)

goal_handle = send_goal_future.result()

if not goal_handle.accepted:
node.get_logger().info('Goal rejected :(')
action_client.destroy()
node.destroy_node()
rclpy.shutdown()
return

node.get_logger().info('Goal accepted :)')

get_result_future = goal_handle.get_result_async()

rclpy.spin_until_future_complete(node, get_result_future)

status = get_result_future.result().action_status
if status == GoalStatus.STATUS_SUCCEEDED:
node.get_logger().info(
'Goal succeeded! Result: {0}'.format(get_result_future.result().sequence))
else:
node.get_logger().info('Goal failed with status code: {0}'.format(status))

action_client.destroy()
node.destroy_node()
rclpy.shutdown()


if __name__ == '__main__':
main()
24 changes: 24 additions & 0 deletions rclpy/actions/minimal_action_client/package.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="2">
<name>examples_rclpy_minimal_action_client</name>
<version>0.6.1</version>
<description>Examples of minimal action clients using rclpy.</description>

<maintainer email="sloretz@openrobotics.org">Shane Loretz</maintainer>
<license>Apache License 2.0</license>

<exec_depend>example_interfaces</exec_depend>
<exec_depend>rclpy</exec_depend>

<!-- These test dependencies are optional
Their purpose is to make sure that the code passes the linters -->
<test_depend>ament_copyright</test_depend>
<test_depend>ament_flake8</test_depend>
<test_depend>ament_pep257</test_depend>
<test_depend>python3-pytest</test_depend>

<export>
<build_type>ament_python</build_type>
</export>
</package>
4 changes: 4 additions & 0 deletions rclpy/actions/minimal_action_client/setup.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[develop]
script-dir=$base/lib/examples_rclpy_minimal_action_client
[install]
install-scripts=$base/lib/examples_rclpy_minimal_action_client
37 changes: 37 additions & 0 deletions rclpy/actions/minimal_action_client/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from setuptools import setup

package_name = 'examples_rclpy_minimal_action_client'

setup(
name=package_name,
version='0.6.1',
packages=[package_name],
data_files=[
('share/ament_index/resource_index/packages',
['resource/' + package_name]),
('share/' + package_name, ['package.xml']),
],
install_requires=['setuptools'],
zip_safe=True,
author='Jacob Perron',
author_email='jacob@openrobotics.org',
maintainer='Shane Loretz',
maintainer_email='sloretz@openrobotics.org',
keywords=['ROS'],
classifiers=[
'Intended Audience :: Developers',
'License :: OSI Approved :: Apache Software License',
'Programming Language :: Python',
'Topic :: Software Development',
],
description='Examples of action clients using rclpy.',
license='Apache License, Version 2.0',
tests_require=['pytest'],
entry_points={
'console_scripts': [
'client = ' + package_name + '.client:main',
'client_cancel = ' + package_name + '.client_cancel:main',
'client_not_composable = ' + package_name + '.client_not_composable:main',
],
},
)
Loading