움직이는 로봇 브로드캐스팅
저작권: 쿼드(QUAD) 드론연구소 https://smartstore.naver.com/maponarooo
지금까지 몇 가지 멋진 트릭을 배웠지만 여전히 정적 프레임을 게시하는 데 어려움을 겪고 있습니다. 로봇이 움직이기를 원합니다! 이 문제를 해결하기 전에 몇 가지 추가 패키지를 설치해야 합니다.
sudo apt install ros-humble-xacro ros-humble-joint-state-publisher-gui
이를 수행하는 첫 번째 단계는 로봇용 URDF 파일이 있는지 확인하는 것입니다. 이것은 크기, 모양, 색상 등과 같은 로봇 구성 요소의 모든 종류의 물리적 특성을 지정할 수 있는 일종의 구성 파일입니다.

URDF에서 로봇은 링크 라고 하는 일련의 견고한 구성 요소로 구성되며 링크는 부모-자식 트리 구조로 함께 연결됩니다. 여기서 서로 다른 링크 간의 관계를 Joint관절 이라고 합니다.
링크/조인트 패턴이 프레임/변환 패턴과 매우 유사한 방식을 보는 것은 그리 어렵지 않습니다. 그것들은 매우 밀접하게 관련되어 있기 때문에 URDF 파일을 가져와 자동으로 모든 변환을 브로드캐스트할 수 있는 ROS 노드 robot_state_publisher
가 있습니다 . /robot_description
또한 필요한 다른 모든 노드가 동일한 파일을 사용할 수 있도록 URDF 파일의 전체 내용을 토픽에 게시합니다 .
URDF 파일에서 각 조인트는 "Static" 또는 다양한 이동 가능 유형(예: 연속 회전, 제한된 회전, 선형 슬라이딩) 중 하나로 정의될 수 있습니다. 고정된 관절의 경우 robot_state_publisher
정적 변환만 게시할 수 있지만 이동하는 관절의 경우 해당 시점에 대한 동적 변환을 계산하기 위해 외부 값(예: 각도 또는 거리)이 필요합니다. 이러한 값을 얻기 위해 JointStat
메시지를 포함할 /joint_states
이라는 토픽을 구독합니다. 이러한 메시지에는 관절의 위치, 속도 또는 노력에 대한 정보가 포함될 수 있습니다(하지만 지금은 위치만 사용합니다).

갑자기 작업이 훨씬 쉬워졌습니다! 전체 변환을 브로드캐스트하는 대신 JointState
메시지를 게시하기만 하면 됩니다. 일반적으로 이 데이터는 인코더 또는 전위차계와 같은 로봇의 액추에이터 피드백 센서에서 나옵니다(시뮬레이션 환경에서는 이러한 센서를 시뮬레이션할 수 있음).
지금은 joint_state_publisher_gui
이라는 도구를 사용하여 관절 상태를 속일 것입니다. 이 노드는 /robot_description
에서 게시한 robot_state_publisher
항목을 보고 이동할 수 있는 관절을 찾은 다음 해당 관절에 대한 슬라이더를 표시합니다. 이 슬라이더에서 값을 읽고 /joint_states
에 게시합니다.
이 모든 것을 실행해 봅시다.
먼저 robot_state_publisher
를 실행할 것입니다 . URDF 파일을 전달하는 것이 약간 까다롭기 때문에 처음 실행할 때 약간 혼란스러울 수 있습니다. URDF는 robot_description
매개변수에서 가져오므로 명령은 다음과 같습니다.
ros2 run robot_state_publisher robot_state_publisher --ros-args -p robot_description:=(something here)
예제 "example_robot.urdf.xacro" 는 다음과 같습니다.
<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="robot">
<material name="grey">
<color rgba="0.2 0.2 0.2 1"/>
</material>
<material name="white">
<color rgba="1 1 1 1"/>
</material>
<material name="orange">
<color rgba="1 0.3 0.1 1"/>
</material>
<material name="blue">
<color rgba="0 0 1 1"/>
</material>
<link name="world"></link>
<joint name="world_to_base" type="fixed">
<parent link="world" />
<child link="base" />
<origin rpy="1.57 0 0" xyz="0 0 0" />
</joint>
<link name="base">
<visual>
<origin xyz="2 0 0" rpy="0 0 0"/>
<geometry>
<box size="5 0.1 3" />
</geometry>
<material name="grey" />
</visual>
</link>
<joint name="base_to_l3" type="revolute">
<parent link="base" />
<child link="l3" />
<origin rpy="0 0 0" xyz="0 0.25 0" />
<axis rpy="0 0 0" xyz="0 0 1" />
<limit effort="0.1" lower="-3.1" upper="3.1" velocity="0.2"/>
</joint>
<link name="l3">
<visual>
<origin xyz="0.75 0 0" rpy="0 1.57 0"/>
<geometry>
<cylinder length="1.5" radius="0.04" />
</geometry>
<material name="orange" />
</visual>
</link>
<joint name="l3_to_l2" type="revolute">
<parent link="l3" />
<child link="l2" />
<!-- <origin xyz="1 0 0" /> -->
<origin rpy="0 0 0" xyz="1.5 0 0" />
<axis rpy="0 0 0" xyz="0 0 1" />
<limit effort="0.1" lower="-3.1" upper="3.1" velocity="0.2"/>
</joint>
<link name="l2">
<visual>
<origin xyz="0.75 0 0" rpy="0 1.57 0"/>
<geometry>
<cylinder length="1.5" radius="0.04" />
</geometry>
<material name="orange" />
</visual>
</link>
<joint name="l2_to_l1" type="revolute">
<parent link="l2" />
<child link="l1" />
<origin rpy="0 0 0" xyz="1.5 0 0" />
<axis rpy="0 0 0" xyz="0 0 1" />
<limit effort="0.1" lower="-3.1" upper="3.1" velocity="0.2"/>
</joint>
<link name="l1">
<visual>
<origin xyz="0.5 0 0" rpy="0 1.57 0"/>
<geometry>
<cylinder length="1" radius="0.04" />
</geometry>
<material name="orange" />
</visual>
</link>
<joint name="l1_to_grip" type="revolute">
<parent link="l1" />
<child link="grip" />
<origin rpy="0 0 0" xyz="1 0 0" />
<axis rpy="0 0 0" xyz="0 0 1" />
<limit effort="0.1" lower="-3.1" upper="3.1" velocity="0.2"/>
</joint>
<link name="grip">
<visual>
<origin xyz="0.25 0 0"/>
<geometry>
<box size="0.5 0.5 0.05" />
</geometry>
<material name="white" />
</visual>
</link>
<joint name="world_to_camera" type="fixed">
<parent link="world" />
<child link="camera" />
<origin rpy="-2 0 2.5" xyz="4 1 2" />
</joint>
<link name="camera">
<visual>
<origin xyz="0 0 -0.2"/>
<geometry>
<box size="0.2 0.2 0.4" />
</geometry>
<material name="blue" />
</visual>
</link>
</robot>
파일을 저장한 다음 Joint 상태를 게시하기 위해 다음을 실행할 수 있습니다.
ros2 run robot_state_publisher robot_state_publisher --ros-args -p robot_description:="$(xacro example_robot.urdf.xacro)"
다음은 joint_state_publisher_gui 를 실행 합니다.
ros2 run joint_state_publisher_gui joint_state_publisher_gui
마지막으로 RViz를 다시 시작하고 TF 시각화를 추가하고 슬라이더를 조정할 때 프레임이 움직이는 것을 볼 수 있습니다.
RViz에서 "RobotModel" 시각화를 추가할 수도 있습니다.
옵션을 사용하여 /robot_description
항목을 선택하면 링크의 시각적 표현이 나타납니다.

tf2_tools
위의 예가 제대로 작동했으면 좋겠지만 때때로 우리가 변환 작업을 할 때 제대로 작동하지 않는 경우가 있으며 그 이유를 알아내야 합니다.
앞서 언급했듯이 변환 데이터는 두 가지 주제 /tf
및 /tf_static
에 게시됩니다 . ros2 topic echo
를 사용하여 이러한 주제를 직접 들을 수 있지만 때때로 해석하기 까다로울 수 있습니다. 도움을 주기 위해 ROS에는 변환 트리를 시각화할 수 있는 도구(tf2_tools)
와 view_frames
함께 브로드캐스팅 방법에 대한 정보가 함께 제공됩니다.
참고로, ROS 1에는 이 작업을 정확히 수행하지만 번거로움은 덜한 유용한 도구
rqt_tf_tree
가 있었습니다 . 그러나 작성 당시에는 ROS2로 포팅되지 않았으므로view_frames을
대신 사용해야 합니다.
이 도구는 실제로 몇 초 동안 변환을 수신한 다음 스크립트가 실행될 때 터미널이 있던 디렉터리에 frames.pd fframes.gv
라는 파일을 생성하는 Python 스크립트입니다(새 터미널의 경우 홈 디렉터리가 됨).
스크립트는 tf2_tools
패키지의 일부이므로 다음을 실행하기만 하면 됩니다.
ros2 run tf2_tools view_frames.py
결과 파일을 보면(파일 브라우저에서 열거나 로 터미널에서 열 때 atril frames.pdf
) 시각화된 트리를 볼 수 있습니다. 여기의 화살표는 RViz와 비교하여 반대입니다(소개에서와 동일함). 지금은 표시되는 나머지 정보에 대해 너무 걱정하지 않을 것입니다.
익숙하지 않다면 "16314… Unix Time(일명 Epoch Time)에 대한 자세한 내용은 Epoch Converter를 확인하십시오 . 시뮬레이션을 실행할 때 Gazebo는 자체 시간 소스를 생성하며 숫자는 Gazebo가 시작된 이후 초 단위입니다.

Last updated