๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

์นดํ…Œ๊ณ ๋ฆฌ ์—†์Œ

10. action, action server, action client ์„ค๋ช…

๐ŸŽฌ

10. action, action server, action client ์„ค๋ช…

์ด๋ฒˆ ์‹œ๊ฐ„์—๋Š” ๋“œ๋””์–ด ROS์˜ ํ†ต์‹  ๋ฉ”์ปค๋‹ˆ์ฆ˜ ๋งˆ์ง€๋ง‰์œผ๋กœ action์— ๋Œ€ํ•ด ๋ฐฐ์›Œ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

์˜ˆ์ œ๋ฅผ ์‚ดํŽด๋ณด์‹œ์ฃ !!

๋ฏธ๋กœ ํƒˆ์ถœ


$ roslaunch gcamp_gazebo maze_world.launch

๋ณธ ์˜ˆ์ œ๋Š” ์„ค๋ช…์ด ์‚ด์ง ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

  1. ๋กœ๋ด‡์„ ์ดˆ๋ก ๋ฐ•์Šค๊ฐ€ ์žˆ๋Š” ํƒˆ์ถœ๊ตฌ๊นŒ์ง€ ์ด๋™์‹œ์ผœ์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  1. ๋กœ๋ด‡์ด ํšŒ์ „ํ•˜๋Š” ๋ฐฉํ–ฅ์€ ์‚ฌ์ง„๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.
  1. ๋กœ๋ด‡์€ ํšŒ์ „ํ•œ ๋’ค, ์ง€๋‚œ topic ์˜ˆ์ œ์ฒ˜๋Ÿผ ๋ฒฝ๊ณผ ์ผ์ •ํ•œ ๊ฑฐ๋ฆฌ๋ฅผ ๋‘๊ณ  ๋ฉˆ์ถ”๋„๋ก ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

2, 1, 0, ... ์ฒ˜๋Ÿผ ๋กœ๋ด‡์—๊ฒŒ ์ด๋™ํ•ด์•ผ ํ•  ๋ฐฉํ–ฅ์„ ์ค˜ ๋ณด์„ธ์š”.

๊ทธ๋Ÿผ action server & client๋ฅผ ์‹คํ–‰์‹œ์ผœ ๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

  • maze_action_server
# ์ƒˆ ํ„ฐ๋ฏธ๋„
$ rosrun action_tutorial maze_action_server.py
==== MazeActionClass Constructed ====
==== Waiting for Client Goal...  ====

  • maze_action_client
  • ์ˆซ์ž๋ฅผ ๋ชจ๋‘ ์ž…๋ ฅํ•œ ๋’ค, ๋๋‚ด๊ณ  ์‹ถ๋‹ค๋ฉด stop์„ ์ž…๋ ฅํ•ฉ๋‹ˆ๋‹ค.
# ์ƒˆ ํ„ฐ๋ฏธ๋„
$ rosrun action_tutorial maze_action_client.py
[INFO] [1610254457.453111, 322.000000]: Action Server Found.../maze_action_server
Enter numbers [or stop] : 
2
1
stop
('Your sequence list : ', [2, 1])
[INFO] [1610254462.019717, 326.139000]: State Result from Server : 0
feedback_msg: "Turning to Down"
feedback_msg: "Moving Forward ..."
feedback_msg: "Turning to Right"
feedback_msg: "Moving Forward ..."
[...]

# server ์ฐฝ์—๋„ ์ง€์†์ ์œผ๋กœ ์ด๋Ÿฌํ•œ ๋‚ด์šฉ๋“ค์ด ๋‚˜์˜ฌ ๊ฒƒ์ž…๋‹ˆ๋‹ค.
==== Maze Action Server Executing ====
Turning Sequence : 2
Turning Sequence : 1
[...]

์ฃผ์–ด์ง„ ๋ช…๋ น์„ ๋ชจ๋‘ ์‹คํ–‰ํ•œ ๋’ค์—๋„ ๋กœ๋ด‡์ด ์ดˆ๋ก ๋ฐ•์Šค์— ๋„๋‹ฌํ•˜๊ธฐ ๋ชปํ•˜๋ฉด, server ํ„ฐ๋ฏธ๋„์—์„œ ์‹คํŒจํ–ˆ๋‹ค๋Š” ๋นจ๊ฐ„ ๊ธ€์ž๊ฐ€ ๋‚˜์˜ค๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

Action ์„ค๋ช…


์ด์ „ service ๊ฐ•์˜์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ถ€๋ถ„์ด ํ˜น์‹œ ๊ธฐ์–ต๋‚˜์‹œ๋‚˜์š”?

A topic์ด ์‹คํ–‰๋˜๋Š” ๋™์•ˆ, ๋‹ค๋ฅธ B topic๋„ ๊ฐ™์ด ์‹คํ–‰๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐ˜๋ฉด, service๋Š” ํ˜„์žฌ ์ง„ํ–‰์ค‘์ธ service์˜ request๊ฐ€ ์ฒ˜๋ฆฌ๋˜๋Š” ๋™์•ˆ, ๋‹ค๋ฅธ service๋Š” ๊ธฐ๋‹ค๋ฆฌ๊ณ  ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

action์€ service์™€ ๋น„์Šทํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ํฐ ์ฐจ์ด์ ์ด ์žˆ๋Š”๋ฐ์š”.

๊ฒฐ๋ก ๋ถ€ํ„ฐ ๋ง์”€๋“œ๋ฆฌ์ž๋ฉด,

  1. action client๋Š” action server๊ฐ€ response๋ฅผ ๋ณด๋‚ด๊ธฐ ์ „๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š๊ณ , ๋‹ค๋ฅธ ์ผ์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค!
  1. action client์€ request๋ฅผ ๋ณด๋‚ธ ๋’ค์—๋„ ์ง€์†์ ์œผ๋กœ feedback์„ ๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค!!

image from : The Construct

์‚ฌ์ง„๊ณผ ๊ฐ™์ด client์™€ server๊ฐ€ ์ฃผ๊ณ ๋ฐ›๋Š” ๋‚ด์šฉ์€ ํฌ๊ฒŒ 5๊ฐ€์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๋”๋ถˆ์–ด ์ด๋“ค์ด ์ „๊ฐœ๋˜๋Š” ์ˆœ์„œ๋ฅผ ์‚ดํŽด๋ณด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  1. client โ‡’ server goal (service request์™€ ์œ ์‚ฌํ•ฉ๋‹ˆ๋‹ค.)
  1. server โ‡’ client feedback
  1. client โ‡’ server cancel
  1. server โ‡’ client status
  1. server โ‡’ client result

ํ•˜๋‚˜์”ฉ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๐Ÿ‘๐Ÿ‘

  1. client โ‡’ server goal : ์ด์ „ service resquest์™€ ์œ ์‚ฌํ•˜๊ฒŒ server๋กœ ์š”์ฒญ์„ ํ•ฉ๋‹ˆ๋‹ค.
  1. server โ‡’ client feedback : goal์— ์–ผ๋งˆ๋‚˜ ๋„๋‹ฌํ–ˆ๋Š”์ง€๋“ฑ์˜ ์ง€์†์ ์ธ ํ”ผ๋“œ๋ฐฑ์„ ์ฃผ๊ณ ๋ฐ›์Šต๋‹ˆ๋‹ค.
  1. client โ‡’ server cancel : feedback์„ ์ง€์ผœ๋ณด๋˜ client๊ฐ€ ๋ถ€๋“์ดํ•˜๊ฒŒ goal์„ ์ทจ์†Œํ•˜๊ณ  ์‹ถ์€ ๊ฒฝ์šฐ cancel์„ ์š”์ฒญํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  1. server โ‡’ client status : Server๊ฐ€ ์ž‘์—…์ค‘์ธ ๋™์•ˆ client๊ฐ€ ๋‹ค๋ฅธ ์ผ์„ ํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ๋งํ–ˆ์ฃ ? ๊ทธ๋Ÿฌ๊ธฐ ์œ„ํ•ด์„œ๋Š” server์˜ ์ƒํƒœ, status๋ฅผ ์•Œ์•„์•ผ ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.
  1. server โ‡’ client result : ๋ชจ๋“  ์š”์ฒญ๋œ ์ž‘์—…์ด ์™„๋ฃŒ๋˜๋ฉด, server๋Š” result๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ, action server๋ฅผ ์‹คํ–‰์‹œํ‚จ ๋’ค, rostopic list๋ฅผ ์‹คํ–‰์‹œ์ผœ๋ณด๋ฉด, ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ธฐ๋ณธ 5๊ฐœ์˜ ๋ฆฌ์ŠคํŠธ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

$ rostopic list 
...
/maze_action_server/cancel
/maze_action_server/feedback
/maze_action_server/goal
/maze_action_server/result
/maze_action_server/status
๐Ÿ’ก
rosaction list๊ฐ€ ์•„๋‹˜์— ์ฃผ์˜ํ•˜์„ธ์š”!!!

topic์˜ msg, service์˜ srv๊ฐ€ ์žˆ๋“ฏ์ด, action์—๋„ ์ฃผ๊ณ ๋ฐ›๋Š” ๋ฐ์ดํ„ฐ ํ˜•์‹์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐ”๋กœ action์ž…๋‹ˆ๋‹ค.

์ด action์€ goal, result, feedback์œผ๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ์œผ๋ฉฐ, service์™€ ๋น„์Šทํ•˜๊ฒŒ ---๋กœ ๊ตฌ๋ถ„๋ฉ๋‹ˆ๋‹ค.

  • Fibonacci.action
#goal definition
int32 order
---
#result definition
int32[] sequence
---
#feedback
int32[] sequence

ํ˜•์‹์€ service์™€ ๋น„์Šทํ•œ๋ฐ, ์‹ค์ œ๋กœ ์กฐํšŒํ•  ๋•Œ์—๋Š” rosmsg๋กœ ์กฐํšŒํ•˜๋‹ˆ, ํ˜ผ๋™ํ•˜์ง€ ์•Š๋„๋ก ์ฃผ์˜ํ•ฉ์‹œ๋‹ค.

$ rosmsg list | grep Fibonacci
actionlib_tutorials/FibonacciAction
actionlib_tutorials/FibonacciActionFeedback
actionlib_tutorials/FibonacciActionGoal
actionlib_tutorials/FibonacciActionResult
actionlib_tutorials/FibonacciFeedback
actionlib_tutorials/FibonacciGoal
actionlib_tutorials/FibonacciResult

์—ฌ๊ธฐ์„œ ์˜๋ฌธ์ด ์ƒ๊ธฐ๋Š”๋ฐ์š”, ๋ถ„๋ช…, action์€ ๋‹ค์„ฏ ์ข…๋ฅ˜์˜ ์ฃผ๊ณ ๋ฐ›๋Š” ํ˜•์‹์ด ์žˆ๋‹ค๊ณ  ํ–ˆ๋Š”๋ฐ, ์œ„์˜ ํŒŒ์ผ์—๋Š” ์„ธ๊ฐ€์ง€๋งŒ ์ ํ˜€ ์žˆ์Šต๋‹ˆ๋‹คโ‰๏ธ

๋‚˜๋จธ์ง€ cancel, status๋Š” action์—์„œ ์ง€์ •ํ•ด๋‘” ๊ณ ์œ ๊ฐ’์ด ์žˆ์Šต๋‹ˆ๋‹ค. status์— ๋Œ€ํ•ด์„œ ์‚ดํŽด๋ด…์‹œ๋‹ค.

uint8 PENDING         = 0   # The goal has yet to be processed by the action server
uint8 ACTIVE          = 1   # The goal is currently being processed by the action server
uint8 PREEMPTED       = 2   # The goal received a cancel request after it started executing
                            #   and has since completed its execution (Terminal State)
uint8 SUCCEEDED       = 3   # The goal was achieved successfully by the action server (Terminal State)
uint8 ABORTED         = 4   # The goal was aborted during execution by the action server due
                            #    to some failure (Terminal State)
uint8 REJECTED        = 5   # The goal was rejected by the action server without being processed,
                            #    because the goal was unattainable or invalid (Terminal State)
uint8 PREEMPTING      = 6   # The goal received a cancel request after it started executing
                            #    and has not yet completed execution
uint8 RECALLING       = 7   # The goal received a cancel request before it started executing,
                            #    but the action server has not yet confirmed that the goal is canceled
uint8 RECALLED        = 8   # The goal received a cancel request before it started executing
                            #    and was successfully cancelled (Terminal State)
uint8 LOST            = 9   # An action client can determine that a goal is LOST. This should not be
                            #    sent over the wire by an action server

#Allow for the user to associate a string with GoalStatus for debugging
string t
actionlib_msgs/GoalStatus Documentation
GoalID goal_iduint8 statusuint8 PENDING = 0 # The goal has yet to be processed by the action server uint8 ACTIVE = 1 # The goal is currently being processed by the action server uint8 PREEMPTED = 2 # The goal received a cancel request after it started executing # and
http://docs.ros.org/en/kinetic/api/actionlib_msgs/html/msg/GoalStatus.html

์˜ˆ์ œ๋ฅผ ์‹คํ–‰ํ•˜๊ธฐ ์ „์—, ์•ž์„  ๋ฏธ๋กœ์ฐพ๊ธฐ ์˜ˆ์ œ๋ฅผ ์ดํ•ดํ•˜๊ธฐ์—๋Š” ์„ ํ–‰ ์ง€์‹๋“ค์ด ์ ์ง€ ์•Š๊ธฐ์—, ์ด๋ฒˆ ๊ฐ•์˜์—์„œ๋Š” wiki.ros.org/์—์„œ ์ œ๊ณตํ•˜๋Š” ์˜ˆ์‹œ๋“ค์„ ์šฐ์„  ์‚ดํŽด๋ณธ ๋’ค์—, ๋‹ค์Œ ๊ฐ•์˜์—์„œ ๋ฏธ๋กœ์ฐพ๊ธฐ ์˜ˆ์ œ ์ฝ”๋“œ๋ฅผ ์‚ดํŽด๋ณด๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค.

OpenCV, ํŒŒ์ด์ฌ ํŒจํ‚ค์ง€์™€ Class, Quaternion๊ณผ Euler Angle์— ๋Œ€ํ•œ ์ง€์‹์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ’ก
์š”์ง€๋Š”, action์˜ ์‚ฌ์šฉ์‚ฌ๋ก€๋ฅผ ๋ณด์ž„๊ณผ ๋™์‹œ์—, ROS Action์— ๋Œ€ํ•œ ์ง„์ž…์žฅ๋ฒฝ์€ ๋‚ฎ์ถ”๊ธฐ ์œ„ํ•จ์ด๋ผ ์ƒ๊ฐํ•ด์ฃผ์‹œ๋ฉด ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ๐Ÿ˜‰

๊ฐ„๋‹จ ์˜ˆ์ œ ์‹คํ–‰


$ roslaunch action_tutorial fibonacci_server.launch
[INFO] [1610620215.219295]: Fibonacci Action Server Executing, creating fibonacci sequence of order 20 with seeds 0, 1
[INFO] [1610620234.220412]: Succeeded calculating the Fibonacci

# ์ƒˆ ํ„ฐ๋ฏธ๋„ ์‹คํ–‰
$ rosrun action_tutorial fibonacci_action_client.py
==== Sending Goal to Server ====
sequence: [0, 1, 1]
sequence: [0, 1, 1, 2]
sequence: [0, 1, 1, 2, 3]
sequence: [0, 1, 1, 2, 3, 5]
sequence: [0, 1, 1, 2, 3, 5, 8]
sequence: [0, 1, 1, 2, 3, 5, 8, 13]
sequence: [0, 1, 1, 2, 3, 5, 8, 13, 21]
(...)

์œ„ ์˜ˆ์ œ๋Š” ํ”ผ๋ณด๋‚˜์น˜ ์ˆ˜์—ด์„ action์œผ๋กœ ๊ตฌํ˜„ํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

  • ํ”ผ๋ณด๋‚˜์น˜ ์ˆ˜์—ด์ด๋ž€?

image from : wiki

์‚ฌ์‹ค ํ”ผ๋ณด๋‚˜์น˜ ์ˆ˜์—ด์ด ์ค‘์š”ํ•˜๊ธฐ๋ณด๋‹ค action server & client์— ๋Œ€ํ•ด ๋ฐฐ์šฐ๋Š” ๊ฒƒ์ด ๋ชฉ์ ์ด๊ธฐ ๋•Œ๋ฌธ์—, ์งง๊ฒŒ ๋„˜์–ด๊ฐ€๊ฒ ์Šต๋‹ˆ๋‹ค. client ๋ถ€๋ถ„๋ถ€ํ„ฐ ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค!!

Fibonacci Action Client


#! /usr/bin/env python

"""
Fibonacci Action Client

referenced from wiki.ros.org

url: http://wiki.ros.org/actionlib_tutorials/Tutorials/Writing%20a%20Simple%20Action%20Client%20%28Python%29 
"""

import rospy
import actionlib
from actionlib_tutorials.msg import FibonacciAction, FibonacciGoal

# Action Status (primitive)
PENDING = 0
ACTIVE = 1
PREEMPTED = 2
SUCCEEDED = 3
ABORTED = 4
REJECTED = 5
PREEMPTING = 6
RECALLING = 7
RECALLED = 8
LOST = 9


def fb_callback(feedback):
    print(feedback)


rospy.init_node("fibonacci_client")
rate = rospy.Rate(5)

client = actionlib.SimpleActionClient("fibonacci_action_server", FibonacciAction)
client.wait_for_server()

goal = FibonacciGoal()
goal.order = 20

print("==== Sending Goal to Server ====")
client.send_goal(goal, feedback_cb=fb_callback)

state_result = client.get_state()

while state_result < PREEMPTED:
    # Doing Stuff while waiting for the Server to give a result....
    rate.sleep()
    state_result = client.get_state()

if state_result == SUCCEEDED:
    rospy.logwarn("Action Done State Result : " + str(client.get_result()))

else:
    rospy.logerr("Something went wrong, result state : " + str(state_result))

ํ•˜๋‚˜์”ฉ ์ฐจ๊ทผ์ฐจ๊ทผ ์งš์–ด๊ฐ€๊ฒ ์Šต๋‹ˆ๋‹ค. action client๋Š” service client์™€ ์•„์ฃผ ์œ ์‚ฌํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ์‰ฝ๊ฒŒ ์ดํ•ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • import ๋ถ€๋ถ„
import rospy
import actionlib
from actionlib_tutorials.msg import FibonacciAction, FibonacciGoal

client๊ฐ€ ํ•„์š”ํ•œ ๊ฒƒ์€ Goal์ด๋ฉฐ ์œ„์™€ ๊ฐ™์ด [ํ•ด๋‹น-action-์ด๋ฆ„]Goal๋กœ import ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ’ก
service๋Š” ์ด๋ ‡๊ฒŒ import ํ–ˆ์Šต๋‹ˆ๋‹ค .
from service_tutorial.srv import ControlTurningMessage, ControlTurningMessageRequest

  • action Goal status
# Action Status (primitive)
PENDING = 0
ACTIVE = 1
PREEMPTED = 2
SUCCEEDED = 3
ABORTED = 4
REJECTED = 5
PREEMPTING = 6
RECALLING = 7
RECALLED = 8
LOST = 9

์•ž์„œ ๋ฐฐ์› ๋˜ ๋ฐ”์™€ ๊ฐ™์ด GoalStatus๋ฅผ ๋ณด๊ธฐ ์‰ฝ๊ฒŒ ์ƒ์ˆ˜๋กœ ์ •์˜ํ•ด ๋‘์—ˆ์Šต๋‹ˆ๋‹ค.

  • client ์ƒ์„ฑ, wait_for_server
rospy.init_node("fibonacci_client")
rate = rospy.Rate(5)

client = actionlib.SimpleActionClient("fibonacci_action_server", FibonacciAction)
client.wait_for_server()
  • actionlib.SimpleActionClient("server-name", "action-type-name")

Action Client๋Š” ์œ„์™€ ๊ฐ™์ด ์ƒ์„ฑํ•˜๋ฉฐ, server์˜ ์ด๋ฆ„๊ณผ ๋™์ผํ•ด์•ผ ํ•จ์— ์œ ์˜ํ•ฉ๋‹ˆ๋‹ค!

service client์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ, server๊ฐ€ ์‹คํ–‰๋  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ ค์•ผ ํ•˜๋ฉฐ ๊ตฌํ˜„์€ ์œ„์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

๐Ÿ’ก
service client ๋ณต์Šต
rospy.wait_for_service("/control_robot_angle")
service_client = rospy.ServiceProxy("/control_robot_angle", ControlTurningMessage)

  • send_goal, feedback
def fb_callback(feedback):
    print(feedback)

(...)

goal = FibonacciGoal()
goal.order = 20

print("==== Sending Goal to Server ====")
client.send_goal(goal, feedback_cb=fb_callback)

์—ฌ๊ธฐ๊นŒ์ง€๋Š” service์™€ ์•„์ฃผ ์œ ์‚ฌํ•˜์ง€์š”??

  • Fibonacci.action
#goal definition
int32 order
---
#result definition
int32[] sequence
---
#feedback
int32[] sequence

  • ์ค‘์š”!! Action๋งŒ์˜ ๊ธฐ๋Šฅ!!
while state_result < PREEMPTED:
    # Doing Stuff while waiting for the Server to give a result....
    rate.sleep()
    state_result = client.get_state()

if state_result == SUCCEEDED:
    rospy.logwarn("Action Done Result : " + str(client.get_result()))

else:
    rospy.logerr("Something went wrong, result state : " + str(state_result))

# Action Status (primitive)
PENDING = 0
ACTIVE = 1
PREEMPTED = 2
SUCCEEDED = 3
ABORTED = 4
REJECTED = 5
PREEMPTING = 6
RECALLING = 7
RECALLED = 8
LOST = 9

action์€ ์œ„์™€ ๊ฐ™์ด server๋กœ๋ถ€ํ„ฐ ์•„์ง ์š”์ฒญ์„ ์‹คํ–‰์ค‘์ด๋ผ๋Š” ์ƒํƒœ๊ฐ€ ๋Œ์•„์˜ค๋ฉด, ๊ทธ๋™์•ˆ ๋‹ค๋ฅธ ์ผ์„ ํ•˜๋„๋ก ํ•  ์ˆ˜๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค!! ํ˜„ ์˜ˆ์ œ์—์„œ๋Š” ํฐ ์ž‘์—…์€ ํ• ๋‹นํ•˜์ง€ ์•Š์•˜์ง€๋งŒ, ์‹ค์ œ ๋กœ๋ด‡์„ ๋งŒ๋“ค๋‹ค๋ณด๋ฉด ์ •๋ง ์œ ์šฉํ•œ ๊ธฐ๋Šฅ์ผ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๐Ÿ‘๐Ÿ‘๐Ÿ‘

๋‹ค์Œ์œผ๋กœ Action Server ์ฝ”๋“œ๋ฅผ ์‚ดํŽด๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

Fibonacci Action Server


#! /usr/bin/env python

"""
Fibonacci Action Server

referenced from wiki.ros.org

url: http://wiki.ros.org/actionlib_tutorials/Tutorials/Writing%20a%20Simple%20Action%20Server%20using%20the%20Execute%20Callback%20%28Python%29#Compiling
"""

import rospy
import actionlib
from actionlib_tutorials.msg import FibonacciFeedback, FibonacciResult, FibonacciAction

g_feedback = FibonacciFeedback()
g_result = FibonacciResult()


def execute_cb(goal):
    r = rospy.Rate(5)
    success = True

    g_feedback.sequence = []
    g_feedback.sequence.append(0)
    g_feedback.sequence.append(1)

    rospy.loginfo(
        "Fibonacci Action Server Executing, creating fibonacci sequence of order %i with seeds %i, %i"
        % (goal.order, g_feedback.sequence[0], g_feedback.sequence[1])
    )

    for i in range(1, goal.order):
        if _as.is_preempt_requested():
            rospy.loginfo("The goal has been cancelled/preempted")
            _as.set_preempted()
            success = False
            break

        g_feedback.sequence.append(g_feedback.sequence[i] + g_feedback.sequence[i - 1])
        _as.publish_feedback(g_feedback)
        r.sleep()

    if success:
        g_result.sequence = g_feedback.sequence
        rospy.loginfo("Succeeded calculating the Fibonacci")
        _as.set_succeeded(g_result)


rospy.init_node("fibonacci")

_as = actionlib.SimpleActionServer(
    "fibonacci_action_server", FibonacciAction, execute_cb, False
)
_as.start()

print("==== Waiting for Client Goal...  ====")

rospy.spin()

goal_callback ๋ถ€๋ถ„์ด ์ƒ๋‹นํžˆ ๊ธธ์ง€์š”? ์ฐจ๊ทผ์ฐจ๊ทผ ๋œฏ์–ด๋ด…์‹œ๋‹ค!!

  • import, global variables
import rospy
import actionlib
from actionlib_tutorials.msg import FibonacciFeedback, FibonacciResult, FibonacciAction

g_feedback = FibonacciFeedback()
g_result = FibonacciResult()

server๋‹จ์—๋Š” Feedback, Result๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

callback์•ˆ์—์„œ๋„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ์ „์—ญ ๋ณ€์ˆ˜๋กœ ํ• ๋‹นํ•˜์˜€์Šต๋‹ˆ๋‹ค.

  • main
rospy.init_node("fibonacci")

_as = actionlib.SimpleActionServer(
    "fibonacci_action_server", FibonacciAction, execute_cb, False
)
_as.start()

print("==== Waiting for Client Goal...  ====")

rospy.spin()

actionlib.SimpleActionServer( "server-name", "action-type-name", "execute_cb", "auto_start")

Action Server๋Š” ์œ„์™€ ๊ฐ™์ด ์ƒ์„ฑํ•˜๋ฉฐ, ์ฒซ๋ฒˆ์งธ ์ธ์ž๋Š” Client ์ƒ์„ฑ ์‹œ์— ๋™์ผํ•˜๊ฒŒ ๊ธฐ์ž…ํ•ด์•ผ ์ œ๋Œ€๋กœ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

False๋กœ ์ ํ˜€ ์žˆ๋Š” ๋ถ€๋ถ„์€ auto_start๋ผ๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜์ธ๋ฐ์š”, ๋ง ๊ทธ๋Œ€๋กœ server๋ฅผ ์ƒ์„ฑ ํ›„ ์ž๋™ ์‹คํ–‰ํ•  ๊ฒƒ์ธ์ง€ ์„ค์ •ํ•˜๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜์ด๋ฉฐ, ์ง€๊ธˆ์€ _as.start()๋ฅผ ํ†ตํ•ด ์ˆ˜๋™์œผ๋กœ ์‹คํ–‰์‹œํ‚ค๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

  • callback
def execute_cb(goal):
    r = rospy.Rate(5)
    success = True

    g_feedback.sequence = []
    g_feedback.sequence.append(0)
    g_feedback.sequence.append(1)

    rospy.loginfo(
        "Fibonacci Action Server Executing, creating fibonacci sequence of order %i with seeds %i, %i"
        % (goal.order, g_feedback.sequence[0], g_feedback.sequence[1])
    )

    for i in range(1, goal.order):
        if _as.is_preempt_requested():
            rospy.loginfo("The goal has been cancelled/preempted")

goal_callback์€ client๋กœ๋ถ€ํ„ฐ์˜ goal์„ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋ฐ›๊ฒŒ ๋˜๋ฉฐ, FibonacciAction์ด ์–ด๋–ป๊ฒŒ ์ƒ๊ฒผ๋Š”์ง€ ๋‹ค์‹œ ํ•œ ๋ฒˆ ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

  • Fibonacci.action
#goal definition
int32 order
---
#result definition
int32[] sequence
---
#feedback
int32[] sequence

  • ํ”ผ๋ณด๋‚˜์น˜ ๋กœ์ง, is_preempt_requested()
		for i in range(1, goal.order):
        if _as.is_preempt_requested():
            rospy.loginfo("The goal has been cancelled/preempted")
            _as.set_preempted()
            success = False
            break

        g_feedback.sequence.append(g_feedback.sequence[i] + g_feedback.sequence[i - 1])
        _as.publish_feedback(g_feedback)
        r.sleep()

    if success:
        g_result.sequence = g_feedback.sequence
        rospy.loginfo("Succeeded calculating the Fibonacci")
        _as.set_succeeded(g_result)

callback ์ค‘์—์„œ๋„ ํ•ต์‹ฌ์ธ for๋ฌธ ์•ˆ์„ ๊ฐ€์ ธ์™”์Šต๋‹ˆ๋‹ค.

์ง€๊ธˆ์˜ ๊ฒฝ์šฐ order ๋งŒํผ ๋ฐ˜๋ณตํ•˜๋ฉฐ ํ”ผ๋ณด๋‚˜์น˜ ์ˆ˜์—ด์„ ๋งŒ๋“ค๊ณ , list ์ž๋ฃŒํ˜•์„ ๊ฐ€์ง„ sequence์— appendํ•˜๊ณ  ์žˆ์ง€์š”?

  • action์˜ ์„ฑ๊ฒฉ์ƒ, ์งง์€ request - response ๋ณด๋‹ค, ๊ธธ๊ณ  ๋ณต์žกํ•œ request๊ฐ€ ๋งŽ์ด ์‚ฌ์šฉ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. (์งง์€ request - response๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ ์ €๋ผ๋ฉด service๋ฅผ ์“ฐ๊ฒ ์Šต๋‹ˆ๋‹ค.)

  • ๊ธด ์‹คํ–‰ ๋™์•ˆ feedback์„ ๋ณด๋‚ด๊ณ , ๋„์ค‘์— client๊ฐ€ cancel, ํ˜น์€ preempt(์„ ์ )์„ ์š”์ฒญํ•  ์ˆ˜๋„ ์žˆ๊ธฐ์— ์ด์— ๋Œ€ํ•œ ์ฒ˜๋ฆฌ๊ฐ€ ๊ตฌํ˜„๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.
    ๐Ÿ’ก
    ์ด๋•Œ, ๊ทธ๋ƒฅ ๊ฐ•์ œ๋กœ client๋ฅผ ์ข…๋ฃŒํ•˜๋Š” ๊ฒƒ๊ณผ cancel request๋Š” ์—„์—ฐํžˆ ๋‹ค๋ฆ„์— ์ฃผ์˜ํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ctrl + c๋ฅผ ํ†ตํ•ด ๊ฐ•์ œ์ข…๋ฃŒ๋ฅผ ์‹œํ‚ค๋ฉด, server๋‹จ์—์„œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ warning์„ ๋ฐœ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค.

[WARN] [1610621159.608869]: Inbound TCP/IP connection failed: connection from sender terminated before handshake header received. 0 bytes were received. Please check sender for additional details.

  • if success๋กœ ์‹œ์ž‘ํ•˜๋Š” ๋ถ€๋ถ„์€ ROS์—์„œ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹Œ, ๋ณดํŽธ์ ์œผ๋กœ ๋งŽ์ด ์‚ฌ์šฉ๋˜๋Š” python flag๋ฅผ ๊ตฌํ˜„ํ•œ ๊ฒƒ์œผ๋กœ ROS API๋ฅผ ์ฐธ๊ณ ํ•˜์—ฌ ๋” ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.
actionlib: actionlib.simple_action_server.SimpleActionServer Class Reference
The SimpleActionServer implements a singe goal policy on top of the ActionServer class.
http://docs.ros.org/en/hydro/api/actionlib/html/classactionlib_1_1simple__action__server_1_1SimpleActionServer.html

[Tip] AxClient


$ rosrun actionlib axclient.py /fibonacci_action_server
or
# ์ด์ „์— ~/.bashrc์— ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค :)
$ axclient /fibonacci_action_server

Goal ๋ถ€๋ถ„์— ์›ํ•˜๋Š” order๋ฅผ ๋„ฃ์–ด์„œ SEND GOAL์„ ํด๋ฆญํ•˜๋ฉด, ๋ณด๊ธฐ ์ข‹๊ฒŒ Feedback๊ณผ Result๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋Œ€์‹  rosrun actionlib axclient.py ๋’ค์—, ์ด๋ฆ„์„ ์ž˜ ๋งž์ถฐ์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค. rostopic list ๋กœ ๋ณผ ์ˆ˜ ์žˆ๊ฒ ์ง€์š”?

์ž ๐Ÿ™ ์—ฌ๊ธฐ๊นŒ์ง€, ROS์˜ ํ†ต์‹  ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ๋ชจ๋‘ ์‚ดํŽด๋ณด์•˜์Šต๋‹ˆ๋‹ค. ์•ž์œผ๋กœ ์ด์–ด์ง€๋Š” ๋ช‡๊ฐœ์˜ ๊ฐ•์˜๋“ค์„ ๋ณด๋‹ค ์‹ฌํ™”๋œ ์˜ˆ์ œ๋ฅผ ๋ถ„์„ํ•ด๋ณด๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค. ํ•ด๋‹น ๋ถ€๋ถ„์€ ํ•„์ˆ˜์ ์ด ์•„๋‹ˆ๋ฉฐ ์ œ๊ฐ€ ์ œ๋ชฉ์—๋„ [Option]์„ ๋ถ™์—ฌ๋‘๊ฒ ์Šต๋‹ˆ๋‹ค!!