만약 ROS 시뮬레이션 관련 패키지들을 다운받지 못했다면 다음 사이트로 넘어가서 클론하도록 하자.
https://emanual.robotis.com/docs/en/platform/turtlebot3/autonomous_driving/#getting-started
Wanderbot
red_light_green_light.py
- 3초간 멈춰 있다가 3초 전진을 반복
1. 패키지 생성
cd ~/catkin_ws/src
catkin_create_pkg wanderbot rospy geometry_msgs sensor_msgs
mkdir scripts
2. python 파일 생성, 권한부여 및 코드 작성
chmod +x red_light_green_light.py
아래의 코드를 py 파일에 작성한다.
#!/usr/bin/env python
import rospy
from geometry_msgs.msg import Twist
cmd_vel_pub = rospy.Publisher('cmd_vel', Twist, queue_size=1)
rospy.init_node('red_light_green_light')
red_light_twist = Twist()
green_light_twist = Twist()
green_light_twist.linear.x = 0.5
driving_forward = False
light_change_time = rospy.Time.now()
rate = rospy.Rate(10)
while not rospy.is_shutdown():
if driving_forward:
cmd_vel_pub.publish(green_light_twist)
else:
cmd_vel_pub.publish(red_light_twist)
if light_change_time < rospy.Time.now():
driving_forward = not driving_forward
light_change_time = rospy.Time.now() + rospy.Duration(3)
rate.sleep()
- 토픽의 이름은 'cmd_vel'이고 타입은 Twist이다. Twist는 linear와 angular의 벡터 데이터이다. 큐사이즈는 1로 큐사이즈를 넘어가는 메시지는 버린다.
- red_light_twist는 매개변수를 변경하지 않은 로봇을 정지하기 위한 객체이다.
- green_light_twist는 정면 방향(direction -x)으로의 선속도를 0.5m/s로 변경하여 로봇을 움직이게 하기 위한 객체이다.
- 처음엔 로봇이 멈춰있는 상황이어야 하므로 driving_forward = False로 설정하였고, light_change_time은 현재의 시간을 담고 있어 로봇이 3초간 움직이고 멈추는 과정에 관여한다.
- while 문에 의해 ctrl+c가 입력되지 않는 이상 프로그램은 계속 구동된다.
- 처음에 driving_forward는 False이므로 else문에 red_light_twist를 실행한다. 매개변수를 변경하지 않았으므로 로봇은 정지해 있는다.
- rospy.Time.now()는 계속 증가하는 상태이므로 처음에는 light_change_time보다 크다. 이때 driving_forward가 True로 바뀌고 light_change_time에 현재 시간과 3초가 더해진다. 즉, 3초간 while 문의 첫번째 if문을 실행하여 직진하고, 3초 뒤 다시 rospy.Time.now()가 커지면 driving_forward가 False가 되어 로봇이 멈춘다.
- 따로 Subscriber.py는 만들지 않는다. Gazebo를 실행하여 로봇이 3초간 움직이는지 확인하자.
가제보의 오토레이스 맵에서 red_light_green_light.py를 실행하면 빨간색 방향인 x방향으로 로봇이 3초간 이동하다가 멈췄다가를 반복한다.
range_ahead.py
- Laser 센서의 정보를 읽어서 로봇 정면에 있는 장애물까지의 거리를 표시한다.
chmod +x range_ahead.py
red_light_green_light.py와 동일한 위치에 파일을 생성하고 권한을 부여한다.
py에는 다음의 코드를 입력한다.
#!/usr/bin/env python
import rospy
from sensor_msgs.msg import LaserScan
def scan_callback(msg):
range_ahead = msg.ranges[len(msg.ranges)/2]
print("range ahead: %0.1f" % range_ahead)
rospy.init_node('range_ahead')
scan_sub = rospy.Subscriber('scan', LaserScan, scan_callback)
rospy.spin()
- LaserScan 데이터를 사용할 것이기 때문에 해당 라이브러리를 임포트한다.
- scan_callback 함수는 라이다에서 추출되는 데이터가 몇인지 나타내는 함수이다. 레이저 포인트가 물체에 닿았다가 돌아온 거리이기 때문에 2로 나누었다.
- 노드의 이름은 'range_ahead'이다.
- Lidar가 Publisher가 되고, 우리는 Subscriber로 'scan'이라는 이름의 LaserScan 토픽을 받는다. 그리고 받음과 동시에 scan_callback 함수를 실행하여 터미널에 표시한다.
- rospy.spin()은 위의 while not rospy.is_shutdown: rate.sleep()과 같은 코드이다. 남는시간은 쉰다는 의미이다.
실행하면 로봇 기준으로 뒤쪽으로 라이다가 쏴지고 그 거리를 측정한다.
wander.py
- 직선 이동과 회전을 동시에 넣은 코드이다.
- 3초동안 회전하면서 0.8 범위 내에 장애물이 없으면 x = 1, z-degree = 1로 회전하면서 이동한다.
동일하게 py 파일을 만들고 권한을 부여한 뒤 아래 코드를 입력한다.
#!/usr/bin/env python
# BEGIN ALL
import rospy
from geometry_msgs.msg import Twist
from sensor_msgs.msg import LaserScan
def scan_callback(msg):
global g_range_ahead
g_range_ahead = min(msg.ranges)
g_range_ahead = 1 # anything to start
scan_sub = rospy.Subscriber('scan', LaserScan, scan_callback)
cmd_vel_pub = rospy.Publisher('cmd_vel', Twist, queue_size=1)
rospy.init_node('wander')
state_change_time = rospy.Time.now()
driving_forward = True
rate = rospy.Rate(10)
while not rospy.is_shutdown():
if driving_forward:
# BEGIN FORWARD
if (g_range_ahead < 0.8 or rospy.Time.now() > state_change_time):
driving_forward = False
state_change_time = rospy.Time.now() + rospy.Duration(5)
# END FORWARD
else: # we're not driving_forward
# BEGIN TURNING
if rospy.Time.now() > state_change_time:
driving_forward = True # we're done spinning, time to go forwards!
state_change_time = rospy.Time.now() + rospy.Duration(30)
# END TURNING
twist = Twist()
if driving_forward:
twist.linear.x = 1
else:
twist.angular.z = 1
cmd_vel_pub.publish(twist)
rate.sleep()
# END ALL
코드의 해석은 이전과 비교하여 크게 어려운 점은 없으므로 직접 해석하길 바란다.