토픽 결과물: Gazebo에 Kobuki가 4대 나타나게 됩니다.
이번 토픽은 아래 그림과 같이 Gazebo에 kobuki를 여러 대 올리는 방법을 설명합니다. 뭔가 멀티로 할 때 유용하겠죠? 요즘 유행하는 Deep Reinforcement Learning에도 유용하게 사용됩니다.
이전 토픽의 kobuki_hokuyo 패키지에 추가하여 작업 합니다. 이번 토픽에서는 소스 파일을 생성하지 않고 urdf 파일과 launch 파일만을 이용하려고 합니다.
kobuki 색깔 추가
kobuki_description 패키지가 없다면 설치합니다.
sudo apt-get install ros-kinetic-kobuki-description
색깔별 mesh 파일 생성
kobuki_description 패키지의 meshes 디렉터리 밑에 main_body.dae라는 파일이 있습니다. 이 파일을 색깔별로 복사하여 생성합니다. 여기서는 검정, 파랑, 초록, 하양 4가지 파일을 만듭니다. 파일 이름은 main_body_black.dae, main_body_blue.dae, main_body_green.dae, main_body_white.dae로 하기로 합니다. main_body_black.dae 파일을 에디터로 열어보면 아래와 같은 코드를 찾아갑니다.
<technique sid="common">
<blinn>
<emission>
<!-- change color here -->
<color>0 0 0 1</color>
</emission>
...
위 코드에 주석을 달아놓았는데 주석 밑의 태그에 값을 변경하면 main_body의 색깔이 변경됩니다. 태그의 값은 R G B Alpha 값으로 <color>0 0 0 1</color>
은 경우 검정색을 나타내고 있습니다. 이것을 파랑으로 하려면 <color>0 0 1 1</color>
로 바꿔주면 됩니다. 나머지 파일들도 색깔에 맞게 바꿔서 저장합니다.
색깔별 urdf 파일 생성
이전 토픽의 kobuki.urdf.xacro 파일을 색깔별로 복사하여 생성합니다. 즉, kobuki_black.urdf.xacro, kobuki_blue.urdf.xacro, kobuki_green.urdf.xacro, kobuki_white.urdf.xacro와 같이 생성합니다. kobuki_black.urdf.xacro 파일을 열어서 먼저 아래와 같이 패키지가 설정되어 있는지 확인합니다.
<robot name="kobuki" xmlns:xacro="http://ros.org/wiki/xacro">
<xacro:include filename="$(find kobuki_hokuyo)/urdf/common_properties.urdf.xacro"/>
<xacro:include filename="$(find kobuki_hokuyo)/urdf/kobuki_gazebo.urdf.xacro"/>
만약 find 패키지 부분이 위와 같지 않다면 위와 같이 변경시켜준니다. 그 다음 파일에서 아래와 같은 부분을 찾습니다.
<link name="base_link">
<visual>
<geometry>
<!-- new mesh -->
<mesh filename="package://kobuki_hokuyo/urdf/meshes/main_body_black.dae" />
</geometry>
위 파일에서 라고 주석을 달아놓은 곳이 변경할 곳입니다. 앞에서 생성한 mesh 파일을 이곳에 적어줍니다. 위의 경우는 검정색이므로 mesh 파일이 main_body_black.dae입니다. 나머지 urdf.xacro 파일들도 색깔별로 맞는 mesh 파일 이름으로 바꿔줍니다.
그 다음으로는 hokuyo 센서가 추가된 kobuki_hokuyo.urdf.xacro 파일도 색깔별로 복사하여 생성합니다. 파일 이름은 kobuki_hokuyo_black.urdf.xacro, kobuki_hokuyo_blue.urdf.xacro, kobuki_hokuyo_green.urdf.xacro, kobuki_hokuyo_white.urdf.xacro로 합니다. kobuki_hokoyo_black.urdf.xacro파일을 열면 아래와 같습니다.
<?xml version="1.0"?>
<robot name="kobuki_hokuyo"
xmlns:sensor="http://playerstage.sourceforge.net/gazebo/xmlschema/#sensor"
xmlns:controller="http://playerstage.sourceforge.net/gazebo/xmlschema/#controller"
xmlns:interface="http://playerstage.sourceforge.net/gazebo/xmlschema/#interface"
xmlns:xacro="http://ros.org/wiki/xacro">
<!-- Defines the kobuki component tag. -->
<xacro:include filename="$(find kobuki_hokuyo)/urdf/kobuki_black.urdf.xacro" />
<!-- include hokuyo model and set its pose -->
<xacro:include filename="$(find kobuki_hokuyo)/urdf/hokuyo.urdf.xacro"/>
<xacro:sensor_hokuyo name="laser" parent="base_link">
<origin xyz="0 0 0.12" rpy="0 0 0"/>
</xacro:sensor_hokuyo>
<kobuki/>
</robot>
위 코드를 보면 <xacro:include filename="$(find kobuki_hokuyo)/urdf/kobuki_black.urdf.xacro" />
부분에 위에서 생성한 kobuki_black.urdf.xacro 파일이 사용되고 있는 것을 알 수 있습니다. 다른 파일들도 색깔별로 변경하여 저장합니다.
이렇게 하면 색깔별 urdf 파일 준비가 완료됩니다. 이제 이 파일들을 gazebo에서 불러들이는 launch 파일들을 생성합니다.
Single robot launch
앞에서 생성한 것들은 동일한 kobuki 로봇인데 main_body 색깔만 다른 것입니다. 따라서 아래와 같이 launch 파일 템플릿을 사용합니다. 단, 로봇의 이름, 초기 위치, 색깔 정보를 argument로 받게 합니다. robot.launch.xml이란 파일을 만들고 아래와 같이 채워넣습니다.
<!--
Spawns Kobuki inside a Gazebo simulation
-->
<launch>
<arg name="robot_name" default="mobile_base"/>
<arg name="init_pose" default="-x 0 -y 0 -z 0"/>
<arg name="color" default="black"/>
<param name="robot_description"
command="$(find xacro)/xacro.py '$(find kobuki_hokuyo)/urdf/kobuki_hokuyo_$(arg color).urdf.xacro'"/>
<node pkg="gazebo_ros" type="spawn_model" name="spawn_$(arg robot_name)"
args="$(arg init_pose) -unpause -urdf -param robot_description -model $(arg robot_name)" respawn="false">
</node>
<node pkg="robot_state_publisher" type="robot_state_publisher" name="robot_state_publisher">
<param name="publish_frequency" type="double" value="30.0" />
</node>
<node pkg="nodelet" type="nodelet" name="$(arg robot_name)_nodelet_manager" args="manager"/>
</launch>
argument가 어떻게 사용되고 있는지 주의깊게 살펴봅니다. <param name="robot_description"
태그에서 위에서 생성한 hokuyo센서가 부착된 색깔별 kobuki urdf 모델을 불러들이고 있습니다. 그리고 <node pkg="gazebo_ros" ..>
에서 로봇의 초기 위치와 robot_name이 설정되는 것을 확인합니다.
Multiple robots launch
위에서 생성한 robot.launch.xml파일을 로봇 수 만큼 불러들이도록 아래와 같이 multi-robots.launch.xml 이름으로 파일을 생성하여 내용을 추가합니다.
<launch>
<!-- BEGIN ROBOT 0. index starts from zero to coinside with the array index in the code-->
<group ns="robot_0">
<param name="tf_prefix" value="robot_0" />
<include file="$(find kobuki_hokuyo)/launch/includes/robot.launch.xml" >
<arg name="init_pose" value="-x 0.5 -y 0.5 -z 0" />
<arg name="robot_name" value="robot_0" />
<arg name="color" value="black" />
</include>
</group>
<!-- BEGIN ROBOT 1-->
<group ns="robot_1">
<param name="tf_prefix" value="robot_1" />
<include file="$(find kobuki_hokuyo)/launch/includes/robot.launch.xml" >
<arg name="init_pose" value="-x 2.5 -y 0.5 -z 0" />
<arg name="robot_name" value="robot_1" />
<arg name="color" value="blue" />
</include>
</group>
<!-- BEGIN ROBOT 2-->
<group ns="robot_2">
<param name="tf_prefix" value="robot_2" />
<include file="$(find kobuki_hokuyo)/launch/includes/robot.launch.xml" >
<arg name="init_pose" value="-x 2.5 -y 2.5 -z 0" />
<arg name="robot_name" value="robot_2" />
<arg name="color" value="green" />
</include>
</group>
<!-- BEGIN ROBOT 3-->
<group ns="robot_3">
<param name="tf_prefix" value="robot_3" />
<include file="$(find kobuki_hokuyo)/launch/includes/robot.launch.xml" >
<arg name="init_pose" value="-x 0.5 -y 2.5 -z 0" />
<arg name="robot_name" value="robot_3" />
<arg name="color" value="white" />
</include>
</group>
</launch>
위 코드에서 4대의 로봇이 생성됩니다. 각각의 로봇을 구별하기 위해 <group ns="robot_0">
를 사용했습니다. 그리고 편의를 위해 group 네임스페이스와 로봇이름을 동일하게 했습니다. 또한 robot index를 0부터 하여 code에서 robot 배열 index와 맞췄습니다. 또한 robot_name에 언더바를 넣어 robot_name 파싱할 때 편리하게 했습니다. <param name="tf_prefix" value="robot_0" />
태그로 인해서 각 로봇과 관련한 topics은 접두에 "robot_0"과 같이 robot_name이 붙어서 publish 됩니다.
gazebo launch
드디어 모든 준비가 끝났습다. 이제 gazebo의 world와 multiple kobuki 로봇들을 불러들이는 launch 파일을 multiple_kobuki.launch 파일이란 이름으로 생성하고 아래와 같이 내용을 입력합니다.
<!-- Launches Kobuki Gazebo simulation in an empty world -->
<launch>
<!-- start Gazebo with an empty world -->
<include file="$(find gazebo_ros)/launch/empty_world.launch">
<arg name="use_sim_time" value="true"/>
<arg name="debug" value="false"/>
<arg name="world_name" value="$(find kobuki_hokuyo)/worlds/simple.world"/>
</include>
<!-- include multiple robots -->
<include file="$(find kobuki_hokuyo)/launch/includes/multi-robots.launch.xml"/>
</launch>
위 코드를 보면 위에서 생성한 <include file="$(find kobuki_hokuyo)/launch/includes/multi-robots.launch.xml"/>
파일을 include하고 있습니다. 자 이제 아래와 같이 launch 해보죠.
$ roslaunch kobuki_hokuyo multiple_kobuki.launch
간혹 launch 시에 segment fault 에러가 발생하기도 하는데, 다시 실행하면 됩니다. 실행 후 아래와 같이 topic list를 출력해 보면, 각 로봇별 topic이 구별되는 것을 알 수 있습니다. 따라서 소스코드에서는 robot_name과 함께 topic을 수신하여 처리하면 됩니다. 예를 들어 robot_1의 odometry는 /robot_1/odom topic을 수신하면 됩니다.
$ rostopic list
/clock
/gazebo/link_states
/gazebo/model_states
/gazebo/parameter_descriptions
/gazebo/parameter_updates
/gazebo/set_link_state
/gazebo/set_model_state
/robot_0/joint_states
/robot_0/mobile_base/commands/motor_power
/robot_0/mobile_base/commands/reset_odometry
/robot_0/mobile_base/commands/velocity
/robot_0/mobile_base/events/bumper
/robot_0/mobile_base/events/cliff
/robot_0/mobile_base/sensors/imu_data
/robot_0/odom
/robot_1/joint_states
/robot_1/mobile_base/commands/motor_power
/robot_1/mobile_base/commands/reset_odometry
/robot_1/mobile_base/commands/velocity
/robot_1/mobile_base/events/bumper
/robot_1/mobile_base/events/cliff
/robot_1/mobile_base/sensors/imu_data
/robot_1/odom
/robot_2/joint_states
/robot_2/mobile_base/commands/motor_power
/robot_2/mobile_base/commands/reset_odometry
/robot_2/mobile_base/commands/velocity
/robot_2/mobile_base/events/bumper
/robot_2/mobile_base/events/cliff
/robot_2/mobile_base/sensors/imu_data
/robot_2/odom
/robot_3/joint_states
/robot_3/mobile_base/commands/motor_power
/robot_3/mobile_base/commands/reset_odometry
/robot_3/mobile_base/commands/velocity
/robot_3/mobile_base/events/bumper
/robot_3/mobile_base/events/cliff
/robot_3/mobile_base/sensors/imu_data
/robot_3/odom
/rosout
/rosout_agg
/scan
/tf
/tf_static