본 블로그 포스팅은 https://www.youtube.com/@pinklab_studio/playlists
에서 현재 강의 중에 있는
ROS2 무작정 따라하기강의의 내용을 필자가 다시 복기하여 기록하는 내용에 관한 것이다.
이번 실습에서 수행하는 작업 내용은 아래와 같다.
- 커스텀 메세지 관리를 위한 패키지 생성
my_first_package_msgs라는 패키지를 생성하여 해당 패키지 내
사용자 정의 메세지 : CmdAndPosVel.msg를 생성해 이를 등록 및 관리한다.
- 커스텀 메세지를 사용하여 토픽 메세지 파싱
기존 my_first_package에 신규 노드 파일turtle_CAP.py을 생성, 해당 노드는 Turtlesim_node의 Publish topic
: /turtle/pose을 수신한 뒤 해당 topic에 담긴 메세지 데이터 정보를 CmdAndPosVel.msg의 규격에 맞춰 파싱 후 이를 print
my_first_package_msg패키지의 생성은 이전 포스트 PinkLAB - 민형기 ROS2 응용 실습 1일차 - 패키지 만들기
를 참조하여
./[ROS_Workspace]/src
폴더에서 아래의 명령어 수행을 통해 패키지 생성을 진행한다
$ ros2 pkg create --build-type ament_cmake my_first_package_msgs --license Apache-2.0
여기서 빌드 옵션이 ament_python
이 아닌 ament_cmake
는 ROS2 또한 ROS와 마찬가지로 C++을 기반으로 하는 미들웨어 이기에
이전 버전인 ROS에서 지원하는 대부분의 기능 구현이 대부분 ROS2도 동일하게 Cmake 기반으로 빌드가 되어 있고
Python 기반의 빌드 옵션은 Python스크립트 기반 노드나 런치파일만을 관리 할 수 있는 등 기능이 제한적이다.
따라서 사용자 정의 msg의 생성/관리는 기존의 Cmake 빌드 옵션으로 수행해야 한다.
ament_python
옵션으로 생성한 패키지 my_first_package와 ament_cmake
옵션으로 생성한 패키지 my_first_package_msgs를 비교해 보자면
패키지 빌드 시 빌드 과정에 대한 가이드 파일이
ament_python
은 setup.py
ament_cmake
은 CMakeLists.txt가 수행한다 보면 된다.
빌드 옵션 별 패키지 구성에 대한 설명은 여기까지 하고
my_first_package_msgs패키지의 생성목적이 사용자 정의 메세지
의 관리 이니 해당 기능을 수행하기 위하여
아래와 같이msg폴더 생성 후 해당 폴더에 CmdAndPosVel.msg를 생성하도록 하자
$ cd my_first_package_msgs/ -> 패키지 폴더로 이동
$ mkdir msg -> `my_first_package_msgs`패키지폴더 내 msg폴더 생성
대략 위 사진처럼 패키지 폴더 내에 msg폴더를 만들고 CmdAndPosVel.msg파일을 만들면 된다.
그리고 생성한 사용자 정의 메세지 파일 : CmdAndPosVel.msg에는 위 사진처럼 총 6개의 메세지 타입을 정의한다.
CmdAndPosVel.msg 파일을 생성한 후에는
해당 패키지가 msg 기능이 있음을 알리는 빌드 옵션 설정과
수정된 옵션에 맞춰 관련 빌드 패키지를 알려주는
빌드 의존성 설정을 수행해야 한다.
1) 빌드 옵션 설정 : CMakeLists.txt
을 통해 작업
2) 빌드 의존성 설정 : Package.xml
을 통해 작업
1)
CMakeLists.txt
작업내용
rosidl_generate_interfaces(${PROJECT_NAME}
"msg/CmdAndPosVel.msg"
)
위 구문을 CMakeLists.txt
에 초록색 박스로 표기된 부분에 삽입
2)
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>
위 구문을 package.xml
에 초록색 박스로 표기된 부분에 삽입
패키지 코드 작업이 완료되었다면 아래의 사진처럼
패키지 빌드
빌드한 패키지 등록
디버그(생성한 사용자 정의 메세지가 잘 출력되는지 확인)
을 수행하자
$ colcon build -> 패키지 빌드
$ r2pkgsetup
r2pkgsetup
명령어는 필자가 강의를 수강하면서 별로 생성한 bash 스크립트 명령어 이고
해당 명령어에 대한 설명은
PinkLAB - 민형기 ROS2 응용 실습 2일차 - 패키지 실행+노드작성
을 참조하기 바란다.
요약하자면
$ source ~/ros2_workspace/install/local_setup.bash
이 명령어의 자동실행 명령어이다.
ros2 interface show my_first_package_msgs/msg/CmdAndPosVel
새로이 생성한 my_first_package_msgs 내 CmdAndPosVel.msg의 메세지 등록이 성공적으로 이뤄졌으니
my_first_package 패키지에 새로운 노드 turtle_CAP.py를 생성하여 해당 노드에서 CmdAndPosVel.msg를 사용해 보는 코드를 작성하자
1) 라이브러리 import
커스텀 메세지의 import방식은 이전 코드에서 설명한 메세지 import의 규칙과 동일하다
my_first_package_msgs / msg / CmdAndPosVel
로 메세지 타입을
from
my_first_package_msgs.msg import
CmdAndPosVel
로 기입하면 된다.
2) 노드 클래스 설계
class CmdAndPose(Node):
def __init__(self):
#turtle_cmd_pose라는 노드명
super().__init__('turtle_cmd_pose')
self.sub_pose = self.create_subscription(
Pose, #수신받는 메세지 타입
'/turtle1/pose', #수신받는 토픽명
self.callback_pose, #메세지 수신 시 수행할 함수
10 #Queue 사이즈
)
#메세지 클래스니까 노드 내부에서 메서드 변수로 사용
self.cmd_pose = CmdAndPosVel()
클래스 노드의 설계는 PinkLAB - 민형기 ROS2 응용 실습 2일차 - 패키지 실행+노드작성
를 참조 바라며
해당 클래스 노드는
1. Subscribe_node로 동작
2. 수신받을 토픽은 /turtle/pose
3. self.cmd_pose = CmdAndPosVel()
사용자 정의 메세지 인스턴스를 생성 이를 cmd_pose
인스턴스 변수에 할당
위 과정을 통해 클래스 : CmdAndPose
의 다른 메서드 에서 cmd_pose
에 담긴 메세지 타입을 사용할 수 있음
그럼 CmdAndPose
의 다른 메서드 를 정의해야 하는데
해당 메서드는
self.callback_pose,
이것이다.
3) 클래스 내부 메서드 : callback_pose 설계
해당 메서드는 /turtle/pose토픽이 수신되면 자동으로 콜백되는 메서드 함수이다.
def callback_pose(self, msg):
# print(f"X: {msg.x:.2f},", end=' ')
# print(f"Y: {msg.y:.2f},", end=' ')
# print(f"theta: {msg.theta:.2f}")
#커스텀 메세지에 인자값을 담아보자
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)
그림으로 해당부분을 좀 더 자세하게 설명하겠다.
토픽 /turtle/pose의 메세지 타입이 turtlesim/msg/Pose이니 해당 메세지 타입을 확인하여
데이터를 추출하고
이를 CmdAndPosVel.msg의 메세지 타입 규격에 맞춰 데이터를 기입하는
파싱과정이라 보면 된다.
4) 메인 루프
def main(args=None):
rp.init(args=args)
#이벤트 루프에서 사용할 노드 인스턴스를 생성
turtle_CAP_node = CmdAndPose()
#메인 이벤트 루프를 노드가 종료될 때까지 반복 실행
#해당 루프가 실행되는 동안
#콜백함수(callback_pose)가 토픽 메세지 수신시 호출됨
rp.spin(turtle_CAP_node)
#키보드 인터럽트(ctrl+c)들어오면 아래 명령 수행됨
turtle_CAP_node.destroy_node()
rp.shutdown()
if __name__ == '__main__':
main()
메인 루프는 위 설계한 클래스 CmdAndPose
노드 인스턴스로 생성하고
해당 노드 인스턴스를 메인 이벤트 루프에 넣어
노드가 종료되는 명령어가 들어올 때까지
반복실행되는 코드라 보면 된다.
따라서 전체 코드는 아래와 같다
import rclpy as rp
from rclpy.node import Node
from turtlesim.msg import Pose
#커스텀 메세지를 import 하기
from my_first_package_msgs.msg import CmdAndPosVel
class CmdAndPose(Node):
def __init__(self):
#turtle_cmd_pose라는 노드명
super().__init__('turtle_cmd_pose')
self.sub_pose = self.create_subscription(
Pose, #수신받는 메세지 타입
'/turtle1/pose', #수신받는 토픽명
self.callback_pose, #메세지 수신 시 수행할 함수
10 #Queue 사이즈
)
#메세지 클래스니까 노드 내부에서 메서드 변수로 사용
self.cmd_pose = CmdAndPosVel()
#토픽 수신시 해당 토픽의 메세지 타입에 맞춰 정보를 print
def callback_pose(self, msg):
# print(f"X: {msg.x:.2f},", end=' ')
# print(f"Y: {msg.y:.2f},", end=' ')
# print(f"theta: {msg.theta:.2f}")
#커스텀 메세지에 인자값을 담아보자
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_CAP_node = CmdAndPose()
#메인 이벤트 루프를 노드가 종료될 때까지 반복 실행
#해당 루프가 실행되는 동안
#콜백함수(callback_pose)가 토픽 메세지 수신시 호출됨
rp.spin(turtle_CAP_node)
#키보드 인터럽트(ctrl+c)들어오면 아래 명령 수행됨
turtle_CAP_node.destroy_node()
rp.shutdown()
if __name__ == '__main__':
main()
5) setup.py 파일 관리
my_first_package의 빌드 옵션을 관리하는 setup.py의
노드를 기재하는 항목에다가
노드명 = [패키지명].[노드파일명]:main
이 순으로 기입하면 된다.
참고로 노드명
이랑 노드파일명
이 같을 필요는 없다
ros2 run 명령어로 노드 실행시에는 노드명
으로 명령어를 입력해야 동작하는 것도 잊지말자.
'turtle_cmd_and_pose = my_first_package.turtle_C_A_P:main'
위 코드를 사진상의 위치에 기입하면 된다.
패키지 빌드의 경우
이번에 수정한 패키지가 my_first_package 단 하나이기 때문에
전체 패키지를 다 빌드하는 옵션이 아닌
특정 패키지만 선택하여 빌드하는 옵션으로 명령어를 작성한다
$ colcon build --package_select my_first_package
옵션에 대한 설명을 하자면
--packages-select [패키지 이름]
이 옵션을 넣은 것이며
기능은 특정 패키지만을 선택하여 빌드
이다.
다음으로 2개의 터미널 창을 띄워서 각각의 명령을 수행한다.
1) 터틀심 노드 실행
$ r2pkgsetup
$ ros2 run turtlesim turtlesim_node
2) 신규 노드 turtle_CAP.py 실행
$ r2pkgsetup
$ ros2 run my_first_package turtle_cmd_and_pose
실행 후에는 위 사진처럼
2) 신규노드
실행창에서 커스텀 메세지 형식으로 파싱된 데이터가 출력됨을 알 수 있다.
ROS를 개발하는데 VScode를 쓴다는 것은
역시 VScode에서 제공하는 다양한 Extension을 잘 사용하기 위해서라 보면 된다.
ROS, URDF항목은 지금 작성하고 있는 코드가 ROS버전의 코드일 때 문법오류나 help를 지원하는 패키지 이고
URDF는 향후 ROS에서 가상 로봇을 구현할 때 작성하는 파일의 네비게이션 기능을 지원한다 보면 된다.
그 아래 Cmake, CMake Tools는 CMakeLists.txt
파일 작성시의 네비게이션 기능
XML, Xml tools는 Package.xml
파일의 네비게이션 기능을 제공한다.
여기서 네비게이션은
구문별로 색깔 이쁘게 입혀주고, 구문오류난거 알려주고 help기능을 지원해준다
이런거라 보면 된다.