본문 바로가기

KAIROS 2기

[카이로스2기] ROS2 기초 : 드디어 만난 ROS를 배워보자

카이로스 2기 교육과정 중 다들 제일 기대가 컸던, 로봇을 개발한다면 가장 먼저 떠오르는 ROS!

드디어 ROS와 SLAM까지 배워서, 기본 교육 과정은 거의 끝을 보이고 있다!😮

이번주와 다음주 포스팅으로 ROS2 교육에 대한 구체적인 내용을 다루고자 한다.

 

1. ROS2 기본

Linux에 이어 구성해놓은 Virtual Box 가상환경에서 ROS2를 세팅하였다.

이전 20.04버전이 아니라, Ubuntu 22.04에 ROS2 humble을 설치했는데, 20.04 버전에 할 분은 Galatic으로 설치하면 된다고 한다!

ROS2에서 가장 중요한 것은 메세지 통신을 어떻게 하는지를 이해하는 것이다.💬

가장 최소 실행 프로그램 단위인 '노드'를 생성하고, 각 노드끼리 어떻게 소통하는지를 잘 정의해야 한다.

소통 방식으로는 토픽(Topic), 서비스(Service), 액션(Action) 3가지가 있다.

- 토픽 : 비동기식 메세지 단방향 송수신 (publisher -> subscriber), 가장 많이 쓰임
- 서비스 : 양방향 통신 (request & response) 서버-클라이언트 구조
- 액션 : 토픽(Feedback) + 서비스(Send_goal, Result), 일회성이 아닌 소통

 

각 소통 방식에 따라 어떤 방식으로 주고받는지도 아래와 같이 달라진다.🔥

그래서 내가 원하는 동작을 위해서는 어떤 소통 체계를 만들것인지 설계를 잘 하는 것이 정말 중요하다고 느꼈다.

- Topic > Msg (1): publish할 때 전달해주는 값
- Service > Srv (2) : Request 시 전달하는 값 --- Response 시 받는 값
- Action > Action (3) : Request 값 --- Response 값 --- Feedback 값

 

실제로 ROS2의 소통이 어떻게 이루어지는지 demo (Talker & Listener)를 통해 확인해보았다.

publisher가 1씩 증가하는 값을 보낼때마다 subscriber가 받은 문자열을 출력하는 것을 볼 수 있다.

아래 rqt_graph를 통해 발행되는 토픽과 노드들 간의 관계를 확인할 수 있다.

 

2. turtlesim

ROS를 배우면 모두 만져봤을 turtlesim demo를 실행시켜보았다.

확실히 가장 기본이 되는 데모이지만, ROS의 동작 방식을 이해하는데 큰 도움이 되었다.😊

그래서 정말 이런저런 트라이를 많이 해보았는데, 처음에는 갈수록 혼란스러웠으나ㅎ 결국 삽질의 끝은 이해하는 결론이기 때문에.. 삽질의 여정을 함께 되돌아보자.😺

 

turtlesim에서는 거북이를 만드는 'turtle' 노드와 거북이를 움직이게하는 'turtle_teleop_key' 노드 두개를 사용한다.

거북이를 움직이기 위해서는 두 노드 사이에 cmd_vel (velocity 속도)이라는 topic을 발행한다.

 

그러면 turtlesim을 터미널 2개에 키면 어떻게 될까? 거북이가 한마리씩 있는 창이 2개 생성되었다.🐢

이 두번째 거북이는 new_turtle 이라는 이름을 주었고, 이 new_turtle도 별도의 하나의 노드이다.

이 두 노드는 모두 turtle_teleop_key에서 발행하는 turtle1/cmd_vel 토픽을 subscribe하기 때문에, 동작 키를 누르면 똑같이 움직이게 된다.

 

그럼 여기서 turtlesim의 service인 spawn을 사용해보자.

service call을 통해 spawn을 하면, 원래 있던 창 안에 한마리씩 거북이가 더 생기게 된다! 🐢

여기서 우선 중요한 것은, spawn은 서비스이므로, topic과 다르게 response를 받게 된다.

spawn의 경우에는 새 거북이의 이름인 name을 받고 있는 것을 볼 수 있다. (우측 하단 터미널)

그림 상에서는 왼쪽 아래에 거북이가 각각 한마리씩 더 생겼다. (총 4마리)

 

그렇다면 이 새로 생긴 거북이들을 움직이고 싶으면 어떻게 해야할까?🤔

--remap 명령어를 통해 기존 turtel1/cmd_vel 에 연결된 teleop_key를 turtle2/cmd_vel로 변경해주면 된다.

아래 rqt_graph를 보면 조금 더 명확한데 teleop 노드에서 나온 화살표가 기존 turtle1에서 turtle2의 cmd_vel로 바뀌는 것을 확인할 수 있다.

ros2 run turtlesim turtle_teleop_key --ros-args --remap turtle2/cmd_vel:=turtle1/cmd_vel

 

마지막으로 파라미터 조정에 대해서도 배웠다.

turtlesim에서는 배경색 RGB 값을 get param을 통해 조회, set param을 통해 변경할 수 있다.

아래 사진에서는 배경의 background_r 값을 150으로 주어서 기존 파랑색 배경을 보라색으로 변경해보았다.💜

이렇게 변경한 파라미터는 dump > .yaml 해서 저장하고 load 하여 다시 사용하는 것도 가능하다!

기본적이어보이지만, turtlesim을 가지고 이렇게 저렇게 놀아보면 정말 다양한 것을 배울 수 있었다.

 

3. 패키지

이제 직접 코드를 사용해서 ROS2를 동작시켜보자. 👊

이를 위해서는 프로그램 단위로 패키지를 생성하고, 패키지 폴더 안에 노드들을 생성한다.

아래 사진 기준으로 보면, my_first_ros_rclpy_pkg 안에 publisher 노드 하나, subscriber 노드 하나가 있다.

 

노드 생성은 기본적으로 rclpy의 Node 클래스를 상속하고 있는 클래스를 만들며 이루어진다.

__init__에서 노드의 create_publisher() 메서드에 1) 어떤 타입(형식)의 메세지를 보낼 것인지, 2) 어떤 이름의 토픽/서비스/액션을 발행할 것인지, 3) 어떤 QoS를 사용할 것인지를 인자로 보낸다.

publish는 publisher 속성의 .publish() 함수를 통해 만들고자 하는 타입에 메세지를 담아서 보낸다. 💌

class HelloworldPublisher(Node):
    
    def __init__(self):
        super().__init__('helloworld_pub')
        qos_profile = QoSProfile(depth=10)
        self.helloworld_publisher = self.create_publisher(String, 'helloworld', qos_profile)
        self.timer = self.create_timer(1, self.publish_helloworld_msg) # callback
        self.count = 0

    def publish_helloworld_msg(self):
        msg = String()
        msg.data = f'Hello World: {self.count}'
        self.helloworld_publisher.publish(msg)
        self.get_logger().info(f'Published msg: {msg.data}')
        self.count += 1

 

위의 예시에서는 타이머로 1초마다 count를 1씩 올리면서 메세지를 publish하고 있고, subscriber 노드를 통해서 이를 받아서 출력해주고 있다.

출력에는 .get_logger().info() 메서드를 사용하면 fstring으로 편하게 출력이 가능하다.

 

조금 더 나아가 Calculator 예제를 실습해보았는데, 

Argument, Operator, Checker, Calculator 총 4개의 노드가 서로 topic, service, action을 주고받는 형태를 만들었다.

 

이를 위해서는 Interface 정의를 직접해보는 것이 필요했는데, 결국 실제로 개발을 하면 반드시 생성하게 될 것 같다.

첫 예제에서는 자체 제공하는 std_msgs의 String()을 사용하였는데, 우리는 원하는 대로 직접 어떤 타입의 메세지를 보낼 것인지를 생성하여 불러올 수 있다.👱‍♀️

예를 들어, 내가 숫자 3개를 보내면, 합한 값 1개를 응답받는 서비스를 만들고자 한다면, int8 3개를 Request로 하고, int8 1개를 Response로 하는 .srv 파일을 만들어 불러오면 된다.

이를  AddThreeInts로 만들어 실습하였다.

 

비슷한 방법으로 반지름을 받으면 원의 넓이를 답변하도록 하는 서비스도 짤 수 있다.

이 경우에는 인터페이스를 int8 radius로 Request하면 float32 area로 Response 하도록 했다.

아래 사진에서 client를 run하는 아래 터미널에서 노드를 실행하면 3을 바로 인자로 넘겼고, 이게 서비스를 run하는 위 터미널에서 Received 3으로 찍히고 넓이를 Sending 하자, 다시 아래 터미널 client에서 Response에 area으로 넘어오는 것을 볼 수 있다.🤖

 

 

ROS2 기초 1차 교육 후기👀

- 너무너무 재밌다 ROS2! 파이썬이라 더 재밌다ㅎㅎㅎ 원하는대로 만들어보고 실행할 수 있는게 정말 매력적이다. 노드 단위로 분리하는 것이 기획 단계에서 어떻게 구성할지, 어떤 메세지 형식을 주고받게할지를 고민하게 해서 더 재밌다. 잘 만들어진 ROS 코드는 어떤 구성일지 궁금하다.🤗

- 토픽, 서비스, 액션 감 잡는게 처음에 정말 어려웠는데, 카이로스 2기 종로반의 우리 조 표미애님 도움을 정말 많이 받아서 그래도 잘 넘어올 수 있었다! 반 친구들 소중해.. (액션은 아직도 어렵다.. 생성법이 달라..)

- 헷갈리는 환경설정 상 이슈들이 소소하게 있는데, 개인적으로 느낀 팁(?)들 몇개를 적어보자면 :

- dependency 설정 필수 : 내 인터페이스가 안불러와지고 ModuleNotFound에러라면 packages.xml과 setup.py가 잘 들어가있는지 확인하고, colcon build와 source install/setup.py도 돌렸는지 확인해보시길..
- build는 ws에서, create pkg는 src에서 : 디렉토리를 오가다보면 헷갈리기 마련.. alias를 .bashrc에 설정해두면 편합니다! 저는 디렉토리를 cw, cs 등으로, colon build packages -> cbp 로. source install/setup.py -> sis 로 alias 해두었답니다.

 

 

🌱 카이로스 2기에서 배운 리눅스 강의가 궁금하다면?

https://bagjo2884.tistory.com/26

 

[카이로스2기] 시스템 프로그래밍(Linux & C++) 학습

딥러닝 수업을 완료하고, 카이로스 2기에서는 ROS 개발을 위한 준비 작업으로 시스템 프로그래밍 강의를 들었다.ROS 실행 환경은 window등 OS에서도 돌아가지만, OS 없이도 무료 플랫폼인 리눅스에서

bagjo2884.tistory.com

 

다음시간에는 더욱 성장한 ROS 수업, 특히 흥미로운 통신 포스팅으로 만나요!🐥