[2023-03-28] 1. 터틀심 8자 주행 및 ROS 예제코드 분석
터틀심 8자 주행
- pub8.py 파일을
cp pub.py pub8.py
로 복사 및 생성한 후 8자 주행 코드로 수정
#!/usr/bin/env python
import rospy
from geometry_msgs.msg import Twist
rospy.init_node('my_node', anonymous=True)
pub = rospy.Publisher('/turtle1/cmd_vel', Twist, queue_size=10)
msg = Twist()
rate = rospy.Rate(1)
while not rospy.is_shutdown():
for i in range(0,4):
msg.linear.x = 3.0
msg.linear.y = 0.0
msg.linear.z = 0.0
msg.angular.x = 0.0
msg.angular.y = 0.0
msg.angular.z = 3.0 # 시간차를 두고 번갈아가며 각속도를 역회전 시킴
pub.publish(msg)
rate.sleep()
for j in range(0,4):
msg.linear.x = 3.0
msg.linear.y = 0.0
msg.linear.z = 0.0
msg.angular.x = 0.0
msg.angular.y = 0.0
msg.angular.z = -3.0 # 시간차를 두고 번갈아가며 각속도를 역회전 시킴
pub.publish(msg)
rate.sleep()
while not rospy.is_shutdown():
pub.publish(msg)
rate.sleep()
-
작성 후 아래와 같은 순서로 작업 실행
-
터미널 #1에서
$ roscore
-
터미널 #2에서
$ rosrun turtlesim turtlesim_node
-
터미널 #3에서
$ rosrun my_pkg1 pub8.py
실행결과
ROS 예제 코드 분석
teacher.py
#!/usr/bin/env python
import rospy
from std_msgs.msg import String
rospy.init_node('teacher')
pub = rospy.Publisher('my_topic', String)
rate = rospy.Rate(2)
while not rospy.is_shutdown():
pub.publish('call me please')
rate.sleep()
(1) #!/usr/bin/env python
-
파일의 첫 줄에 #! 로 시작되는 라인을 셔뱅(Shebang) 라인이라고 함
-
해당 파일의 실행에 어떤 인터프리터를 사용할지 지정함
-
PATH 환경변수에서 가장 우선 되는 인터프리터를 찾아 해당 스크립트 파일을 실행함
-
파이썬에서 #로 시작되는 라인은 코멘트로 간주되므로 소스코드에 미치는 영향 x
-
터미널에서
$ python teacher.py
명령으로 실행시킬 수 있지만 Shebang 라인이 들어간 경우에는$ ./teacher.py
명령으로 직접 실행 시킬 수도 있음 -
파이썬 버전을 구분해서 지정할 때도 사용함
e.g. #!/usr/bin/env python2.6
e.g. #!/usr/bin/env python3
(2) import rospy
-
rospy라는 라이브러리를 import해서 사용하겠음
-
import 키워드는 모듈, 패키지, 파이썬 표준 라이브러리 등을 가져옴
- 파이썬 표준 라이브러리는 파이썬에서 기본적으로 설치된 모듈과 패키지를 묶은 것
-
rospy는 ROS의 파이썬 클라이언트 라이브러리
-
rospy를 이용하여 ROS Topics, Services, Parameter의 interface를 사용할 수 있음
-
ROS 프로그래밍을 할 때 필수적인 라이브러리
-
rospy는 실행 속도 보다는 구현의 편의성을 더 중요하게 생각하였기 때문에 빠르게 prototype을 만들 수 있음
-
(3) from std_msgs.msg import String
-
std_msgs.msg라는 모듈에서 String 관련 부분만 가져와 사용하겠음
-
from import는 모듈의 일부를 가져올 수 있는 키워드
-
from 뒤에 모듈 이름을 지정하고 import 뒤에 가져올 변수, 함수, 클래스를 입력함
-
import 뒤에 여러가지를 넣어도 됨
-
as를 이용하여 별명을 붙일 수 있음
e.g. import numpy as np : np라는 이름으로 numpy 모듈을 사용
(4) rospy.init_node(‘teacher’)
-
해당 노드를 초기화하고 노드의 이름을 ‘teacher’로 함
-
init_node는 함수를 사용하여 생성된 노드는 다른 노드와 통신하면서 topic을 주고 받음
-
ROS 시스템 상에서는 노드들이 topic을 주고 받기 위해서는 노드에 고유의 이름을 할당해야 함
-
노드의 이름에는 ‘/’ 와 같은 namespaces를 포함할 수 없음
-
(5) pub = rospy.Publisher(‘my_topic’, String)
-
‘my_topic’이라는 이름의 토픽을 발행하고 토픽에 담는 메시지의 데이터 타입은 String으로 정함
-
여기서 String은 위에서 선언한
from std_msgs.msg import String
에서의 String 임 -
나중에 String 타입의 메시지를 담아서 ‘my_topic’이라는 이름의 토픽을 발행하려면 아래와 같이 코딩하면 됨
e.g. pub.publish(‘call me please’)
(6) rate = rospy.Rate(2)
-
1초에 2번 loop를 반복할 수 있도록 rate라는 객체를 만드는 코드
-
1초 안에 2번 반복하므로 0.5초에 한번씩 루프를 돌아야 함
-
0.5초는 작업시간 + 휴식시간이 합쳐진 시간으로 0.5초 전에 작업이 끝나도 휴식을 하여 0.5초를 채움
(7) while not rospy.is_shutdown():
-
rospy.is_shutdown()이 True가 될 때까지 loop 안에 있는 실행문을 반복함
-
rospy.is_shutdown() 함수는 rospy 내부의 shutdown_flag를 검사함
-
간단하게 ROS 시스템이 shutdown 되었는지의 여부를 검사하는 함수
(8) pub.publish(‘call me please’)
-
pub은 ‘my_topic’이라는 이름의 토픽을 발행하기 위해 만든 Publisher 인스턴스임
-
publish는 토픽에 데이터를 담아서 발행하는 기능을 수행함
-
publish의 2가지 Exception
-
ROSException : rospy 노드가 initalization 되지 않았을 때 생기는 에러로 init_node 함수로 이름을 할당해야 함
-
ROSSerializationException : message의 type error(타입이 다른 이유로)로 인해 serialize 할 수 없을 때 일어남
-
(9) rate.sleep()
- rate.sleep()는 while 루프 안에서 호출됨
- rate.sleep 코드로 인해 휴식 시간을 가질 수 있어 while 루프가 0.5초마다 반복함
student.py
#!/usr/bin/env python
import rospy
from std_msgs.msg import String
def callback(msg):
print msg.data
rospy.init_node('student')
sub = rospy.Subscriber('my_topic', String, callback)
rospy.spin()
(1) def callback(msg):
- ‘callback’ 이라는 이름의 함수를 정의함
(2) print msg.data
- msg.data를 화면에 출력함
- rospy는 공식적으로 python2 버전을 사용하는데 python2에서는 print문을 사용할 때 괄호를 사용 x
- print 작업시 object들을 string으로 내부적으로 바꿔주므로 변수를 넣어도 string으로 변환되어 출력이 가능함
(3) rospy.init_node(‘student’)
- 해당 노드를 초기화하고 노드의 이름을 ‘student’로 함
(4) sub = rospy.Subscriber(‘my_topic’, String, callback)
-
이번에 만든 노드는 토픽을 받는 구독자(Subsriber)임을 선언함
- 받고자 하는 토픽의 이름은 ‘my_topic’
- 그 토픽 안에 담긴 데이터는 String 타입
- 토픽이 도착할 때 마다 ‘callback’ 함수를 실행시킬 것을 ROS 시스템에 요청함
(5) rospy.spin()
- ROS 노드가 shutdown될 때 까지 Block 하는 함수
- shutdown signal을 받을 때 까지 무한루프를 돔
- 무한루프에서 topic을 받거나 time triggering 같은 이벤트가 발생하면 callback 함수가 호출되고 그렇지 않으면 sleep 상태가 됨
- 사용자의 노드가 callback 이외에 어떤 일도 하지 않는다면, spin() 함수를 사용해야 함
- rospy.sleep()은 특정한 시간이 주어진다는 점에서 차이가 있음
상호동작
teacher 노드가 ‘my_topic’라는 이름의 topic에 “call me please” 문자열을 담아 보내면,
student 노드에서 해당 topic을 받아 그 안에 담긴 문자열을 꺼내 화면에 출력함