(!) Please ask about problems and questions regarding this tutorial on answers.ros.org. Don't forget to include in your question the link to this page, the versions of your OS & ROS, and also add appropriate tags.

从状态机调用行为(ROS)

Description: 本教程将教会你如何从SMACH状态机内部调用行为服务。

Tutorial Level: BEGINNER

Next Tutorial: 查看你的状态机

   1 from smach_ros import SimpleActionState

你可以简单地从通用状态调用任何Action,但是smach有特定的支持来调用Action,为你节省了大量代码,!SMACH提供了一个作为actionlib操作代理的状态类。状态的实例化采用主题名称(topic name)、动作类型(action type)和生成目标(goal)的一些策略。简单动作状态的可能结果是'成功(succeeded)', '抢占(preempted)' 和 '中止(aborted)'。

取决于你的目标,SimpleActionState的实现可以很简单,也可以很复杂

目标消息

空目标消息

这是一种少见的情况,它将调用一个action服务器,而不需要填写目标信息。

   1 sm = StateMachine(['succeeded','aborted','preempted'])
   2 with sm:
   3     smach.StateMachine.add('TRIGGER_GRIPPER',
   4                            SimpleActionState('action_server_namespace',
   5                                              GripperAction),
   6                            transitions={'succeeded':'APPROACH_PLUG'})

固定的目标信息

稍微高级一点的用法是,你指定一个硬编码的固定目标,它将被传递给行为服务器:

   1 sm = StateMachine(['succeeded','aborted','preempted'])
   2 with sm:
   3     gripper_goal = Pr2GripperCommandGoal()
   4     gripper_goal.command.position = 0.07
   5     gripper_goal.command.max_effort = 99999
   6     StateMachine.add('TRIGGER_GRIPPER',
   7                       SimpleActionState('action_server_namespace',
   8                                         GripperAction,
   9                                         goal=gripper_goal),
  10                       transitions={'succeeded':'APPROACH_PLUG'})

=== 从用户数据获取目标=== 假设你在用户数据中有许多字段,这些字段已经包含了你的目标消息所需的所有结构。然后,你可以将用户数据直接连接到目标消息中的字段。因此,从上面的例子中我们了解到,抓取动作的目标有两个字段:max_effort和position。假设我们的用户数据包含了相应的字段user_data_max和user_data_position。下面的代码连接相应的字段。

   1 sm = StateMachine(['succeeded','aborted','preempted'])
   2 with sm:
   3     StateMachine.add('TRIGGER_GRIPPER',
   4                       SimpleActionState('action_server_namespace',
   5                                         GripperAction,
   6                                         goal_slots=['max_effort', 
   7                                                     'position']),
   8                       transitions={'succeeded':'APPROACH_PLUG'},
   9                       remapping={'max_effort':'user_data_max',
  10                                  'position':'user_data_position'})

同样的方法也适用于'result_slots':操作的结果字段可以自动地写入到你的用户数据中。还要注意,在'goal_slots'和'result_slots'中指定的所有字段都自动放入'input_keys'和'output_keys'。

目标回调

这是权力最大的版本:当Action需要一个Goal时,你可以得到一个回调,你可以根据需要,在回调函数中创建自己的目标消息(goal)。

   1 sm = StateMachine(['succeeded','aborted','preempted'])
   2 with sm:
   3     def gripper_goal_cb(userdata, goal):
   4        gripper_goal = GripperGoal()
   5        gripper_goal.position.x = 2.0
   6        gripper_goal.max_effort = userdata.gripper_input
   7        return gripper_goal
   8 
   9     StateMachine.add('TRIGGER_GRIPPER',
  10                       SimpleActionState('action_server_namespace',
  11                                         GripperAction,
  12                                         goal_cb=gripper_goal_cb,
  13                                         input_keys=['gripper_input'])
  14                       transitions={'succeeded':'APPROACH_PLUG'},
  15                       remapping={'gripper_input':'userdata_input'})

在你的目标回调中,你可以使用userdata,只要你在构造函数中列出input_keys即可。回调的一个参数是默认目标。如果你在构造函数中指定了'goal=...',该对象将被传递到回调函数中。

对于更多高级回调使用方法查看 @smach.cb_interface (ROS Jade的API)

结果消息

结果到userdata

你可以将操作的结果直接写入你的状态的userdata。

   1 sm = StateMachine(['succeeded','aborted','preempted'])
   2 with sm:
   3     StateMachine.add('TRIGGER_GRIPPER',
   4                       SimpleActionState('action_server_namespace',
   5                                         GripperAction,
   6                                         result_slots=['max_effort', 
   7                                                       'position']),
   8                       transitions={'succeeded':'APPROACH_PLUG'},
   9                       remapping={'max_effort':'user_data_max',
  10                                  'position':'user_data_position'})

结果回调

结果回调与目标回调非常类似。它允许你从操作结果字段中读取任何数据,甚至返回一个不同的结果,而不是默认的'成功(succeeded)', '抢占(preempted)', '中止(aborted)'。

   1 sm = StateMachine(['succeeded','aborted','preempted'])
   2 with sm:
   3     def gripper_result_cb(userdata, status, result):
   4        if status == GoalStatus.SUCCEEDED:
   5           userdata.gripper_output = result.num_iterations
   6           return 'my_outcome'
   7 
   8     StateMachine.add('TRIGGER_GRIPPER',
   9                       SimpleActionState('action_server_namespace',
  10                                         GripperAction,
  11                                         result_cb=gripper_result_cb,
  12                                         output_keys=['gripper_output'])
  13                       transitions={'succeeded':'APPROACH_PLUG'},
  14                       remapping={'gripper_output':'userdata_output'})

在结果回调中,你获得了动作的状态,它告诉你动作是否成功,中止或被抢占。此外,还可以访问用户数据,以及操作的结果。

可选地,你可以从结果回调中返回一个不同的结果。如果你不返回任何东西,状态将返回相应的操作结果。

对于更多高级回调使用方法, 查看@smach.cb_interface

Wiki: cn/smach/Tutorials/Calling Actions (last edited 2018-03-10 03:36:44 by Playfish)