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 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。