로보테크AI

융합_로보테크 AI 자율주행 로봇 개발자 과정-26/03/16[트위니 특강]

steezer 2026. 3. 16. 18:30

여러 거북이를 원 모양으로 배치하기 위한 고민

Jupyter Notebook | Python/Calc Position

 

개수가 정해졌을 때 각 원 사이의 각도를 구하기

import numpy as np

n = 3 # 3개로 원 두르기 가정
to_degree = 180/np.pi # 라디안을 쓰므로 변환해야

gap_theta = 2*np.pi / n
gap_theta * to_degree
119.99999999999999

 

list형으로 원의 각도를 저장

theta = [gap_theta*n for n in range(n)]
[each * to_degree for each in theta]
[0.0, 119.99999999999999, 239.99999999999997]

 

theta에 따라 x, y를 생성

r = 3

x = [r*np.cos(th) for th in theta]
y = [r*np.sin(th) for th in theta]
x
[np.float64(3.0),
 np.float64(-1.4999999999999993),
 np.float64(-1.5000000000000013)]
y
[np.float64(0.0),
 np.float64(2.598076211353316),
 np.float64(-2.598076211353315)]

 

결과를 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()

 

원 4개를 배치해보는 것을 테스트

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

 

원 15개 배치해보는 것을 테스트

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

 

8장 마무리 실습

1. 주피터 노트북을 통해 거북이의 배치를 어떻게 계산하는지 실험 완료하기

2. 244~245p 소개된 final 버전 코드를 워크스페이스의 src 폴더 안에 완성하기

3. 워크스페이스(ros2_study)에서 colcon build하기

4. source install/local_setup.bash 명령어 실행하기

5. 실행 절차에 맞춰 노드 실행해보기

 

여러 거북이 배치하는 서비스 서버 구현

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()

자율주행로봇 전문기업 트위니 기업 특강[트위니 천홍석 대표이사]

 

자율주행 물류로봇 전문기업 | 주식회사 트위니

주요 제품: 자율주행물류로봇(나르고 오더피킹, 나르고 팩토리)

 

AI

인간의 지능을 모방

인공 신경망

파라미터 조정을 통한 학습

학습: 모델의 파라미터(가중치)를 데이터로부터 최적화하는 과정

추론: 학습이 완료된 모델로 새로운 입력에 대한 출력을 계산하는 과정

 

GPU: 병렬 연산에 특화된 범용 프로세서

행렬 연산, 벡터 연산을 대규모 병렬로 처리

원래 그래픽 렌더링 목적 → AI/딥러닝 연산에 전용

 

NPU : 신경망 연산만을 위해 설계된 전용 프로세서

행렬 곱셈, 활성화 함수 등 딥러닝 특화 연산만 처리

GPU보다 전력 효율 높음, 속도 빠름

 

HVM: CPU 하드웨어 수준에서 가상화를 지원하는 기술
소프트웨어만으로 하드웨어를 에뮬레이션하면 느림
CPU 자체에 가상화 명령어 내장 → 오버헤드 최소화

 

학습

지도: 정답 레이블이 있는 데이터로 학습

비지도 : 정답 레이블 없이 데이터의 구조/패턴을 스스로 파악

강화: 에이전트가 환경과 상호작용하며 보상을 최대화하는 정책을 학습

 

뉴럴 네트워크 -> 수학적 접근은 어디?

딥러닝의 과정은? 알수 없음

 

이제와서 뉴럴 네트워크 붐이 된 이유

하드웨어(그래픽카드)의 성능이 올라가면서 학습 시간이 줄어듦

 

에이전트 AI
목표가 주어지면 스스로 계획을 세우고, 도구를 사용하며, 행동을 순차적으로 실행하는 AI 시스템단순히 질문에 답하는 것을 넘어서 "무엇을 어떻게 할지"를 스스로 결정하고 실행

 

Physical AI =/= 휴머노이드

휴머노이드에는 AI가 없음(과거 기준)

이제는 수학적인 연산보다 딥러닝으로

이전엔 관절 하나하나를 수학적 연산으로 통제했으나 현재는 딥러닝으로

AI 기반 사고를 하는 옵티머스(테슬라)는 Physical AI라 할 수 있음

테슬라(자동차)에는 카메라가 전부 달려 있음 -> 운전 데이터를 가져와 학습

 

AI vs Physical AI
모터(관절)의 차이

사람을 모사하여 생각하고 관절을 통한 Output

사람이 기존에 머리 써서 하던 일을 컴퓨터가 대신 하는 것

ㄴ> 사람이 기존에 머리+관절을 써서 하던 일을 기계가 대신 하는 것

 

협동 로봇

사람과 같은 공간에서 직접 상호작용하며 작업하도록 설계된 로봇기존 산업용 로봇과 달리
사람을 대체하는 것이 아닌 사람과 협력하는 것이 목적

공장의 로봇 팔 같은 것

 

먼저 생각할 것(생산성, 비용절감, 효율성)

휴머노이드, 견마로봇(스팟) -> 더 저렴하고 효율적이게 대체 가능
시장에서 팔릴 수 없음

공학적으로 어려운 것이 중요한 것이 아님

 

오더피킹

물류센터에서 작업자가 보관용 랙에 담긴 물품을 가져올 때 미리 주문 건별로 물품을 구분하여 포장 장소로 가져오는 방식

 

현재

물류 70% 공장 30%

계획

테슬라 휴머노이드에 올릴 소프트웨어


ROS2 이어서

 

빌드

steezer@DESKTOP-TF8J569:~/ros2_study$ rm -rf build install log
steezer@DESKTOP-TF8J569:~/ros2_study$ colcon build
[0.807s] WARNING:colcon.colcon_ros.prefix_path.ament:The path '/home/steezer/ros2_study/install/my_first_package_msgs' in the environment variable AMENT_PREFIX_PATH doesn't exist
[0.807s] WARNING:colcon.colcon_ros.prefix_path.ament:The path '/home/steezer/ros2_study/install/my_first_package' in the environment variable AMENT_PREFIX_PATH doesn't exist
[0.808s] WARNING:colcon.colcon_ros.prefix_path.catkin:The path '/home/steezer/ros2_study/install/my_first_package_msgs' in the environment variable CMAKE_PREFIX_PATH doesn't exist
Starting >>> my_first_package
Starting >>> my_first_package_msgs
Finished <<< my_first_package [0.90s]
Finished <<< my_first_package_msgs [5.47s]

Summary: 2 packages finished [6.12s]
steezer@DESKTOP-TF8J569:~/ros2_study$ ros2 run turtlesim turtlesim_node
[INFO] [1773630353.650666672] [turtlesim]: Starting turtlesim with node name /turtlesim
[INFO] [1773630353.661893072] [turtlesim]: Spawning turtle [turtle1] at x=[5.544445], y=[5.544445], theta=[0.000000]

 

steezer@DESKTOP-TF8J569:~/ros2_study$ ros2 run my_first_package my_service_server
steezer@DESKTOP-TF8J569:~/ros2_study$ ros2 service call /multi_spawn my_first_package_msgs/srv/MultiSpawn "{num: 9}"
waiting for service to become available...
requester: making request: my_first_package_msgs.srv.MultiSpawn_Request(num=9)

response:
my_first_package_msgs.srv.MultiSpawn_Response(x=[3.0, 2.298133329356934, 0.5209445330007912, -1.4999999999999993, -2.819077862357725, -2.8190778623577253, -1.5000000000000013, 0.5209445330007899, 2.2981333293569333], y=[0.0, 1.9283628290596178, 2.954423259036624, 2.598076211353316, 1.0260604299770066, -1.026060429977006, -2.598076211353315, -2.9544232590366244, -1.9283628290596186], theta=[0.0, 0.6981317007977318, 1.3962634015954636, 2.0943951023931953, 2.792526803190927, 3.490658503988659, 4.1887902047863905, 4.886921905584122, 5.585053606381854])

 

액션 익숙해지기

 

steezer@DESKTOP-TF8J569:~/ros2_study/src$ cd my_first_package_msgs/
steezer@DESKTOP-TF8J569:~/ros2_study/src/my_first_package_msgs$ mkdir action
steezer@DESKTOP-TF8J569:~/ros2_study/src/my_first_package_msgs$ ls
action  build  CMakeLists.txt  include  install  log  msg  package.xml  src  srv
steezer@DESKTOP-TF8J569:~/ros2_study/src/my_first_package_msgs$

# Request
float32 linear_x
float32 angular_z
float32 dist
---
# Result
float32 pos_x
float32 pos_y
float32 pos_theta
float32 result_dist
---
# Feedback
float32 remained_dist

"action/DistTuretle.action"

<depend>action_msgs</depend>

 

my_first_package / my_first_package / dist_turtle_action_server.py

import rclpy as rp
from rclpy.action import ActionServer
from rclpy.node import Node

from my_first_package_msgs.action import DistTurtle


class DistTurtleServer(Node):

	def __init__(self):
		super().__init__('dist_turtle_action_server')
		self._action_server = ActionServer(
			self,
			DistTurtle,
			'dist_turtle',
			self.execute_callback)

	def execute_callback(self, goal_handle):
		goal_handle.succeed()
		result = DistTurtle.Result()
		return result


def main(args=None):
	rp.init(args=args)
	dist_turtle_action_server = DistTurtleServer()
	rp.spin(dist_turtle_action_server)


if __name__ == '__main__':
	main()

 

setup.py

    entry_points={
        'console_scripts': [
            'my_first_node = my_first_package.my_first_node:main',
            'my_subscriber = my_first_package.my_subscriber:main',
            'my_publisher = my_first_package.my_publisher:main',
            'turtle_cmd_and_pose = my_first_package.turtle_cmd_and_pose:main',
            'my_service_server = my_first_package.my_service_server:main',
            'dist_turtle_action_server = my_first_package.dist_turtle_action_server:main'
        ],
    },
)

 

steezer@DESKTOP-TF8J569:~/ros2_study$ ros2 run my_first_package dist_turtle_action_server
steezer@DESKTOP-TF8J569:~/ros2_study$ ros2 action send_goal /dist_turtle my_first_package_msgs/action/DistTurtle "{'linear_x': 0, 'angular_z': 0, 'dist': 0}"
Waiting for an action server to become available...
Sending goal:
     linear_x: 0.0
angular_z: 0.0
dist: 0.0

Goal accepted with ID: b0ee19a3499d4620809894e42a568b38

Result:
    pos_x: 0.0
pos_y: 0.0
pos_theta: 0.0
result_dist: 0.0

Goal finished with status: SUCCEEDED

 

dist_turtle_action_server.py 수정

import rclpy as rp
from rclpy.action import ActionServer
from rclpy.node import Node
import time

from my_first_package_msgs.action import DistTurtle


class DistTurtleServer(Node):

	def __init__(self):
		super().__init__('dist_turtle_action_server')
		self._action_server = ActionServer(
			self,
			DistTurtle,
			'dist_turtle',
			self.execute_callback)

	def execute_callback(self, goal_handle):
		feedback_msg = DistTurtle.Feedback()
		for n in range(0,10):
			feedback_msg.remained_dist = float(n)
			goal_handle.publish_feedback(feedback_msg)
			time.sleep(0.5)

		goal_handle.succeed()
		result = DistTurtle.Result()
		return result


def main(args=None):
	rp.init(args=args)
	dist_turtle_action_server = DistTurtleServer()
	rp.spin(dist_turtle_action_server)


if __name__ == '__main__':
	main()
steezer@DESKTOP-TF8J569:~/ros2_study$ ros2 run my_first_package dist_turtle_action_server
steezer@DESKTOP-TF8J569:~/ros2_study$ ros2 action send_goal --feedback /dist_turtle my_first_package_msgs/action/DistTurtle "{'linear_x': 0, 'angular_z': 0, 'dist': 0}"
Waiting for an action server to become available...
Sending goal:
     linear_x: 0.0
angular_z: 0.0
dist: 0.0

Goal accepted with ID: 81e2be7d5697416194334182519a677d

Feedback:
    remained_dist: 0.0

Feedback:
    remained_dist: 1.0

Feedback:
    remained_dist: 2.0

Feedback:
    remained_dist: 3.0

Feedback:
    remained_dist: 4.0

Feedback:
    remained_dist: 5.0

Feedback:
    remained_dist: 6.0

Feedback:
    remained_dist: 7.0

Feedback:
    remained_dist: 8.0

Feedback:
    remained_dist: 9.0

Result:
    pos_x: 0.0
pos_y: 0.0
pos_theta: 0.0
result_dist: 0.0

Goal finished with status: SUCCEEDED

 

ROS2 Multi Thread 기초

 

my_first_package/ my_first_package/ my_multithread.py

import rclpy as rp
from rclpy.executors import MultiThreadedExecutor
from rclpy.node import Node

from my_first_package.my_publisher import TurtlesimPublisher
from my_first_package.my_subscriber import TurtlesimSubscriber


def main(args=None):
    rp.init()
    
    sub = TurtlesimSubscriber()
    pub = TurtlesimPublisher()

    executor = MultiThreadedExecutor()
    
    executor.add_node(sub)
    executor.add_node(pub)

    try:
        executor.spin()
    
    finally:
        executor.shutdown()
        #sub.destroy_node()
        pub.destroy_node()
        rp.shutdown()


if __name__ == '__main__':
    main()
    entry_points={
        'console_scripts': [
            'my_first_node = my_first_package.my_first_node:main',
            'my_subscriber = my_first_package.my_subscriber:main',
            'my_publisher = my_first_package.my_publisher:main',
            'turtle_cmd_and_pose = my_first_package.turtle_cmd_and_pose:main',
            'my_service_server = my_first_package.my_service_server:main',
            'dist_turtle_action_server = my_first_package.dist_turtle_action_server:main',
            'my_multi_thread = my_first_package.my_multi_thread:main'
        ],
    },
)

 

steezer@DESKTOP-TF8J569:~/ros2_study$ ros2 run turtlesim turtlesim_node
steezer@DESKTOP-TF8J569:~$ cd ros2_study
steezer@DESKTOP-TF8J569:~/ros2_study$ rm -rf build install log
steezer@DESKTOP-TF8J569:~/ros2_study$ colcon build
[0.340s] WARNING:colcon.colcon_ros.prefix_path.ament:The path '/home/steezer/ros2_study/install/my_first_package_msgs' in the environment variable AMENT_PREFIX_PATH doesn't exist
[0.340s] WARNING:colcon.colcon_ros.prefix_path.ament:The path '/home/steezer/ros2_study/install/my_first_package' in the environment variable AMENT_PREFIX_PATH doesn't exist
[0.340s] WARNING:colcon.colcon_ros.prefix_path.catkin:The path '/home/steezer/ros2_study/install/my_first_package_msgs' in the environment variable CMAKE_PREFIX_PATH doesn't exist
Starting >>> my_first_package
Starting >>> my_first_package_msgs
Finished <<< my_first_package [0.81s]
Finished <<< my_first_package_msgs [6.08s]

Summary: 2 packages finished [6.33s]
ros2 run my_first_package my_multi_thread
X :  6.399092197418213 , Y :  6.052364349365234
X :  6.414387226104736 , Y :  6.080472469329834
X :  6.428775787353516 , Y :  6.109055042266846
X :  6.44224214553833 , Y :  6.1380839347839355
X :  6.45477294921875 , Y :  6.16752815246582
X :  6.466355323791504 , Y :  6.19735860824585
X :  6.476977348327637 , Y :  6.22754430770874
X :  6.48662805557251 , Y :  6.258054256439209
X :  6.495297431945801 , Y :  6.288857460021973

 

 

코드, 데이터, 힙은 공유

스택은 별도로

프로세스 안에서 스택 데이터만 나눠서 할당 받음 -> 프로세스 스레드

 

지정한 거리만큼 이동하는 액션 서버 만들기

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

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.execute_callback)

    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 execute_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)
            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()

 

액션 서버 간단히 사용해보기

steezer@DESKTOP-TF8J569:~/ros2_study$ rm -rf build install log
steezer@DESKTOP-TF8J569:~/ros2_study$ colcon build
[0.335s] WARNING:colcon.colcon_ros.prefix_path.ament:The path '/home/steezer/ros2_study/install/my_first_package_msgs' in the environment variable AMENT_PREFIX_PATH doesn't exist
[0.335s] WARNING:colcon.colcon_ros.prefix_path.ament:The path '/home/steezer/ros2_study/install/my_first_package' in the environment variable AMENT_PREFIX_PATH doesn't exist
[0.335s] WARNING:colcon.colcon_ros.prefix_path.catkin:The path '/home/steezer/ros2_study/install/my_first_package_msgs' in the environment variable CMAKE_PREFIX_PATH doesn't exist
Starting >>> my_first_package
Starting >>> my_first_package_msgs
Finished <<< my_first_package [0.82s]
Finished <<< my_first_package_msgs [6.01s]

Summary: 2 packages finished [6.26s]
steezer@DESKTOP-TF8J569:~/ros2_study$ ros2 run turtlesim turtlesim_node
[INFO] [1773642671.492339381] [turtlesim]: Starting turtlesim with node name /turtlesim
[INFO] [1773642671.496131775] [turtlesim]: Spawning turtle [turtle1] at x=[5.544445], y=[5.544445], theta=[0.000000]
steezer@DESKTOP-TF8J569:~/ros2_study$ ros2 run my_first_package dist_turtle_action_server
steezer@DESKTOP-TF8J569:~/ros2_study$ ros2 action send_goal --feedback /dist_turtle my_first_package_msgs/action/DistTurtle "{'linear_x': 0.8, 'angular_z': 0.4, 'dist': 2.}"
Waiting for an action server to become available...
Sending goal:
     linear_x: 0.8
angular_z: 0.4
dist: 2.0

Goal accepted with ID: 59444f6920aa41b3a4e95a62fdcb29e1

Feedback:
    remained_dist: 2.0

Feedback:
    remained_dist: 1.9872000217437744

Feedback:
    remained_dist: 1.9872000217437744

Feedback:
    remained_dist: 1.9872000217437744

Feedback:
    remained_dist: 1.9616000652313232

Feedback:
    remained_dist: 1.9616000652313232

Feedback:
    remained_dist: 1.948799967765808

Feedback:
    remained_dist: 1.9360003471374512

Feedback:
    remained_dist: 1.859204649925232

Feedback:
    remained_dist: 1.8464049100875854

Feedback:
    remained_dist: 1.8336046934127808

Feedback:
    remained_dist: 1.8336046934127808

Feedback:
    remained_dist: 1.7952051162719727

Feedback:
    remained_dist: 1.680021047592163

Feedback:
    remained_dist: 1.552042841911316

Feedback:
    remained_dist: 1.5392423868179321

Feedback:
    remained_dist: 1.5392423868179321

Feedback:
    remained_dist: 1.5264426469802856

Feedback:
    remained_dist: 1.5264426469802856

Feedback:
    remained_dist: 1.5008429288864136

Feedback:
    remained_dist: 1.4880427122116089

Feedback:
    remained_dist: 1.4880427122116089

Feedback:
    remained_dist: 1.4752426147460938

Feedback:
    remained_dist: 1.4752426147460938

Feedback:
    remained_dist: 1.4496427774429321

Feedback:
    remained_dist: 1.436842918395996

Feedback:
    remained_dist: 1.436842918395996

Feedback:
    remained_dist: 1.424042820930481

Feedback:
    remained_dist: 1.4112427234649658

Feedback:
    remained_dist: 1.4112427234649658

Feedback:
    remained_dist: 1.3984428644180298

Feedback:
    remained_dist: 1.3856427669525146

Feedback:
    remained_dist: 1.3728429079055786

Feedback:
    remained_dist: 1.3728429079055786

Feedback:
    remained_dist: 1.360042929649353

Feedback:
    remained_dist: 1.347243070602417

Feedback:
    remained_dist: 1.347243070602417

Feedback:
    remained_dist: 1.3344428539276123

Feedback:
    remained_dist: 1.3216427564620972

Feedback:
    remained_dist: 1.3216427564620972

Feedback:
    remained_dist: 1.3088430166244507

Feedback:
    remained_dist: 1.296042799949646

Feedback:
    remained_dist: 1.296042799949646

Feedback:
    remained_dist: 1.2832428216934204

Feedback:
    remained_dist: 1.2832428216934204

Feedback:
    remained_dist: 1.2576431035995483

Feedback:
    remained_dist: 1.2448428869247437

Feedback:
    remained_dist: 1.2448428869247437

Feedback:
    remained_dist: 1.2320430278778076

Feedback:
    remained_dist: 1.219243049621582

Feedback:
    remained_dist: 1.219243049621582

Feedback:
    remained_dist: 1.2064430713653564

Feedback:
    remained_dist: 1.1936428546905518

Feedback:
    remained_dist: 1.1936428546905518

Feedback:
    remained_dist: 1.1808429956436157

Feedback:
    remained_dist: 1.1680428981781006

Feedback:
    remained_dist: 1.1424429416656494

Feedback:
    remained_dist: 1.1296430826187134

Feedback:
    remained_dist: 1.1296430826187134

Feedback:
    remained_dist: 1.1168432235717773

Feedback:
    remained_dist: 1.1040431261062622

Feedback:
    remained_dist: 1.1040431261062622

Feedback:
    remained_dist: 1.0912432670593262

Feedback:
    remained_dist: 1.078443169593811

Feedback:
    remained_dist: 1.065643072128296

Feedback:
    remained_dist: 1.0528430938720703

Feedback:
    remained_dist: 1.0272432565689087

Feedback:
    remained_dist: 1.0144431591033936

Feedback:
    remained_dist: 1.0144431591033936

Feedback:
    remained_dist: 0.988843560218811

Feedback:
    remained_dist: 0.9760433435440063

Feedback:
    remained_dist: 0.9760433435440063

Feedback:
    remained_dist: 0.9632435441017151

Feedback:
    remained_dist: 0.9504435062408447

Feedback:
    remained_dist: 0.9504435062408447

Feedback:
    remained_dist: 0.9376433491706848

Feedback:
    remained_dist: 0.9248433113098145

Feedback:
    remained_dist: 0.9248433113098145

Feedback:
    remained_dist: 0.912043571472168

Feedback:
    remained_dist: 0.8992432355880737

Feedback:
    remained_dist: 0.8992432355880737

Feedback:
    remained_dist: 0.8864436149597168

Feedback:
    remained_dist: 0.8864436149597168

Feedback:
    remained_dist: 0.8480438590049744

Feedback:
    remained_dist: 0.8480438590049744

Feedback:
    remained_dist: 0.8352438807487488

Feedback:
    remained_dist: 0.8224440217018127

Feedback:
    remained_dist: 0.8096436262130737

Feedback:
    remained_dist: 0.7968440055847168

Feedback:
    remained_dist: 0.7840439677238464

Feedback:
    remained_dist: 0.7712438106536865

Feedback:
    remained_dist: 0.7712438106536865

Feedback:
    remained_dist: 0.7584436535835266

Feedback:
    remained_dist: 0.7456438541412354

Feedback:
    remained_dist: 0.7456438541412354

Feedback:
    remained_dist: 0.7328441143035889

Feedback:
    remained_dist: 0.7328441143035889

Feedback:
    remained_dist: 0.7328441143035889

Feedback:
    remained_dist: 0.6944441795349121

Feedback:
    remained_dist: 0.6176490187644958

Feedback:
    remained_dist: 0.592049241065979

Feedback:
    remained_dist: 0.592049241065979

Feedback:
    remained_dist: 0.5792491436004639

Feedback:
    remained_dist: 0.5664488673210144

Feedback:
    remained_dist: 0.5536492466926575

Feedback:
    remained_dist: 0.5408493876457214

Feedback:
    remained_dist: 0.5152494311332703

Feedback:
    remained_dist: 0.5024494528770447

Feedback:
    remained_dist: 0.5024494528770447

Feedback:
    remained_dist: 0.4896491765975952

Feedback:
    remained_dist: 0.4896491765975952

Feedback:
    remained_dist: 0.46404948830604553

Feedback:
    remained_dist: 0.45124953985214233

Feedback:
    remained_dist: 0.4384492337703705

Feedback:
    remained_dist: 0.4384492337703705

Feedback:
    remained_dist: 0.42564940452575684

Feedback:
    remained_dist: 0.41284945607185364

Feedback:
    remained_dist: 0.41284945607185364

Feedback:
    remained_dist: 0.4000492990016937

Feedback:
    remained_dist: 0.4000492990016937

Feedback:
    remained_dist: 0.3488506078720093

Feedback:
    remained_dist: 0.33605068922042847

Feedback:
    remained_dist: 0.32325077056884766

Feedback:
    remained_dist: 0.27205201983451843

Feedback:
    remained_dist: 0.27205201983451843

Feedback:
    remained_dist: 0.22085340321063995

Feedback:
    remained_dist: 0.20805321633815765

Feedback:
    remained_dist: 0.19525322318077087

Result:
    pos_x: 7.11163854598999
pos_y: 6.310008525848389
pos_theta: 0.902400016784668
result_dist: 1.8047467470169067

Goal finished with status: SUCCEEDED

main

멀티스레드 적용

 

TurtleSub_action

pose 토픽 구독

 

DistTurtleServer
사용자가 지정한 거리만큼 이동

 

 

10장 Parameter 다루기

책이 입문자라 생략된 내용 있음

 

조사할 것

1. yaml이라는 형식은 어떤 것이고, 어떻게 사용하는가?

Yet Another Markup Language

데이터를 구조적으로 표현하기 위한 텍스트 기반 데이터 직렬화 형식

주로 설정 파일, 데이터 교환에 사용되며 JSON보다 가독성이 높음

 

 

2. yaml과 ros parameter가 어떤 관계인가?

ros parameter

노드가 실행될 때 외부에서 주입할 수 있는 변수

코드를 수정하고 재빌드하지 않아도 노드의 동작을 바꿀 수 있음

 

 

YAML(YAML Ain't Markup Language)은 서로 다른 데이터 구조를 가진 언어들 간의 데이터 교환을 위해 설계된, 사람이 읽기 쉬운 데이터 직렬화(Data Serialization) 언어

주로 설정(Configuration) 파일 작성, 시스템 간 데이터 교환, 그리고 복잡한 데이터 구조를 직관적이고 읽기 쉬운 형태로 표현하는 데 사용

 

주요 특징 (Key Features)

가독성 (Human Readable): 사람과 기계 모두가 직관적으로 읽고 쓰기 쉬움
언어 독립성 (Language Independent): 특정 프로그래밍 언어에 종속되지 않으며 다양한 플랫폼에서 작동
계층적 구조 (Hierarchical): 들여쓰기를 통해 복잡하게 중첩된(Nested) 데이터 구조를 쉽게 지원
확장성 (Extensible): 사용자 정의 데이터 타입(Custom data types)을 추가하여 확장 가능
보안성 (Secure): 처음부터 보안을 고려하여 설계

 

AML은 복잡한 기호나 괄호 대신, 들여쓰기(Space)와 하이픈(-), 콜론(:)만으로 데이터 구조를 표현

# 1. 주석은 '#' 기호를 사용합니다.
# 2. Key와 Value 사이에는 반드시 콜론(:) 뒤에 띄어쓰기가 하나 있어야 합니다.

이름: 김로봇
나이: 25
학생여부: true

# 리스트(배열)는 같은 들여쓰기 라인에서 하이픈(-)으로 시작합니다.
사용언어:
  - Python
  - C++
  - JavaScript

# 중첩된 데이터(딕셔너리)는 들여쓰기(보통 스페이스 2칸)로 계층을 표현합니다.
연락처:
  이메일: robot@example.com
  전화번호: "010-1234-5678" # 숫자로만 된 문자열은 따옴표로 묶어주는 것이 안전합니다.

 

steezer@DESKTOP-TF8J569:~$ ros2 run turtlesim turtlesim_node
[INFO] [1773648302.436125131] [turtlesim]: Starting turtlesim with node name /turtlesim
[INFO] [1773648302.448123061] [turtlesim]: Spawning turtle [turtle1] at x=[5.544445], y=[5.544445], theta=[0.000000]
ROS2 humble is activated!
steezer@DESKTOP-TF8J569:~$ ros2 run turtlesim turtle_teleop_key
Reading from keyboard
---------------------------
Use arrow keys to move the turtle.
Use G|B|V|C|D|E|R|T keys to rotate to absolute orientations. 'F' to cancel a rotation.
'Q' to quit.
steezer@DESKTOP-TF8J569:~$ ros2 param list
/teleop_turtle:
  qos_overrides./parameter_events.publisher.depth
  qos_overrides./parameter_events.publisher.durability
  qos_overrides./parameter_events.publisher.history
  qos_overrides./parameter_events.publisher.reliability
  scale_angular
  scale_linear
  use_sim_time
/turtlesim:
  background_b
  background_g
  background_r
  qos_overrides./parameter_events.publisher.depth
  qos_overrides./parameter_events.publisher.durability
  qos_overrides./parameter_events.publisher.history
  qos_overrides./parameter_events.publisher.reliability
  use_sim_time
steezer@DESKTOP-TF8J569:~$ ros2 param get /turtlesim background_g
Integer value is: 86
steezer@DESKTOP-TF8J569:~$ ros2 param set /turtlesim background_r 250
Set parameter successful
steezer@DESKTOP-TF8J569:~$ ros2 param set /turtlesim background_b 250
Set parameter successful
steezer@DESKTOP-TF8J569:~$ ros2 param set /turtlesim background_g 250
Set parameter successful

steezer@DESKTOP-TF8J569:~$ cd ros2_study/src/my_first_package
steezer@DESKTOP-TF8J569:~/ros2_study/src/my_first_package$ mkdir params
steezer@DESKTOP-TF8J569:~/ros2_study/src/my_first_package$ cd params
steezer@DESKTOP-TF8J569:~/ros2_study/src/my_first_package/params$ ros2 param dump /turtlesim > turtlesim.yaml
steezer@DESKTOP-TF8J569:~/ros2_study/src/my_first_package/params$ ls
turtlesim.yaml
steezer@DESKTOP-TF8J569:~/ros2_study/src/my_first_package/params$ cd src
-bash: cd: src: No such file or directory
steezer@DESKTOP-TF8J569:~/ros2_study/src/my_first_package/params$ cd ..
steezer@DESKTOP-TF8J569:~/ros2_study/src/my_first_package$ cd ..
steezer@DESKTOP-TF8J569:~/ros2_study/src$ subl .
/turtlesim:
  ros__parameters:
    background_b: 250
    background_g: 250
    background_r: 250
    qos_overrides:
      /parameter_events:
        publisher:
          depth: 1000
          durability: volatile
          history: keep_last
          reliability: reliable
    use_sim_time: false

 

191 * 3

/turtlesim:
  ros__parameters:
    background_b: 191
    background_g: 191
    background_r: 191
    qos_overrides:
      /parameter_events:
        publisher:
          depth: 1000
          durability: volatile
          history: keep_last
          reliability: reliable
    use_sim_time: false
steezer@DESKTOP-TF8J569:~/ros2_study/src/my_first_package/params$ ros2 param load /turtlesim ./turtlesim.yaml
Set parameter background_b successful
Set parameter background_g successful
Set parameter background_r successful
Set parameter qos_overrides./parameter_events.publisher.depth failed: parameter 'qos_overrides./parameter_events.publisher.depth' cannot be set because it is read-only
Set parameter qos_overrides./parameter_events.publisher.durability failed: parameter 'qos_overrides./parameter_events.publisher.durability' cannot be set because it is read-only
Set parameter qos_overrides./parameter_events.publisher.history failed: parameter 'qos_overrides./parameter_events.publisher.history' cannot be set because it is read-only
Set parameter qos_overrides./parameter_events.publisher.reliability failed: parameter 'qos_overrides./parameter_events.publisher.reliability' cannot be set because it is read-only
Set parameter use_sim_time successful

 

코드로 접근하는 파라미터

 

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

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.declare_parameter('quatile_time', 0.75)
        self.declare_parameter('almost_goal_time', 0.95)
        self.publisher = self.create_publisher(Twist, '/turtle1/cmd_vel', 10)
        self._action_server = ActionServer(self, DistTurtle, 'dist_turtle', self.execute_callback)
        

    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 execute_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)
            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()
steezer@DESKTOP-TF8J569:~/ros2_study$ ros2 param list
/dist_turtle_action_server:
  almost_goal_time
  quatile_time
  use_sim_time
/turtlesim_subscriber:
  use_sim_time
steezer@DESKTOP-TF8J569:~/ros2_study$ ros2 param get /dist_turtle_action_server quatile_time
Double value is: 0.75