지난번 처럼 rviz_launcher에 있는 launch 파일로 열었지만
이번에는 직접 rviz를 꾸며보자
roslaunch turtlebot_navigation_gazebo main.launch
roslaunch turtlebot_navigation_gazebo gmapping_demo.launch
rviz
빈 rviz 창에서 add를 눌러 LaserScan을 추가해준 후
topic에서 kobuki/laser/scan으로 설정해준다.
size > 0.05 (=5cm)
Global option > Fixed frame > map
add > RobotModel
add > map > topic > /map
roslaunch turtlebot_teleop keyboard_teleop.launch
로봇을 움직이며 mapping을 하면 된다.
view_mapping.launch에서 저장한 .rviz파일을 실행할 수 있음
teleop_keyboard로 mapping을 하고나면
map을 저장해야 한다.
.
.
(자료출처; programmers)
rviz는 topic을 시각화하는 tool이므로 nav_msgs/OccupancyGrid를 시각화한다.
오픈소스 기반 Map 관리 패키지 : map_server
2가지 노드를 제공 (map_saver & map_server)
map_saver 노드
- 현재 얻은 지도를 저장할 수 있다
(확장자 .pgm & .yaml 파일이 저장된다)
.
키보드로 mapping을 모두 한 이후에 아래 명령어로 mapping한 map을 저장해주자
-f 뒤에 적는 것이 파일명이 된다.
roscd turtlebot_navigation_gazebo/maps
rosrun map_server map_saver -f my_map
<my_map.yaml>
image : grid map pgm 파일이름 지정
resolution : map의 해상도를 pixel 당 m 단위로 지정 (e.g. 0.05m x 0.05m = 1 pixel)
origin : 전역 좌표계에서 지도 원점을 지정 - 2D (x, y, rotate)
negate : map 반전 여부 결정 (boolean), 빈칸 - 점유된 칸 반전 -> 장애물 map을 만들 때 유용
occupied_thresh : occupied 되었는지 결정하는 threshold, 이 값보다 크거나 같은 값은 점유된 것으로 간주
free_thresh : map에서 cell이 사용가능한지 결정하는 threshold, 이 값보다 작거나 같은 값은 모두 비어있는 것으로 간주
<my_map.pgm>
pixel의 점유 / 빈칸 여부를 색으로 표시
ros_msgs로 map의 정보가 전달될 때는 0~100까지의 정수를 이용하는데
0 = 빈칸
100 = 장애물 (occupied)
-1 = unknown
아래 명령어로 지도의 정보를 또ㅍ다른 노드 (e.g move_base) 에게 제공할 수 있다.
(파일이 저장된 경로에서는 경로 지정없이 파일 이름만으로 가능)
rosrun map_server map_server map_file.yaml
rqt
rqt를 열어보면 /map 과 /map_metadata가 topic으로 publishing되는 것을 확인할 수 있다.
rviz를 켜고 map을 add 한 후 rosrun으로 map을 켜주면 rviz에 map이 나타난다.
code ~/sim_ws/src/programmers_turtlebot/turtlebot_navigation_gazebo/launch/map_server.launch
<map_server.launch>
<node name="map_server" pkg="map_server" type="map_server" args="$(arg map_file)" />
```
roscore
rviz
roslaunch turtlebot_navigation_gazebo map_server.launch
.
mkdir ~/sim_ws/src/programmers_turtlebot/turtlebot_navigation_gazebo/script
code ~/sim_ws/src/programmers_turtlebot/turtlebot_navigation_gazebo/script/call_map_service.py
<call_map_service.py>
#! /usr/bin/env python3
import rospy
from nav_msgs.srv import GetMap, GetMapRequest
import sys
rospy.init_node('service_client') # Initialise a ROS node with the name service_client
rospy.wait_for_service('static_map') # Wait for the service /static_map to be running
rospy.loginfo("init complete")
get_map_service = rospy.ServiceProxy('static_map', GetMap) # Create the connection to the service
get_map = GetMapRequest() # Create an object of type GetMapRequest
result = get_map_service(get_map) # Call the service
print(result) # Print the result given by the service called
chmod +x call_map_service.py
roscore
roslaunch turtlebot_navigation_gazebo map_server.launch
rosrun turtlebot_navigation_gazebo call_map_service.py
(rviz)
terminal에서 map이 출력된 것을 확인할 수 있다.
static map - 호출했을 때의 상태를 유지 (환경이 변화해도 반영되지 않음, 2D map이므로 높이 정보 없음 - 드론에는 적용 불가)
.
.
Odometry : 원점으로부터 로봇이 얼마나 이동했는가를 나타내는 값
.
- 한 프레임에 표현된 데이터를 다른 프레임으로 변환하는 방법
- Laser 정보를 이용해 map을 구성하기 위해서는 Laser의 위치와 방향을 로봇에게 알려줘야함 = transform
- Position, Orientation 정보를 포함
예를 들어 lidar가 30cm 앞에 장애물이 있다고 인지했다면 로봇의 중심으로부터의 거리는 또 다를 것이다.
그래서 lidar frame에서 robot frame으로 변환이 필요하다.
.
.
tf 시각화하기
roslaunch turtlebot_navigation_gazebo main.launch
rqt
rqt에서
plugins > visualization > tf tree
변환관계를 확인할 수 있음!
방법1
rosrun tf static_transform_publisher x y z yaw pitch roll frame_id child_frame_id period_in_ms
방법2
<launch>
<node pkg="tf" type="static_transform_publisher name="node_name"
arg="x y z yaw pitch roll frame_id child_frame_id period_in_ms">
</node>
</launch>
code ~/sim_ws/src/programmers_turtlebot/turtlebot_navigation_gazebo/launch/pub_static.launch
<pub_static.launch>
<launch>
<node pkg="tf" type="static_transform_publisher" name="static_tf_node"
args="0 0 2 0 0 0 base_link button 30">
</node>
</launch>
roslaunch turtlebot_navigation_gazebo main.launch
rviz
roslaunch turtlebot_navigation_gazebo pub_static.launch
rqt
button 노드가 추가되었다.
rviz 상에서 RobotModel과 TF 를 추가하여 만든 tf를 확인할 수 있다.
.
.
roslaunch, yaml 파일에서 별도로 설정할 수 있음
base_frame (default: "base_link")
- 모바일 베이스의 기준 프레임 - 모바일 베이스, 로봇의 기준점을 의미
map_frame (default: "map")
- 지도의 기준이 될 프레임의 이름 - map을 기준으로 baseframe이 어디있는지 알기 위해
odom_frame (default: "odom")
- Odometry 시스템에 연결된 프레임의 이름 - map -> odom -> base
map_update_interval(default: 5.0)
- 지도를 업데이트할 때까지 대기할 시간을 설정
<rosparam file=$(find my_mapping_launcher)/params/gmapping_params.yaml" commad = "load" />
name_of_parameter: value_of_parameter
map_update_interval을 15로 바꾸고 map update 주기 확인
maxUrange를 2로 바꾸고 mapping 영역 확인
xmin, ymin를 -100d으로 xmax, ymax를 100으로 설정하고 초기 map이 어떻게 보이는지 확인
모든 파라미터 변겅은 yaml파일에서 하도록 함 (gmapping_params.yaml)
.
code ~/srm_ws/src/programmers_turtlebot/turtlebot_navigation_gazebo/param/gmapping_params.yaml
programmers_turtlebot/turtlebot_navigation/launch/includes/gmapping/gmapping.launch.xml 파일을 참고해서 yaml 파일의 양식에 맞게 작성해주면 된다.
<gmapping_params.yaml>
base_frame: base_footprint
odom_frame: odom
map_update_interval: 5.0
maxUrange: 6.0
maxRange: 8.0
minimumScore: 200
linearUpdate: 0.5
angularUpdate: 0.436
temporalUpdate: -1.0
resampleThreshold: 0.5
particles: 80
xmin: -1.0
ymin: -1.0
xmax: 1.0
ymax: 1.0
delta: 0.05
llsamplerange: 0.01
llsamplestep: 0.01
lasamplerange: 0.005
lasamplestep: 0.005
이제 이 파라미터들을 읽어올 launch 파일을 만들어보자
code ~/sim_ws/src/programmers_turtlebot/turtlebot_navigation_gazebo/launch/my_gmapping.launch
<my_gmapping.launch>
<launch>
<arg name="scan_topic" default="kobuki/laser/scan" />
<node pkg="gmapping" type="slam_gmapping" name="slam_gmapping" output="screen">
<rosparam file="$(find turtlebot_navigation_gazebo)/param/gmapping_params.yaml" command="load" />
<remap from="scan" to="$(arg scan_topic)" />
</node>
</launch>
일단 launch 파일을 잘 설정했는지 확인하기 위해 실행해본 후
.yaml 파일의 파라미터를 바꿔보면 된다.
(yaml 파일에서 파라미터를 바꿀 때마다 launch 다시 해야함)
roslaunch turtlebot_navigation_gazebo main.launch
roslaunch turtlebot_navigation_gazebo my_gmapping.launch
roslaunch turtlebot_rviz_launchers my_gmapping.launch
.
.
robag record -O mylaserdata /laser_topic /tf_topic
2.bag file을 재생하고 map 생성하기
rosbag play bag_file_name
키보드로 로봇을 움직이며 그동안의 bag file 을 저장
roslaunch turtlebot_navigation_gazebo main.launch
roslaunch turtlebot_teleop keyboard_teleop.launch
rosbag record -O mylaser /kobuki/laser/scan /tf /tf_static
모두 종료 후 저장된 bag 파일로 mapping
roslaunch turtlebot_navigation_gazebo my_gmapping.launch
roslaunch turtlebot_rviz_launchers view_mapping.launch
rosbag info mylaser.bag (잘 저장되었나 확인)
rosbag play mylaser.bag
rviz상에서 map이 점점 그려지는 것을 확인할 수 있다.
.
.
나는 corridor_main.launch 라는 이름으로 파일을 만들어주었다.
나머지는 main.launch 파일과 모두 동일하고 world 파일만 corridor로 바꾸어주면 된다.
<corridor_main.launch>
<launch>
<arg name="world_file" default="$(find turtlebot_navigation_gazebo)/worlds/corridor.world"/>
<!-- <arg name="world_file" default="$(find summit_xl_gazebo)/worlds/test_zone.sdf"/> -->
<arg name="base" value="$(optenv TURTLEBOT_BASE kobuki)"/> <!-- create, roomba -->
<!-- arg name="battery" value="$(optenv TURTLEBOT_BATTERY /proc/acpi/battery/BAT0)"/ --> <!-- /proc/acpi/battery/BAT0 -->
<arg name="stacks" value="$(optenv TURTLEBOT_STACKS hexagons)"/> <!-- circles, hexagons -->
<arg name="3d_sensor" value="$(optenv TURTLEBOT_3D_SENSOR kinect)"/> <!-- kinect, asus_xtion_pro -->
<arg name="paused" default="false"/>
<arg name="use_sim_time" default="true"/>
<arg name="gui" default="true"/>
<arg name="headless" default="false"/>
<arg name="debug" default="false"/>
<include file="$(find gazebo_ros)/launch/empty_world.launch">
<arg name="world_name" value="$(arg world_file)" />
<arg name="paused" value="$(arg paused)"/>
<arg name="use_sim_time" value="$(arg use_sim_time)"/>
<arg name="gui" value="$(arg gui)"/>
<arg name="headless" value="$(arg headless)"/>
<arg name="debug" value="$(arg debug)"/>
</include>
<include file="$(find turtlebot_navigation_gazebo)/launch/includes/$(arg base).launch.xml">
<arg name="base" value="$(arg base)"/>
<arg name="stacks" value="$(arg stacks)"/>
<arg name="3d_sensor" value="$(arg 3d_sensor)"/>
</include>
<node pkg="robot_state_publisher" type="robot_state_publisher" name="robot_state_publisher">
<param name="publish_frequency" type="double" value="30.0" />
</node>
<!-- Fake laser -->
<!-- node pkg="nodelet" type="nodelet" name="laserscan_nodelet_manager" args="manager"/>
<node pkg="nodelet" type="nodelet" name="depthimage_to_laserscan"
args="load depthimage_to_laserscan/DepthImageToLaserScanNodelet laserscan_nodelet_manager">
<param name="scan_height" value="10"/>
<param name="output_frame_id" value="/camera_depth_frame"/>
<param name="range_min" value="0.45"/>
<remap from="image" to="/camera/depth/image_raw"/>
<remap from="scan" to="/scan"/>
</node -->
</launch>
.
.
roslaunch turtlebot_navigation_gazebo corridor_main.launch
roslaunch turtlebot_navigation_gazebo gmapping_demo.launch
roslaunch turtlebot_rviz_launchers view_mapping.launch
roslaunch turtlebot_teleop keyboard_teleop.launch
위 launch 파일들을 모두 실행하고 키보드로 world를 돌아다니며 mapping을 해주면 된다.
rosrun map_server map_saver -f corridor_map_yj
이 명령어를 실행하고 나면 .pgm파일과 .yaml 파일로 map이 저장된다.
<corridor_map_yj.pgm>
<corridor_map_yj.yaml>
image: corridor_map_yj.pgm
resolution: 0.050000
origin: [-12.200000, -13.800000, 0.000000]
negate: 0
occupied_thresh: 0.65
free_thresh: 0.196