3 분 소요

Intro

나만의 모바일 로봇을 만들어보자. 모바일 로봇의 이미지부터 컨트롤 관련 코드를 직접 작성해보도록 하겠다. 더불어 카메라, 센서 등을 통해 얻은 데이터를 처리할 수 있도록 하는 것이 최종 목표이다.

1. Robot_pkg

Workspace에 'my_robot_pkg' 를 생성하자.

$ ros2 pkg create --build-type ament_cmake --node-name my_robot my_robot_pkg
$ mkdir launch rviz udrf
📦my_robot_pkg
 ┣ 📂include
 ┃ ┗ 📂my_robot_pkg
 ┣ 📂launch
 ┣ 📂rviz
 ┣ 📂src
 ┃ ┗ 📜my_robot.cpp
 ┣ 📂urdf
 ┃ ┗ 📜my_robot.urdf
 ┣ 📜CMakeLists.txt
 ┗ 📜package.xml

이때 생성한 launch, rviz, udrf 디렉터리의 용도는 다음과 같다.

  • launch Directory: launch 디렉터리는 ROS에서 사용되는 런치 파일들을 저장하는 곳이다. 런치 파일은 ROS 시스템을 시작하는 데 사용되는 XML 형식의 설정 파일이다. 이 파일들은 ROS 노드, 토픽, 서비스, 액션 서버 등과 같은 ROS 구성 요소들을 정의하고 실행하는 데 사용된다.

  • rviz Directory: 로봇 패키지의 rviz 디렉토리는 ROS 시각화 도구 중 하나인 RViz와 관련된 파일을 포함하는 디렉토리이다. RViz는 로봇의 상태, 센서 데이터, 경로 등을 시각화하는 강력한 도구로, 로봇 개발 및 디버깅에 유용하게 사용된다.

  • urdf Directory: urdf 디렉터리는 ROS에서 사용되는 URDF(Unified Robot Description Format) 파일을 저장하는 곳이다. URDF는 로봇 모델의 기하학적 형태, 관절, 연결 및 센서 등의 정보를 정의하는 XML 기반의 파일 형식이다. 이 디렉터리에는 로봇의 URDF 파일 뿐만 아니라 관련된 자원 파일들도 함께 저장될 수 있다.

이후에 'CMakeLists.txt' 파일에 아래의 코드를 추가해준다.

# Install launch files
install(DIRECTORY
  launch
  urdf
  rviz
  DESTINATION share/${PROJECT_NAME}
)

1.1. URDF

우선 'urdf' 디렉터리를 생성하고 'my_robot.urdf'를 통해 로봇을 만들어보자.

xacro를 통해 inertial와 관련하여 정의를 한 뒤에 링크를 서로 연결하여 간단하게 모델을 생성해보자.

코드는 다음과 같다.

<?xml version="1.0"?>
<robot name="my_robot" xmlns:xacro="http://ros.org/wiki/xacro">

<xacro:property name="base_width" value = "0.31"/>
<xacro:property name="base_length" value = "0.42"/>
<xacro:property name="base_height" value = "0.18"/>

<xacro:property name="wheel_radius" value="0.10"/>
<xacro:property name="wheel_width" value="0.04"/>
<xacro:property name="wheel_ygap" value="0.025"/>
<xacro:property name="wheel_zoff" value="0.01"/>
<xacro:property name="wheel_xoff" value="0.05"/>

<xacro:property name="caster_xoff" value="0.1"/>

  <!-- Define intertial property macros  -->
  <xacro:macro name="box_inertia" params="m w h d">
    <inertial>
      <origin xyz="0 0 0" rpy="${pi/2} 0 ${pi/2}"/>
      <mass value="${m}"/>
      <inertia ixx="${(m/12) * (h*h + d*d)}" ixy="0.0" ixz="0.0" iyy="${(m/12) * (w*w + d*d)}" iyz="0.0" izz="${(m/12) * (w*w + h*h)}"/>
    </inertial>
  </xacro:macro>

  <xacro:macro name="cylinder_inertia" params="m r h">
    <inertial>
      <origin xyz="0 0 0" rpy="${pi/2} 0 0" />
      <mass value="${m}"/>
      <inertia ixx="${(m/12) * (3*r*r + h*h)}" ixy = "0" ixz = "0" iyy="${(m/12) * (3*r*r + h*h)}" iyz = "0" izz="${(m/2) * (r*r)}"/>
    </inertial>
  </xacro:macro>

  <xacro:macro name="sphere_inertia" params="m r">
    <inertial>
      <mass value="${m}"/>
      <inertia ixx="${(2/5) * m * (r*r)}" ixy="0.0" ixz="0.0" iyy="${(2/5) * m * (r*r)}" iyz="0.0" izz="${(2/5) * m * (r*r)}"/>
    </inertial>
  </xacro:macro>


  <!-- Robot Base -->
  <link name="base_link">
    <visual>
    <origin xyz="0 0 ${base_height/2}" rpy="0 0 0"/>
      <geometry>
        <box size="${base_length} ${base_width} ${base_height}"/>
      </geometry>
      <material name="Cyan">
        <color rgba="0 1.0 1.0 1.0"/>
      </material>
    </visual>
    <collision>
    <origin xyz="0 0 ${base_height/2}" rpy="0 0 0"/>
      <geometry>
        <box size="${base_length} ${base_width} ${base_height}"/>
      </geometry>
    </collision>

    <xacro:box_inertia m="5" w="${base_width}" d="${base_length}" h="${base_height}"/>
  </link>

   <gazebo reference="base_link">
    <material>Gazebo/Orange</material>
  </gazebo>

    <!-- Caster Wheel -->
  <link name="caster">
    <visual>
      <geometry>
        <sphere radius="${wheel_zoff}"/>
      </geometry>
      <material name="white">
        <color rgba="1 1.0 1.0 1.0"/>
      </material>
    </visual>
    <collision>
      <origin xyz="0 0 0" rpy="0 0 0"/>
      <geometry>
        <sphere radius="${wheel_zoff}"/>
      </geometry>
    </collision>

    <xacro:sphere_inertia m="0.5" r="${(wheel_radius+wheel_zoff-(base_height/2))}"/>
  </link>

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

  <joint name="caster_joint" type="fixed">
    <parent link="base_link"/>
    <child link="caster"/>
    <origin xyz="${-caster_xoff} 0.0 0" rpy="0 0 0"/>
  </joint>

    <!-- Wheels -->
  <xacro:macro name="wheel" params="prefix x_reflect y_reflect">
    <link name="${prefix}_link">
      <visual>
        <origin xyz="0 0 0" rpy="${pi/2} 0 0"/>
        <geometry>
            <cylinder radius="${wheel_radius}" length="${wheel_width}"/>
        </geometry>
        <material name="Gray">
          <color rgba="0.5 0.5 0.5 1.0"/>
        </material>
      </visual>
      <collision>
        <origin xyz="0 0 0" rpy="${pi/2} 0 0"/>
        <geometry>
          <cylinder radius="${wheel_radius}" length="${wheel_width}"/>
        </geometry>
      </collision>

      <xacro:cylinder_inertia m="0.5" r="${wheel_radius}" h="${wheel_width}"/>
    </link>

    <joint name="${prefix}_joint" type="continuous">
      <parent link="base_link"/>
      <child link="${prefix}_link"/>
      <origin xyz="${-x_reflect*wheel_xoff} ${y_reflect*(base_width/2+wheel_ygap)} ${wheel_radius-wheel_zoff}" rpy="0 0 0"/>
      <axis xyz="0 1 0"/>
    </joint>
  </xacro:macro>

  <xacro:wheel prefix="drivewhl_l" x_reflect="-1" y_reflect="1" />
  <xacro:wheel prefix="drivewhl_r" x_reflect="-1" y_reflect="-1" />


</robot>
$ ros2 launch urdf_tutorial display.launch.py model:=my_robot.urdf

image

이때 rviz 디렉터리에 RViz 설정 파일인 'config.rviz' 파일을 만들자.

상단의 RViz 창에서 File 에 들어가서 Save Config As 를 통해 rviz 디렉터리에 'config.rviz'로 저장하면 된다.


2. Launch

간단한 launch 파일 두 개를 작성해보자.

2.1. display.launch.py

모델을 RViz를 통해서 확인할 수 있도록 한 런치파일을 작성해보자.

2.2. gazebo.launch.py

Reference

  • https://github.com/aarsht7/teleop_cpp_ros2/blob/main/README.md