터틀심 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

실행결과

image

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을 받아 그 안에 담긴 문자열을 꺼내 화면에 출력함