[Project#] 시리즈는 프로젝트(Trash Throwing Simulation with Deep Reinforcement Learning) 제작 과정을 설명한다. 본 포스팅은 Gazebo에 장애물을 생성하는 과정에 대한 내용이다.

모든 내용은 이전 글과 이어진다. 이전 글을 읽어봤다고 가정한 후 글을 작성하였다. (이전 글 LINK)

1. 간단한 모형 생성 후 테스트

1.1. my_obstacle_pkg 생성

cd ~/ros2_ws/src/robotic_arm_environment/
ros2 pkg create --build-type ament_python my_obstacle_pkg

1.2. description 폴더 생성

cd my_obstacle_pkg/
mkdir description

1.3. URDF 파일 생성

cd description/
touch obstacle.urdf

urdf 파일의 내용은 아래와 같다.

<?xml version="1.0"?>
<robot name="obstacle">
  <link name="world"/>

  <joint name="fixed" type="fixed">
    <parent link="world"/>
    <child link="link1"/>
  </joint>

  <link name="link1">
    <visual>
      <origin xyz="0 0 0" rpy="0 0 0" />
      <geometry>
        <box size="1 1 1" />
      </geometry>
      <material name="blue">
        <color rgba="0 0 1 1" />
      </material>
    </visual>
    
    <collision>
      <origin xyz="0 0 0" rpy="0 0 0" />
      <geometry>
        <box size="1.01 1.01 1.01" />
      </geometry>
    </collision>
    
    <inertial>
      <mass value="1" />
      <inertia ixx= "1.0" ixy="0.0" ixz="0.0" iyy="1.0" iyz="0.0" izz="1.0"/>
    </inertial>
  </link>
</robot>

1.4. Launch 파일 생성

  • 파일 생성

        
      cd ~/ros2_ws/src/robotic_arm_environment/my_obstacle_pkg/
      mkdir launch
      cd launch/
      touch my_obstacle.launch.py
    
  • 코드

      import os
      import launch
      import launch_ros.actions
        
      from ament_index_python.packages import get_package_share_directory
        
      def generate_launch_description():
        
          pkg_dir = get_package_share_directory('my_obstacle_pkg') 
          urdf_path = os.path.join(pkg_dir, 'description', 'obstacle.urdf')
          urdf = open(urdf_path).read()
            
          robot_state_publisher_node = launch_ros.actions.Node(
              name="robot_state_publisher",
              package="robot_state_publisher",
              executable="robot_state_publisher",
              parameters=[{"robot_description": urdf}]
          )
            
          return launch.LaunchDescription([robot_state_publisher_node])
    

1.5. setup.py 수정

  • launch, urdf 파일 경로 설정
  • 구체적으로는 import 추가 및 data_files에 launch 파일 경로와 urdf 파일 경로 생성
import os
from glob import glob
from setuptools import setup

package_name = 'my_obstacle_pkg'

setup(
    name=package_name,
    version='0.0.0',
    packages=[package_name],
    data_files=[
        ('share/ament_index/resource_index/packages',
            ['resource/' + package_name]),
        ('share/' + package_name, ['package.xml']),
        (os.path.join('share', package_name, 'launch'), glob(os.path.join('launch', '*.launch.py'))),
        (os.path.join('share', package_name, 'models', 'urdf'), glob(os.path.join('models','urdf', '*.urdf')))
    ],
    install_requires=['setuptools'],
    zip_safe=True,
    maintainer='dndqodqks',
    maintainer_email='dndqodqks@gmail.com',
    description='TODO: Package description',
    license='TODO: License declaration',
    tests_require=['pytest'],
    entry_points={
        'console_scripts': [
        ],
    },
)

1.6. Rviz에 출력하기

  • launch 파일 실행

      rf
      cb
      ros2 launch my_obstacle_pkg my_obstacle.launch.py
    
  • rivz 실행

      # 새 터미널
      rf
      rviz2
    

    Untitled

  • Fixed Frame 설정

    Displays에서 Fixed Frame을 확인하고자 하는 프레임으로 변경한다. 여기선 link1로 설정하였다.

    Untitled

  • RobotModel 추가

    왼쪽 아래 부분에 Add 버튼이 있다. Add 버튼을 누르면 시각화를 위한 다양한 도구들이 있다. 여기서 RobotModel을 클릭하고 OK 버튼을 누른다.

    Untitled

  • Description Topic 설정

    RobotModel을 클릭하면 내부에 Description Topic이라는 부분이 보인다. /robot_description 으로 변환하면 URDF 파일을 시각적으로 볼 수 있다.

    Untitled

2. Gazebo에 출력해보기

2.1. URDF 파일 수정

  • gazebo에서 색상과 출력 위치를 수정한다.
  • gazebo 플러그인을 추가하여 색상을 적용한다.
  • xzy를 0 0 0으로 설정하면 world와 물체가 겹쳐서 세탁기 돌아가듯이 회전한다. 그래서 z축의 좌표를 0.5로 설정하였다.
<?xml version="1.0"?>
<robot name="obstacle">
  <link name="world"/>

  <joint name="fixed" type="fixed">
    <parent link="world"/>
    <child link="base_0"/>
  </joint>

  <link name="base_0">
    <visual>
      <origin xyz="0 0 0.5" rpy="0 0 0" />
      <geometry>
        <box size="1 1 1" />
      </geometry>
      <material name="blue">
        <color rgba="0 0 1 1" />
      </material>
    </visual>
    
    <collision>
      <origin xyz="0 0 0.5" rpy="0 0 0" />
      <geometry>
        <box size="1.01 1.01 1.01" />
      </geometry>
    </collision>
    
    <inertial>
      <mass value="1" />
      <inertia ixx= "1.0" ixy="0.0" ixz="0.0" iyy="1.0" iyz="0.0" izz="1.0"/>
    </inertial>
  </link>

  <gazebo reference="base_0">
    <material>Gazebo/Blue</material>
  </gazebo>

</robot>

2.2. 런치 파일 수정

  • robot_description 토픽을 바탕으로 장애물을 출력하는 노드를 생성한다.
  • Gazebo 실행 노드를 추가한다.
import os
import launch
from launch_ros.actions import Node
from launch.actions import ExecuteProcess
from ament_index_python.packages import get_package_share_directory

def generate_launch_description():

    pkg_dir = get_package_share_directory('my_obstacle_pkg') 
    urdf_path = os.path.join(pkg_dir, 'description', 'obstacle.urdf')
    urdf = open(urdf_path).read()
    
    robot_state_publisher_node = Node(
        name="robot_state_publisher",
        package="robot_state_publisher",
        executable="robot_state_publisher",
        parameters=[{"robot_description": urdf}]
    )
    
    spawn_entity_obstacle = Node(package='gazebo_ros',
                              executable='spawn_entity.py',
                              arguments=['-entity', 'obstacle', '-topic', 'robot_description'],
                              output='screen')
    
    gazebo_node = ExecuteProcess(cmd=['gazebo', '-s', 'libgazebo_ros_factory.so'], output='screen')
    
    return launch.LaunchDescription([robot_state_publisher_node, spawn_entity_obstacle, gazebo_node])

2.3. 빌드 후 런치 파일 실행

rf
cb
ros2 launch my_obstacle_pkg my_obstacle.launch.py

Untitled

3. 장애물 형태, 좌표 수정 및 메인 런치 파일에 결합하기

3.1. URDF 파일 수정

  • 장애물 형태 및 좌표 수정
<?xml version="1.0"?>
<robot name="obstacle">
  <link name="world"/>

  <joint name="fixed" type="fixed">
    <parent link="world"/>
    <child link="base_0"/>
    <origin xyz="0 1.1 0.5" rpy="0 0 0" />
  </joint>

  <link name="base_0">
    <visual>
      <origin xyz="0 0 0" rpy="0 0 0" />
      <geometry>
        <box size="0.1 1 1" />
      </geometry>
      <material name="blue">
        <color rgba="0 0 1 1" />
      </material>
    </visual>
    
    <collision>
      <origin xyz="0 0 0" rpy="0 0 0" />
      <geometry>
        <box size="0.1 1 1" />
      </geometry>
    </collision>
    
    <inertial>
      <mass value="1" />
      <inertia ixx= "1.0" ixy="0.0" ixz="0.0" iyy="1.0" iyz="0.0" izz="1.0"/>
    </inertial>
  </link>

  <gazebo reference="base_0">
    <material>Gazebo/Blue</material>
  </gazebo>

</robot>

Untitled

3.2. my_obstacle.launch.py 파일 수정

  • 스폰하는 코드만 남기고 모두 삭제하였다. 또한, urdf_path를 넣어서 스폰한다.
import os
import launch
from launch_ros.actions import Node
from ament_index_python.packages import get_package_share_directory

def generate_launch_description():

    pkg_dir = get_package_share_directory('my_obstacle_pkg') 
    urdf_path = os.path.join(pkg_dir, 'description', 'obstacle.urdf')
    
    spawn_entity_obstacle = Node(package='gazebo_ros',
                              executable='spawn_entity.py',
                              arguments=['-entity', 'obstacle', '-file', urdf_path],
                              output='screen')
    
    return launch.LaunchDescription([spawn_entity_obstacle])

3.3. my_environment.launch.py 파일 수정

  • obstacle_object를 생성하는 노드를 추가한다.
import os
from launch import LaunchDescription
from launch_ros.actions import Node
from ament_index_python.packages import get_package_share_directory
from launch.actions import IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.actions import ExecuteProcess

def generate_launch_description():

	my_doosan_robot_files = get_package_share_directory('my_doosan_pkg')
	my_sphere_files       = get_package_share_directory('my_sphere_pkg')
	my_obstacle_files	  = get_package_share_directory('my_obstacle_pkg')
	my_environmets_files  = get_package_share_directory('my_environment_pkg')

	# Start doosan robot and controller
	doosan_robot = IncludeLaunchDescription(PythonLaunchDescriptionSource(my_doosan_robot_files + '/launch/my_doosan_controller.launch.py')) 
	
	# Start sphere mark
	sphere_mark  = IncludeLaunchDescription(PythonLaunchDescriptionSource(my_sphere_files + '/launch/my_sphere.launch.py')) 

	# Start obstacle object
	obstacle_object = IncludeLaunchDescription(PythonLaunchDescriptionSource(my_obstacle_files + '/launch/my_obstacle.launch.py')) 
 
	'''
	# Start Rviz
	rviz_file = my_environmets_files + "/rviz/my_rviz_env.rviz"
	rviz_node = Node( package='rviz2',
					  executable='rviz2',
					  name='rviz2',
					  output='log',
					  arguments=['-d', rviz_file])
	'''

	# Start Gazebo   
	world_file_name = 'my_world.world'
	world = os.path.join(get_package_share_directory('my_environment_pkg'), 'worlds', world_file_name)
	gazebo_node = ExecuteProcess(cmd=['gazebo', '--verbose', world,'-s', 'libgazebo_ros_factory.so'], output='screen')

	# Node 

	ld = LaunchDescription()

	ld.add_action(doosan_robot)
	ld.add_action(sphere_mark)
	ld.add_action(obstacle_object)
	ld.add_action(gazebo_node)
	#ld.add_action (rviz_node)

	return ld

3.4. 빌드 후 실행

rf
cb
ros2 launch my_environment_pkg my_environment.launch.py

Screenshot from 2023-05-25 00-27-21.png

4. 참고 문헌