본 블로그 포스팅은 https://www.youtube.com/@pinklab_studio/playlists
에서 현재 강의 중에 있는
ROS2 무작정 따라하기강의의 내용을 필자가 다시 복기하여 기록하는 내용에 관한 것이다.
그간 다른공부를 좀 하느라 두달 가까이 ROS2 무작정 따라하기포스팅이 밀렸고 다시 시리즈를 이어서 복기를 하려니 조금 헤멘 감이 있었다.
역시 공부는 끊기면 안된다.
기존 내용을 다시 복기한 후 마지막 영상이
2-4.내가 정의한 메세지 사용해보기
까지 진행했었고
이번 실습 파트는 2-5, 2-6
에 관한 내용이다.
실습내용을 요약하면 아래와 같다.
- 신규 패키지 my_second_package 생성
기존 my_first_package에 신규 노드(파이선파일)을 추가하는 것보다는 포스팅 텀이 길어졌기에 신규 패키지 my_second_package를 새로 생성해서
해당 패키지 내 실습 목표일 2종 이상의 토픽 핸들링 노드
작성을 수행한다.
- 2종 이상의 토픽 핸들링
Subscriber
: /turtel1/pose, /turtle1/cmd_vel 2개의 토픽을 수신
custom_msg
: 커스텀 메세지 CmdAndPosVel로 Subscriber
로 수신한 메세지 정보를 전달하는데
1) 단순 print
: my_second_node.py에서는 CmdAndPosVel에 저장된 정보를 단순히 print
2) 별도 토픽으로 Publish
: my_sec_subpub.py에서는 CmdAndPosVel에 저장된 정보를 1.0 sec
단위로 /cmd_and_pos_vel토픽 명으로 Publish
my_second_package패키지의 생성은 이전 포스트 PinkLAB - 민형기 ROS2 응용 실습 1일차 - 패키지 만들기
를 참조하여
./[ROS_Workspace]/src
폴더에서 아래의 명령어 수행을 통해 패키지 생성을 진행한다
$ ros2 pkg create --build-type ament_python --node-name my_second_node my_second_pkg --license Apache-2.0
pkg create의 옵션별 설명은 아래와 같다.
1) --build-type
: ROS2의 Python기반 패키지 생성 ament_python
2) --node-name
: 생성할 패키지 안에 기본 노드를 하나 만듬 my_second_node
로 노드를 하나 만들며, 해당 노드와 매칭되는 my_second_node.py파일이 자동 생성됨
3) --license
: 패키지의 라이센스를 지정안하면 waring 메세지가 출력됨, 지정 가능한 옵션은 아래와 같다.
생성한 패키지 my_second_package에는
--node-name
옵션으로 기본 노드파이썬 파일 my_second_node.py이 자동생성 및 등록되고 두번째 실습용 노드 파이썬 파일인 my_sec_subpub.py는 새로 생성해야 하며, 패키지 관리파일인 setup.py에 신규 노드로 등록을 해줘야 한다.
/turtel1/pose, /turtle1/cmd_vel 두개의 토픽을 수신받아서 데이터를 처리한 뒤
커스텀 메세지 CmdAndPosVel으로 처리된 데이터를 저장하고, 이를 단순 print
하는 코드를 작성한다.
라이브러리 import
import rclpy as rp #ros2 사용을 위한 python 라이브러리
from rclpy.node import Node #ros2에서 노드 작성시 필요한 라이브러리
# ros2 run turtlesim turtlesim_node 터틀심 노드 구동
# ros2 topic list 터틀심 노드 구동 후 Pub/Sub토픽 리스트 확인
# Sub할 토픽 항목 : /turtle1/pose, /turtle1/cmd_vel
# 각 항목별 토픽의 메세지 타입 확인하기
# ros2 topic type /turtle1/pose : turtlesim/msg/Pose
# ros2 topic type /turtle1/cmd_vel : geometry_msgs/msg/Twist
from turtlesim.msg import Pose
from geometry_msgs.msg import Twist
# 임의 커스텀 메세지 : my_first_package_msgs의 커스텀 메세지
from my_first_package_msgs.msg import CmdAndPosVel
라이브러리는 크게 ROS2-Python을 사용하기 위한
rclpy
, rclpy.node
라이브러리
그리고 나머지 3개는 메세지타입을 다루기 위한 라이브러리 이다.
이때 어떤 메세지를 사용해야 할 지?
에 대한것은 주석으로 메세지를 찾는 명령어를 주석으로 달았다.
main 구문
def main(args=None):
rp.init(args=args) #ros2 노드 초기화 + 명령줄 구문으로 node조정이 가능함
#클래스 SecondNode의 설계도에 맞춰 `my_second_node`를 인스턴스화
my_second_node = SecondNode()
rp.spin(my_second_node) #객체 `my_second_node`를 반복 호출
#키보드 인터럽트(ctrl+c)들어오면 아래 명령 수행됨
my_second_node.destroy_node() #노드 삭제 및 노드 기능 종료
rp.shutdown()
if __name__ == '__main__':
main() # main 함수 구동
main
함수는 ros2 Node에 대한 초기화 / 구동방식 / 종료 방법에 관한 코드를 포함한다
여기서 rp.init(args=args)
는 ROS 2 노드를 초기화하면서, 노드가 커맨드 라인에서 전달된 인자를 받아들여 실행 중에 해당 인자에 따라 노드의 동작을 조정할 수 있도록 설정하는 역할을 한다.
노드의 구동방식(설계도) 클래스 설계 부
class SecondNode(Node): #클래스 SecondNode는 Node클래스를 상속함
def __init__(self):
super().__init__('my_second_node') #노드 이름명 정의
self.sub_topic_1 = self.create_subscription(
msg_type=Pose,
topic= '/turtle1/pose',
callback= self.callback_pose,
qos_profile=10,
)
self.sub_topic_2 = self.create_subscription(
msg_type=Twist,
topic='/turtle1/cmd_vel',
callback=self.callback_cmd_val,
qos_profile=10
)
# 커스텀 메세지는 print용으로만 구현하기
self.cmd_pos_val = CmdAndPosVel()
# Pose의 메세지 타입 내 인자 확인하기
# ros2 interface show turtlesim/msg/Pose
def callback_pose(self, msg):
# 커스텀 메세지 CmdAndPosVel의 pose_x에 msg.x인자값 넘기기
self.cmd_pos_val.pose_x = msg.x
self.cmd_pos_val.pose_y = msg.y
self.cmd_pos_val.linear_vel = msg.linear_velocity
self.cmd_pos_val.angular_vel = msg.angular_velocity
# Twist의 메세지 타입 내 인자 확인
# ros2 interface show geometry_msgs/msg/Twist
def callback_cmd_val(self, msg):
self.cmd_pos_val.cmd_vel_linear = msg.linear.x
self.cmd_pos_val.cmd_vel_angular = msg.angular.z
print(self.cmd_pos_val) #입력받은 메세지 항목 전체 출력
노드의 작동 방식을 설계하는 것은 class를 통해 정의하며,
위 코드를 해석한다면
1) subscriber
로 topic
을 수신하는데 이때 2종의 topic
를 수신한다.
각 topic
의 Quality of Service (QoS) 정책은 Message queue
를 10
개로 제한하는 정책으로, 최신 10개의 메세지만을 히스토리에 유지해 통신 안정성과 성능을 보증한다.
2) 각각의 subscriber
는 topic
를 수신 시 정의한 callback function : callback_pose
, callback_cmd_val
을 호출하고
호출된콜백 함수는 수신받은 topic
메세지 내 인자값들을 추출 사용자 정의 메세지 타입 CmdAndPosVel객체의 클래스 변수에 각각 데이터를 전달한다.
패키지 빌드 + 등록
아래의 명령어를 신규 커맨드창을 열고 차례로 입력하여 수정한 my_second_pkg를 빌드하고 빌드한 package내 새로 생성된 node를 등록한다.
$ humble # -> 예전 포스팅에서 ros2활성화를 위해 등록한 커스텀 명령어
$ colcon build --packages-select my_second_pkg # `my_second_pkg` 패키지만 선택해서 빌드
$ r2pkgsetup # -> 예전 포스팅에서 변경된 패키지 정보를 현재 셀에서 인식 가능하게 등록
노드 구동
1) bash 1번 스크립트
$ humble
$ ros2 run turtlesim turtlesim_node #터틀심 노드 구동
2) bash 2번 스크립트
$ r2pkgsetup
$ ros2 run my_first_package my_publisher #터틀심 제어 퍼블리셔 구동
3) bash 3번 스크립트
$ r2pkgsetup
$ ros2 rum my_second_pkg my_second_node #실습목표 노드 구동
노드 구성 분석
1) bash 4번 스크립트
$ r2pkgsetup
$ ros2 topic list # 현재 실행중인 토픽 리스트 확인
2) bash 5번 스크립트
$ r2pkgsetup
$ rqt_graph #현재 구동중인 도드 간 연결 구성도 확인
총 3개의 노드 turtlesim_publisher, turtle1, my_second_node에서 각각의 메세지가 전달되는 구조를 확인할 수 있다.
이때 my_second_node는 실습 목표처럼 2개 이상의 topic
을 구독중인 것을 확인할 수 있다.
코드의 전체 구조는 3. my_second_node.py
를 거의 따르며, 커스텀 메세지 CmdAndPosVel타입으로 /cmd_and_pos_vel토픽을 Publish
하는 기능을 추가하는 것이기에 변경된 코드 부분만 하이라이트 하겠다.
class SecondNode(Node): #클래스 SecondNode는 Node클래스를 상속함
def __init__(self):
super().__init__('my_second_node') #노드 이름명 정의
self.sub_topic_1 = self.create_subscription(
msg_type=Pose,
topic= '/turtle1/pose',
callback= self.callback_pose,
qos_profile=10,
)
self.sub_topic_2 = self.create_subscription(
msg_type=Twist,
topic='/turtle1/cmd_vel',
callback=self.callback_cmd_val,
qos_profile=10
)
# 커스텀 메세지를 퍼블리싱 하기
self.pub_topic = self.create_publisher(
msg_type=CmdAndPosVel,
topic='/cmd_and_pos_vel',
qos_profile=10
)
#타이머 형태로 퍼블리싱 하려고 타이머 주기 설정
self.timer_period = 1.0
self.timer = self.create_timer(
timer_period_sec=self.timer_period,
callback=self.timer_callback,
)
self.cmd_pos_val = CmdAndPosVel() #Sub메세지를 여기에 담는다.
# Pose의 메세지 타입 내 인자 확인하기
# ros2 interface show turtlesim/msg/Pose
def callback_pose(self, msg):
# 커스텀 메세지 CmdAndPosVel의 pose_x에 msg.x인자값 넘기기
self.cmd_pos_val.pose_x = msg.x
self.cmd_pos_val.pose_y = msg.y
self.cmd_pos_val.linear_vel = msg.linear_velocity
self.cmd_pos_val.angular_vel = msg.angular_velocity
# Twist의 메세지 타입 내 인자 확인
# ros2 interface show geometry_msgs/msg/Twist
def callback_cmd_val(self, msg):
self.cmd_pos_val.cmd_vel_linear = msg.linear.x
self.cmd_pos_val.cmd_vel_angular = msg.angular.z
# 타이머 콜백 함수를 통해 커스텀 메시지 타입의 토픽을 발행
def timer_callback(self):
# self.cmd_pos_val여기에 있는 메세지 정보를 퍼블리싱해라
self.pub_topic.publish(msg=self.cmd_pos_val)
기존의 클래스 설계도 SecondNode
에서
토픽 퍼블리싱을 위한 기능 pub_topic
토픽 발행주기를 다루는 기능 timer
을 추가하고 timer
의 콜백 함수 timer_callback
에서 Publish
를 수행한다.
설계한 신규노드
my_sec_subpub.py
setup.py 수행
위 사진처럼 , '노드명' = 패키지명.파이썬파일명:main'
순으로 추가하면 된다.
패키지 빌드 + 등록
신규 노드 my_sec_subpub_node를 my_second_pkg에 신설했으니 아래의 명령어를 다시 수행한다.
$ humble # -> 예전 포스팅에서 ros2활성화를 위해 등록한 커스텀 명령어
$ colcon build --packages-select my_second_pkg # `my_second_pkg` 패키지만 선택해서 빌드
$ r2pkgsetup # -> 예전 포스팅에서 변경된 패키지 정보를 현재 셀에서 인식 가능하게 등록
노드 구동
1) bash 1번 스크립트
$ humble
$ ros2 run turtlesim turtlesim_node #터틀심 노드 구동
2) bash 2번 스크립트
$ r2pkgsetup
$ ros2 run my_first_package my_publisher #터틀심 제어 퍼블리셔 구동
3) bash 3번 스크립트
$ r2pkgsetup
$ ros2 rum my_second_pkg my_sec_subpub_node #실습목표 노드 구동
이번에는 print
기능을 삭제하고 신규 토픽으로 퍼블리싱을 하는 것이기에 위 코드까지만 수행하면 구동이 잘 되고있는지 확인하기 어렵다.
4) bash 4번 스크립트
$ r2pkgsetup
$ ros2 topic list #신규 토픽이 발행중인지 확인하기
5) bash 5번 스크립트
$ r2pkgsetup
$ ros2 topic echo /cma_and_pos_vel #신규토픽 `/cmd_and_pos_vel` 메세지를 확인하기
위와 같이 신규 발행중인 토픽 : /cmd_and_pos_vel을
echo
옵션으로 디버그를 수행해야 제대로 발행되는지 여부를 확인할 수 있다.
rqt_graph
기능을 사용하면 신규 발행중인 토픽 : /cmd_and_pos_vel을 확인할 수 있다.
오랜만에 포스트를 다시 이어가려니 민형기교수님 진도가 많이 앞서나가고 있기에 빨리빨리 해야겠다....