ROS(Robot Operating System)에 대해 연재합니다.
Contents
(New Update Version. Original 2018-12-28)
이번 포스팅에서는 ROS 설치부터, ROS 통신에서 기본적인 publisher and subscriber, server and client를 테스트 하는 것 까지 step by step으로 진행합니다.
- ROS 설치
- Workspace 생성
- ROS Package
- ROS Node
- ROS Topic
- ROS Service
- ROS msg, srv
- Publisher and Subscriber
- Server and Client
ROS 설치
우분투 16.04, ROS Kinetic
$ sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list'
$ sudo apt-key adv --keyserver hkp://ha.pool.sks-keyservers.net:80 --recv-key 421C365BD9FF1F717815A3895523BAEEB01FA116
$ sudo apt-get update
$ sudo apt-get install ros-kinetic-desktop-full
rosdep 초기화
rosdep을 사용하면 컴파일하려는 소스에 대한 시스템 종속성을 쉽게 설치할 수 있습니다.
$ sudo rosdep init
$ rosdep update
환경 변수 설정
새 shell이 시작될 때마다 ROS 환경변수가 bash 세션에 자동으로 추가되도록 설정합니다.
bashrc 파일을 수정해줍니다.
$ gedit ~/.bashrc
bashrc 파일 맨 하단에
아래의 text를 모두 적습니다.
alias eb='nano ~/.bashrc'
alias sb='source ~/.bashrc'
alias cw='cd ~/catkin_ws'
alias cs='cd ~/catkin_ws/src'
alias cm='cd ~/catkin_ws && catkin_make'
source /opt/ros/kinetic/setup.bash
source ~/catkin_ws/devel/setup.bash
export ROS_MASTER_URI=http://localhost:11311
export ROS_HOSTNAME=localthost
#exprot ROS_MASTER_UTR=https://192.168.1.100:11311
#export ROS_HOSTNAME=192.168.1.100
파일을 수정하고 저장한 후 마무리 해줍니다.
$ source ~/.bashrc
빌드 패키지에 대한 종속성 설치
ROS 명령어를 사용하는데 필요한 의존성 패키지 설치를 설치합니다.
설치하면 이후에 사용할 roscore, rospack, roscd 등의 명령어를 사용할 수 있습니다.
$ sudo apt install python-rosinstall python-rosinstall-generator python-wstool build-essential
ROS 패키지를 찾거나 사용하는데 문제가 있을 때
ROS에서 사용되는 환경변수인 ROS_ROOT, ROS_PACAKGE_PATH 등이 제대로 설정되어 있는지 확인하는 명령어 입니다.
$ export | grep ROS
Workspace 생성
catkin workspace 생성
$ mkdir -p ~/catkin_ws/src
$ cd ~/catkin_ws/src
$ catkin_init_workspace
처음 작업공간을 만들었을 땐는 catkin_ws/src/ 폴더 안에 CMakeLists.txt 파일 하나가 존재합니다.
catkin_make 에 대한 추가 내용은 다음의 포스트를 참조해주세요.
[post] https://subinlab.github.io/2018/12/28/ROS-catkin-make.html
CMakeLists.txt 파일만 존재하지만 catkin_make 명령으로 이 작업공간을 "빌드"하는 것이 가능합니다.
cakin_make에 대한 추가 설명은 다음 링크를 참고해주세요.
$ cd ~/catkin_ws/
$ catkin_make
ROS Package 생성/빌드
ROS Package 구성
workspace_folder/ -- 작업공간
src/ -- 소스 폴더
CMakeLists.txt -- catkin이 제공하는 '최상위'의 CMake 파일,
package_1/
CMakeLists.txt -- package_1에 대한 CMakeLists.txt 파일
package.xml -- package_1에 대한 매니패스트
...
package_n/
CMakeLists.txt -- package_n에 대한 CMakeLists.txt 파일
package.xml -- package_n에 대한 매니패스트
std_msgs, roscpp, rospy에 대한 의존성을 가지는 ‘beginner_tutorials’ 패키지를 만들기 위해 catkin_creaet_pkg 스크립트를 사용합니다.
아래를 수행하면 package.xml과 CMakeLists.txt이 들어있는 beginner_tutorials 폴더가 만들어집니다.
$ cd ~/catkin_ws/src
$ catkin_create_pkg beginner_tutorials std_msgs rospy roscpp
# catkin_create_pkg <package_name> [depend1] [depend2] [depend3]
catkin_make
$ cd ~/catkin_ws
$ catkin_make
rospack 명령어를 사용하여 의존성을 가지는 패키지들을 확인할 수 있습니다.
beginner_tutorials > rospy > roscpp
$ rospack depends1 beginner_tutorials
$ roscd beginner_tutorials
$ cat package.xml
ROS 노드 이해하기
ROS graph의 개념을 소개하고 roscore, rosnode, rosrun 을 다루고 있습니다.
그래프 컨셉 소개
- Nodes
- Messages
- Topics
- Master
- rosout
- roscore
rospack 명령어를 사용하여 의존성을 가지는 패키지들을 확인할 수 있다.
beginner_tutorials > rospy > roscpp
$ roscore
rosnode list 명령어는 실행중인 노드를 보여줍니다.
$ rosnode list
/rosout
패키지 안의 노드를 직접 실행할 수 있게 해줍니다. (package path를 몰라도 됩니다.)
$ rosrun turtlesim turtlesim_node
# rosrun [package_name] [node_name]
$ rosnode list
/ rosout
/ tutlesim
$ rosnode ping my_turtle
ROS Topic 이해하기
$ roscore
$ rosrun tutlesim turtlesim_node
위는 잘 따라왔다면 원래 되어있는 상태
아래의 명령어부터 시작하기
$ rosrun turtlesim turtle_teleop_key
rqt_graph를 사용할 수 있도록 아래의 명령어로 패키지를 설치해줍니다. rqt_graph는 rqt 패키지의 일부입니다.
$ sudo apt-get install ros-kinetic-rqt
$ sudo apt-get install ros-kinetic-rqt-common-plugins
$ rosrun rqt_graph rqt_graph
처음 위의 명령어를 입력하면 아래와 같은 그래프를 볼 수 있습니다.
파란색, 초록색 : nodes
빨간색 : topic
키보드로 거북이를 움직이게 합니다.
teleop_turtle 노드가 키보드 입력 정보를 topic에 담아 publishing을 하면, turtlesim 노드는 키보드 입력 정보를 받기 위해서 같은 topic을 subscribe 합니다.
teleop_turtle 노드와 turtlesim 노드는 하나의 topic에 대해서 통신하고 있습니다. topic의 이름은 ‘turtle1/command_velocity’ 입니다.
$ rostopic -h
rostopic bw display bandwidth used by topic
rostopic echo 화면에 메세지들을 보여준다.
rostopic hz display publishing rate of topic
rostopic list 현재 활성중인 topic의 정보를 보여준다.
rostopic pub publish data to topic
rostopic type print topic type
rostopic 사용하기 (Using rostopic)
-
rostopic echo
키보드 자판을 입력하지 않았을 때는 topic에 data가 publish되지 않았기 때문입니다. 키보드 자판을 입력한 후 에는 topic에 publish된 데이터가 보일 것입니다.
$ rostopic echo /turtle1/cmd_vel
# rostopic echo [topic]
rostopic echo node : /rostopic_14245_1355179857944
teleop_turtle 노드가 tutle1/command_vel이라는 topic으로 publish하면, rostopic echo 노드가 turtle1/command_vel topic으로부터 subsribe 한다.
- rostopic list
$ rostopic list -h
$ rostopic list -v
ROS Messages
topic에 대해 통신하는 일은, 노드 사이에 ROS 메세지를 보내는 것을 통해 수행됩니다. publisher와 subscriber가 통신을 하기 위해서, publisher와 subscriber는 같은 타입의 메세지를 보내고 받아야 합니다.
즉, topic 타입은, topic에 publish 된 메세지 타입에 의해 정의가 됩니다. topic에 보내진 메세지 타입은 rostopic type을 통해 결정이 될 수 있습니다.
-
rostopic type
-
rostopic pub
rostopic pub은 topic에 데이터를 publish 한다.
$ rostopic pub -1 /turtle1/cmd_vel geometry_msgs/Twist -- '[2.0, 0.0, 0.0]' '[0.0,0.0,1.8]'
# rostopic pub [topic] [msg_type] [args]
$ rostopic pub /turtle1/cmd_vel geometry_msgs/Twist -r 1 -- '[2.0, 0.0, .0.0]' '[0.0, 0.0, -1.8]'
새로 생긴 rostopic_3537_1545998424077은 rostopic pub 노드이다.
- rostopic hz
- rqt_plot
ROS Services and Parameters 이해하기
서비스는 노드 간 통신하는 또 다른 방법입니다. 서비스는 노드가 request를 보내고 response를 받게 합니다.
rosservice 이용하기 (Using rosservice)
- rosservice list
- rosservice type
$ rosservice list
$ rosservice type /clear
# rosservice type [service]
$ rosservice call /clear
# rosservicee [service] [args]
$ rosservice type /spawn | rossrv show
$ rosservice cal /spawn 2 2 0.2 ""
rosparam 이용하기 (Using rosparam)
- rosparam list
- rosparam set
- rosparam get
- rosparma dump
- rosparam load
$ rosparam list
$ rosparam set /background_r 150
# rosparam set [param_name]
# rosparam get [param_name]
$ rosservice call /clear
ROS msg 파일과 srv 파일
msg : msg 파일은 ROS 메세지 파일의 field를 설명하는 간단한 텍스트 파일이다.
srv : srv 파일은 서비스를 설명한다. request와 response라는 두개의 파트로 구성되어 있다.
msg 생성하기
$ roscd beginner_tutorials
$ mkdir msg
$ echo "int64 num" > msg/Num.msg
위의 명령어를 입력하고 나면 해당 디렉토리에 Num.msg 파일이 생깁니다.
Num.msg 파일을 확인해보면 위에서 입력했던 int64 num이 적혀져있는 것을 볼 수 있습니다.
string first_name
string last_name
uint8 age
uint32 score
를 Num.msg에 추가로 입력해줍니다.
현재 작업하고 있는 beginner_tutorials 패키지의 xml 파일을 편집하여 아래의 텍스트를 추가해줍니다.
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
마찬가지로 beginner_tutorials 패키지의 CMakeLists.txt 파일을 편집하여 주석을 지우거나 텍스트를 추가해줍니다.
CMakeLists.txt 는 디폴드로 작성이 되어있기 때문에, 새로 작성하기 보다는 기존에 있는 파일을 수정하여줍니다. 아래에 해당하는 부분만 수정하여줍니다.
find_package(catkin REQUIRED COMPONENS
roscpp
rospy
std_msgs
message_generation
)
...
catkin_package(
...
CATKIN_DEPENDS message_runtime...
...
)
...
add_message_files(
FILES
Num.msg
)
...
generate_messages(
DEPENDENCIES
std_msgs
)
$ rosmsg show beginner_tutorials/Num
$ rosmsg show Num
# rosmsg show [message type]
Num.msg파일의 메세지 타입을 볼 수 있습니다.
srv 생성하기
$ roscd beginner_tutorials
$ mkdir srv
$ roscp rospy_tutorials AddTwoInts.srv srv/AddTwoInts.srv
# roscp [package_name] [file_to_copy_path] [copy_path]
msg 파일을 생성할 때 beginner_tutorials 패키지의 xml 파일을 수정해주었으므로, 다시 확인만 해줍니다.
beginner_tutorials 파일의 CMakeLists.txt 파일을 수정하여, msg 파일을 생성할 때 수정하였던 부분은 그대로 두고, srv 파일을 생성하기 위해 나머지 부분을 수정하여줍니다.
이 부분도 확인만해주고
find_package(catkin REQUIRED COMPONENS
roscpp
rospy
std_msgs
message_generation
)
아래의 부분을 찾아서 수정하여 줍니다.
...
add_service_files(
FILES
AddTwoInts.srv
)
...
$ rossrv show beginner_tutorials/AddTwoInts
# rosmsg show <service type>
srv 파일의 타입을 확인하였습니다!
beginner_tutorials 패키지의 파일들을 수정하였으니, 빌드를 해봅내다.
$ roscd beginner_tutorials
$ cd ../..
$ catkin_make install
$ cd -
아래의 경로에 C++ 헤더파일이 생성된 것을 볼 수 있습니다.
~/catkin_ws/devel/include/beginner_tutorials/
파이썬 스크립트 파일은 아래의 경로에서 확인할 수 있습니다.
~/catkin_ws/devel/lib/python2.7/dist-packages/beginner_tutorials/msg
Publisher와 Subscriber 작성/확인하기
Publisher 작성하기
$ roscd beginner_tutorials
$ mkdir scripts
$ cd scripts
$ wget https://raw.github.com/ros/ros_tutorials/kinetic-devel/rospy_tutorials/001-talker_listner/talker.py
$ chmot +x talker.py # make file executable
talker.py
#!/usr/bin/env python
## Simple talker demo that published std_msgs/Strings messages
## to the 'chatter' topic
import rospy
from std_msgs.msg import String
def talker():
pub = rospy.Publisher('chatter', String, queue_size=10)
rospy.init_node('talker', anonymous=True)
rate = rospy.Rate(10) # 10hz
while not rospy.is_shutdown():
hello_str = "hello world %s" % rospy.get_time()
rospy.loginfo(hello_str)
pub.publish(hello_str)
rate.sleep()
if __name__ == '__main__':
try:
talker()
except rospy.ROSInterruptException:
pass
Subscriber 작성하기
$ roscd beginner_tutorials/scripts
$ wget https://raw.github.com/ros/ros_tutorials/kinetic-devel/rospy_tutorials/001-talker_listner/listenter.py
$ chmot +x listener.py # make file executable
listener.py
#!/usr/bin/env python
## Simple talker demo that listens to std_msgs/Strings published
## to the 'chatter' topic
import rospy
from std_msgs.msg import String
def callback(data):
rospy.loginfo(rospy.get_caller_id() + 'I heard %s', data.data)
def listener():
# In ROS, nodes are uniquely named. If two nodes with the same
# name are launched, the previous one is kicked off. The
# anonymous=True flag means that rospy will choose a unique
# name for our 'listener' node so that multiple listeners can
# run simultaneously.
rospy.init_node('listener', anonymous=True)
rospy.Subscriber('chatter', String, callback)
# spin() simply keeps python from exiting until this node is stopped
rospy.spin()
if __name__ == '__main__':
listener()
빌드합시다!
$ cd ~/catkin_ws
$ catkin_make
Publisher 실행하기
$ roscore
roscore가 실행되어있다면
rosrun을 통해 실행하여 봅시다!
$ rosrun beginner_tutorials talker.py
$ rosrun beginner_tutorials listener.py
Service 와 Client 작성하기
Service 작성하기
$ roscd beginner_tutorials
$ mkdir scripts
$ cd scripts
$ wget /////
$ chmot +x add_two_ints_server.py # make file executable
add_two_ints_server.py
#!/usr/bin/env python
from beginner_tutorials.srv import *
import rospy
def handle_add_two_ints(req):
print("Returning [%s + %s = %s]"%(req.a, req.b, (req.a + req.b)))
return AddTwoIntsResponse(req.a + req.b)
def add_two_ints_server():
rospy.init_node('add_two_ints_server')
s = rospy.Service('add_two_ints', AddTwoInts, handle_add_two_ints)
print("Ready to add two ints.")
rospy.spin()
if __name__ == "__main__":
add_two_ints_server()
Client 작성하기
$ roscd beginner_tutorials
$ mkdir scripts
$ cd scripts
$ wget /////
$ chmot +x add_two_ints_client.py # make file executable
add_two_ints_client.py
#!/usr/bin/env python
import sys
import rospy
from beginner_tutorials.srv import *
def add_two_ints_client(x, y):
rospy.wait_for_service('add_two_ints')
try:
add_two_ints = rospy.ServiceProxy('add_two_ints', AddTwoInts)
resp1 = add_two_ints(x, y)
return resp1.sum
except rospy.ServiceException, e:
print("Service call failed: %s"%e)
def usage():
return "%s [x y]"%sys.argv[0]
if __name__ == "__main__":
if len(sys.argv) == 3:
x = int(sys.argv[1])
y = int(sys.argv[2])
else:
print(usage())
sys.exit(1)
print("Requesting %s+%s"%(x, y))
print("%s + %s = %s"%(x, y, add_two_ints_client(x,y)))
HTML
Server와 Client 확인하기
$ rosrun beginner_tutorials add_two_int_server.py
$ rosrun beginner_tutorials add_two_int_client.py
덧셈을 하고 프로세스가 종료됩니다.
server에서는 cilent을 실행한 후
returning이 된 것을 볼 수 있습니다.