[ROS2] ROS2의 단위, 좌표, 시간

HY K·2024년 9월 18일
0

ROS2

목록 보기
7/18

이번 포스팅에서는 ROS2의 표준 단위(standard unit), 좌표 표현, 시간에 대해서 공부를 해보겠다.

참고한 링크는 다음과 같다.
https://cafe.naver.com/openrt/24272
https://cafe.naver.com/openrt/24274
https://cafe.naver.com/openrt/24953


ROS2의 표준 단위

💡 ROS2 표준 단위
ROS 커뮤니티에서는 ROS 프로그래밍에 사용하는 표준 단위로, SI 유도 단위(SI Derived Unit)을 표준 단위로 결정하였다. SI 단위는 총 7개이고, 유도단위는 20개가 존한다. 이와 관련해 정리하면 다음과 같다.

- 길이(lenght) : 미터(m)
- 질량(mass) : 킬로그램(kg)
- 시간(time) : 초(second, s)
- 전류(current) : 암페어(ampere, A)
- 평면각(angle) : 라디안(radian, rad)
- 주파수(frequency) : 헤르츠(Hz)
- 힘(force) : 뉴턴(N)
- 일률(power) : 와트(W)
- 전압(voltage) : 볼트(V)
- 온도(temperature) : 섭씨(celcius)
- 자기장(magnetism) : 테슬라(T)

ROS2의 좌표 표현

단위와 마찬가지로, ROS2에서는 좌표 표현 역시 통일할 필요가 있다. 아니면, 최소한 서로 다른 좌표계를 제대로 정의하고 이에 대한 변환 표현을 도입할 필요가 있다.

예를 들어서, 컴퓨터 비전의 경우 카메라 좌표계를 사용한다. 카메라 좌표계는 z forward, x right, y down을 기본 좌표계로 사용하고 있다. 반면, 로봇은 기본적으로 x forward, y left, z up을 기본으로 사용하고 있다. 따라서 각 좌표계를 제대로 숙지하고, 이들에 대한 변환 행렬을 올바르게 구하는 것은 매우 중요하다.

ROS2는 이러한 문제로 인해서 좌표를 통일하였다. ROS2의 가장 기본적인 3축 좌표는 다음과 같다. 일반적으로 오른손 법칙을 통해서 사용한다.


출처: https://cafe.naver.com/openrt/24274

Gazebo 등 ROS 시뮬레이터에서는 이러한 방향 표현을 효과적으로 표현하기 위해서 3원색을 사용한다.

  • x축 : 빨간색
  • y축 : 초록색
  • z축 : 파란색

그 외 좌표계들은 다음과 같다.

ENU 좌표계

ENU는 East North Up의 줄임말로, 드론 / 실외 자율주행 로봇 등에 주로 사용되는 좌표계이다.

Optical 좌표계

컴퓨터 비전에서 사용하는 카메라 좌표계로 앞서 얘기하였듯 z forward, x right, y down 좌표를 사용한다. 이 좌표계를 사용하는 경우 보통 _optical 접미사를 통해서 구분지어준다.

Ned 좌표계

실외 시스템의 경우 ENU 대신 NED(North East Down) 좌표계를 사용하기도 한다. 이때는 _ned 접미사를 통해서 구현한다.

회전 표현

💡 ROS2의 회전 표현
1. 쿼터니언 : 간결한 표현 방식(x,y,z,w)
2. 회전 변환 행렬
3. 고정축 roll, pitch, yaw : 각속도에 사용
4. 오일러 각도 : ROS 커뮤니티에서는 별로 안좋아하는 분위기다.


ROS2의 시간 표현

왜 시간이 중요한가?
센서들로부터 얻는 데이터들이 시간에 따른 변화량이 중요할 때가 많기 때문이다. 따라서 센서들 간의 시간 동기화가 매우 중요하다. 특히, 해당 센서 데이터들이 퍼블리시 된 정확한 시간이 중요하다고 할 수 있다.

ROS2에서는 퍼블리시 되는 토픽에 주요 데이터 뿐만 아니라 해당 토픽이 퍼블리시 되는 시간을 함게 포함시킬 수 있다. 대표적으로 다음의 메시지가 있다.

std_msgs/msg/header -> stamp 및 frame id 포함

또한, ROS2에서는 기준이 되는 시계를 노드가 생성될 때 정해지도록 하였다. 예를 들어 다음 명령어를 입력해보자.

$ ros2 run time_rclcpp_example time_example --ros-args -p use_sim_time:=False

이 명령어를 입력하면, time_example 노드가 가진 시계의 시간을 확인할 수 있다. System clock을 ROS2에서는 기본 시계로 사용하며, 국제 표준시인 협정 세계시(UTC) 를 사용하고 있다. rclcpp에서는 std::chrono 라이브러리를, rclpy에서는 time 모듈을 캡슐화 해서 사용한다.

시간 추상화

ROS2에서는 기본적으로 UTC에 기반한 시간을 제공하지만, 필요에 따라서 시점을 다르게 바꿀 수 있는 시계도 제공한다. 이를 시간을 추상화(Abstraction) 해서 제공한다고 말한다. ROS2는 추상화 한 시간을 총 3가지 제공한다.

이는 RCL에 포함된 time.h 헤더파일을 살펴보면 확인 가능하다.

<!--rcl/time.h-->
enum rcl_time_source_type_t
{
	RCL_TIME_SOURCE_UNINITIALIZED = 0,
    RCL_ROS_TIME,
    RCL_SYSTEM_TIME,
    RCL_STEADY_TIME
};

💡 System Time
위에서 다룬 system clock을 사용하는 시간을 의미한다. 기본적으로 단조증가(monotonic) 하지만, 타임 서버 동기화를 통해 거꾸로 가는 경우도 있다.

$ sudo ntpdate ntp.ubuntu.com

이러한 명령어처럼 특정 서버와의 시간 동기화를 통해 기존 서버와는 다른 시간으로 맞출 수도 있다.

💡 ROS Time
시뮬레이션 용도로 많이 사용한다. use_sim_time 파라미터를 통해서 사용하며, 이를 True로 설정하면 /clock 토픽을 sub하기 전까지 시간을 계속 0으로 초기화 한다.

💡 Steady Time
Hardware Timeouts를 사용한 시간으로, 무조건 단조증가한다.

Time API

ROS2에서는 시간과 관련된 API를 제공한다.

  • time API
  • duration API
  • rate API

예제 코드를 보면 도움이 될 것이다.
먼저 rclcpp 예제 코드를 살펴보자.

#include <memory>
#include <utility>

#include "rclcpp/rclcpp.hpp"
#include "rclcpp/time_source.hpp"
#include "std_msgs/msg/header.hpp"

int main(int argc, char *argv[]){
	rclcpp::init(argc, argv); # 노드 초기화
    
    auto node = rclcpp::Node::make_shared("time_example_node");
    # 노드 선언
    auto time_publisher = node->create_publisher<std_msgs::msg::Header>("time",10);
    std_msgs::msg::Header  msg;
    # 퍼블리셔 및 필요한 메시지(인터페이스) 선언
    
    rclcpp::WallRate loop_rate(1.0);
    # 토픽 발행 주기 1Hz로 설정
    rclcpp::Duration duration(1,0);
    
    while(rclcpp::ok()){
    	static rclcpp::Time past = node->now();
        
        rclcpp::Time now = node->now();
        RCLCPP_INFO(node->get_logger(), "sec %lf nsec %ld", now.seconds(), now.nanoseconds());
        
        if((now-past).nanoseconds()*1e-9 > 5){
        	RCLCPP_INFO(node->get_logger(), "Over 5 seconds!");
            past = node->now();
        }
        msg.stamp = now + duration;
        time_publisher->publish(msg);
        
        rclcpp::spin_some(node);
        loop_rate.sleep();
    }
    rclcpp::shutdown();
}

다음으로는 rclpy에서서 time을 다루는 예제 코드를 살펴보자.

import rclpy
from rclpy.duration import Duration
from std_msgs.msg import Header

def main(args=None):
	rclpy.init(args=args)
    
    node = rclpy.create_node("time_example_node")
    time_publisher = node.create_publisher(Header, "time", 10)
    msg = Header()
    
    rate = node.create_rate(1.0)
    duration = Duration(seconds=1, nanoseconds=0)
    past = node.get_clock().now()
    
    try:
    	while rclpy.ok():
        	now = node.get_clock().now()
            seconds, nanoseconds = now.seconds_nanoseconds()
            node.get_logger().info("sec {0} nsec {1}".format(seconds, nanoseconds)
            
            if((now-past).nanoseconds * 1e-9) > 5 :
            	node.get_logger().info("Over 5 seconds")
                past = node.get_clock().now()
                
            msg.stamp = (now+duration).to_msg()
            time_publisher.publish(msg)
            
            rclpy.spin_once(node)
            rate.sleep()
            
    except KeboardInterrupt:
    	node.get_logger().info("Keyboard Interrupt SIGINT")
    finally:
    	node.destroy_node()
        rclpy.shutdown()

if __name__ == "__main__":
	main()

코드를 보면 알겠지만, Time 클래스는 시간을 다룰 수 있는 오퍼레이터를 제공하고, 그 결과를 second 혹은 nanosecond 단위로 반환해준다.

  • second : double 형으로 반환
  • nanosecond : unsigned int64 형으로 반환. 더 정확함

그리고 ROS2에서는 now() 함수를 통해서 노드 시간을 확인 가능하다.

rclcpp::Time now = node->now();

또한 ROS2에서는 Duration 클래스를 통해 기간(3시간 전 등등) 단위의 시간을 다룰 수 있게 하며, 그 결과를 seconds 혹은 nanoseconds 단위로 반환한다. Duration은 Time과 연산이 가능해서 직관적으로 계산이 가능하다. 다음 예시 코드는 실제 시간보다 1초 느린 값을 계산하는 코드이다.

rclcpp::Duration duration(1,0);
msg.stamp = now + duration;
time_publisher->publish(msg);

그리고 ROS2에서는 Rate 클래스도 제공하여, 반복문에서 특정 주기를 유지시켜주는 API를 제공한다. 앞선 예시 코드에서 Hz 단위로 주기를 설정한 후 sleep 함수를 통해 주기에 맞춰 실행되도록 설정한 것을 볼 수 있다. 여기서 중요한 것은 Rate는 System Clock을, WallRate는 Steady Clock을 사용한다는 것이다. 하지만 ROS2에서는 Timer API를 별개로 제공하고 있기 때문에, 이를 사용하는 것이 나을 수도 있다.

profile
로봇, 드론, SLAM, 제어 공학 초보

0개의 댓글