
์ด ๊ธ์์๋ Ch11. ๋๋ฒ๊ทธ์ ๊ด์ฐฐ์ ์ํ ์ฌ๋ฌ ๋๊ตฌ๋ค ์ ๋ํด์ ์จ๋ณด๊ณ ์ ํ๋ค.
1. ๋ชฉ์
์ค์ ๋ก ์์คํ ์ ๊ณต๋ถํ๊ฑฐ๋ ๊ฐ๋ฐํ ๋ ์กฐ๊ธ๋ง ๋ณต์กํด๋ ์๋ํ ๋์์ด ๊ตฌํ๋์ง ์๋ ๊ฒฝ์ฐ๊ฐ ์๋ค.
๊ทธ๋ฌํ ๋์ ์ ์ฉํ ๋๊ตฌ๊ฐ ๋ก๊ทธ์ด๋ค. ์๊ฐ ์ ๋ณด์ ์ ๊ธฐ๋ก๋ ๋ก๊ทธ ์ ๋ณด๋ฅผ ๋ณด๋ฉด ํจ์จ์ ์ผ๋ก ๋ก๋ด์ ์ ๊ฒํ ์ ์๋ค.
์ด ๊ธ์์๋ ์ด๋ด ๋ ์ฌ์ฉํ ์ ์๋ ๋๊ตฌ์ธ ๋ก๊ทธ์ ๋ํด์ ์ด๋ป๊ฒ ์ฌ์ฉ์ ํ๋์ง ์์๋ณด๊ณ , rosbag๊ณผ GUI ํ๊ฒฝ์์ ์ง๊ธ๊น์ง ๋ค๋ฃฌ ROS ๋ช ๋ น์ ์ฌ์ฉํ ์ ์๊ฒ ํด์ฃผ๋ rqt ๋ฑ๋ ๋ค๋ค๋ณผ ๊ฒ์ด๋ค.
2. ๋ก๊ทธ
(1) ๊ฐ๋จํ rqt_console์ ์ด์ฉํด ๋ก๊ทธ ํ์ธํ๊ธฐ
ํน์๋ผ๋ ์ค๋ฅ๊ฐ ๋์ง ์๊ธฐ ์ํด์ rqt ํจํค์ง๋ฅผ ์ค์นํ๋ค.
๋ค์ ๋ช ๋ น์ด๋ฅผ ํตํด์ ๊ฐ๋จํ๊ฒ ์ค์น ๊ฐ๋ฅํ๋ค.
sudo apt install ros-humble-rqt*
์ดํ turtlesim๊ณผ rqt๋ฅผ ์ด์ด๋ณด์.
rqt๋ฅผ ์ด๋ฉด ๋ค์๊ณผ ๊ฐ์ ์ฐฝ์ด ๋์ค๊ฒ ๋๋ค.

์ฌ๊ธฐ์ Plugins์ Logging์ Console์ ์ ํํ๋ค.

์ด ํ๋ฉด์์ ๋ค์ ๋ช ๋ น์ผ๋ก ์ง์ ์ผ๋ก ์ฃผํํ๋๋ก ์ํจ๋ค.
ros2 topic pub -r 1 /turtle1/cmd_vel geometry_msgs/msg/Twist "{linear: {x: 2, y: 0, z: 0.}, angular: {x: 0, y: 0, z: 0.}}"

๊ทธ๋ฆฌ๊ณ ๋ฒฝ์ ๋ถ๋ชํ๋ฉด ๋ค์๊ณผ ๊ฐ์ด [WARN] ๋ฉ์์ง๊ฐ ๋์ค๋ฉด์ ๋ฉ์์ง๊ฐ ๋ณด์ด๊ณ , ๊ทธ ๋ฉ์์ง๊ฐ rqt์๋ ๋์ค๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
(2) ๋ก๊ทธ ๋ฉ์์ง ์ง์ ๋ง๋ค๊ธฐ
dist_turtle_action_server.py ํ์ผ์ DistTurtleServer ํด๋์ค์ ๋๋ init ํจ์์ ํด๋์ค๊ฐ ์์๋๋ค๋ ๊ฒ์ ๋ก๊ทธ ๋ ๋ฒจ info๋ก ๋จ๊ธฐ๊ณ ์ ํ๋ค.
๋ค์ ์ฝ๋๋ฅผ ์ถ๊ฐํด๋ณด์.
self.get_logger().info('Dist turtle action server is started.')

์ดํ ์คํํด ๋ณด๋ฉด ๋ฉ์์ง๊ฐ ์ถ๋ ฅ๋๋ ๋ชจ์ต์ ๋ณผ ์ ์๋ค.

๊ทธ๋ฆฌ๊ณ ๋ค์ quantile_time๊ณผ almost_time ๋ณ์๋ฅผ ๋ก๊ทธ๋ก ์ด๊ธฐ์น๋ฅผ ์ถ๋ ฅํ๋ ์ฝ๋๋ฅผ ์์ฑํด๋ณด๊ณ ์ ํ๋ค.



๋ค์๊ณผ ๊ฐ์ด ์ฝ๋๋ฅผ ๋ณ๊ฒฝํด ๋ณด์.
์ ์ฒด ์ฝ๋๋ ๋ค์๊ณผ ๊ฐ๋ค.
import rclpy as rp
from rclpy.action import ActionServer
from rclpy.executors import MultiThreadedExecutor
from rclpy.node import Node
from turtlesim.msg import Pose
from geometry_msgs.msg import Twist
from my_first_package_msgs.action import DistTurtle
from my_first_package.my_subscriber import TurtlesimSubscriber
from rcl_interfaces.msg import SetParametersResult
import math
import time
class TurtleSub_Action(TurtlesimSubscriber):
def __init__(self, ac_server):
super().__init__()
self.ac_server = ac_server
def callback(self, msg):
self.ac_server.current_pose = msg
class DistTurtleServer(Node):
def __init__(self):
super().__init__('dist_turtle_action_server')
self.total_dist = 0
self.is_first_time = True
self.current_pose = Pose()
self.previous_pose = Pose()
self.publisher = self.create_publisher(Twist, '/turtle1/cmd_vel', 10)
self.action_server = ActionServer(self, DistTurtle, 'dist_turtle', self.excute_callback)
self.get_logger().info('Dist turtle action server is started.')
self.declare_parameter('quatile_time', 0.75)
self.declare_parameter('almost_goal_time', 0.95)
(quantile_time, almosts_time) = self.get_parameters(
['quatile_time', 'almost_goal_time'])
self.quantile_time = quantile_time.value
self.almosts_time = almosts_time.value
output_msg = "quantile_time is " + str(self.quantile_time) + ". "
output_msg = output_msg + "and almost_goal_time is " + str(self.almosts_time) + ". "
self.get_logger().info(output_msg)
self.add_on_set_parameters_callback(self.parameter_callback)
def parameter_callback(self, params):
for param in params:
print(param.name, " is changed to ", param.value)
if param.name == 'quatile_time':
self.quantile_time = param.value
if param.name == 'almost_goal_time':
self.almosts_time = param.value
output_msg = "quantile_time is " + str(self.quantile_time) + ". "
output_msg = output_msg + "and almost_goal_time is " + str(self.almosts_time) + ". "
self.get_logger().info(output_msg)
return SetParametersResult(successful=True)
def calc_diff_pose(self):
if self.is_first_time:
self.previous_pose.x = self.current_pose.x
self.previous_pose.y = self.current_pose.y
self.is_first_time = False
diff_dist = math.sqrt((self.current_pose.x - self.previous_pose.x)**2 +\
(self.current_pose.y - self.previous_pose.y)**2)
self.previous_pose = self.current_pose
return diff_dist
def excute_callback(self, goal_handle):
feedback_msg = DistTurtle.Feedback()
msg = Twist()
msg.linear.x = goal_handle.request.linear_x
msg.angular.z = goal_handle.request.angular_z
while True:
self.total_dist += self.calc_diff_pose()
feedback_msg.remained_dist = goal_handle.request.dist - self.total_dist
goal_handle.publish_feedback(feedback_msg)
self.publisher.publish(msg)
tmp = feedback_msg.remained_dist - goal_handle.request.dist * self.quantile_time
tmp = abs(tmp)
if tmp < 0.02:
output_msg = 'The turtle passes the ' + str(self.quantile_time) + ' point. '
output_msg = output_msg + ' : ' + str(tmp)
self.get_logger().info(output_msg)
time.sleep(0.01)
if feedback_msg.remained_dist < 0.2:
break
goal_handle.succeed()
result = DistTurtle.Result()
result.pos_x = self.current_pose.x
result.pos_y = self.current_pose.y
result.pos_theta = self.current_pose.theta
result.result_dist = self.total_dist
self.total_dist = 0
self.is_first_time = True
return result
def main(args=None):
rp.init(args=args)
executor = MultiThreadedExecutor()
ac = DistTurtleServer()
sub = TurtleSub_Action(ac_server = ac)
executor.add_node(sub)
executor.add_node(ac)
try:
executor.spin()
finally:
executor.shutdown()
sub.destroy_node()
ac.destroy_node()
rp.shutdown()
if __name__ == '__main__':
main()
์ดํ dist_turtle_action_server, turtlesim, ros2 action(2,2,2), rqt๋ฅผ ์คํํด ๋ณด์.

๋ค์๊ณผ ๊ฐ์ด rqt๋ง ์คํ ํ ๋ค turtlesim, dist_turtle_action_server์์ผ๋ก ์คํํ๊ณ , ros2 action์ ์คํํ๋ค.
๊ทธ๋ฌ๋ฉด ๋ค์๊ณผ ๊ฐ์ด ๋ก๊ทธ ์ ๋ณด๊ฐ ์ ๋๋ก ์ถ๋ ฅ๋๋ ๊ฒ์ ๋ณผ ์ ์๋ค.


์ด๋ ๊ฒ ์งํํ๋ฉด ๋ค์๊ณผ ๊ฐ์ ๊ฒฐ๊ณผ๋ฅผ ๋ณผ ์ ์๋ค.
3. rqt
(1) rqt_graph
rqt_graph๋ฅผ ํ์ฉํ๊ธฐ ์ํด์ turtlesim, rqt_grph, ros2 topic ๋ง ์คํํด ๋ณด์.


๊ทธ๋ฆฌ๊ณ rqt_graph๋ฅผ ์ด์ฉํ๋ฉด ๋ค์๊ณผ ๊ฐ์ ๋ชจ์ต์ ๋ณผ ์ ์๋ค.
(2) rqt_plot
์ดํ rqt_graph๋ฅผ ์ค๋จํ๊ณ rqt๋ฅผ ์คํํ ๋ค Plugins์ Visualization์ Plot์ ์ ํํด ๋ณด์.

์ดํ topic ๋ถ๋ถ์ ์ํ๋ ํ ํฝ์ ์ ๋ ฅํ๊ณ +๋ฒํผ์ ๋๋ฅด๋ฉด ์ถ๊ฐ๋๊ณ , -๋ฒํผ์ ๋๋ฅด๋ฉด ์ ํํด์ ์ ๊ฑฐ ๊ฐ๋ฅํ๋ค.
(3) topic monitor
๊ทธ๋ฆฌ๊ณ Plugins์ Topics์ Topic Monitor์ ๋ณด๋ฉด ํ ํฝ๋ฆฌ์คํธ์ ์ํ๋ ํ ํฝ์ ์ ํํ์ฌ ๊ด์ฐฐํ ์ ์๋ค.

(4) topic publisher
topic publisher์ ์ ํํด์ ๋ณด๋ฉด ๋ฐํํ๊ณ ์ ํ๋ ํ ํฝ์ ์ ํํ๊ณ ๊ทธ ๊ฐ์ ๋ณ๊ฒฝํ ์ ์๋ค.


์ํ๋ ๊ฐ์ผ๋ก ๋ณ๊ฒฝํ๊ณ ์ฒดํฌ ๋ฐ์ค๋ฅผ ํด๋ฆญํ๋ฉด ๊ทธ ํ ํฝ์ด ๋ฐํ๋๋ค.

๊ทธ๋ฌ๋ฉด ๋ค์๊ณผ ๊ฐ์ด ํ ํฝ์ด ์ค์ฒฉ๋๋ ๋ชจ์ต์ ๋ณผ ์ ์๋ค.
4. rosbag

์ด๋ ๊ฒ ์คํํ ๋ค ์ ํฐ๋ฏธ๋์ ๋ค์๊ณผ ๊ฐ์ด ์ ๋ ฅํ์ฌ ํ ํฝ์ ๊ธฐ๋กํ๋ค.
ros2 bag record -o turtle_test -a
์ดํ ์ ์ ํ ํ์ด๋ฐ์ ctrl+c๋ก ์ข ๋ฃํ๋ค.
๋ค์ turtlesim์ ์คํํ๊ณ ๋ค์ ๋ช ๋ น์ ํตํด์ ์ ์ฅํ ํ ํฝ์ ๋ค์ ๋ฐํํ ์ ์๋ค.
ros2 bag play turtle_test

๋ํ rqt์ Logging์ Bag์์ ์ ์ฅํ ํด๋๋ฅผ ํด๋ฆญํ๋ฉด ํ ํฝ์ด ๋ณด์ด๊ณ , publish๋ฅผ ํด๋ฆญํ๋ฉด ํ ํฝ์ด ๋ฐํ๋๋ค.
์ด๋ฅผ ํตํด์ ํ ํฝ์ ๋ฐํ ํ์ด๋ฐ์ ๊ด์ฐฐ ํ ์ ์๋ค.

5. ROSLAUNCH
ROS์์๋ ์ฝ๊ฒ ์ฌ๋ฌ ๋ ธ๋๋ฅผ ์คํํ๊ฑฐ๋ ์์ฝ๊ฒ ๋ ธ๋์ ํ๋ผ๋ฏธํฐ์ ์ค์ ์ ์ง์ ํ ์ ์๋ roslaunch๊ฐ ์๋ค.
(1) roslaunch ๊ธฐ๋ณธ
my_first_package ํด๋์ launch ํด๋๋ฅผ ๋ง๋ค๊ณ ๋ค์๊ณผ ๊ฐ์ ํ์ผ์ ์์ฑํ๋ค.

from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
return LaunchDescription(
[
Node(
namespace= "turtlesim", package='turtlesim',
executable= "turtlesim_node", output='screen'),
Node(
namespace= "pub_cmd_vel", package='my_first_package',
executable='my_publisher', output='screen'),
]
)
์ดํ ์ด๋ฅผ setup.py์ ๋ฑ๋กํ๋ค.

๊ทธ๋ฆฌ๊ณ ๋ค์์คํ์ด์ค๋ฅผ ๋ฐ๋ก ์ง์ ํ์ผ๋ฏ๋ก my_publisher.py ํ์ผ์ ์ด์ด์ ํ ํฝ ์ด๋ฆ์ /turtlesim/turtle1/cmd_vel ๋ก ๋ณ๊ฒฝํ๋ค.

์ดํ ๋น๋ํ๊ณ ros2 launch ๋ฅผ ํตํด์ py ํ์ผ์ ์คํํ๋ค.
์ด๋ฅผ ๋ณด๋ฉด ํ ๋ฒ์ ์คํ๋ ๊ฒ์ ๋ณผ ์ ์์ผ๋ฉฐ, rqt_graph๋ก ํ์ธํ๋ฉด ๋ค์๊ณผ ๊ฐ์ ๋ชจ์ต์ ๋ณผ ์ ์๋ค.


์ฌ๊ธฐ์ ์ง์ ํ ๋ค์์คํ์ด์ค๊ฐ ์ ์์ ์ผ๋ก ๋์จ๋ค๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
(2) parameter ์ง์ ํ๊ธฐ
ros launch์์ ํ๋ผ๋ฏธํฐ๋ฅผ ์ง์ ํ๊ธฐ ์ํด์ LaunchDescription์ผ๋ก ์ ์ธ๋ง ํ ๋ค์, add_action์ ํตํด์ ์ถ๊ฐํ๋ค.
๋ค์๊ณผ ๊ฐ์ด ์์ฑํ๊ณ ๋น๋ํ๋ฉด ์ ์์ ์ผ๋ก ๋ณ๊ฒฝ๋๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
ํ์ผ ์ด๋ฆ์ dist_turtle_action.launch.py๋ก ํ์๋ค.
from launch import LaunchDescription
from launch_ros.actions import Node, ExcuteProcess
def generate_launch_description():
my_launch = LaunchDescription()
turtlesim_node = Node(
package='turtlesim',
executable='turtlesim_node',
output='screen',
parameters=[
{"background_r": 255},
{"background_g": 192},
{"background_b": 203},
]
)
dist_turtle_action_node = Node(
package='my_first_package',
executable='dist_turtle_action_server',
output='screen',
)
my_launch.add_action(turtlesim_node)
my_launch.add_action(dist_turtle_action_node)
return my_launch
6. ๋ง๋ฌด๋ฆฌ
์ด๊ฒ์ผ๋ก ๋ฌด์์ ๋ฐ๋ผ ํด๋ณด๋ ์๊ฐ์ ๊ฐ์ ธ๋ณด์๋ค.
์ด๊ฒ์ ์์ํ๊ธฐ ์ ์ ์ ์์ด์ ๋ฏผํ๊ธฐ ๋ฐ์ฌ๋๊ป ์์ฑํด๋ ๋๋๊ณ ์ฌ์ญค๋ณด๋ ๋ฑ ์ฌ๋ฌ ๊ฐ์ง ์ผ๋ค๋ ์์๊ณ , ๋ค ์ดํดํ ์ ์์์ง ๊ฑฑ์ ๋ ๋์์ง๋ง, ๊ฒฐ๊ตญ ROS2์ ์ฒซ ๋ฐ์ ๋๋ ๋ด๋ฏผ ๊ฑฐ ๊ฐ์์ ๊ธฐ๋ถ์ด ์ข๋ค.
์ด์ ๋ ๋ค์ ๊ถ์ ๊ธฐ๋ค๋ฆฌ๋ฉด์ ๋ค๋ฅธ ์ฑ ๋ ๊ณต๋ถํ๊ณ ๋ก๋ดํ ๋ํ ์งํํด ๋ณผ ์์ ์ด๋ค.
์ง๊ธ๊น์ง์ github์ ์๋ ๋งํฌ๋ก ์ฒจ๋ถํ์๋ค.
https://github.com/MOSW626/ros2_study.git
GitHub - MOSW626/ros2_study
Contribute to MOSW626/ros2_study development by creating an account on GitHub.
github.com
https://github.com/PinkWink/ros2_basic.git
GitHub - PinkWink/ros2_basic
Contribute to PinkWink/ros2_basic development by creating an account on GitHub.
github.com
์ด๋ฌํ ์ฑ ์ ๋ณผ ์ ์๊ฒ ์ฑ ์ ์งํํด ์ฃผ์ , ๋ฏผํ๊ธฐ ๋ฐ์ฌ๋ ๊ฐ์ฌํฉ๋๋ค!