본문으로 바로가기

1. 이번 실습의 목적

ROS 2에서 노드를 개발하다 보면 단순히 코드가 실행되는지만 보는 것으로는 부족합니다.

노드가 어떤 토픽을 발행하는지, 어떤 노드와 연결되어 있는지, 내부 상태가 어떻게 변하는지 확인해야 합니다.

 

이번 글에서는 ROS 2에서 자주 사용하는 디버깅 도구인 다음 항목을 다룹니다.

1. 로그 출력
2. rqt_console
3. rqt_graph
4. rqt_plot
5. Action Server 로그 확인
 

 

실습 환경은 turtlesim, 사용자 정의 액션 서버, 그리고 rqt 도구를 기준으로 구성합니다.

 

 

 

2. rqt 관련 패키지 설치

 

먼저 rqt 관련 패키지를 설치합니다.

 

sudo apt install ros-humble-rqt*

 

 

 

 

아래와 같이 필요한 패키지만 설치할 수 있습니다.

 

sudo apt install ros-humble-rqt ros-humble-rqt-graph ros-humble-rqt-console ros-humble-rqt-plot

 

설치 후에는 ROS 2 환경을 다시 불러옵니다.

 

source /opt/ros/humble/setup.bash
source ~/ros2_study/install/setup.bash

 

 

 

 

3. rqt의 console에서 로그 확인

 

터미널을 여러 개 열고 각각 다음 명령을 실행합니다.

 

터미널 1: turtlesim 실행

 

ros2 run turtlesim turtlesim_node

 

 

 

터미널 2: rqt 실행

 

rqt

 

 

 

rqt를 실행하고 Plugins 메뉴에서 Logging의 Console을 선택합니다. 아래와 같은 logging console 창이 추력됩니다.

 

Plugins → Logging → Console

 

아래의 명령처럼 직접 실행도 가능합니다.

 

ros2 run rqt_console rqt_console

 

또는 환경에 따라 다음 명령도 사용할 수 있습니다.

 

rqt_console

 

 

 

 

터미널 3: turtle 이동 명령 발행

 

ros2 topic pub -r 1 /turtle1/cmd_vel geometry_msgs/msg/Twist "{linear: {x: 2.0, y: 0.0, z: 0.0}, angular: {x: 0.0, y: 0.0, z: 0.0}}"

 

 

 

 

이 명령은 /turtle1/cmd_vel 토픽으로 Twist 메시지를 1초에 한 번씩 발행합니다.
turtlesim_node는 이 토픽을 구독하고 turtle을 전진시킵니다.

 

 

 

4. turtlesim 경고 로그 확인

 

turtle을 계속 직진시키면 벽에 부딪힙니다.


이때 turtlesim은 다음과 같은 경고 로그를 출력합니다.

[WARN] [turtlesim]: Oh no! I hit the wall! (Clamping from [x=..., y=...])
 

 

이 로그는 turtle이 시뮬레이션 공간의 경계에 도달했기 때문에 좌표가 제한되었다는 의미입니다.

 

rqt_console에서는 이 메시지를 표 형태로 확인할 수 있습니다.

 

확인 가능한 정보는 다음과 같습니다.

Message
Severity
Node
Stamp
Location
 

예를 들어 다음과 같이 표시됩니다.

Message: Oh no! I hit the wall!
Severity: Warn
Node: turtlesim
 

 

이 방식은 터미널 로그보다 훨씬 보기 편합니다.

 

특히 여러 노드가 동시에 실행되는 로봇 시스템에서는 특정 노드의 로그만 필터링할 수 있어 유용합니다.

 

 

 

 

5. ROS 2 로그의 필요성

 

초기 실습에서는 print()를 사용해 값을 확인할 수 있습니다. 하지만 실제 로봇 시스템에서는 print()만으로는 부족합니다.

 

ROS 2에서는 노드별로 로그를 남기는 기능을 제공합니다.

 
self.get_logger().info('Dist turtle action server is started.')
 

 

위 코드는 현재 노드에서 Info 수준의 로그를 출력합니다.

 

로그 수준은 보통 다음과 같이 구분합니다.

로그 수준의미
Debug 개발 중 상세 정보 확인
Info 일반적인 실행 상태 확인
Warn 경고 상황
Error 오류 발생
Fatal 시스템 중단이 필요한 심각한 문제

 

실무에서는 print()보다 self.get_logger() 사용을 권장합니다.

 

이유는 로그 수준 필터링, 노드 이름 표시, 시간 정보 확인, GUI 도구 연동이 가능하기 때문입니다.

 

 

 

 

6. 사용자 정의 Action Server에 로그 추가하기

 

수정할 소스 코드 파일은 다음입니다.

my_first_package/my_first_package/dist_turtle_action_server.py
 

 

init() 함수에 아래와 같이 직접 로그 메세지를 출력하는 문장을 추가합니다.

 

self.get_logger().info('Dist turtle action server is started.')

 

 

이 로그는 액션 서버가 정상적으로 시작되었는지 확인하기 위한 메시지입니다. 이 메세지는 터미널에도 출력되고 로그 기록에도 남게 됩니다.

 

 

수정 후에 워크스페이스에서 다시 빌드하고 "sl" 명령으로 환경을 다시 적용합니다.

 

 

 

서버를 실행하면 아래와 같이 메세지가 출력됩니다.

 

 

 

 

7. 파라미터 선언과 초기 로그 출력

 

이전 실습에서 이미 소스 코드에는 두 개의 파라미터를 선언합니다.

 

self.declare_parameter('quantile_time', 0.75)
self.declare_parameter('almost_goal_time', 0.95)
 

 

의미는 다음과 같습니다.

 

                파라미터                                                                    의미

 

quantile_time 목표 거리의 특정 비율 지점
almost_goal_time 목표에 거의 도달했다고 판단하는 비율

 

초기값을 읽은 뒤 로그로 출력합니다.

 

(quatile_time, almosts_time) = self.get_parameters(
    ['quantile_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)

 

 

위의 소스에서 저절한 위치를 찾아 아래 3줄을 추가합니다.

 

 

워크스페이스에서 다시 빌드를 수행하고 환경을 적용한 다음 액션 서버를 실행합니다.

 

실행하면 액션 서버에서는 다음과 유사한 로그가 출력됩니다.

[INFO] [dist_turtle_action_server]: Dist turtle action server is started.
[INFO] [dist_turtle_action_server]: quantile_time is 0.75. and almost_goal_time is 0.95.

 

 

 

8. 파라미터 변경 콜백

 

ROS 2에서는 실행 중에도 파라미터 값을 변경할 수 있습니다.
이때 특정 함수가 호출되도록 콜백을 등록할 수 있습니다.

 
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 == 'quantile_time':
                self.quantile_time = param.value

            if param.name == 'almost_goal_time':
                self.almosts_time = param.value

        print(
            'quatile_time and almost_goal_time is',
            self.quantile_time,
            self.almosts_time
        )

        return SetParametersResult(successful=True)

 

 

블로그용으로는 print()보다 다음처럼 로그로 바꾸는 구성이 더 좋습니다.

 
self.get_logger().info(
    f"{param.name} is changed to {param.value}"
)
 

 

개선하면 다음과 같습니다.

 

def parameter_callback(self, params):
    for param in params:
        self.get_logger().info(
            f"Parameter changed: {param.name} = {param.value}"
        )

        if param.name == 'quantile_time':
            self.quantile_time = param.value

        elif param.name == 'almost_goal_time':
            self.almosts_time = param.value

    self.get_logger().info(
        f"Current parameters: quantile_time={self.quantile_time}, "
        f"almost_goal_time={self.almosts_time}"
    )

    return SetParametersResult(successful=True)

 

이렇게 하면 로그 관리가 더 깔끔해집니다.

 

 

 

9. Action Server 실행 콜백 수정

 

아래와 같이 남은 거리가 0.02보다 작은 값일 때 로그를 남기도록 execute_callback() 함수의 내용을 수정합니다.

 

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)

        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

 

 

이 코드의 동작 흐름은 다음과 같습니다.

1. 액션 목표값을 받습니다.
2. linear_x, angular_z 값을 Twist 메시지에 저장합니다.
3. turtle을 움직입니다.
4. 현재 위치와 이전 위치 차이로 이동 거리를 계산합니다.
5. 목표 거리까지 남은 거리를 feedback으로 보냅니다.
6. 특정 지점에 도달하면 로그를 출력합니다.
7. 목표에 가까워지면 액션을 성공 처리합니다.
8. 최종 위치와 이동 거리를 result로 반환합니다.
 

 

 

10. 로그 수정 내용 실행

 

워크스페이스를 다시 빌드하고 환경을 적용합니다.

 

 

먼저 rqt를 실행합니다.

 

rqt

 

rqt를 실행한 뒤 메뉴에서 선택할 수도 있습니다.

Plugins → Introspection → Node Graph

 

 

다음은 turtlesim_node를 실행합니다.

 

 

그리고 액션 서버를 실행합니다.

 

ros2 run my_first_package dist_turtle_action_server

 

다른 터미널에서 아래와 같이 액션 목표 전송 명령을 실행합니다.

 

os2 action send_goal /dist_turtle my_first_package_msgs/action/DistTurtle "{linear_x: 2., angular_z: 2., dist: 2.}"

 

 

이 명령은 /dist_turtle 액션 서버에 목표값을 보냅니다.

 

각 필드의 의미는 다음과 같습니다.

                    필드                                                   의미
linear_x 전진 속도
angular_z 회전 속도
dist 목표 이동 거리

 

실행 결과는 다음과 비슷하게 나옵니다.

Waiting for an action server to become available...
Sending goal:
  linear_x: 2.0
  angular_z: 2.0
  dist: 2.0

Goal accepted with ID: ...
Result:
  pos_x: ...
  pos_y: ...
  pos_theta: ...
  result_dist: ...

Goal finished with status: SUCCEEDED

 

 

 

 

 

 

ROS 2를 처음 배울 때는 print()가 편합니다.
하지만 노드가 많아지고 터미널이 여러 개가 되면 print()는 관리가 어렵습니다.

 

예를 들어 다음 코드는 단순 출력입니다.

 
print("server started")
 

 

반면 ROS 2 logger는 노드 이름과 로그 레벨을 포함합니다.

 
self.get_logger().info("server started")
 

 

출력은 다음처럼 표시됩니다.

[INFO] [dist_turtle_action_server]: server started
 

 

이 차이는 작아 보이지만, 실제 로봇 시스템에서는 매우 큽니다.

특히 다음 상황에서는 logger가 필수입니다.

여러 노드가 동시에 실행될 때
특정 노드의 로그만 보고 싶을 때
Warn/Error 로그만 필터링하고 싶을 때
rqt_console로 GUI 확인을 하고 싶을 때
rosbag 또는 시스템 로그와 함께 분석할 때

 

 

 

 

728x90
728x90