ROS 설치 및 기본 기능 구현

제투아·2023년 8월 11일

ROS setting

Ros install

# 1. Ubuntu 18.04 이미지 다운로드
docker pull ubuntu:bionic
# 2. Ubuntu 이미지를 이용한 컨테이너 생성
docker run -it --name ros ubuntu:bionic
# 3. ROS 설치
apt-get update
apt-get install lsb -y --fix-missing # fix-missing option 을 넣어줄 것

# 4. 공식 페이지에 있는 코드 사용 (sudo를 모두 뺄 것)
sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list'
apt install curl
curl -s https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | apt-key add -
apt update
apt install ros-melodic-desktop-full # 오래 걸리는 작업

# 5. 환경 셋업
echo "source /opt/ros/melodic/setup.bash" >> ~/.bashrc
source ~/.bashrc

# 6. 의존성 파일 설치 (python2)
apt install python-rosdep python-rosinstall python-rosinstall-generator python-wstool build-essential
rosdep update
rosdep init

# 7. 매크로 설정
echo "alias cm='cd ~/catkin_ws && catkin_make'" >> ~/.bashrc
echo "eb='vim ~/.bashrc'" >> ~/.bashrc
echo "sb='source ~/.bashrc'" >> ~/.bashrc

# 8. 설치 완료 확인
roscore

ROS 컨테이너를 이미지로 저장

# 컨테이너를 이미지로 변환
docker commit -a "commit name"  {CONTAINER IR} {IMAGE NAME:TAG}

docker image/container save

# image 저장 및 불러오기
docker save -o "file_name.tar" image_name
docker load -i tar file_name.tar

# container 저장 및 불러오기
docker export container_name > file_name.tar
docker import file_name.tar > - image_name

ROS 이미지 다운로드

  • 아래의 docker hub에서 이미지를 받아오면 ros 설치를 하지 않아도 됨
https://hub.docker.com/r/osrf/ros/tags?page=3

ROS tutorials

http://wiki.ros.org/ROS/Tutorials

Ros 관련 기초

  • ROS node: ROS에서 실행되는 하나의 실행 가능한 프로그램

  • package: ros를 구성하는 기본단위, ROS의 기본 프로그램은 패키지 단위로 개발되며 하나의 node 이거나 다른 패키지의 노드를 실행하기 한 프로그램으로 구성

  • catkin: ROS의 빌드시스템. CMakeList.txt라는 파일에 빌드 환경을 작성후 catkin_make 를 실행하면 flow 대로 생성이 됨

  • roscore: ROS 마스터를 구동하는 명령어. 각각의 패키지를 실행하기 전에 master를 실행해야 함.

  • rosrun: 하나의 node를 실행하는 명령어

  • roslaunch: roscore를 포함하여 여러 노드를 실행할 때 사용

  • package.xml: 패키지의 정보를 담은 파일로 패키지 이름, 저작자, 라인센스 등의 정보가 있는 파일

  • Topic: 단방향 송수신 방식으로 publish, subscribe 형태로 되어 있음

  • Service: 양방향 통신이나 일회성 메세지, 서비스의 요청과 응답이 완료되면 두 노드의 접속이 끊임

  • Action: 양방향이지만 통신을 계속 하는 방식

Basic setting

# Version check
printenv | grep ROS
# bsetup bash 실행
source /opt/ros/melodic/setup.bash

Catkin Package

# 기본 명령어: rospackage 의 위치 확인
rospack find [rosnam_name]
# 기본 명령어: pkg 경로로 이동
roscd [package_name]
# creating a catkin package (패키지 만들기)
# catkin_create_pkg <package_name> [depend1] [depend2] [depend3]
cd ~/catkin_ws/src
catkin_create_pkg **beginner_tutorials** std_msgs rospy roscpp

# building a catkin workspace and sourcing the setup file
# 생성된 package로 build 실행
cd ~/catkin_ws
catkin_make
# 환경 변수 추가
source ~/catkin_ws/devel/setup.bash

# package dependency 설정
rospack depends1 beginner_tutorials
# pkg dependency
roscd beginner_tutorials
# dependency가 패키지에 저장됨을 확인
cat pakcage.xml

rospack depends1 rospy

Building packages

source /opt/ros/melodic/setup.bash # for melodic for instance

# catkin_make : CMake file의 flow 대로 빌드해주는 명령어
catkin_make [make_targets]

catkin_make --source my_src
catkin_make install --source my_src

ROS topics

# 1번 창에서 실행
roscore
# 2번 창에서 실행 => turtlesim 
rosrun turtlesim turtlesim_node
# 3번 창에서 실행 => 거북이를 움직일 수 있게 됨
rosrun turtlesim turtle_teleop_key
# node와 topic간의 관계를 보여줌
$ rosrun rqt_graph rqt_graph

위 과정을 통해 tutlesim_node와 turtle_teleop_key node는 ros topic을 통해 communicating을 한다. tuetle_teleop_key는 key를 topic에 publishing하고, turtlesim 은 이를 subscribe하여 사용한다.

rostopic -h
rostopic echo [topic]
rostopic echo /turtle1/cmd_vel

ROS service

rosservice list
rosservice type /clear

rosparam list
rosparam get /turtlesim/background_g

ROS rqt_console and rosluanch

  • rqt_ronsole 은 debugging을 위해 사용
  • roslaunch 는 여러 node를 한번에 실행하기 위해 사용
rosrun rqt_console rqt_console
rospack depends1 beginner_tutorials

roscd beginner_tutorials
cat package.xml

rospack depends1 rospy

rosed

  • rosed는 rosbash의 일부이다. 패키지를 바로 입력할 수 있도록 명령어를 만들어주는 역할을 한다.
rosed roscpp <tab><tab>

ROS msg and srv

  • Creating a msg
roscd beginner_tutorials
# msg 폴더 만들기
mkdir msg
echo "int64 num" > msg/Num.msg

# package.xml 에 아래 두 줄 삽입
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>

# CMakeLists.txt 에 아래 두 줄 삽입
find_package(catkin REQUIRED COMPONENTS
   roscpp
   rospy
   std_msgs
   message_generation
)

ROS pub & sub

# 의존성 패키지 설치
carkin_create_pkg custom_msg_pkg std_msgs rospy roscpp
# 해당 패키지 폴더로 이동
cd /catkin_ws/src/custom_msg_pkg
# 하위 폴더에 아래의 두 *.msg 파일 생성 (스크립트 이용)
first_msg.msg
second_msg.msg
## first_msg.msg 의 내부
tims start_time
uint16 msg_seq
uint16 original_num

## second
time start_time
uint16 msg_seq
uint16 original_num
float32 square_num
float32 sqrt_num
  • 예제) start_node_script.py
#!/usr/bin/env python
#-*- coding:utf-8 -*-

import rospy
import random
from custom_msg_pkg.msg import first_msg

def main():
	rospy.init_node('start_node', anonymous=False)
	pub = rospy.Publisher('first_topic', first_msg, queue_size=10)
  rate = rospy.Rate(1) # 전송 속도 (1Hz)
	msg = first_msg()
	count = 1

	while not rospy.is_shutdown():
		msg.start_time = rospy.Time.now()
		msg.msg_seq = count
		msg.original_num = random.randrange(1,101)

		# 터미널에 출력
    rospy.loginfo("------")
    rospy.loginfo("Start Time(sec): %d", msg.start_time.secs)
    rospy.loginfo("Message Sequence: %d", msg.msg_seq)
    rospy.loginfo("Original Number: %d", msg.original_num)
            
    # 메시지를 퍼블리시
    pub.publish(msg)
    
    # 정해둔 주기(hz)만큼 일시중단
    rate.sleep()

if __name__ == '__main__':
    try:
        main()
    except rospy.ROSInterruptException:
        pass
#!/usr/bin/env python
#-*- coding:utf-8 -*-

import rospy				# ROS 라이브러리
import random               # 랜덤 수를 추출하기 위한 라이브러리
import math
from custom_msg_pkg.msg import first_msg, second_msg
    # 서브스크라이브하는 first_msg.msg 파일과, 퍼블리시하는 second_msg.msg 파일 불러오기

class Calc:
    def __init__(self):
        rospy.Subscriber("/first_topic", first_msg, self.first_topic_callback)
        self.pub = rospy.Publisher("/second_topic", second_msg, queue_size=10)

        self.start_time = rospy.Time.now()
        self.msg_seq = 0
        self.original_num = 0
        self.square_num = 0
        self.sqrt_num = 0
        
    # 퍼블리셔 노드로부터 토픽을 받아들이는 콜백 함수
    def first_topic_callback(self, data):
        self.start_time = data.start_time
        self.msg_seq = data.msg_seq
        self.original_num = data.original_num
        self.square_num = math.pow(self.original_num, 2)
        self.sqrt_num = math.sqrt(self.original_num)

        # 받은 내용(data)를 터미널에 출력
        rospy.loginfo("------")
        rospy.loginfo("Message Sequence: %d", self.msg_seq)
        rospy.loginfo("Original Number: %d", self.original_num)
        rospy.loginfo("Square Number: %d", self.square_num)
        rospy.loginfo("Square Root Number: %d", self.sqrt_num)

    def second_msg_publish(self):
        msg = second_msg()	# 메시지 변수 선언

        # 메시지 내용 담기
        ## start_time: start_node가 메시지를 생성해 publish 하는 시각
        ## msg_seq: 메시지 순서(번호)
        ## original_num: 계산의 대상이 될 수
        ## square_num: original_num의 제곱
        ## sqrt_num: original_num의 제곱근
        msg.start_time = self.start_time
        msg.msg_seq = self.msg_seq
        msg.original_num = self.original_num
        msg.square_num = self.square_num
        msg.sqrt_num = self.sqrt_num

        self.pub.publish(msg)
    
def main():
    # 노드 초기화.
    rospy.init_node('middle_node', anonymous=False)
    rate = rospy.Rate(1)

    calc = Calc()
    while not rospy.is_shutdown():
        calc.second_msg_publish()
        rate.sleep()

if __name__ == '__main__':
    try:
        main()
    except rospy.ROSInterruptException:
        pass
  • 예제) end_node_script.py
#!/usr/bin/env python
#-*- coding:utf-8 -*-

import rospy				# ROS 라이브러리
from custom_msg_pkg.msg import second_msg    # 서브스크라이브하는 second_msg.msg 파일 불러오기


# 퍼블리셔 노드로부터 토픽을 받아들이는 콜백 함수
def second_topic_callback(data):
    # 받은 내용(data)를 터미널에 출력
    rospy.loginfo("------")
    rospy.loginfo("Processing Time(nsec): %d", rospy.Time.now().nsecs - data.start_time.nsecs)
    rospy.loginfo("Message Sequence: %d", data.msg_seq)
    rospy.loginfo("Original | Square | Square Root: %d | %d | %d", data.original_num, data.square_num, data.sqrt_num)
    
def main():
    rospy.init_node('end_node', anonymous=False)
    rospy.Subscriber("second_topic", second_msg, second_topic_callback)

    rospy.spin()

if __name__ == '__main__':
    main()
    
profile
Zero to AGI

1개의 댓글

comment-user-thumbnail
2023년 8월 11일

유익한 자료 감사합니다.

답글 달기