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

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

9. Service Server & Client ์ž‘์„ฑํ•ด๋ณด๊ธฐ – python

๐Ÿ›Ž๏ธ

9. Service Server & Client ์ž‘์„ฑํ•ด๋ณด๊ธฐ โ€“ python

์ง€๋‚œ ์‹œ๊ฐ„์— ์ด์•ผ๊ธฐํ•œ ๋ฐ”์™€ ๊ฐ™์ด service server๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

์ด๋ฒˆ ์‹œ๊ฐ„์˜ ์˜ˆ์ œ๋ฅผ ์‹คํ–‰์‹œ์ผœ ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

Turning tinybot


$ roslaunch gcamp_gazebo gazebo_world.launch 
# gazebo ์‹œ๋ฎฌ๋ ˆ์ด์…˜์ด ์™„์ „ํžˆ ์ผœ์งˆ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ ค ์ฃผ์„ธ์š”!!

# ์ƒˆ ํ„ฐ๋ฏธ๋„์„ ์—ฝ๋‹ˆ๋‹ค.
$ rosrun service_tutorial robot_turning_srv.py

# ๋˜ ๋‹ค๋ฅธ ํ„ฐ๋ฏธ๋„์„ ์—ฝ๋‹ˆ๋‹ค.
$ rosrun service_tutorial robot_turning_client.py
[INFO] [1610111418.391926, 0.000000]: ==== Robot Turning Server Started ====
> Type turning time duration: 

์ด๋ฒˆ ์˜ˆ์ œ๋Š”, ์‚ฌ์šฉ์ž๋กœ๋ถ€ํ„ฐ time, velocity input์„ ๋ฐ›๋„๋ก ๋˜์–ด ์žˆ์œผ๋‹ˆ, ์ ์ ˆํ•œ ๊ฐ’์„ ์ž…๋ ฅํ•ด ์ฃผ์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

  • ์—ฌ๊ธฐ์„œ์˜ ์†๋„๋Š” ํšŒ์ „ํ•˜๋Š” ๊ฐ์†๋„๋ฅผ ์˜๋ฏธํ•˜๋ฉฐ, ๋‹จ์œ„๋Š” radian์ž…๋‹ˆ๋‹ค.
  • ์‹œ๊ฐ„์€ ์•ž์„  ๊ฐ์†๋„๋กœ ํšŒ์ „ํ•˜๋Š” ์‹œ๊ฐ„์„ ์˜๋ฏธํ•˜๋ฉฐ, ๋‹จ์œ„๋Š” ์ดˆ์ž…๋‹ˆ๋‹ค.

๋กœ๋ด‡์ด ์›ํ•˜๋Š” ์‹œ๊ฐ„, ์›ํ•˜๋Š” ์†๋„๋กœ ์›€์ง์˜€๋‚˜์š”??


์‹œ๊ฐ„๊ณผ ์†๋„๋ฅผ ์ž˜ ์กฐ์ ˆํ•ด์„œ 90๋„, 180๋„ ๋“ฑ ์›ํ•˜๋Š” ๊ฐ๋„๋งŒํผ ๋กœ๋ด‡์ด ํšŒ์ „ํ•˜๋„๋ก ํ•ด๋ณด์„ธ์š” ๐Ÿ™‚

๊ทธ๋Ÿผ, ์ฝ”๋“œ๋ฅผ ๋ถ„์„ํ•ด ๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค!! client, server ์ˆœ์œผ๋กœ ๋ถ„์„ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

robot_turning_client.py


#! /usr/bin/env python

"""
basic rospy service example

referenced from wiki.ros.org

url : http://wiki.ros.org/ROS/Tutorials/WritingServiceClient%28python%29 
"""

import sys
import rospy
from service_tutorial.srv import ControlTurningMessage, ControlTurningMessageRequest

rospy.init_node("robot_turning_client")
rospy.loginfo("==== Robot Turning Server Started ====")

rospy.wait_for_service("/control_robot_angle")
service_client = rospy.ServiceProxy("/control_robot_angle", ControlTurningMessage)

request_srv = ControlTurningMessageRequest()

while not rospy.is_shutdown():
    try:
        t = input("> Type turning time duration: ")
        vel = input("> Type turning angular velocity: ")

        if vel > 1.5707:
            raise ArithmeticError("Velocity too high !!")

        request_srv.time_duration = t
        request_srv.angular_vel = vel
        break
    except ArithmeticError as e:
        rospy.logerr(e)
    except Exception as e:
        rospy.logerr("Not a number type number plz !!")

result = service_client(request_srv)

print(result)

  • topic์—์„œ message import๋ฅผ ํ•ด์ฃผ์—ˆ๋˜ ๊ฒƒ๊ณผ ๊ฐ™์ด service์—์„œ ์‚ฌ์šฉ๋˜๋Š” srv๋ฅผ import ํ•ด์ค๋‹ˆ๋‹ค.
# ์ด์ „ topic message์—์„œ๋Š” ์ด๋ ‡๊ฒŒ ์‚ฌ์šฉํ–ˆ์ฃ ?
from sensor_msgs.msg import LaserScan 

# service๋Š” ์ด๋ ‡๊ฒŒ .srv๋กœ import ํ•ฉ๋‹ˆ๋‹ค.
from service_tutorial.srv import ControlTurningMessage, ControlTurningMessageRequest

client๊ฐ€ request๋ฅผ ํ•ด์•ผ ํ•˜๋Š”๋ฐ, ๊ทธ ๋Œ€์ƒ์ด ๋˜๋Š” server๊ฐ€ ์—†๋‹ค๋ฉด ๊ณค๋ž€ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ด๋ฅผ ์œ„ํ•ด wait_for_service๋ฅผ ํ†ตํ•ด server๊ฐ€ ๋™์ž‘์ค‘์ธ์ง€ ํ™•์ธํ•˜๊ณ , ServiceProxy๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

rospy.wait_for_service('/control_robot_angle')

  • result ๋ถ€์—๋Š” server๋กœ ๋ถ€ํ„ฐ ๋˜๋Œ์•„์˜จ response๊ฐ€ ๋‹ด๊ฒจ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•ด๋‹น ์˜ˆ์ œ์—์„œ๋Š” ๋‹จ์ˆœํžˆ ์„ฑ๊ณต ์—ฌ๋ถ€๋งŒ ํŒ๋‹จํ•˜์˜€์ง€๋งŒ, ์ถ”๊ฐ€ ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด ๋” ๋งŽ์€ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๊ฒ ์ฃ ?
service_client = rospy.ServiceProxy('/control_robot_angle', ControlTurningMessage)

(...)

result = service_client(request_srv)

print(result)

๋‚˜๋จธ์ง€ ๋ถ€๋ถ„์€ ์‚ฌ์šฉ์ž๋กœ๋ถ€ํ„ฐ ์ž…๋ ฅ์„ ๋ฐ›๊ณ , ์˜ˆ์™ธ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๋Š” ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค.

์ง€๊ธˆ ๊ฐ•์˜๋ฅผ ๋“ฃ๋Š” ์‹œ์ ์—์„œ๋Š” ๋ชจ๋‘ ๋ฌธ์ œ์—†์ด ์˜๋ฏธ์žˆ๋Š” ์ˆซ์ž๋ฅผ ์ž…๋ ฅํ•˜๊ฒ ์ง€๋งŒ, ๋‚ด๊ฐ€ ๋งŒ๋“  ์ฝ”๋“œ๋ฅผ ๋‹ค๋ฅธ ์‚ฌ๋žŒ์ด ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ, ์˜จ๊ฐ– ์˜ˆ์™ธ์ƒํ™ฉ๋“ค์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด, ํŒŒ์ด์ฌ์˜ try/except๋ฌธ์„ ์‚ฌ์šฉํ•˜์˜€์Šต๋‹ˆ๋‹ค.

while not rospy.is_shutdown():
    try:
        t = input('> Type turning time duration: ')
        vel = input('> Type turning angular velocity: ')

        if vel > 1.5707:    
            raise ArithmeticError("Velocity too high !!")

        request_msg.time_duration = t
        request_msg.angular_vel = vel
        break
    except ArithmeticError as e:
        rospy.logerr(e)
    except Exception as e:
        rospy.logerr("Not a number type number plz !!")
๐Ÿ’ก
์ฐธ๊ณ ๋กœ, 1.5707์ด ์–ด๋– ํ•œ ์ˆซ์ž๋ƒ๋ฉด, pi (3.141)์˜ ์ ˆ๋ฐ˜์ด ๋˜๋Š” ์ˆซ์ž์ž…๋‹ˆ๋‹ค. ํŒŒ์ด์ฌ์˜ math ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๊ฐ€์ ธ์™€๋„ ์ข‹์ง€๋งŒ, ์ž์ฃผ ์‚ฌ์šฉ๋˜๋Š” ์ˆซ์ž๋ผ ์™ธ์›Œ๋‘๋ฉด ํŽธํ•ฉ๋‹ˆ๋‹ค.

์ด๋ฒˆ์—๋Š” service server ์ฝ”๋“œ๋ฅผ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค!

robot_turning_srv.py


#! /usr/bin/env python

import time
import rospy
from service_tutorial.srv import ControlTurningMessage, ControlTurningMessageResponse
from geometry_msgs.msg import Twist

def callback(request):

    print(request)
    
    # publish to cmd_vel
    cmd_vel = Twist()
    cmd_vel.angular.z = request.angular_vel
    start_time = time.time()

    print('\nRobot Turning...')
    while time.time() - start_time < request.time_duration:
        velocity_publisher.publish(cmd_vel)
    
    print("Done ...")
    cmd_vel.angular.z = 0.0
    velocity_publisher.publish(cmd_vel)

    response = ControlTurningMessageResponse()
    response.success = True

    return response

rospy.init_node('robot_turning_server')
rospy.loginfo("==== Robot Turning Server Started ====")

service_server = rospy.Service('/control_robot_angle', ControlTurningMessage, callback)
velocity_publisher = rospy.Publisher('/cmd_vel', Twist, queue_size=1 )

rospy.spin()

์ด์ „ ์ฝ”๋“œ์™€ ๋น„๊ตํ•ด์„œ, import ๋ถ€๋ถ„์— ์ฐจ์ด๊ฐ€ ๋ณด์ž…๋‹ˆ๋‹ค.

# client
from service_tutorial.srv import ControlTurningMessage, ControlTurningMessageRequest

# server
from service_tutorial.srv import ControlTurningMessage, ControlTurningMessageResponse

  • ControlTurningMessage srv ํ˜•์‹์— ๋Œ€ํ•ด ์กฐํšŒํ•ด๋ณผ๊นŒ์š”?
$ rossrv info ControlTurningMessage
[service_tutorial/ControlTurningMessage]:
uint32 time_duration
float64 angular_vel
---
bool success

[cpp_service_tutorial/ControlTurningMessage]:
uint32 time_duration
float64 angular_vel
---
bool success

์ด๊ฒƒ์„ ํŒŒ์ด์ฌ ์ฝ”๋“œ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ๋Š” [MessageName]Request, [MessageName]Response ์ด๋Ÿฐ ํ˜•ํƒœ๋ฅผ ๊ฐ–๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. import ํ•œ ๋’ค์—๋Š” ์ ์ ˆํ•œ ๊ฐ’์„ ์ฑ„์›Œ์ค˜์•ผ ํ•˜๊ฒ ์ง€์š”

    response = ControlTurningMessageResponse()
    response.success = True
		
		return response

ControlTurningMessage๋Š” ์–ด๋””์—์„œ ๋‚˜์˜จ ๊ฒƒ์ผ๊นŒ์š”? srv๋“ค์„ ์กฐํšŒํ•˜๋ ค๋ฉด, rossrv ์ปค๋งจ๋“œ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

$ rossrv list
control_msgs/QueryCalibrationState
control_msgs/QueryTrajectoryState
control_toolbox/SetPidGains
controller_manager_msgs/ListControllerTypes
controller_manager_msgs/ListControllers
controller_manager_msgs/LoadController
controller_manager_msgs/ReloadControllerLibraries
controller_manager_msgs/SwitchController
controller_manager_msgs/UnloadController
diagnostic_msgs/AddDiagnostics
diagnostic_msgs/SelfTest
dynamic_reconfigure/Reconfigure
(...)

์ง€๊ธˆ์€ ์˜ˆ์ œ๋ฅผ ์œ„ํ•ด ์ œ๊ฐ€ ๋งŒ๋“ค์–ด๋‘” srv๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์œผ๋ฉฐ, ๋‹ค์Œ ์œ„์น˜์—์„œ ํ™•์ธ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

$ cd ~/gcamp_ws/src/gcamp_ros_basic/service_tutorial/srv
$ ls -al
ControlTurningMessage.srv
$ cat ControlTurningMessage.srv
uint32 time_duration
float64 angular_vel
---
bool success

์ตœ์ข…์ ์œผ๋กœ, service_tutorial์—์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์„ธ๊ฐ€์ง€ srv๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ControlTurningMessage
  • ControlTurningMessageRequest
  • ControlTurningMessageResponse

  • ์ฝ”๋“œ๋กœ ๋Œ์•„์™€์„œ, import ๋ถ€๋ฅผ ๋‹ค์‹œ ์ •๋ฆฌํ•˜๋ฉด, client์—๊ฒŒ response๋Š” ๋ถˆ๊ฐ€ํ•˜๋‹ˆ request๋งŒ import ํ•˜๋ฉด ๋  ๊ฒƒ์ด๋ฉฐ, ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ด๋ฆ„์œผ๋กœ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.
from service_tutorial.srv import ControlTurningMessage, ControlTurningMessageResponse

  • ์ด์ œ๋Š” ์นœ์ˆ™ํ•œ init_node, loginfo๊ฐ€ ๋ณด์ด๊ณ , Service Server class๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋ถ€๋ถ„์ด ๋ณด์ž…๋‹ˆ๋‹ค.
rospy.init_node('robot_turning_server')
rospy.loginfo("==== Robot Turning Server Started ====")

service_server = rospy.Service('/control_robot_angle', ControlTurningMessage, callback)
velocity_publisher = rospy.Publisher('/cmd_vel', Twist, queue_size=1 )

rospy.spin()
  • rospy.Sevice('service-server-name', 'srv', 'callback')

์ด์ฒ˜๋Ÿผ, ๊ธฐ๋ณธ ์ •๋ณด๋“ค๊ณผ ํ•จ๊ป˜ ์ดˆ๊ธฐํ™”๋ฅผ ํ•ด์ค€ ๋’ค, ์š”์ฒญ์ด ๋“ค์–ด์˜ฌ ๋•Œ๋งˆ๋‹ค ์ฝœ๋ฐฑ์ด ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

๐Ÿ’ก
์—ฌ๊ธฐ์„œ, ํ˜ผ๋ž€์Šค๋Ÿฌ์šธ ์ˆ˜ ์žˆ๋Š” ์ ์ด ControlTurningMessage๋ฅผ ์ธ์ž๋กœ ๋„ฃ์–ด์ฃผ์–ด์•ผ ํ•œ๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค.
rospy.Service('/control_robot_angle', ControlTurningMessageResponse, callback)

server๋Š” response๋งŒ ์‚ฌ์šฉํ•˜๋‹ˆ๊นŒ ControlTurningMessageResponse๋ฅผ ๋„ฃ์–ด์•ผ ํ•˜์ง€ ์•Š๋‚˜? ํ•˜๋Š” ์ƒ๊ฐ์œผ๋กœ ์‹ค์ˆ˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ๋‹ค์‹œ ์ฝ”๋“œ๋กœ ๋Œ์•„์™€์„œ, ๋ณธ ์˜ˆ์ œ๊ฐ€ ๊ฐ์†๋„, ์‹œ๊ฐ„์„ request ๋ฐ›์•„ ๋กœ๋ด‡์„ ์›€์ง์ด๋Š” ๊ฒƒ์ด์—ˆ๊ธฐ์— cmd_vel๋กœ publishํ•˜๋Š” publisher๋„ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.
velocity_publisher = rospy.Publisher("/cmd_vel", Twist, queue_size=1)

  • ๋‹ค์Œ์€ callback์ž…๋‹ˆ๋‹ค. client๋กœ๋ถ€ํ„ฐ request๊ฐ€ ์˜ฌ ๋•Œ๋งˆ๋‹ค ์‹คํ–‰๋  ๋ถ€๋ถ„์œผ๋กœ, request srv์—์„œ ๊ฐ์†๋„์™€ ์‹œ๊ฐ„์„ ๊ฐ€์ ธ์™€ cmd_vel๋กœ publishํ•˜๋Š” ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค.
def callback(request):

    print(request)
    
    # publish to cmd_vel
    cmd_vel = Twist()
    cmd_vel.angular.z = request.angular_vel
    start_time = time.time()

    print('\nRobot Turning...')
    while time.time() - start_time < request.time_duration:
        velocity_publisher.publish(cmd_vel)
    
    print("Done ...")
    cmd_vel.angular.z = 0.0
    velocity_publisher.publish(cmd_vel)

    response = ControlTurningMessageResponse()
    response.success = True

    return response

๋งˆ์ง€๋ง‰์œผ๋กœ, ๋ชจ๋“  ์ž‘์—…์ด ์™„๋ฃŒ๋˜์—ˆ๋‹ค๋ฉด, client์—๊ฒŒ ์„ฑ๊ณตํ–ˆ๋‹ค๋Š” response๋ฅผ ๋ณด๋‚ด์•ผ ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

ํ˜„์žฌ๋Š” ํƒ€์ž…์ด bool์ด๊ธฐ ๋•Œ๋ฌธ์— True๋ฅผ ๋„ฃ์–ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.

gazebo services


์šฐ๋ฆฌ๊ฐ€ roslaunch๋ฅผ ํ†ตํ•ด gazebo๋ฅผ ์‹คํ–‰์‹œํ‚ค๋Š” ๊ฒƒ๊ณผ, ์ผ๋ฐ˜์ ์œผ๋กœ gazebo๋ฅผ ์‹คํ–‰์‹œํ‚ฌ ๋•Œ์—๋Š” ์ฐจ์ด๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

roslaunch๋กœ ์‹คํ–‰์‹œํ‚ค๋Š” ๊ฒฝ์šฐ์—๋Š” gazebo_ros๋ฅผ ํ†ตํ•ด rosnode๋“ค๋„ ๊ฐ™์ด ์‹คํ–‰๋œ๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ, ๋ชจ๋ธ์„ ๋ถˆ๋Ÿฌ์˜ค๊ณ , ์ œ๊ฑฐํ•˜๊ณ , ์œ„์น˜๋ฅผ ์ด๋™์‹œํ‚ค๊ณ , ์ค‘๋ ฅ ์ƒ์ˆ˜๋ฅผ ๋ฐ”๊พธ๋Š” ๋“ฑ์˜ ์ˆ˜๋งŽ์€ service๋“ค์ด ๊ฐ™์ด ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

๊ทธ ์ค‘์—์„œ, ์ด๋ฒˆ ์‹œ๊ฐ„์— ๋ฐฐ์šด ๋ชจ๋ธ spawn์„ ํฌํ•จํ•˜์—ฌ, ๋ชจ๋ธ ์ œ๊ฑฐ, ์‹œ๋ฎฌ๋ ˆ์ด์…˜ ์ดˆ๊ธฐํ™”๋ฅผ requestํ•˜๋Š” service client๋“ค์„ ์˜ˆ์‹œ๋กœ ์ œ๊ณตํ•˜๋‹ˆ, gazebo๋ฅผ ํ†ตํ•ด ๋” ๋งŽ์€ ์‘์šฉ๊ณผ ์—ฐ์Šต์„ ํ•ด๋ณด์‹œ๊ธธ ๋ฐ”๋ž๋‹ˆ๋‹ค. ๐Ÿคฉ๐Ÿคฉ

#! /usr/bin/env python

"""
Fuctions for Gazebo model Spawn and Deletion
created by kimsooyoung : https://github.com/kimsooyoung
"""

import rospy
import rospkg

from std_srvs.srv import Empty
from geometry_msgs.msg import Pose
from gazebo_msgs.srv import SpawnModel, DeleteModel

def GazeboSpawnModel( name, pkg_name, pose, namespace='', frame='world'):
    # model_name
    model_name = name

    # model_xml
    rospack = rospkg.RosPack()
    model_path = rospack.get_path(pkg_name)+'/models/'

    with open (model_path + model_name + '.urdf', 'r') as xml_file:
        model_xml = xml_file.read().replace('\n', '')

    # robot_namespace
    robot_namespace = namespace

    # initial_pose
    initial_pose = Pose()
    initial_pose.position = pose.position

    # z rotation -pi/2 to Quaternion
    initial_pose.orientation = pose.orientation

    # reference_frame
    reference_frame = frame

    # service call
    spawn_model_prox = rospy.ServiceProxy('gazebo/spawn_urdf_model', SpawnModel)
    result = spawn_model_prox(
        model_name, model_xml,
        robot_namespace, initial_pose,
        reference_frame) 

    ''' result fromat
    bool success
    string status_message
    '''

    print(result.status_message)
    

def GazeboDeleteModel(name):

    '''
    string model_name
    ---
    bool success
    string status_message
    '''

    delete_srv = DeleteModel()

    model_name = name

    spawn_model_prox = rospy.ServiceProxy('gazebo/delete_model', DeleteModel)
    result = spawn_model_prox(model_name)

    print(result.status_message)


def GazeboResetSimulation():
    spawn_model_prox = rospy.ServiceProxy('gazebo/reset_simulation', Empty)
    result = spawn_model_prox()

    rospy.loginfo('Gazebo Simulation Reset')

rospy.init_node("gazebo_spawn_handler")

์ง€๊ธˆ๊นŒ์ง€ ROS์˜ service์— ๋Œ€ํ•ด ๋ฐฐ์›Œ๋ณด์•˜์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ์œผ๋กœ action์— ๋Œ€ํ•ด ๋ฐฐ์›Œ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค!! ๐Ÿ‘จ๐Ÿปโ€๐Ÿ’ป