[Project#] 시리즈는 프로젝트(Trash Throwing Simulation with Deep Reinforcement Learning) 제작 과정을 설명한다. 본 포스팅은 로봇팔 튜토리얼에 대한 내용이다.

모든 내용은 이전 글과 이어진다. 이전 글을 오류 없이 모두 진행했다고 가정하고 글을 작성하였다. (이전 글 LINK)


‼️ 우선, 제작자의 Github Repository를 그대로 git clone 해서 수정하는 방식으로 한다는 것을 밝힌다. 아래 내용은 튜토리얼을 진행하며 해결하지 못한 문제를 나열했을 뿐이다. 결국, 성공하지 못하였다. 결과만 확인하고 넘어가고 싶다면 아래 코드만 확인하길 바란다.

cd ros2_ws/src
git clone https://github.com/dvalenciar/robotic_arm_environment.git
cd ..
sb
colcon build --symlink-install
# terminal 1
ros2 launch my_doosan_pkg my_doosan_gazebo_controller.launch.py

# terminal 2
ros2 run my_doosan_pkg trajectory_points_act_server
  • 각 터미널마다 rf 명령어를 실행해야 한다.

1. 로봇팔 가져오기

cd ros2_ws/src
git clone https://github.com/dvalenciar/robotic_arm_environment.git
cd ..
colcon build --symlink-install

2. 로봇팔 움직이기 테스트

  • gazebo 런치 파일 실행
cd ros2_ws
. install/setup.bash
ros2 launch my_doosan_pkg my_doosan_gazebo.launch.py

Untitled

  • rviz 파일 실행 - 위 gazebo 파일 실행과는 다른 터미널에서 코드를 입력해야 한다.
cd ros2_ws
. install/setup.bash
ros2 launch my_doosan_pkg my_doosan_rviz.launch.py

Untitled

3. 로봇팔 궤적 생성하기 테스트

rviz를 통해 각 joint를 조작해보았다. 이제 로봇팔이 특정 궤적을 그리며 이동시켜보자.

3.1. 패키지 생성하기

cd ~/ros2_ws/src/
ros2 pkg create --build-type ament_python my_doosan_pkg --dependencies rclpy
cd ..
colcon build --packages-up-to my_doosan_pkg
  • –packages-up-to: 뒤에 적힌 패키지와 현재 빌드하려는 패키지의 dependency가 서로 얽혀있는 패키지일 때, 먼저 빌드해야 하는 것을 찾아서 빌드해준다. (출처 링크)

❗에러 발생

[0.539s] ERROR:colcon:colcon build: Duplicate package names not supported:
- my_doosan_pkg:
  - src/my_doosan_pkg
  - src/robotic_arm_environment/my_doosan_pkg
  • my_doosan_pkg 패키지가 2개이기 때문에 에러가 발생하였다.
  • src/my_doosan_pkg → src/my_doosan_pkg2 로 이름을 변경한다.
cd ~/ros2_ws/src/
ros2 pkg create --build-type ament_python my_doosan_pkg2 --dependencies rclpy
cd ..
colcon build --packages-up-to my_doosan_pkg2
cd ~/ros2_ws/src/my_doosan_pkg2
mkdir config description launch rviz worlds
  • 패키지를 생성했으므로 설명 및 구성 파일이 포함된 필요한 폴더를 생성한다.
  • mkdir 은 폴더 생성 명령어이고, 띄어쓰기를 통해서 여러 폴더를 한 번에 생성할 수 있다.

3.2. meshes files 추가하기

두산 로보틱스의 6DoF 로봇 팔(A0912 모델)을 사용할 예정이므로, 이 로봇을 구성하는 각 부품의 3D 모델 파일이 필요하다.시뮬레이터는 견고하고 현실적인 시뮬레이션을 만들기 위해 이러한 파일이 필요합니다. 3D 모델 파일은 DAE 파일을 사용한다.

DAE(Digital Asset Exchange) 3D 애플리케이션간에 데이터를 교환하는데 사용되는 파일 형식이다.

모든 DAE 파일은 doosan-robot2 github 제공한다. /description 폴더 내에 /mesh라는 새 폴더를 만들고 필요한 모든 메쉬 파일이 포함된 전체 /a0912_blue 폴더를 추가해야 한다.

cd ~/ros2_ws/src/my_doosan_pkg2/description
mkdir meshes
cd meshes
  • 우선 meshes 폴더를 생성한다.
cd ~/ros2_ws/src/robotic_arm_environment/my_doosan_pkg/description/meshes/
cp -r a0912_blue ~/ros2_ws/src/my_doosan_pkg2/description/meshes
  • 위에서 git clone repository를 하였다. 해당 repository 안에 meshes 파일이 존재한다. /robotic_arm_environment/my_doosan_pkg/description/meshes/a0912_blue 폴더를 meshes 폴더로 옮긴다.

3.3. XACRO-URDF 파일 작성하기

XACRO-URDF 파일은 링크 및 조인트 측면에서 로봇 본체를 고정하는 방법을 알려주는 트리 구조가 포함되어 있다. 또한, 시뮬레이터(Gazebo)에게 각 링크와 조인트의 위치를 알려준다.

/description 폴더 안에 안에 /xacro 라는 새 폴더를 만들고 .xacro 확장자를 가진 파일을 생성한다. https://github.com/doosan-robotics/doosan-robot2 에선 xacro 파일도 제공하며, /description/xacro/macro.a0912.blue.xacro 파일이다.

xacro 파일과 URDF 파일은 다르지 않다. xacro는 URDF를 정의하는 또 다른 방법일 뿐이며, URDF의 대안이 아니다. 가장 큰 차이점은 매크로를 사용하여 특정 작업을 쉽게 한다는 것이다.

cd ~/ros2_ws/src/my_doosan_pkg2/description
mkdir xacro
cd xacro
  • xacro 폴더 생성
cd ~/ros2_ws/src/my_doosan_pkg2/description/xacro
touch macro.a0912.blue.xacro
  • /xacro 폴더 내에 macro.a0912.blue.xacro 파일을 생성한다.
<?xml version="1.0"?>

<robot name="a0912" xmlns:xacro="http://www.ros.org/wiki/xacro">

	<!-- I did some modifications concerning the original file-->
	<!-- The  damping and friction where added in each joint-->
	<!-- The created added, and called the gazebo_config_control.xacro -->
	<!-- The created added, and called the bumper_config.xacro -->

	<xacro:include filename="$(find my_doosan_pkg2)/description/xacro/macro.gazebo_config_control.xacro" />
	<xacro:include filename="$(find my_doosan_pkg2)/description/xacro/macro.transmission.xacro" />
	<xacro:include filename="$(find my_doosan_pkg2)/description/xacro/macro.internal_config.xacro" />
	<xacro:include filename="$(find my_doosan_pkg2)/description/xacro/macro.bumper_config.xacro" />

	<xacro:macro name="a0912" params="namespace">
		
		<!-- Base -->
		<link name="base_0">
			<inertial>
				<mass value="3.635"/>
				<origin xyz="4e-06   -0.001016     0.08181"/>
				<inertia ixx="0.02216" ixy="5.8001e-06" ixz="6.1941e-06" iyy="0.024835"
					iyz="-5.3034e-05"
					izz="0.021063"/>
			</inertial>
			<visual>
				<origin rpy="0 0 0" xyz="0 0 0" />
				<geometry>
					<mesh filename="file:///$(find my_doosan_pkg2)/description/meshes/a0912_blue/A0912_0_0.dae" scale="0.001 0.001 0.001" />
				</geometry>
			</visual>
			<collision>
        		<geometry>
					<mesh filename="file:///$(find my_doosan_pkg2)/description/meshes/a0912_blue/A0912_0_0.dae" scale="0.001 0.001 0.001" />
        		</geometry>
      		</collision>
		</link>

		<!-- joint 1 -->
		<joint name="joint1" type="revolute">
			<parent link="base_0" />
			<child link="link1" />
			<origin rpy="0 0 0" xyz="0 0 0.195" />
			<axis xyz="0 0 1" />
			<limit effort="411" lower="-6.2832" upper="6.2832" velocity="3.1416"/>
			<dynamics damping="60.0" friction="4.0" />
		</joint>

		<!-- Link 1 -->
		<link name="link1">
			<inertial>
				<mass value="7.863"/>
				<origin xyz="4.4e-05    0.029982    -0.01112"/>
				<inertia ixx="0.045839" ixy="1.2759e-05" ixz="-3.6168e-05" iyy="0.032913"
					iyz="-0.0029965"
					izz="0.044399"/>
			</inertial>
            <collision>
                <geometry>
                    <cylinder radius="0.08" length="0.2"/>
                </geometry>
            </collision>
			<visual>
				<origin rpy="0 0 0" xyz="0 0 0" />
				<geometry>
					<mesh filename="file:///$(find my_doosan_pkg2)/description/meshes/a0912_blue/A0912_1_0.dae" scale="0.001 0.001 0.001" />
				</geometry>
			</visual>
            <visual>
				<origin rpy="0 0 0" xyz="0 0 0" />
				<geometry>
					<mesh filename="file:///$(find my_doosan_pkg2)/description/meshes/a0912_blue/A0912_1_1.dae" scale="0.001 0.001 0.001" />
				</geometry>
			</visual>
            <visual>
				<origin rpy="0 0 0" xyz="0 0 0" />
				<geometry>
					<mesh filename="file:///$(find my_doosan_pkg2)/description/meshes/a0912_blue/A0912_1_2.dae" scale="0.001 0.001 0.001" />
				</geometry>
			</visual>
		</link>

		<!-- joint2 -->
		<joint name="joint2" type="revolute">
			<parent link="link1" />
			<child link="link2" />
			<origin rpy="0 -1.571 -1.571" xyz="0 0.039 0" />
			<axis xyz="0 0 1" />
			<limit effort="411" lower="-6.2832" upper="6.2832" velocity="3.1416"/>
			<dynamics damping="50.0" friction="3.0" />		
		</joint>
		

		<!-- Link 2 -->
		<link name="link2">
			<inertial>
				<mass value="10.985"/>
				<origin xyz="0.19748    0.001426     0.13191"/>
				<inertia ixx="0.04446" ixy="-0.0049948" ixz="-0.010992" iyy="0.67642" iyz="-2.8483e-05"
					izz="0.67428"/>
			</inertial>
            <collision>
                <origin rpy="0 -1.571 0" xyz="0.3 0 0.1525" />
                <geometry>
                    <cylinder radius="0.08" length="0.6"/>
                </geometry>
            </collision>
			<visual>
				<origin rpy="0 0 0" xyz="0 0 0" />
				<geometry>
					<mesh filename="file:///$(find my_doosan_pkg2)/description/meshes/a0912_blue/A0912_2_0.dae" scale="0.001 0.001 0.001" />
				</geometry>
			</visual>
			<visual>
				<origin rpy="0 0 0" xyz="0 0 0" />
				<geometry>
					<mesh filename="file:///$(find my_doosan_pkg2)/description/meshes/a0912_blue/A0912_2_1.dae" scale="0.001 0.001 0.001" />
				</geometry>
			</visual>
			<visual>
				<origin rpy="0 0 0" xyz="0 0 0" />
				<geometry>
					<mesh filename="file:///$(find my_doosan_pkg2)/description/meshes/a0912_blue/A0912_2_2.dae" scale="0.001 0.001 0.001" />
				</geometry>
			</visual>
		</link>

		<!-- joint3 -->
		<joint name="joint3" type="revolute">
			<parent link="link2" />
			<child link="link3" />
			<origin rpy="0 0 1.571" xyz="0.56 0 0" />
			<axis xyz="0 0 1" />
			<limit effort="194" lower="-2.7925" upper="2.7925" velocity="3.1416"/>
			<dynamics damping="40.0" friction="2.0"/>
		</joint>
		

		<!-- Link 3 -->
		<link name="link3">
			<inertial>
				<mass value="2.881"/>
				<origin xyz="8.5e-05   -0.002513    0.032058"/>
				<inertia ixx="0.012756" ixy="-5.4257e-06" ixz="1.6402e-05" iyy="0.011416"
					iyz="-0.0001989"
					izz="0.0080652"/>
			</inertial>
			<visual>
				<origin rpy="0 0 0" xyz="0 0 0" />
				<geometry>
					<mesh filename="file:///$(find my_doosan_pkg2)/description/meshes/a0912_blue/A0912_3_0.dae" scale="0.001 0.001 0.001" />
				</geometry>
			</visual>
            <visual>
				<origin rpy="0 0 0" xyz="0 0 0" />
				<geometry>
					<mesh filename="file:///$(find my_doosan_pkg2)/description/meshes/a0912_blue/A0912_3_1.dae" scale="0.001 0.001 0.001" />
				</geometry>
			</visual>
		</link>

		<!-- joint4 -->
		<joint name="joint4" type="revolute">
			<parent link="link3" />
			<child link="link4" />
			<origin rpy="1.571 0 0" xyz="0 -0.516 0" />
			<axis xyz="0 0 1" />
			<limit effort="50" lower="-6.2832" upper="6.2832" velocity="6.2832"/>
			<dynamics damping="20.0" friction="1.0" />
		</joint>

		<!-- Link 4 -->
		<link name="link4">
			<inertial>
				<mass value="3.273"/>
				<origin xyz="-9.6e-05    0.085314    -0.13975"/>
				<inertia ixx="0.12246" ixy="2.3905e-05" ixz="2.5022e-05" iyy="0.11307" iyz="-0.027995"
					izz="0.017186"/>
			</inertial>
            <collision>
                <origin rpy="-0.46 0 0" xyz="0 0.075 -0.2" />
                <geometry>
                    <cylinder radius="0.05" length="0.4"/>
                </geometry>
            </collision>
			<visual>
				<origin rpy="0 0 0" xyz="0 0 0" />
				<geometry>
					<mesh filename="file:///$(find my_doosan_pkg2)/description/meshes/a0912_blue/A0912_4_0.dae" scale="0.001 0.001 0.001" />
				</geometry>
			</visual>
            <visual>
				<origin rpy="0 0 0" xyz="0 0 0" />
				<geometry>
					<mesh filename="file:///$(find my_doosan_pkg2)/description/meshes/a0912_blue/A0912_4_1.dae" scale="0.001 0.001 0.001" />
				</geometry>
			</visual>
		</link>

		<!-- joint5 -->
		<joint name="joint5" type="revolute">
			<parent link="link4" />
			<child link="link5" />
			<origin rpy="-1.571 0 0" xyz="0 0 0" />
			<axis xyz="0 0 1" />
			<limit effort="50" lower="-6.2832" upper="6.2832" velocity="6.2832"/>
			<dynamics damping="10.0" friction="1.0" />
		</joint>

		<!-- Link 5 -->
		<link name="link5">
			<inertial>
				<mass value="2.141"/>
				<origin xyz="0.000109   -0.003286     0.01477"/>
				<inertia ixx="0.013145" ixy="-4.7132e-06" ixz="8.5804e-06" iyy="0.0073072"
					iyz="-5.999e-05"
					izz="0.0090476"/>
			</inertial>
            <collision>
                <origin rpy="1.571 0 0" xyz="0 0 0" />
                <geometry>
                    <cylinder radius="0.06" length="0.15"/>
                </geometry>
            </collision>
			<visual>
				<origin rpy="0 0 0" xyz="0 0 0" />
				<geometry>
					<mesh filename="file:///$(find my_doosan_pkg2)/description/meshes/a0912_blue/A0912_5_0.dae" scale="0.001 0.001 0.001" />
				</geometry>
			</visual>
            <visual>
				<origin rpy="0 0 0" xyz="0 0 0" />
				<geometry>
					<mesh filename="file:///$(find my_doosan_pkg2)/description/meshes/a0912_blue/A0912_5_1.dae" scale="0.001 0.001 0.001" />
				</geometry>
			</visual>
		</link>

		<!-- joint6 -->
		<joint name="joint6" type="revolute">
			<parent link="link5" />
			<child link="link6" />
			<origin rpy="1.571 0 0" xyz="0 -0.124 0" />
			<axis xyz="0 0 1" />
			<limit effort="50" lower="-6.2832" upper="6.2832" velocity="6.2832"/>
			<dynamics damping="10.0" friction="1.0" />
		</joint>

		<!-- Link 6 -->
		<link name="link6">
			<inertial>
				<mass value="0.784"/>
				<origin xyz="-1.9e-05      -6e-06    -0.05227"/>
				<inertia ixx="0.0067403" ixy="5.2373e-06" ixz="2.4957e-06" iyy="0.0048015"
					iyz="8.3671e-06"
					izz="0.0059696"/>
			</inertial>
			<visual>
				<origin rpy="0 0 0" xyz="0 0 0" />
				<geometry>
					<mesh filename="file:///$(find my_doosan_pkg2)/description/meshes/a0912_blue/A0912_6_0.dae" scale="0.001 0.001 0.001" />
				</geometry>
			</visual>
			<collision>
        		<geometry>
          			<mesh filename="file:///$(find my_doosan_pkg2)/description/meshes/a0912_blue/A0912_6_0.dae" scale="0.001 0.001 0.001" />
        		</geometry>
      		</collision>
		</link>

		<!-- the following lines instantiates all the info described in the files:
		 macro.gazebo_config_control.xacro, transmissions,  internal_config, macro.bumper_config  by david-->
		<xacro:config_ctr_gazebo /> 
		<xacro:dsr_transmission />
		<xacro:dsr_config_coeff />
		<xacro:bumper_ctr_sensor />

	</xacro:macro>
</robot>
  • 위 코드를 빈 파일인 macro.a0912.blue.xacro에 삽입한다.
  • xacro 파일에는 수동으로 포함된 매개변수가 있다. 감쇠(damping)와 마찰(friction)에 대한 것이다. 두산 로보틱스 github에서는 포함하지 않았다. 그러나 https://github.com/dvalenciar/robotic_arm_environment.git 의 제작자는 오랜 분석 끝에, 시뮬레이션 동안 로봇이 더 단단지도록 값을 추가하였다고 한다. 또한, 값을 추가함으로써 관절에서 이상한 움직임을 하지 않도록 한다는 것을 발견하였다.

3.4. 제어 플러그인 생성

원래 두산 로보틱스 github에서는 urdf-xacro 파일에 ros2_control 및 gazebo_ros2_control에 필요한 플러그인을 넣어두지 않았다. 최신 ros2_control 버전과 호환되도록 제어 플러그인을 추가해보도록 하자.

cd ~/ros2_ws/src/my_doosan_pkg2/description/xacro
touch macro.gazebo_config_control.xacro
  • /xacro 폴더 안에 macro.gazebo_config_control.xacro라는 새 파일을 만든다.이 파일에는 ros2_control 태그가 포함되며 시스템을 정의한다.
<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro">
	<xacro:macro name="config_ctr_gazebo">

		<!-- I need this for ROS2 control -->
		<!-- I create this file, it was NOT available in the original Doosan repo -->

		<ros2_control name="GazeboSystem" type="system">
			<hardware>
				<plugin>gazebo_ros2_control/GazeboSystem</plugin>
	    	</hardware>

	    	<joint name="joint1">
	    		<command_interface name="position">
	    			<param name="min">-6.2832</param>
	    			<param name="max">6.2832</param>
	    		</command_interface>
	    		<state_interface name="position"/>
	    		<state_interface name="velocity"/>
	    		<state_interface name="effort"/>
	    	</joint>

	    	<joint name="joint2">
	    		<command_interface name="position">
	    			<param name="min">-6.2832</param>
	    			<param name="max">6.2832</param>
	    		</command_interface>
	    		<state_interface name="position"/>
	    		<state_interface name="velocity"/>
	    		<state_interface name="effort"/>
	    	</joint>

	    	<joint name="joint3">
	    		<command_interface name="position">
	    			<param name="min">-2.7925</param>
	    			<param name="max">2.7925</param>
	    		</command_interface>
	    		<state_interface name="position"/>
	    		<state_interface name="velocity"/>
	    		<state_interface name="effort"/>
	    	</joint>

	    	<joint name="joint4">
	    		<command_interface name="position">
	    			<param name="min">-6.2832</param>
	    			<param name="max">6.2832</param>
	    		</command_interface>
	    		<state_interface name="position"/>
	    		<state_interface name="velocity"/>
	    		<state_interface name="effort"/>
	    	</joint>

	    	<joint name="joint5">
	    		<command_interface name="position">
	    			<param name="min">-6.2832</param>
	    			<param name="max">6.2832</param>
	    		</command_interface>
	    		<state_interface name="position"/>
	    		<state_interface name="velocity"/>
	    		<state_interface name="effort"/>
	    	</joint>

	    	<joint name="joint6">
	    		<command_interface name="position">
	    			<param name="min">-6.2832</param>
	    			<param name="max">6.2832</param>
	    		</command_interface>
	    		<state_interface name="position"/>
	    		<state_interface name="velocity"/>
	    		<state_interface name="effort"/>
	    	</joint>
	  	</ros2_control>

		<gazebo>
			<plugin name="gazebo_ros2_control" filename="libgazebo_ros2_control.so">
        		<!--<robot_sim_type>gazebo_ros2_control/DefaultRobotHWSim</robot_sim_type> -->
				<robot_sim_type>gazebo_ros2_control/GazeboSystem</robot_sim_type>
				<parameters>$(find my_doosan_pkg2)/config/simple_controller.yaml</parameters>
		    </plugin>
		</gazebo>

	</xacro:macro>
</robot>
  • 위 코드를 빈 파일인 macro.gazebo_config_control.xacro에 삽입한다. 각 조인트에 필요한 ros2_control 태그와 구성(명령 인터페이스 및 가능한 상태 인터페이스)을 추가한다.
  • 위 코드를 빈 파일인 macro.gazebo_config_control.xacro에 삽입한다. 이 파일은 각 조인트에 필요한 ros2_control 태그와 구성(명령 인터페이스 및 가능한 상태 인터페이스)을 포함되어 있다.
cd ~/ros2_ws/src/my_doosan_pkg2/
touch config/simple_controller.yaml
  • config 폴더 내부에 simple_controller.yaml 파일을 생성한다.
controller_manager:
  ros__parameters:
    update_rate: 100 # Hz

    joint_state_broadcaster:
      type: joint_state_broadcaster/JointStateBroadcaster

    joint_trajectory_controller:
      type: joint_trajectory_controller/JointTrajectoryController

joint_trajectory_controller:
  ros__parameters:
    joints:
      - joint1
      - joint2
      - joint3
      - joint4
      - joint5
      - joint6

    write_op_modes:
      - joint1
      - joint2
      - joint3
      - joint4
      - joint5
      - joint6
    
    interface_name: position

    command_interfaces:
      - position
    
    state_interfaces:
      - position
      - velocity

    state_publish_rate: 50.0 # Defaults to 50
    action_monitor_rate: 20.0 # Defaults to 20

    allow_partial_joints_goal: false # Defaults to false
    hardware_state_has_offset: true
    deduce_states_from_derivatives: true
    
    constraints:
      stopped_velocity_tolerance: 0.01 # Defaults to 0.01
      goal_time: 0.0 # Defaults to 0.0 (start immediately)
  • 위 코드를 simple_controller.yaml 파일에 복사해준다.
  • simple_controller.yaml 파일에는 컨트롤러에 대한 설정이 포함되어 있다.

3.5. Launch 파일과 Setup 설정

지금까지 로봇팔의 3D 모델을 가져오고 제어 플러그인을 생성하였다. 이제 모든 파일을 한 번에 실행시킬 수 있는 런치 파일을 생성해야 한다.

cd ~/ros2_ws/src/my_doosan_pkg2/launch
touch my_doosan_gazebo_controller.launch.py
  • launch 폴더 안에 my_doosan_gazebo_controller.launch.py 파일을 생성한다.
import os
from launch_ros.actions import Node
from launch import LaunchDescription
from launch.substitutions import Command
from launch.actions import ExecuteProcess
from ament_index_python.packages import get_package_share_directory

def generate_launch_description():

	
	robot_model = 'a0912'
	#robot_model = 'm1013'

	xacro_file = get_package_share_directory('my_doosan_pkg2') + '/description'+'/xacro/'+ robot_model +'.urdf.xacro'

	
	# Robot State Publisher 
	robot_state_publisher = Node(package    ='robot_state_publisher',
								 executable ='robot_state_publisher',
								 name       ='robot_state_publisher',
								 output     ='both',
								 parameters =[{'robot_description': Command(['xacro', ' ', xacro_file])           
								}])

	# Spawn the robot in Gazebo
	spawn_entity_robot = Node(package     ='gazebo_ros', 
							  executable  ='spawn_entity.py', 
							  arguments   = ['-entity', 'my_doosan_robot', '-topic', 'robot_description'],
							  output      ='screen')

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

	# load and START the controllers in launch file
	
	load_joint_state_broadcaster = ExecuteProcess(
										cmd=['ros2', 'control', 'load_controller', '--set-state', 'start','joint_state_broadcaster'],
										output='screen')

	
	load_joint_trajectory_controller = ExecuteProcess( 
										cmd=['ros2', 'control', 'load_controller', '--set-state', 'start', 'joint_trajectory_controller'], 
										output='screen')

	return LaunchDescription([robot_state_publisher, spawn_entity_robot, gazebo_node, load_joint_state_broadcaster, load_joint_trajectory_controller])
  • 위 코드를 빈 파일인 my_doosan_gazebo_controller.launch.py에 삽입한다.
import os
from glob import glob

(os.path.join('share', package_name, 'launch'), glob(os.path.join('launch', '*.launch.py'))),
(os.path.join('share', package_name, 'rviz'), glob(os.path.join('rviz', '*.rviz'))),    
(os.path.join('share', package_name, 'config'), glob(os.path.join('config', '*.*'))),
(os.path.join('share', package_name, 'worlds'), glob(os.path.join('worlds', '*.world'))),           
(os.path.join('share', package_name, 'description', 'urdf'), glob(os.path.join('description','urdf', '*.urdf'))),
(os.path.join('share', package_name, 'description', 'xacro'), glob(os.path.join('description','xacro', '*.xacro'))),
(os.path.join('share', package_name, 'description', 'meshes', 'a0912_blue'), glob(os.path.join('description','meshes','a0912_blue', '*.dae'))),
(os.path.join('share', package_name, 'description', 'meshes', 'm1013_white'), glob(os.path.join('description','meshes','m1013_white', '*.dae'))),
  • ROS2에서 python 패키지로 작업할 때 ROS2가 방금 만든 폴더와 파일을 찾기 위해 setup.py 파일을 구성해야 한다. 따라서 ~/ros2_ws/src/my_doosan_pkg2/setup.py를 열고 data_files 괄호 안에 위 코드를 추가합니다.

Untitled

  • 위 사진처럼 빨간 부분에 삽입해야 한다.
cd ~/ros2_ws
colcon build --packages-up-to my_doosan_pkg2
. install/setup.bash
ros2 launch my_doosan_pkg2 my_doosan_gazebo_controller.launch.py
  • 마지막으로 파일을 모두 저장한 다음 패키지를 빌드한다. 이후 ros2 launch 명령어를 통해 실행 시켜본다.
[INFO] [launch]: All log files can be found below /home/dndqodqks/.ros/log/2023-05-03-23-13-03-939002-dndqodqks-VivoBook-ASUSLaptop-X571LI-X571LI-31695
[INFO] [launch]: Default logging verbosity is set to INFO
Task exception was never retrieved
future: <Task finished name='Task-2' coro=<LaunchService._process_one_event() done, defined at /opt/ros/foxy/lib/python3.8/site-packages/launch/launch_service.py:226> exception=SubstitutionFailure('executed command failed. Command: xacro /home/dndqodqks/ros2_ws/install/my_doosan_pkg2/share/my_doosan_pkg2/description/xacro/a0912.urdf.xacro')>
Traceback (most recent call last):
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch/launch_service.py", line 228, in _process_one_event
    await self.__process_event(next_event)
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch/launch_service.py", line 248, in __process_event
    visit_all_entities_and_collect_futures(entity, self.__context))
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch/utilities/visit_all_entities_and_collect_futures_impl.py", line 45, in visit_all_entities_and_collect_futures
    futures_to_return += visit_all_entities_and_collect_futures(sub_entity, context)
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch/utilities/visit_all_entities_and_collect_futures_impl.py", line 45, in visit_all_entities_and_collect_futures
    futures_to_return += visit_all_entities_and_collect_futures(sub_entity, context)
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch/utilities/visit_all_entities_and_collect_futures_impl.py", line 45, in visit_all_entities_and_collect_futures
    futures_to_return += visit_all_entities_and_collect_futures(sub_entity, context)
  [Previous line repeated 1 more time]
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch/utilities/visit_all_entities_and_collect_futures_impl.py", line 38, in visit_all_entities_and_collect_futures
    sub_entities = entity.visit(context)
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch/action.py", line 108, in visit
    return self.execute(context)
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch_ros/actions/node.py", line 444, in execute
    self._perform_substitutions(context)
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch_ros/actions/node.py", line 399, in _perform_substitutions
    evaluated_parameters = evaluate_parameters(context, self.__parameters)
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch_ros/utilities/evaluate_parameters.py", line 164, in evaluate_parameters
    output_params.append(evaluate_parameter_dict(context, param))
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch_ros/utilities/evaluate_parameters.py", line 71, in evaluate_parameter_dict
    evaluated_value = perform_substitutions(context, list(value))
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch/utilities/perform_substitutions_impl.py", line 26, in perform_substitutions
    return ''.join([context.perform_substitution(sub) for sub in subs])
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch/utilities/perform_substitutions_impl.py", line 26, in <listcomp>
    return ''.join([context.perform_substitution(sub) for sub in subs])
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch/launch_context.py", line 232, in perform_substitution
    return substitution.perform(self)
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch/substitutions/command.py", line 116, in perform
    raise SubstitutionFailure(f'executed command failed. Command: {command_str}')
launch.substitutions.substitution_failure.SubstitutionFailure: executed command failed. Command: xacro /home/dndqodqks/ros2_ws/install/my_doosan_pkg2/share/my_doosan_pkg2/description/xacro/a0912.urdf.xacro
  • 무엇이 문제가 무엇일까? 이제 삽질을 시작해보자.
      1. ChatGPT 검색을 통한 카테고리 획득하기

      ChatGPT에 아래와 같이 질문을 하였고, 아래 사진처럼 답변을 얻었다.

      launch.substitutions.substitution_failure.SubstitutionFailure: executed command failed. Command: xacro /home/dndqodqks/ros2_ws/install/my_doosan_pkg2/share/my_doosan_pkg2/description/xacro/a0912.urdf.xacro

      How to solve this error?”

      Untitled

      → 다양한 답변 중 3번에 주목하였다. 그 이유는 VS Code에서 macro.gazebo_config_control.xacro 파일을 그냥 메모장으로 인식하고 있기 때문이다. (아래 사진 참고)

      Untitled

      ❓어떻게 해결해볼까?

      우선, 우분투가 macro.gazebo_config_control.xacro 파일을 어떤 형식으로 인식하고 있는지 확인해보았다.

        cd ~/ros2_ws/src/my_doosan_pkg2/description/xacro
        file macro.gazebo_config_control.xacro
      

      Untitled

      결과는 위 사진과 같다. macro.gazebo_config_control.xacro 파일은 xml 파일로 인식하고 있다. 비교를 위해 제대로 인식하고 있는 macro.a0912.blue.xacro 파일도 확인하였다. 둘 다 xml 파일로 인식되고 있다.

      1. 파일 경로 다시 확인하기

      /home/dndqodqks/ros2_ws/install/my_doosan_pkg2/share/my_doosan_pkg2/description/xacro/a0912.urdf.xacro

      에러 코드에 나온 문제의 주소는 위와 같다. 위 주소에 가보니 a0912.urdf.xacro이라는 파일은 존재하지 않았다. 대신 macro.a0912.blue.xacro 파일이 존재했다.

      ❓ 왜 이름을 인식하지 못한 걸까?

      launch 파일(my_doosan_pkg2/launch/my_doosan_gazebo_controller.launch.py)을 다시 확인하였다.

        robot_model = 'a0912'
        xacro_file = get_package_share_directory('my_doosan_pkg2') + '/description'+'/xacro/'+ robot_model +'.urdf.xacro'
      

      xacro_file을 위 코드로 정의하였다. 파일명에 맞게 코드를 아래와 같이 수정하였다.

        robot_model = 'a0912'
        xacro_file = get_package_share_directory('my_doosan_pkg2') + '/description'+'/xacro/macro.'+ robot_model +'.blue.xacro'
      

      이후 다시 빌드하고 런치 파일을 실행하였다.

        cd ~/ros2_ws
        colcon build --packages-up-to my_doosan_pkg2
        . install/setup.bash
        ros2 launch my_doosan_pkg2 my_doosan_gazebo_controller.launch.py
      

      그러나 이번에도 에러가 발생하였다. 에러의 마지막 부분만 확인해보았다.

        launch.substitutions.substitution_failure.SubstitutionFailure: executed command failed. Command: xacro /home/dndqodqks/ros2_ws/install/my_doosan_pkg2/share/my_doosan_pkg2/description/xacro/macro.a0912.blue.xacro
      

      에러 주소를 확인해 보니 macro.a0912.blue.xacro 파일이 존재하였다. 그렇다면 뭐가 문제일까

      xacro command를 실행하다가 발생한 오류라고 하니 터미널에 똑같이 입력해보았다.

        cd /home/dndqodqks/ros2_ws/install/my_doosan_pkg2/share/my_doosan_pkg2/description/xacro
        xacro macro.a0912.blue.xacro
      

      그랬더니 아래와 같은 오류가 발생하였다.

        No such file or directory: /home/dndqodqks/ros2_ws/install/my_doosan_pkg2/share/my_doosan_pkg2/description/xacro/macro.transmission.xacro [Errno 2] No such file or directory: '/home/dndqodqks/ros2_ws/install/my_doosan_pkg2/share/my_doosan_pkg2/description/xacro/macro.transmission.xacro'
        when processing file: macro.a0912.blue.xacro
      

      Untitled

      macro.a0912.blue.xacro 파일의 상단을 확인해보니 여러 파일을 include 하고 있었다.

      현재 생성된 파일은 macro.gazebo_config_control.xacro 뿐이니 나머지는 github 에서 가져왔다.

        cd ~/ros2_ws/src/my_doosan_pkg2/description/xacro
        touch macro.transmission.xacro macro.internal_config.xacro macro.bumper_config.xacro
      

      필요한 각 파일을 생성하고 각 파일에 맞는 코드를 삽입한다. 각 코드는 아래를 참고한다.

      1. macro.transmission.xacro code
        <?xml version="1.0"?>
              
        <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
        	<xacro:macro name="dsr_transmission">
        		<!-- Transmission 1 -->
        		<transmission name="tran1">
        			<type>transmission_interface/SimpleTransmission</type>
        			<joint name="joint1">
        				<hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
        			</joint>
        			<actuator name="motor1">
        				<hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
        				<mechanicalReduction>1</mechanicalReduction>
        			</actuator>
        		</transmission>
        		<!-- Transmission 2 -->
        		<transmission name="tran2">
        			<type>transmission_interface/SimpleTransmission</type>
        			<joint name="joint2">
        				<hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
        			</joint>
        			<actuator name="motor2">
        				<hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
        				<mechanicalReduction>1</mechanicalReduction>
        			</actuator>
        		</transmission>
        		<!-- Transmission 3 -->
        		<transmission name="tran3">
        			<type>transmission_interface/SimpleTransmission</type>
        			<joint name="joint3">
        				<hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
        			</joint>
        			<actuator name="motor3">
        				<hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
        				<mechanicalReduction>1</mechanicalReduction>
        			</actuator>
        		</transmission>
        		<!-- Transmission 4 -->
        		<transmission name="tran4">
        			<type>transmission_interface/SimpleTransmission</type>
        			<joint name="joint4">
        				<hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
        			</joint>
        			<actuator name="motor4">
        				<hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
        				<mechanicalReduction>1</mechanicalReduction>
        			</actuator>
        		</transmission>
        		<!-- Transmission 5 -->
        		<transmission name="tran5">
        			<type>transmission_interface/SimpleTransmission</type>
        			<joint name="joint5">
        				<hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
        			</joint>
        			<actuator name="motor5">
        				<hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
        				<mechanicalReduction>1</mechanicalReduction>
        			</actuator>
        		</transmission>
        		<!-- Transmission 6 -->
        		<transmission name="tran6">
        			<type>transmission_interface/SimpleTransmission</type>
        			<joint name="joint6">
        				<hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
        			</joint>
        			<actuator name="motor6">
        				<hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
        				<mechanicalReduction>1</mechanicalReduction>
        			</actuator>
        		</transmission>
        	</xacro:macro>
        </robot>
      
      1. macro.internal_config.xacro code
        <?xml version="1.0"?>
              
        <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
        	<xacro:macro name="dsr_config_coeff">
              
            <!-- I need this for a better simulation in Gazebo-->
            <!-- I modified this file a bit, it was available in the original Doosan repo as macro.gazebo.xacro -->
            <!-- This include the Contact Coefficients and other parameters, still no sure if this is correct --> 
            <!-- More info here http://gazebosim.org/tutorials?tut=ros_urdf -->
              
            <!-- Base -->
            <gazebo reference="base_0">
              <kp>1000000.0</kp>
              <kd>100.0</kd>
              <mu1>30.0</mu1>
              <mu2>30.0</mu2>
              <maxVel>1.0</maxVel>
              <minDepth>0.001</minDepth>
            </gazebo>
              
            <!-- Link1 -->
            <gazebo reference="link1">
              <kp>1000000.0</kp>
              <kd>100.0</kd>
              <mu1>30.0</mu1>
              <mu2>30.0</mu2>
              <maxVel>1.0</maxVel>
              <minDepth>0.001</minDepth>
            </gazebo>
                  
            <!-- Link2 -->
            <gazebo reference="link2">
              <kp>1000000.0</kp>
              <kd>100.0</kd>
              <mu1>30.0</mu1>
              <mu2>30.0</mu2>
              <maxVel>1.0</maxVel>
              <minDepth>0.001</minDepth>
            </gazebo>
                  
            <!-- Link3 -->
            <gazebo reference="link3">
              <kp>1000000.0</kp>
              <kd>100.0</kd>
              <mu1>30.0</mu1>
              <mu2>30.0</mu2>
              <maxVel>1.0</maxVel>
              <minDepth>0.001</minDepth>
            </gazebo>
                  
            <!-- Link4 -->
            <gazebo reference="link4">
              <kp>1000000.0</kp>
              <kd>100.0</kd>
              <mu1>30.0</mu1>
              <mu2>30.0</mu2>
              <maxVel>1.0</maxVel>
              <minDepth>0.001</minDepth>
            </gazebo>
                  
            <!-- Link5 -->
            <gazebo reference="link5">    
              <kp>1000000.0</kp>
              <kd>100.0</kd>
              <mu1>30.0</mu1>
              <mu2>30.0</mu2>
              <maxVel>1.0</maxVel>
              <minDepth>0.001</minDepth>
            </gazebo>
                  
            <!-- Link6 -->
            <gazebo reference="link6">
              <kp>1000000.0</kp>
              <kd>100.0</kd>
              <mu1>30.0</mu1>
              <mu2>30.0</mu2>
              <maxVel>1.0</maxVel>
              <minDepth>0.001</minDepth>
            </gazebo>
              
        	</xacro:macro>
        </robot>
      
      1. macro.bumper_config.xacro code
        <?xml version="1.0"?>
        <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
        	<xacro:macro name="bumper_ctr_sensor">
        		<!-- I need this for contact sensor, "bumper sensor"  -->
        		<!-- I create this file in order to detect self-collisions and collisions with the ground  -->
        		<!-- This helps me to detect if the end effect touch the ground so I can reset the environment -->
              
        		<gazebo reference="link6">
        			<!-- contact sensor -->
        			<sensor name="end_effector_sensor" type="contact">
        				<selfCollide>true</selfCollide>
        				<alwaysOn>true</alwaysOn>
        				<update_rate>50</update_rate>
        				<contact>
        					<collision>link6_collision</collision>
        				</contact>
        				<!-- gazebo plugin -->
        				<plugin name="gazebo_ros_bumper_sensor" filename="libgazebo_ros_bumper.so">
        					<ros>
        						<namespace>contact_sensor</namespace>
        						<remapping>bumper_states:=bumper_link6</remapping>
        					</ros>
        					<frame_name>link6</frame_name>
        				</plugin>
        			</sensor>
        		</gazebo>
        	</xacro:macro>
        </robot>
      

      이제 다시 빌드하고 런치 파일을 실행한다.

        cd ~/ros2_ws
        colcon build --packages-up-to my_doosan_pkg2
        . install/setup.bash
        ros2 launch my_doosan_pkg2 my_doosan_gazebo_controller.launch.py
      
        [INFO] [launch]: All log files can be found below /home/dndqodqks/.ros/log/2023-05-04-20-27-39-498952-dndqodqks-VivoBook-ASUSLaptop-X571LI-X571LI-53442
        [INFO] [launch]: Default logging verbosity is set to INFO
        [INFO] [robot_state_publisher-1]: process started with pid [53445]
        [INFO] [spawn_entity.py-2]: process started with pid [53447]
        [INFO] [gazebo-3]: process started with pid [53449]
        [INFO] [ros2-4]: process started with pid [53451]
        [INFO] [ros2-5]: process started with pid [53453]
        [robot_state_publisher-1] Parsing robot urdf xml string.
        [robot_state_publisher-1] Error:   No link elements found in urdf file
        [robot_state_publisher-1]          at line 179 in /tmp/binarydeb/ros-foxy-urdfdom-2.3.3/urdf_parser/src/model.cpp
        [robot_state_publisher-1] terminate called after throwing an instance of 'std::runtime_error'
        [robot_state_publisher-1]   what():  Unable to initialize urdf::model from robot description
        [gazebo-3] Gazebo multi-robot simulator, version 11.12.0
        [gazebo-3] Copyright (C) 2012 Open Source Robotics Foundation.
        [gazebo-3] Released under the Apache 2 License.
        [gazebo-3] http://gazebosim.org
        [gazebo-3] 
        [gazebo-3] Gazebo multi-robot simulator, version 11.12.0
        [gazebo-3] Copyright (C) 2012 Open Source Robotics Foundation.
        [gazebo-3] Released under the Apache 2 License.
        [gazebo-3] http://gazebosim.org
        [gazebo-3] 
        [spawn_entity.py-2] [INFO]: Spawn Entity started
        [spawn_entity.py-2] [INFO]: Loading entity published on topic robot_description
        [spawn_entity.py-2] [INFO]: Waiting for entity xml on robot_description
        [gazebo-3] [Msg] Waiting for master.
        [gazebo-3] [Msg] Connected to gazebo master @ http://127.0.0.1:11345
        [gazebo-3] [Msg] Publicized address: 10.30.12.210
        [gazebo-3] [Wrn] [SystemPaths.cc:459] File or path does not exist ["/home/dndqodqks/ros2_ws/install/my_doosan_pkg2/share/my_doosan_pkg2/worlds/my_empty_world.world"] [/home/dndqodqks/ros2_ws/install/my_doosan_pkg2/share/my_doosan_pkg2/worlds/my_empty_world.world]
        [gazebo-3] [Err] [Server.cc:472] Could not open file[/home/dndqodqks/ros2_ws/install/my_doosan_pkg2/share/my_doosan_pkg2/worlds/my_empty_world.world]
        [gazebo-3] [Wrn] [Server.cc:381] Falling back on worlds/empty.world
        [gazebo-3] [INFO]: ROS was initialized without arguments.
        [ERROR] [robot_state_publisher-1]: process has died [pid 53445, exit code -6, cmd '/opt/ros/foxy/lib/robot_state_publisher/robot_state_publisher --ros-args -r __node:=robot_state_publisher --params-file /tmp/launch_params_skkybwzf'].
        [gazebo-3] [Msg] Waiting for master.
        [gazebo-3] [Msg] Connected to gazebo master @ http://127.0.0.1:11345
        [gazebo-3] [Msg] Publicized address: 10.30.12.210
        [gazebo-3] [Wrn] [Event.cc:61] Warning: Deleting a connection right after creation. Make sure to save the ConnectionPtr from a Connect call
        [gazebo-3] [Wrn] [Event.cc:61] Warning: Deleting a connection right after creation. Make sure to save the ConnectionPtr from a Connect call
        [gazebo-3] [Msg] Loading world file [/usr/share/gazebo-11/worlds/empty.world]
        [INFO] [gazebo-3]: process has finished cleanly [pid 53449]
        [gazebo-3]
      
      • setup.py 파일을 확인했을 때 중간에 os.path.join() 함수를 활용하여 다양한 주소를 불러오고 있다. my_doosan_pkg2 패키지에는 불러오고자 하는 파일이 없어서 오류가 발생한 것 같다. 누락된 파일들을 robotic_arm_environment 패키지에서 가져와보자.
        cd ~/ros2_ws/src/robotic_arm_environment/my_doosan_pkg
              
        cp launch/my_doosan_controller.launch.py ~/ros2_ws/src/my_doosan_pkg2/launch
        cp launch/my_doosan_gazebo.launch.py ~/ros2_ws/src/my_doosan_pkg2/launch
        cp launch/my_doosan_rviz.launch.py ~/ros2_ws/src/my_doosan_pkg2/launch
              
        cp rviz/view_config.rviz ~/ros2_ws/src/my_doosan_pkg2/rviz
              
        cp worlds/my_empty_world.world ~/ros2_ws/src/my_doosan_pkg2/worlds
              
        mkdir ~/ros2_ws/src/my_doosan_pkg2/description/urdf
        cp description/urdf/a0912.blue.urdf ~/ros2_ws/src/my_doosan_pkg2/description/urdf
      

      이제 다시 빌드 후 런치해보자.

        cd ~/ros2_ws
        colcon build --packages-up-to my_doosan_pkg2
        . install/setup.bash
        ros2 launch my_doosan_pkg2 my_doosan_gazebo_controller.launch.py
      

      my_doosan_gazebo_controller.launch.py 파일의 xacro_file 선언 부분 수정함으로써 에러 해결

        # 수정된 코드
        xacro_file = get_package_share_directory('my_doosan_pkg2') + '/description'+'/xacro/'+ robot_model +'.blue.xacro'
      
        [INFO] [launch]: All log files can be found below /home/dndqodqks/.ros/log/2023-05-04-21-13-53-344228-dndqodqks-VivoBook-ASUSLaptop-X571LI-X571LI-61912
        [INFO] [launch]: Default logging verbosity is set to INFO
        [INFO] [robot_state_publisher-1]: process started with pid [61915]
        [INFO] [spawn_entity.py-2]: process started with pid [61917]
        [INFO] [gazebo-3]: process started with pid [61919]
        [INFO] [ros2-4]: process started with pid [61921]
        [INFO] [ros2-5]: process started with pid [61923]
        [robot_state_publisher-1] Parsing robot urdf xml string.
        [robot_state_publisher-1] Link base_0 had 1 children
        [robot_state_publisher-1] Link link1 had 1 children
        [robot_state_publisher-1] Link link2 had 1 children
        [robot_state_publisher-1] Link link3 had 1 children
        [robot_state_publisher-1] Link link4 had 1 children
        [robot_state_publisher-1] Link link5 had 1 children
        [robot_state_publisher-1] Link link6 had 0 children
        [robot_state_publisher-1] [INFO]: got segment base_0
        [robot_state_publisher-1] [INFO]: got segment link1
        [robot_state_publisher-1] [INFO]: got segment link2
        [robot_state_publisher-1] [INFO]: got segment link3
        [robot_state_publisher-1] [INFO]: got segment link4
        [robot_state_publisher-1] [INFO]: got segment link5
        [robot_state_publisher-1] [INFO]: got segment link6
        [robot_state_publisher-1] [INFO]: got segment world
        [spawn_entity.py-2] [INFO]: Spawn Entity started
        [spawn_entity.py-2] [INFO]: Loading entity published on topic robot_description
        [spawn_entity.py-2] [INFO]: Waiting for entity xml on robot_description
        [spawn_entity.py-2] [INFO]: Waiting for service /spawn_entity, timeout = 30
        [spawn_entity.py-2] [INFO]: Waiting for service /spawn_entity
        [gazebo-3] Gazebo multi-robot simulator, version 11.12.0
        [gazebo-3] Copyright (C) 2012 Open Source Robotics Foundation.
        [gazebo-3] Released under the Apache 2 License.
        [gazebo-3] http://gazebosim.org
        [gazebo-3] 
        [gazebo-3] Gazebo multi-robot simulator, version 11.12.0
        [gazebo-3] Copyright (C) 2012 Open Source Robotics Foundation.
        [gazebo-3] Released under the Apache 2 License.
        [gazebo-3] http://gazebosim.org
        [gazebo-3] 
        [gazebo-3] [INFO]: ROS was initialized without arguments.
        [gazebo-3] [INFO]: Publishing states of gazebo models at [/gazebo/model_states]
        [gazebo-3] [INFO]: Publishing states of gazebo links at [/gazebo/link_states]
        [spawn_entity.py-2] [INFO]: Calling service /spawn_entity
        [spawn_entity.py-2] [INFO]: Spawn status: SpawnEntity: Successfully spawned entity [my_doosan_robot]
        [gazebo-3] [INFO]: Publishing contact states to [/contact_sensor/bumper_link6]
        [INFO] [spawn_entity.py-2]: process has finished cleanly [pid 61917]
        [gazebo-3] [INFO]: Loading gazebo_ros2_control plugin
        [gazebo-3] [INFO]: Starting gazebo_ros2_control plugin in namespace: /
        [gazebo-3] [INFO]: Starting gazebo_ros2_control plugin in ros 2 node: gazebo_ros2_control
        [gazebo-3] [INFO]: Loading parameter files /home/dndqodqks/ros2_ws/install/my_doosan_pkg/share/my_doosan_pkg/config/simple_controller.yaml
        [gazebo-3] [INFO]: connected to service!! robot_state_publisher
        [gazebo-3] [INFO]: Received urdf from param server, parsing...
        [gazebo-3] [INFO]: Loading joint: joint1
        [gazebo-3] [INFO]: 	State:
        [gazebo-3] [INFO]: 		 position
        [gazebo-3] [INFO]: 		 velocity
        [gazebo-3] [INFO]: 		 effort
        [gazebo-3] [INFO]: 	Command:
        [gazebo-3] [INFO]: 		 position
        [gazebo-3] [INFO]: Loading joint: joint2
        [gazebo-3] [INFO]: 	State:
        [gazebo-3] [INFO]: 		 position
        [gazebo-3] [INFO]: 		 velocity
        [gazebo-3] [INFO]: 		 effort
        [gazebo-3] [INFO]: 	Command:
        [gazebo-3] [INFO]: 		 position
        [gazebo-3] [INFO]: Loading joint: joint3
        [gazebo-3] [INFO]: 	State:
        [gazebo-3] [INFO]: 		 position
        [gazebo-3] [INFO]: 		 velocity
        [gazebo-3] [INFO]: 		 effort
        [gazebo-3] [INFO]: 	Command:
        [gazebo-3] [INFO]: 		 position
        [gazebo-3] [INFO]: Loading joint: joint4
        [gazebo-3] [INFO]: 	State:
        [gazebo-3] [INFO]: 		 position
        [gazebo-3] [INFO]: 		 velocity
        [gazebo-3] [INFO]: 		 effort
        [gazebo-3] [INFO]: 	Command:
        [gazebo-3] [INFO]: 		 position
        [gazebo-3] [INFO]: Loading joint: joint5
        [gazebo-3] [INFO]: 	State:
        [gazebo-3] [INFO]: 		 position
        [gazebo-3] [INFO]: 		 velocity
        [gazebo-3] [INFO]: 		 effort
        [gazebo-3] [INFO]: 	Command:
        [gazebo-3] [INFO]: 		 position
        [gazebo-3] [INFO]: Loading joint: joint6
        [gazebo-3] [INFO]: 	State:
        [gazebo-3] [INFO]: 		 position
        [gazebo-3] [INFO]: 		 velocity
        [gazebo-3] [INFO]: 		 effort
        [gazebo-3] [INFO]: 	Command:
        [gazebo-3] [INFO]: 		 position
        [gazebo-3] [INFO]: Loading controller_manager
        [gazebo-3] [WARN]:  Desired controller update period (0.01 s) is slower than the gazebo simulation period (0.001 s).
        [gazebo-3] [INFO]: Loaded gazebo_ros2_control.
        [gazebo-3] [INFO]: Loading controller 'joint_state_broadcaster'
        [gazebo-3] [ERROR]: Loader for controller 'joint_state_broadcaster' not found.
        [gazebo-3] [INFO]: Available classes:
        [gazebo-3] [INFO]:   controller_manager/test_controller
        [gazebo-3] [INFO]:   controller_manager/test_controller_failed_init
        [gazebo-3] [INFO]:   controller_manager/test_controller_with_interfaces
        [ros2-4] Error loading controller, check controller_manager logs
        [gazebo-3] [INFO]: Loading controller 'joint_trajectory_controller'
        [gazebo-3] [ERROR]: Loader for controller 'joint_trajectory_controller' not found.
        [gazebo-3] [INFO]: Available classes:
        [gazebo-3] [INFO]:   controller_manager/test_controller
        [gazebo-3] [INFO]:   controller_manager/test_controller_failed_init
        [gazebo-3] [INFO]:   controller_manager/test_controller_with_interfaces
        [ros2-5] Error loading controller, check controller_manager logs
        [ERROR] [ros2-4]: process has died [pid 61921, exit code 1, cmd 'ros2 control load_controller --set-state start joint_state_broadcaster'].
        [ERROR] [ros2-5]: process has died [pid 61923, exit code 1, cmd 'ros2 control load_controller --set-state start joint_trajectory_controller'].
        [gazebo-3] [Msg] Waiting for master.
        [gazebo-3] [Msg] Connected to gazebo master @ http://127.0.0.1:11345
        [gazebo-3] [Msg] Publicized address: 10.30.12.210
        [gazebo-3] [Wrn] [Event.cc:61] Warning: Deleting a connection right after creation. Make sure to save the ConnectionPtr from a Connect call
        [gazebo-3] [Msg] Waiting for master.
        [gazebo-3] [Msg] Connected to gazebo master @ http://127.0.0.1:11345
        [gazebo-3] [Msg] Publicized address: 10.30.12.210
        [gazebo-3] [Msg] Loading world file [/home/dndqodqks/ros2_ws/install/my_doosan_pkg/share/my_doosan_pkg/worlds/my_empty_world.world]
        [INFO] [gazebo-3]: process has finished cleanly [pid 61919]
        [gazebo-3]
      

      ros2 control framework 다시 설치

        cd ~/ros2_ws
        wget https://github.com/ros-controls/ros2_control/raw/foxy/ros2_control/ros2_control.repos
        vcs import src < ros2_control.repos
        colcon build
      
        [INFO] [launch]: All log files can be found below /home/dndqodqks/.ros/log/2023-05-04-21-52-54-632734-dndqodqks-VivoBook-ASUSLaptop-X571LI-X571LI-70834
        [INFO] [launch]: Default logging verbosity is set to INFO
        Task exception was never retrieved
        future: <Task finished name='Task-2' coro=<LaunchService._process_one_event() done, defined at /opt/ros/foxy/lib/python3.8/site-packages/launch/launch_service.py:226> exception=SubstitutionFailure('executed command failed. Command: xacro /home/dndqodqks/ros2_ws/install/my_doosan_pkg2/share/my_doosan_pkg2/description/xacro/a0912.blue.xacro')>
        Traceback (most recent call last):
          File "/opt/ros/foxy/lib/python3.8/site-packages/launch/launch_service.py", line 228, in _process_one_event
            await self.__process_event(next_event)
          File "/opt/ros/foxy/lib/python3.8/site-packages/launch/launch_service.py", line 248, in __process_event
            visit_all_entities_and_collect_futures(entity, self.__context))
          File "/opt/ros/foxy/lib/python3.8/site-packages/launch/utilities/visit_all_entities_and_collect_futures_impl.py", line 45, in visit_all_entities_and_collect_futures
            futures_to_return += visit_all_entities_and_collect_futures(sub_entity, context)
          File "/opt/ros/foxy/lib/python3.8/site-packages/launch/utilities/visit_all_entities_and_collect_futures_impl.py", line 45, in visit_all_entities_and_collect_futures
            futures_to_return += visit_all_entities_and_collect_futures(sub_entity, context)
          File "/opt/ros/foxy/lib/python3.8/site-packages/launch/utilities/visit_all_entities_and_collect_futures_impl.py", line 45, in visit_all_entities_and_collect_futures
            futures_to_return += visit_all_entities_and_collect_futures(sub_entity, context)
          [Previous line repeated 1 more time]
          File "/opt/ros/foxy/lib/python3.8/site-packages/launch/utilities/visit_all_entities_and_collect_futures_impl.py", line 38, in visit_all_entities_and_collect_futures
            sub_entities = entity.visit(context)
          File "/opt/ros/foxy/lib/python3.8/site-packages/launch/action.py", line 108, in visit
            return self.execute(context)
          File "/opt/ros/foxy/lib/python3.8/site-packages/launch_ros/actions/node.py", line 444, in execute
            self._perform_substitutions(context)
          File "/opt/ros/foxy/lib/python3.8/site-packages/launch_ros/actions/node.py", line 399, in _perform_substitutions
            evaluated_parameters = evaluate_parameters(context, self.__parameters)
          File "/opt/ros/foxy/lib/python3.8/site-packages/launch_ros/utilities/evaluate_parameters.py", line 164, in evaluate_parameters
            output_params.append(evaluate_parameter_dict(context, param))
          File "/opt/ros/foxy/lib/python3.8/site-packages/launch_ros/utilities/evaluate_parameters.py", line 71, in evaluate_parameter_dict
            evaluated_value = perform_substitutions(context, list(value))
          File "/opt/ros/foxy/lib/python3.8/site-packages/launch/utilities/perform_substitutions_impl.py", line 26, in perform_substitutions
            return ''.join([context.perform_substitution(sub) for sub in subs])
          File "/opt/ros/foxy/lib/python3.8/site-packages/launch/utilities/perform_substitutions_impl.py", line 26, in <listcomp>
            return ''.join([context.perform_substitution(sub) for sub in subs])
          File "/opt/ros/foxy/lib/python3.8/site-packages/launch/launch_context.py", line 232, in perform_substitution
            return substitution.perform(self)
          File "/opt/ros/foxy/lib/python3.8/site-packages/launch/substitutions/command.py", line 116, in perform
            raise SubstitutionFailure(f'executed command failed. Command: {command_str}')
        launch.substitutions.substitution_failure.SubstitutionFailure: executed command failed. Command: xacro /home/dndqodqks/ros2_ws/install/my_doosan_pkg2/share/my_doosan_pkg2/description/xacro/a0912.blue.xacro
      

      github 와 비교해보니 누락된 xacro 파일이 있었네요. 해당 파일을 복사해옵니다.

        cd ~/ros2_ws/src/robotic_arm_environment/my_doosan_pkg
              
        cp description/xacro/a0912.urdf.xacro ~/ros2_ws/src/my_doosan_pkg2/description/xacro
      

      ❗a0912.urdf.xacro 파일에서 my_doosan_pkg를 모두 my_doosan_pkg2로 변경해야 한다.

      또한, my_doosan_gazebo_controller.launch.py 파일의 xacro_file 선언 부분을 수정했었는데 다시 원래대로 복구한다.

        xacro_file = get_package_share_directory('my_doosan_pkg') + '/description' + '/xacro/' + robot_model + '.urdf.xacro'
      

      이제 다시 빌드 후 런치 파일을 실행한다.

        cd ~/ros2_ws
        colcon build --packages-up-to my_doosan_pkg2
        . install/setup.bash
        ros2 launch my_doosan_pkg2 my_doosan_gazebo_controller.launch.py
      
        [ERROR] [gazebo-3]: process has died [pid 10384, exit code 255, cmd 'gazebo --verbose /home/dndqodqks/ros2_ws/install/my_doosan_pkg2/share/my_doosan_pkg2/worlds/my_empty_world.world -s libgazebo_ros_factory.so'].
        [ros2-4] Could not contact service /controller_manager/load_controller
        [ERROR] [ros2-4]: process has died [pid 10386, exit code 1, cmd 'ros2 control load_controller --set-state start joint_state_broadcaster'].
        [ros2-5] Could not contact service /controller_manager/load_controller
        [ERROR] [ros2-5]: process has died [pid 10388, exit code 1, cmd 'ros2 control load_controller --set-state start joint_trajectory_controller'].
      

      이후 위의 과정을 모두 반복하고 왔다.

        Starting >>> control_msgs
        Starting >>> realtime_tools
        --- stderr: control_msgs
        CMake Error at CMakeLists.txt:13 (find_package):
          By not providing "Findament_cmake.cmake" in CMAKE_MODULE_PATH this project
          has asked CMake to find a package configuration file provided by
          "ament_cmake", but CMake did not find one.
              
          Could not find a package configuration file provided by "ament_cmake" with
          any of the following names:
              
            ament_cmakeConfig.cmake
            ament_cmake-config.cmake
              
          Add the installation prefix of "ament_cmake" to CMAKE_PREFIX_PATH or set
          "ament_cmake_DIR" to a directory containing one of the above files.  If
          "ament_cmake" provides a separate development package or SDK, be sure it
          has been installed.
              
        ---
      

      아래 명령어 실행 후 빌드하니 오류 해결됨.

        source /opt/ros/foxy/setup.bash
        source ~/ros2_ws/install/local_setup.bash;
      

      Untitled

3.6. 궤적 생성을 위한

. install/setup.bash

ros2 run my_doosan_pkg trajectory_points_act_server

Untitled

4. 참고 문헌