
์ด๋ฒ์๋ Ch8. ๋ฉ์์ง ์ ์ ๋ง๋ค๊ณ ํ ํฝ๊ณผ ์๋น์ค์์ ๋ค๋ฃจ๊ธฐ์ ๋ํด์ ์์ฑํ๊ณ ์ ํ๋ค.
์ด๋ฒ์๋ ๋ฉ์์ง๋ฅผ ์ ์ํ๊ณ ์ด๋ฅผ ๋ค๋ฃจ๋ ๊ฒ์ ๋ํด์ ์์ฑํ ๊ฒ์ด๋ค.
1. ๋ฉ์์ง ์ ์
(1) ๋ฉ์์ง ์ ์๋ฅผ ์ํ ๋ณ๋์ ํจํค์ง ๋ง๋ค๊ธฐ
์์ 7์ฅ์์ ํจํค์ง๋ฅผ ๋ง๋ค ๋ ์ฌ์ฉํ ๋ช ๋ น์ ๋ค์๊ณผ ๊ฐ๋ค.
ros2 pkg create --build-type ament_python --node-name my_first_node my_first_package
์ด ๋ช ๋ น์ด๋ ๋น๋ ๋๊ตฌ๋ฅผ ament_python์ผ๋ก ํ๊ฒ ๋ค๊ณ ํ์๋๋ฐ, ์ด ๊ฒฝ์ฐ์ colcon์ ์๋ก์ด ๋ฉ์์ง ์ ์๋ฅผ ๋ง๋ค์ง ๋ชปํ๋ค.
๊ทธ ์ด์ ๋ CMakeLists.txt๊ฐ ํ์ํ๊ณ , ์ด ํ์ผ์ ament_python์ด ๋น๋ ๋๊ตฌ ์ผ ๋ ์ฌ์ฉ ๋ถ๊ฐ๋ฅํ๊ธฐ ๋๋ฌธ์ด๋ค.
๋ฐ๋ผ์ C++๋ก ํจํค์ง๋ฅผ ๋ง๋ค๊ธฐ ์ํด์ ๋ค์๊ณผ ๊ฐ์ ๋ช ๋ น์ด๋ฅผ ์ํฌ์คํ์ด์ค์ src ํด๋์์ ์คํํ๋ค.
ros2 pkg create --build-type ament_cmake my_first_package_msgs

์ดํ tree ๋ช ๋ น์ด๋ฅผ ์คํ ์ ๋ค์๊ณผ ๊ฐ์ ๊ฒฐ๊ณผ๊ฐ ๋์จ๋ค.

(2) ๋ฉ์์ง ์ ์ msg definition ๋ง๋ค๊ธฐ
์ด์ my_first_package_msgs ํด๋์ msg๋ผ๋ ํด๋๋ฅผ ๋ง๋ค๊ณ , ์ดํ my_first_package_msgs ํด๋๋ฅผ ํธ์ง๊ธฐ๋ก ์ด์ด CmdAndPoseVel.msg ๋ผ๋ ํ์ผ์ ๋ง๋ ๋ค.


์ดํ ๋ค์๊ณผ ๊ฐ์ด ์์ฑ์ ํ๋ค.
float32 cmd_vel_linear
float32 cmd_vel_angular
float32 pose_x
float32 pose_y
float32 linear_vel
float32 angular_vel
์ด๋ ํด์ํด๋ณด๋ฉด, cmd_vel ํ ํฝ์ linear์ x ์ฑ๋ถ๊ณผ angular์ z ์ฑ๋ถ์ ๊ฐ๊ฐ cmd_vel_linear์ cmd_vel_angular๋ก ๋ฐ๊ณ , turtlesim์ pose ํ ํฝ์์ x, y ์ขํ์ linear_velocity, angular_velocity ๊ฐ์ ๊ฐ๊ฐ pose_x, pose_y, linear_vel, angular_vel๋ก ๋ฐ์ ์์ ์ด๋ค.
(3) ์๋ก ์ ์๋ msg๋ฅผ ํฌํจํ ํจํค์ง ๋น๋ํ๊ธฐ
์ด๋ ๊ฒ ์๋ก ์ ์๋ msg๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด์๋ 2๊ฐ์ ํ์ผ์ ์์ ๋์ผ ํ๋ค.
>> CMakeList.txt
>> package.xml
์ด๋ ๊ฒ ๋๊ฐ์ง ํ์ผ์ด๋ค.
๋จผ์ , CMake ํ์ผ์์ ๋ค์ ์์น์ ๋ค์ ๋ฌธ๊ตฌ๋ฅผ ์ถ๊ฐํ๋ค.
find_package(rosidl_default_generators REQUIRED)
rosidl_generate_interfaces(${PROJECT_NAME}
"msg/CmdAndPoseVel.msg"
)

์ด ๋ฌธ๊ตฌ๋ CmdAndPoseVel.msg ํ์ผ์ ์ฐพ์ ๋ฉ์์ง๋ก ๋ฑ๋กํด์ ๋น๋ํ๋ผ๋ ์ค์ ์ Cmake์ ์ถ๊ฐํ๋ ๋ด์ฉ์ด๋ค.
์ดํ package.xml ํ์ผ์ ๋ค์ ์ฝ๋๋ฅผ ์์ฑํ๋ค.
<build_depend>rosidl_default_generators</build_depend>
<exec_depend>rosidl_default_runtime</exec_depend>
<member_of_group>rosidl_interface_packages</member_of_group>

์ดํ ์ํฌ ์คํ์ด์ค๋ก ๋์๊ฐ์ ๋น๋ํ ๋ค, ros2 interface show my_first_package_msgs/msg/CmdAndPoseVel
๋ช ๋ น์ ํตํด ์ธ์๋๋์ง ํ์ธํ๋ค.

ros2 interface show my_first_package_msgs/msg/CmdAndPoseVel

์ด๋ฅผ ํตํด ์ ๋๋ก msg๊ฐ ์๋กญ๊ฒ ์ ์ธ์๋ ๊ฒ์ ํ์ธ ๊ฐ๋ฅํ๋ค.
2. ๋ ๊ฐ์ ํ ํฝ ๊ตฌ๋ ํ๊ณ ํ๋์ ํ ํฝ์ ๋ฐํํ๊ธฐ
< ๋ชฉํ >
- turtlesim์ pose ํ ํฝ ๊ตฌ๋
- my_publisher ๋ ธ๋์ cmd_vel ํ ํฝ ๊ตฌ๋
- ๋ฐ์ดํฐ ํ์ ์ ์ฌ์ฉํ๋ ํ ํฝ์ผ๋ก ๋ฐฉ๊ธ ๊ตฌ๋ ํ ๋ ํ ํฝ์ ์ ์ฅ, ๋ฐํ
(1) turtlesim์ด ๋ฐํํ๋ pose ํ ํฝ ๊ตฌ๋ ๋ถ๋ถ๋ถํฐ ์์ํ๊ธฐ
์ํฌ ์คํ์ด์ค์ src ํด๋๋ฅผ code๋ก ์ฐ ๋ค, my_first_package ํด๋์์ turtle_cmd_and_pose.py๋ก ๋ง๋ค๊ณ , ์ด ํ์ผ์ setup.py์ ์ถ๊ฐํ๋ค.
import rclpy as rp
from rclpy.node import Node
from turtlesim.msg import Pose
class CmdAndPose(Node):
def __init__(self):
super().__init__('turtle_cmd_pose')
self.sub_pose = self.create_subscription(Pose, '/turtle1/pose', self.callback_pose, 10)
def callback_pose(self, msg):
print(msg)
def main(args=None):
rp.init(args=args)
turtle_cmd_pose_node = CmdAndPose()
rp.spin(turtle_cmd_pose_node)
turtle_cmd_pose_node.destroy_node()
rp.shutdown()
if __name__ == '__main__':
main()
์ดํ ์ํฌ ์คํ์ด์ค๋ก ์ด๋ํ์ฌ ๋น๋๋ฅผ ์ํํ ๋ค, ํ์ชฝ์์ turtlesim_node, ํ์ชฝ์์ turtle_cmd_and_pose ๋ ธ๋๋ฅผ ์คํํ๋ค.

์ด๋ ๊ฒ ์ ์คํ๋๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
(2) ์๋ก ์ ์ํ CmdAndPoseVel ์ฌ์ฉํด ๋ณด๊ธฐ
์ด์ pose ๊ตฌ๋ ์ด ์ ๋๋ ๊ฒ์ ํ์ธํ์ผ๋ฏ๋ก, ์ด์ CmdAndPoseVel์ด๋ผ๋ ๋ฐ์ดํฐ ์ ์๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด์ ๊ฐ๊ฐ์ ๋ฐ์ดํฐ ์ ์๋ฅผ ์ด์ ์ฌ์ฉํ ๋ฐ์ดํฐ ์ ์์ ์ ์ฅํด์ผ ํ๊ธฐ ๋๋ฌธ์ ํ ํฝ ๋ฐ์ดํฐ ์ ์๋ฅผ import ํ๋ค.
์ฝ๋๋ ๋ค์๊ณผ ๊ฐ๊ณ , ๋๋ init์ CmdAndVelPose๋ฅผ ๊ฐ์ฒดํํ๋ ์ฝ๋๋ฅผ ํฌํจํ๋ค.
from my_first_package_msgs.msg import CmdAndPoseVel
self.cmd_pose = CmdAndPoseVel()
์ดํ callback_pose ํจ์์ ๋ด์ฉ์ turtlesim์ pose๋ฅผ ๋ฐ์์ CmdAndPoseVel์์ ํด๋นํ๋ ์์ฑ๋ง ๋ฐ๊พธ์ด ์ ์ฅํ๋๋ก ๋ค์๊ณผ ๊ฐ์ ์ฝ๋๋ฅผ ์์ฑํ๋ค.
def callback_pose(self, msg):
self.cmd_pose.pose_x = msg.x
self.cmd_pose.pose_y = msg.y
self.cmd_pose.linear_vel = msg.linear_velocity
self.cmd_pose.angular_vel = msg.angular_velocity
print(self.cmd_pose)
์ ์ฒด ์ฝ๋๋ ๋ค์๊ณผ ๊ฐ๋ค.
import rclpy as rp
from rclpy.node import Node
from turtlesim.msg import Pose
from my_first_package_msgs.msg import CmdAndPoseVel
class CmdAndPose(Node):
def __init__(self):
super().__init__('turtle_cmd_pose')
self.sub_pose = self.create_subscription(Pose, '/turtle1/pose', self.callback_pose, 10)
self.cmd_pose = CmdAndPoseVel()
def callback_pose(self, msg):
self.cmd_pose.pose_x = msg.x
self.cmd_pose.pose_y = msg.y
self.cmd_pose.linear_vel = msg.linear_velocity
self.cmd_pose.angular_vel = msg.angular_velocity
print(self.cmd_pose)
def main(args=None):
rp.init(args=args)
turtle_cmd_pose_node = CmdAndPose()
rp.spin(turtle_cmd_pose_node)
turtle_cmd_pose_node.destroy_node()
rp.shutdown()
if __name__ == '__main__':
main()
์ดํ ์ํฌ ์คํ์ด์ค๋ฅผ ๋น๋ํ๋๋ฐ, ํ๋์ ํจํค์ง๋ง ๋น๋ํ๊ธฐ ์ํด์ ๋ค์ ๋ช ๋ น์ด๋ฅผ ์คํํ๋ค.
colcon build --packages-select my_first_package

์ดํ ๋ค์ turtlesim ๋ ธ๋์ cmd_and_pose๋ฅผ ์คํํ๋ฉด cmd_pose๋ผ๋ ๋ฉ์์ง์ ํน์ ๋ฐ์ดํฐ๋ค์ด ์ ์ ์ฅ๋๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.

(3) cmd_vel ํ ํฝ๋ ๊ตฌ๋ ํด ๋ณด๊ธฐ
์ ์ํฉ์์ ๋ ํ๋์ ํ ํฝ์ ๊ตฌ๋ ํ๊ธฐ ์ํด์ create_subscription์ ํ๋ฒ ๋ ์๋์ ๊ฐ์ด ์ฌ์ฉํ์๊ณ , ์ฝ๋ฐฑ ํจ์๋ฅผ callback_cmd๋ผ๊ณ ํ์๋ค.
self.sub_cmdvel = self.create_subscription(Twist, '/turtle1/cmd_vel', self.callback_cmd, 10)
์ฌ๊ธฐ์ ์ฌ์ฉํ๋ ๋ฉ์์ง ์ ์๋ Twist์๊ณ ์ด๋ฅผ ์๋์ ๊ฐ์ด ์ง์ ํ์๋ค.
from geometry__msgs.msg import Twist
์ฝ๋ฐฑํจ์ callback_cmd๋ ๋ค์๊ณผ ๊ฐ์ด ๋ง๋ค๊ณ , cmd_pose์ vel_linear, vel_angular๋ฅผ ๊ฐ๊ฐ cmd_vel, linear์ x, angular์ z๋ฅผ ์ ์ฅํ๋๋ก ํ๋ค.
def callback_cmd(self, msg):
self.cmd_pose.cmd_vel_linear = msg.linear.x
self.cmd_pose.cmd_vel_angular = msg.angular.z
import rclpy as rp
from rclpy.node import Node
from turtlesim.msg import Pose
from geometry_msgs.msg import Twist
from my_first_package_msgs.msg import CmdAndPoseVel
class CmdAndPose(Node):
def __init__(self):
super().__init__('turtle_cmd_pose')
self.sub_pose = self.create_subscription(Pose, '/turtle1/pose', self.callback_pose, 10)
self.sub_cmdvel = self.create_subscription(Twist, '/turtle1/cmd_vel', self.callback_cmd, 10)
self.cmd_pose = CmdAndPoseVel()
def callback_pose(self, msg):
self.cmd_pose.pose_x = msg.x
self.cmd_pose.pose_y = msg.y
self.cmd_pose.linear_vel = msg.linear_velocity
self.cmd_pose.angular_vel = msg.angular_velocity
def callback_cmd(self, msg):
self.cmd_pose.cmd_vel_linear = msg.linear.x
self.cmd_pose.cmd_vel_angular = msg.angular.z
print(self.cmd_pose)
def main(args=None):
rp.init(args=args)
turtle_cmd_pose_node = CmdAndPose()
rp.spin(turtle_cmd_pose_node)
turtle_cmd_pose_node.destroy_node()
rp.shutdown()
if __name__ == '__main__':
main()
์ฝ๋ ์ ๋ฌธ์ ๋ค์๊ณผ ๊ฐ๋ค.
์ดํ ๋น๋๋ฅผ ์ฌ์ํ ํ๊ณ , ํฐ๋ฏธ๋์ ์ด์ด์ turtlesim๊ณผ my_publisher, turtle_cmd_and_pose๋ฅผ ์คํํ๋ค.

์ด๋ ๊ฒ turtle์ด ๋๊ณ ๊ทธ ์์น๋ฅผ CmdAndPoseVel์ ์ ์๋ ๋ฐ์ดํฐ์ ์ ์ฅํ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
(4) ๋ ๊ฐ์ ํ ํฝ์ ๊ตฌ๋ ํ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํด ๋ณด๊ธฐ
ํ์ฌ ๋ง๋ค์ด์ง my_publisher ๋ ธ๋๋ 0.5์ด๋ง๋ค ํ ๋ฒ์ฉ cmd_vel์ ๋ฐํํ๋ค.
์ด๋ฒ์๋ cmd_and_pose๋ผ๋ ์ด๋ฆ์ ํ ํฝ์ผ๋ก 1์ด์ ํ๋ฒ ๋ฐํํ๋๋ก ํ๊ณ ์ ํ๋ค.
self.publisher = self.create_publisher(CmdAndPoseVel, '/cmd_and_pose', 10)
์ดํ ์ผ์ ๊ฐ๊ฒฉ์ ์ ์งํ๋๋ก ํ์ด๋จธ๋ฅผ timer_callback์ด ์คํ๋๋๋ก ์ค์ ํ์๋ค.
self.timer = self.create_timer(self.timer_period, self.timer_callback)
๊ทธ timer_callback ํจ์์์ ํ ํฝ์ ๋ฐํํ๋๋ก ๋ค์๊ณผ ๊ฐ์ด ์ค์ ํ์๋ค.
def timer_callback(self):
self.publisher.publish(self.cmd_pose)
์์ฑ๋ ์ฝ๋๋ ๋ค์๊ณผ ๊ฐ๋ค.
import rclpy as rp
from rclpy.node import Node
from turtlesim.msg import Pose
from geometry_msgs.msg import Twist
from my_first_package_msgs.msg import CmdAndPoseVel
class CmdAndPose(Node):
def __init__(self):
super().__init__('turtle_cmd_pose')
self.sub_pose = self.create_subscription(Pose, '/turtle1/pose', self.callback_pose, 10)
self.sub_cmdvel = self.create_subscription(Twist, '/turtle1/cmd_vel', self.callback_cmd, 10)
self.timer_period = 1.0
self.publisher = self.create_publisher(CmdAndPoseVel, '/cmd_and_pose', 10)
self.timer = self.create_timer(self.timer_period, self.timer_callback)
self.cmd_pose = CmdAndPoseVel()
def callback_pose(self, msg):
self.cmd_pose.pose_x = msg.x
self.cmd_pose.pose_y = msg.y
self.cmd_pose.linear_vel = msg.linear_velocity
self.cmd_pose.angular_vel = msg.angular_velocity
def callback_cmd(self, msg):
self.cmd_pose.cmd_vel_linear = msg.linear.x
self.cmd_pose.cmd_vel_angular = msg.angular.z
def timer_callback(self):
self.publisher.publish(self.cmd_pose)
def main(args=None):
rp.init(args=args)
turtle_cmd_pose_node = CmdAndPose()
rp.spin(turtle_cmd_pose_node)
turtle_cmd_pose_node.destroy_node()
rp.shutdown()
if __name__ == '__main__':
main()
์ ์ฝ๋๋ฅผ ๋ค์ ๋น๋ํ๊ณ , 4๊ฐ์ ํฐ๋ฏธ๋์์ ๊ฐ๊ฐ turtlesim, my_publisher, turtle_cmd_and_pose, rqt_graph๋ฅผ ์ด์๋ค.

๋ค์๊ณผ ๊ฐ์ ๋ชจ์ต์ ๋ณผ ์ ์์ผ๋ฉฐ, ํ๋์ ํฐ๋ฏธ๋์ ์๋ก ์ด์ด cmd_and_pose๋ฅผ ๊ด์ฐฐํ๊ณ ์ topic echo ๋ช ๋ น์ผ๋ก ์ด์ด๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.

3. ์๋น์ค ์ ์ Service definition ๋ง๋ค์ด ๋ณด๊ธฐ
์ด๋ฒ์๋ ์ฌ์ฉ์์๊ฒ ์ ๋ ฅ๋ฐ์ ์๋งํผ turtle์ ์ํ์ผ๋ก ๋ฐฐ์นํ๋ ์๋น์ค๋ฅผ ๋ง๋ค์ด๋ณด๊ณ ์ ํ๋ค.
์ํํ ๋ชฉํ๋ ๋ค์๊ณผ ๊ฐ๋ค.
< ๋ชฉํ >
- ์ฌ์ฉ์๊ฐ ์๋น์ค ์ ์ ๋ง๋ค๊ธฐ
- ์๋น์ค ์ ์๋ฅผ ์ฌ์ฉํ๋ ์๋น์ค ์๋ฒ ๋ง๋ค๊ธฐ
- ์๋น์ค ์๋ฒ์์ ํด๋ผ์ด์ธํธ ๋ง๋ค์ด ๋ณด๊ธฐ
- ์ฌ์ฉ์๊ฐ ์์ฒญํ ์๋งํผ์ turtle์ ์์ฑํด์ ์ํ์ผ๋ก ๋ฐฐ์นํ๊ธฐ
(1) ์๋น์ค ์ ์ ๋ง๋ค๊ณ ๋น๋ํ๊ธฐ
์๋น์ค ์ ์๋ฅผ ๋ค๋ฃจ๊ธฐ ์ํด์ my_first_package_msgs ํด๋์ srv ํด๋๋ฅผ ๋ง๋ค๊ณ ๊ทธ ์์ MultiSpawn.srv ํ์ผ์ ๋ง๋ค์๋ค.

์ดํ ๋ด๋ถ์ ๋ค์๊ณผ ๊ฐ์ด ์ ๋ ฅํ์๋ค.
int64 num
---
float64[] x
float64[] y
float64[] theta
์ด ๋ป์ int64์ ํํ๋ก num ๋ฐ์ดํฐ๋ฅผ ๋ฐ๊ณ , float64[]์ ํํ๋ก x, y, theta ๊ฐ์ ์ถ๋ ฅํ๋ค๋ ๋ป์ด๋ค.
์ดํ, ์๋ ์ฌ์ง์ฒ๋ผ srv/MultiSpawn.srv๋ผ๋ ๋ถ๋ถ์ ์ถ๊ฐํด ์ฃผ๊ณ , package.xml์ด ๋ค์๊ณผ ๊ฐ์์ง ํ์ธํ๋ค.


์ดํ ๋น๋ํ ํ ros2 interface show ๋ช ๋ น์ผ๋ก ์๋น์ค ์ ์๋ฅผ ์กฐํํด ๋ณด์๋ค.

๋ค์๊ณผ ๊ฐ์ด ์ ์์ ์ผ๋ก ์ถ๋ ฅ๋์๋ค.
(2) ์๋น์ค ์๋ฒ ๋ง๋ค๊ธฐ
์ด๋ฒ์๋ my_first_package ํจํค์ง์ my_service_server.py ํ์ผ์ ๋ง๋ ๋ค.
๊ทธ๋ฆฌ๊ณ ๋ค์๊ณผ ๊ฐ์ด ์์ฑํ๋ค.
from my_first_package_msgs.srv import MultiSpawn
import rclpy as rp
from rclpy.node import Node
class MultiSpawning(Node):
def __init__(self):
super().__init__('multi_spawn')
self.server = self.create_service(MultiSpawn, 'multi_spawn', self.callback_service)
def callback_service(self, request, response):
print('request : ', request)
response.x = [1., 2., 3.]
response.y = [10., 20.]
response.theta = [100., 200., 300.]
return response
def main(args=None):
rp.init(args=args)
multi_spawn = MultiSpawning()
rp.spin(multi_spawn)
rp.shutdown()
if __name__ == '__main__':
main()
์ดํ serup.py์ ๋ค์๊ณผ ๊ฐ์ด ์ถ๊ฐํ๋ค.

์ดํ ๋น๋๋ฅผ ์งํํ ๋ค, my_service_server์ ์ด๊ณ ๋ค๋ฅธ ํฐ๋ฏธ๋์์ ์๋น์ค ๋ชฉ๋ก์ ์กฐํํ๋ฉด ๋ค์๊ณผ ๊ฐ์ด ์ ๋์ค๋ ๊ฒ์ ๋ณผ ์ ์๋ค.

์ดํ ๋ค์ ๋ช ๋ น์ ํตํด multi_spawn ์๋น์ค๋ฅผ ์์ฒญํ๊ณ ์ด๋ฅผ print ํ๋ ๋ชจ์ต์ ๋ณผ ์ ์๋ค.

(3) ์๋น์ค ์๋ฒ ์ฝ๋ ์์ ํด๋ผ์ด์ธํธ ์ฝ๋ ๋ง๋ค๊ธฐ
์ด์ ์ฝ๋์์ turtlesim์ ์์น๋ฅผ ์ด๋์ํค๊ธฐ ์ํด์ ๋ค์๊ณผ ๊ฐ์ ๋ถ๋ถ์ ์์ฑํ๋ค.
from turtlesim.srv import TeleportAbsolute
์ดํ MultiSpawning ํด๋์ค์ ๋๋ init ๋ถ๋ถ์ TeleportAbsolute ์ ์๋ฅผ ์ฌ์ฉํ๋ turtle1/teleport_absolute ์๋น์ค ํด๋ผ์ด์ธํธ๋ฅผ ๋ง๋๋ ๋ค์ ์ฝ๋๋ฅผ ์ถ๊ฐํ๋ค.
self.teleport = self.create_client(TeleportAbsolute, '/turtle1/teleport_absolute')
์ดํ ์๋น์ค ํด๋ผ์ด์ธํธ์์๋ ์๋น์ค ์ ์๋ฅผ ๊ฐ์ฒดํํ๋ ๊ฒ์ด ํ์ํ๊ธฐ ๋๋ฌธ์ ๋ค์๊ณผ ๊ฐ์ ์ฝ๋๋ฅผ ์ถ๊ฐํ๋ค.
self.req_teleport = TeleportAbsolute.Request()
์ดํ multi_spawn ์๋น์ค ์๋ฒ์ callback_service ํจ์์ teleport_absolute ์๋น์ค์ ํด๋ผ์ด์ธํธ๋ฅผ ํธ์ถํ๋ ๋ค์ ์ฝ๋๋ก ๋ฐ๊ฟ์ค๋ค.
def callback_service(self, request, response):
self.req_teleport.x = float(1)
self.teleport.call_async(self.req_teleport)
return response
์ ์ฒด ์ฝ๋๋ ๋ค์๊ณผ ๊ฐ๋ค.
from my_first_package_msgs.srv import MultiSpawn
from turtlesim.srv import TeleportAbsolute
import rclpy as rp
from rclpy.node import Node
class MultiSpawning(Node):
def __init__(self):
super().__init__('multi_spawn')
self.server = self.create_service(MultiSpawn, 'multi_spawn', self.callback_service)
self.teleport = self.create_client(TeleportAbsolute, '/turtle1/teleport_absolute')
self.req_teleport = TeleportAbsolute.Request()
def callback_service(self, request, response):
self.req_teleport.x = float(1)
self.teleport.call_async(self.req_teleport)
return response
def main(args=None):
rp.init(args=args)
multi_spawn = MultiSpawning()
rp.spin(multi_spawn)
rp.shutdown()
if __name__ == '__main__':
main()
์ดํ ๋น๋ํ๊ณ , turtlesim๊ณผ my_service_server์ ์คํํ ๋ค, ๋ค์ ์๋น์ค๋ฅผ ์์ฒญํ์ฌ ์๋น์ค ์๋ฒ ์ฝ๋ ์์ ํด๋ผ์ด์ธํธ ์ฝ๋๋ฅผ ๋ฃ์ ์ ์์์ ํ์ธํ๋ค.
ros2 service call /multi_spawn my_first_package_msgs/srv/MultiSpawn "{num: 1}"

(4) ์ฌ๋ฌ ๊ฑฐ๋ถ์ด๋ฅผ ์ ๋ชจ์์ผ๋ก ๋ฐฐ์นํ๊ธฐ
๋ชฉ์ ์ ๋ง๊ฒ ๊ฑฐ๋ถ์ด๋ฅผ ๋ฐฐ์นํ๊ณ ์ ํ๋ค.
์ด๋ ๊ฒ ํ ๋ ๋ฉ์ธ์ฝ๋์ ๋ฐ๋ก ์์ฑํ๋ฉด ๋งค๋ฒ ๋น๋ํด์ผ ํ๊ธฐ์ jupyter notebook์ ํตํด์ ์งํํ๊ณ ์ ํ๋ค.
๊ทธ๋ฆฌ๊ณ home์ python ํด๋์ Calc Position ํ์ผ์ ๋ง๋ค๊ณ ์งํํ๊ณ ์ ํ๋ค.
๋ค์๊ณผ ๊ฐ์ด ์์ฑํ์ฌ ๊ฐ์๊ฐ ์ฃผ์ด์ก์ ๋ ๊ฐ ์ ์ฌ์ด์ ๊ฐ๋๋ฅผ ๊ตฌํ ์ ์๋ค.
import numpy as np
n = 3
to_degree = 180/np.pi
gap_theta = 2*np.pi / n
gap_theta * to_degree

๊ทธ ๋ค์ list ํ์ผ๋ก ์์ ๊ฐ๋๋ฅผ ์ ์ฅํ๊ธฐ ์ํด์ ๋ค์๊ณผ ๊ฐ์ด ์์ฑํ๋ค.
theta = [gap_theta*n for n in range(n)]
[each * to_degree for each in theta]

์ด๋ gap_theta๋ฅผ ์ด์ฉํ์ฌ ์์ ๊ฐ์ n ๋งํผ์ ๊ฐ๋๋ฅผ ๋ฏธ๋ฆฌ ์์ฑํด์ list์ ์ ์ฅํ๊ณ , 0๋ถํฐ n-1๋งํผ ์ซ์๋ฅผ ์์ฑํ๊ณ ์ด๋ฅผ gap_theta์ ๊ณฑํด์ ๋ฆฌ์คํธํ์ ์ ์ฅํ์๋ค.
๊ทธ๋ฆฌ๊ณ ์ผ๊ฐํจ์๋ฅผ ์ด์ฉํด์ x, y ์ขํ๋ฅผ ๊ตฌํ์๋ค.
r = 3
x = [r*np.cos(th) for th in theta]
y = [r*np.sin(th) for th in theta]


๊ทธ๋ฆฌ๊ณ ์ด๋ฅผ scatter ํจ์๋ฅผ ์ด์ฉํด ๊ทธ๋ ค๋ณด์๋ค.
import matplotlib.pyplot as plt
plt.scatter(x, y)
plt.axis('equal')
plt.show()

์ดํ ์์ ๋ฐฐ์นํ๋ ์ฝ๋๋ฅผ ํจ์๋ก ์์ฑํ์๋ค.
def calc_position(n, r):
gap_theta = 2*np.pi / n
theta = [gap_theta*n for n in range(n)]
x = [r*np.cos(th) for th in theta]
y = [r*np.sin(th) for th in theta]
return x, y, theta
์ง๊ธ๊น์ง์ ๊ฒฐ๊ณผ๋ฅผ ๊ทธ๋ํ๋ก ํ์ธํ๊ธฐ ์ํด์ ๋ค์ ์ฝ๋๋ฅผ ์์ฑํ์๋ค.
def draw_pos(x, y):
plt.scatter(x, y)
plt.axis('equal')
plt.show()
์ดํ ํ ์คํธ๋ฅผ ํ ๊ฒฐ๊ณผ๋ก, ์ ๊ตฌํ๋์์์ ์ ์ ์๋ค.
X, Y, theta = calc_position(4, 3)
draw_pos(X, Y)

X, Y, theta = calc_position(15,3)
draw_pos(X, Y)

(5) ์ฌ๋ฌ ๊ฑฐ๋ถ์ด๋ฅผ ๋ฐฐ์นํ๋ ์๋น์ค ์๋ฒ ๊ตฌํ
์ด์ ์์ ์๋ ์ฝ๋๋ค์ ์๋ ์ฌ์ฉํ๋ ์๋น์ค ์๋ฒ์ ๋ฃ๊ธฐ ์ํด์
๋ค์๊ณผ ๊ฐ์ด Spawn์ import ํ๊ณ , numpy๊น์ง import ํ๋ค.
from turtlesim.srv import Spawn
import numpy as np
๊ทธ๋ฆฌ๊ณ spawn ์๋น์ค๋ฅผ ์ด์ฉํ๊ธฐ ์ํด์ ๋ค์๊ณผ ๊ฐ์ ๋ด์ฉ์ ๋๋ init์ ์ถ๊ฐํ๋ค.
self.spawn = self.create_client(Spawn, '/spawn')
๋ํ ํ๋ฉด์ ์ผํฐ๊ฐ์ด 5.54 ์์ ๊ณ ๋ คํ์ฌ center ๊ฐ ๋ณด์ ์ ์งํํ๋ค.
self.center_x = 5.54
self.center_y = 5.54
๊ทธ๋ฌ๊ณ ๋์ spawn ์๋น์ค๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด์ Spawn ์๋น์ค ์ ์๋ฅผ ์ด๊ธฐํํ๊ธฐ ์ํด ๋ค์ ๋ด์ฉ์ ์ถ๊ฐํ ๋ค, calc_position ํจ์๋ฅผ ์ฝ์ ํ๋, self๋ฅผ ์ธ์๋ก ๋ฐ๋๋ก ํ๋ค.
self.req_spawn = Spawn.Request()
๋ชจ๋ ์ ์ฉํ ์ฝ๋๋ ๋ค์๊ณผ ๊ฐ๋ค.
from my_first_package_msgs.srv import MultiSpawn
from turtlesim.srv import TeleportAbsolute
from turtlesim.srv import Spawn
import rclpy as rp
import numpy as np
from rclpy.node import Node
class MultiSpawning(Node):
def __init__(self):
super().__init__('multi_spawn')
self.server = self.create_service(MultiSpawn, 'multi_spawn', self.callback_service)
self.teleport = self.create_client(TeleportAbsolute, '/turtle1/teleport_absolute')
self.spawn = self.create_client(Spawn, '/spawn')
self.req_teleport = TeleportAbsolute.Request()
self.req_spawn = Spawn.Request()
self.center_x = 5.54
self.center_y = 5.54
def calc_position(self, n, r):
gap_theta = 2*np.pi / n
theta = [gap_theta*n for n in range(n)]
x = [r*np.cos(th) for th in theta]
y = [r*np.sin(th) for th in theta]
return x, y, theta
def callback_service(self, request, response):
x, y, theta = self.calc_position(request.num, 3)
for n in range(len(theta)):
self.req_spawn.x = x[n] + self.center_x
self.req_spawn.y = y[n] + self.center_y
self.req_spawn.theta = theta[n]
self.spawn.call_async(self.req_spawn)
response.x = x
response.y = y
response.theta = theta
return response
def main(args=None):
rp.init(args=args)
multi_spawn = MultiSpawning()
rp.spin(multi_spawn)
rp.shutdown()
if __name__ == '__main__':
main()
(6) ์ฌ๋ฌ ๊ฑฐ๋ถ์ด๋ฅผ ๋ฑ์ฅ์ํค๊ธฐ
์ดํ ๋น๋ํ๊ณ , turtlesim, my_service_server์ ์ด๊ณ ์๋น์ค์ {num: 9}๋ก ์๋น์ค๋ฅผ ์์ฒญํ๋ค.

๋ค์๊ณผ ๊ฐ์ด ์ฑ๊ณต์ ์ผ๋ก ๋๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
4. ๋ง๋ฌด๋ฆฌ
๋ค์์๋ Ch9. ์ก์ ์ต์ํด์ง๊ธฐ ์ ๋ํด์ ์จ๋ณด๊ณ ์ ํ๋ค.
๋ํ ์ด ๊ณผ์ ์์ ์ฌ์ฉํ ํ์ผ์ ๋ค์๊ณผ ๊ฐ์ด ์ฌ๋ ค๋ณธ๋ค.