ros2 moveit TF 표시

moveit_setup_assistant를 사용하면 대상이 되는 package에서 urdf를 추출한 다음, 필요한 설정 파일을 만든 후 package+moveit_config 디렉토리에 생성한 파일을 저장한다. lanch 디렉토리에 demo.launch.py 파일이 있는데, 코드에는 별 내용은 없고, 같은 디렉토리 launch 파일들을 실행한다. 최소한 어느 부분을 실행해야 rviz에서 로봇이 제대로 보일지 궁금했다

demo.launch.py 파일이 다음을 실행한다.

  • static_virtual_joint_tfs
  • robot_state_publisher
  • move_group
  • rviz
  • ros2_contol_node + controller spawners

robot_state_publisher만 실행하면 로봇이 정확하게 보일 줄 알았다. robot_state_publisher가 입력으로 robot_description을 받아들이고, TF는 robot_description에 있는 링크, 조인트 관계만 읽어들여 알아낼 줄 알았다. move_group은 path를 찾을 때 사용하므로 당장 필요해 보이지 않았다.

from launch import LaunchDescription
from moveit_configs_utils import MoveItConfigsBuilder
from launch_ros.actions import Node
from moveit_configs_utils.launches import generate_rsp_launch, generate_moveit_rviz_launch
from launch.actions import (
        DeclareLaunchArgument,
        IncludeLaunchDescription,
        )
from launch.substitutions import LaunchConfiguration
import os
from ament_index_python.packages import get_package_share_directory
from launch.launch_description_sources import PythonLaunchDescriptionSource


def generate_launch_description():
    moveit_config = MoveItConfigsBuilder("abb_irb2400", package_name="hello_moveit_config").to_moveit_configs()
    #dict2 = moveit_config.to_dict()
    #for key, val in (dict2.items()):
    #    print(key, ":", val) 
    ld = LaunchDescription()
    ld.add_action(DeclareLaunchArgument("publish_frequency", default_value="15.0"))

    rsp_node = Node(
            name = "rsp_node",
            executable = "robot_state_publisher",
            #prefix = "xterm -e gdb run --args",
            package = "robot_state_publisher",
            output = "both",
            respawn = True,
            #namespace = 'my_robot',
            parameters=[
                moveit_config.robot_description,
                {
                    "publish_frequency": LaunchConfiguration("publish_frequency"),
                    },
                #moveit_config.robot_description_semantic,
                #moveit_config.robot_description_kinematics,
                ],
            )
    ld.add_action(rsp_node)
    return ld

launch를 실행하면 tf가 제대로 표시되지 않는다. tf_static은 정확하게 표시된다.

root@rygen3600:/home/ros2_test/ws_root/ws_user# ros2 topic list 
/attached_collision_object
/clicked_point
/display_planned_path
/goal_pose
/initialpose
/joint_states
/parameter_events
/planning_scene
/planning_scene_world
/recognized_object_array
/robot_description
/rosout
/rviz_moveit_motion_planning_display/robot_interaction_interactive_marker_topic/feedback
/rviz_moveit_motion_planning_display/robot_interaction_interactive_marker_topic/update
/tf
/tf_static
/trajectory_execution_event
/visualization_marker_array
root@rygen3600:/home/ros2_test/ws_root/ws_user# ros2 topic echo /tf


^Croot@rygen3600:/home/ros2_test/ws_root/ws_user# ros2 topic echo /tf_static 
transforms:
- header:
    stamp:
      sec: 1691830604
      nanosec: 3856748
    frame_id: base_link
  child_frame_id: base
  transform:
    translation:
      x: 0.0
      y: 0.0
      z: 0.0
    rotation:
      x: 0.0
      y: 0.0
      z: 0.0
      w: 1.0
- header:
    stamp:
      sec: 1691830604
      nanosec: 3856748
    frame_id: link_6
  child_frame_id: tool0
  transform:
    translation:
      x: 0.0
      y: 0.0
      z: 0.0
    rotation:
      x: 0.0
      y: 0.0
      z: 0.0
      w: 1.0
---

왜 이런지 한참 고민하다, 결국 control node와 controller spawner가 실행되어야 TF가 제대로 표시됨을 알았다.

root@rygen3600:/home/ros2_test/ws_root/ws_user# ros2 topic echo /tf
...
- header:
    stamp:
      sec: 1691830991
      nanosec: 709634293
    frame_id: base_link
  child_frame_id: link_1
  transform:
    translation:
      x: 0.0
      y: 0.0
      z: 0.0
    rotation:
      x: 0.0
      y: 0.0
      z: 0.0
      w: 1.0
- header:
    stamp:
      sec: 1691830991
      nanosec: 709634293
    frame_id: link_1
  child_frame_id: link_2
  transform:
    translation:
      x: 0.1
      y: 0.0
      z: 0.615
    rotation:
      x: 0.0
      y: 0.0
      z: 0.0
      w: 1.0
- header:
    stamp:
      sec: 1691830991
      nanosec: 709634293
    frame_id: link_2
  child_frame_id: link_3
  transform:
    translation:
      x: 0.0
      y: 0.0
      z: 0.705
    rotation:
      x: 0.0
      y: 0.0
      z: 0.0
      w: 1.0

최소 2개 node가 필요하다. robot_state_publisher와 controller node.이거 알아낸다고 별 짓을 다했다. 다수 로봇을 처리하려면 namespace로 할당하여 불러야 할텐데, 잘 안된다.

rviz 12.7 update 실패->성공

osrf/ros:humble-desktop 도커 이미지를 설치하면 rviz 12.6가 설치된다. 가끔 node를 만들어 robot_description을 입력하면, rviz2가 제대로 표시 안할 경우가 있다. moveit_setup_assistant도 기존 설정 파일을 수정하면 robot_model_loader가 실행되지 않을 경우도 있다. 아마 12.6 버전의 문제인 듯 하여 버전을 올려 보기로 했다. 최신 버전은 12.8인데, humble이 사용할 수 있는 가장 최근 버전은 12.7이다. 12.8을 사용하면 display panel이 안보이는 듯 안정적으로 동작하지 않는다.

now0930@rygen3600:~/ros2/test/ws_root/ws_user$ git clone -b humble https://github.com/ros2/rviz.git
'rviz'에 복제합니다...
remote: Enumerating objects: 30445, done.
remote: Counting objects: 100% (4756/4756), done.
remote: Compressing objects: 100% (1181/1181), done.
remote: Total 30445 (delta 3857), reused 4031 (delta 3399), pack-reused 25689
오브젝트를 받는 중: 100% (30445/30445), 16.45 MiB | 16.27 MiB/s, 완료.
델타를 알아내는 중: 100% (22231/22231), 완료.
now0930@rygen3600:~/ros2/test/ws_root/ws_user$ cd rviz/
now0930@rygen3600:~/ros2/test/ws_root/ws_user/rviz$ git checkout 
브랜치가 'origin/humble'에 맞게 업데이트된 상태입니다.

branch를 선택하지 않고, default 값으로 humble에서 12.8을 컴파일 하고 있었다. 잘 동작되지 않아 2일을 날렸다. work space 구조도 각 패키지에 맞도록 세부적으로 나누어야 한다. 섞이면 컴파일 양도 많고, 각 패키지별로 옵션을 설정할 수 없다. workspace에 대한 내용은 여기에 있다. overlay, underlay를 정확하게 이해하지 않고 가 시간 날렸다.

rviz2 readme를 따라 complie 한다. 컴파일 중 ignition_math6_vendor가 없어 에러로 멈춘다.

CMake Error at CMakeLists.txt:64 (find_package):
  By not providing "Findignition_math6_vendor.cmake" in CMAKE_MODULE_PATH
  this project has asked CMake to find a package configuration file provided
  by "ignition_math6_vendor", but CMake did not find one.

  Could not find a package configuration file provided by
  "ignition_math6_vendor" with any of the following names:

    ignition_math6_vendorConfig.cmake
    ignition_math6_vendor-config.cmake

  Add the installation prefix of "ignition_math6_vendor" to CMAKE_PREFIX_PATH
  or set "ignition_math6_vendor_DIR" to a directory containing one of the
  above files.  If "ignition_math6_vendor" provides a separate development
  package or SDK, be sure it has been installed.

apt-cache로 ros-humble-ignition-math6-vendor를 검색하면, 해당 패키지가 설치되어 있다. dpkg로 어떤 파일들이 있는지 보면, 실재 파일은 없는 듯 하다.

root@rygen3600:/home/ros2_test# dpkg -L ros-humble-ignition-math6-vendor
/.
/opt
/opt/ros
/opt/ros/humble
/opt/ros/humble/share
/opt/ros/humble/share/ignition_math6_vendor
/opt/ros/humble/share/ignition_math6_vendor/package.xml
/usr
/usr/share
/usr/share/doc
/usr/share/doc/ros-humble-ignition-math6-vendor
/usr/share/doc/ros-humble-ignition-math6-vendor/changelog.Debian.gz
/usr/share/doc/ros-humble-ignition-math6-vendor/copyright
root@rygen3600:/home/ros2_test# 

ROS Index를 찾아보면 rviz2 github를 찾을 수 있는데, 이를 rviz에 있는 디렉토리에 받아주면 된다. ignition-math6-vendor가 gz_math6_vendor로 바뀐 느낌이다. CMakefile을 보면 project로 ignition_math6_vendor로 자기를 명명한다.

oot@rygen3600:/home/ros2_test/rviz# ls -alh
total 92K
drwxrwxr-x 17 1000 1000 4.0K Aug  8 21:43 .
drwxrwxr-x  7 1000 1000 4.0K Aug  6 20:45 ..
drwxrwxr-x  8 1000 1000 4.0K Aug  6 20:45 .git
drwxrwxr-x  3 1000 1000 4.0K Aug  6 20:45 .github
-rw-rw-r--  1 1000 1000   55 Aug  6 20:45 .gitignore
-rw-rw-r--  1 1000 1000   84 Aug  6 20:45 CODEOWNERS
-rw-rw-r--  1 1000 1000 1.7K Aug  6 20:45 LICENSE
-rw-rw-r--  1 1000 1000 8.2K Aug  6 20:45 README.md
drwxr-xr-x 11 root root 4.0K Aug  8 21:49 build
drwxrwxr-x  2 1000 1000 4.0K Aug  6 20:45 docs
drwxrwxr-x  5 1000 1000 4.0K Aug  8 21:43 gz_math6_vendor
drwxr-xr-x  7 root root 4.0K Aug  8 21:49 install
drwxr-xr-x 10 root root 4.0K Aug  8 21:44 log
drwxrwxr-x  6 1000 1000 4.0K Aug  6 20:45 rviz2
drwxrwxr-x  3 1000 1000 4.0K Aug  6 20:45 rviz_assimp_vendor
drwxrwxr-x  8 1000 1000 4.0K Aug  6 20:45 rviz_common
drwxrwxr-x  6 1000 1000 4.0K Aug  8 21:53 rviz_default_plugins
drwxrwxr-x  3 1000 1000 4.0K Aug  6 20:45 rviz_ogre_vendor
drwxrwxr-x  6 1000 1000 4.0K Aug  6 20:45 rviz_rendering
drwxrwxr-x  4 1000 1000 4.0K Aug  6 20:45 rviz_rendering_tests
drwxrwxr-x  7 1000 1000 4.0K Aug  8 20:54 rviz_visual_testing_framework
root@rygen3600:/home/ros2_test/rviz# 
root@rygen3600:/home/ros2_test/rviz# cd gz_math6_vendor/
root@rygen3600:/home/ros2_test/rviz/gz_math6_vendor# ls
CHANGELOG.rst	CONTRIBUTING.md  package.xml
CMakeLists.txt	LICENSE		 patches
root@rygen3600:/home/ros2_test/rviz/gz_math6_vendor# cat CMakeLists.txt 
cmake_minimum_required(VERSION 3.10)
project(ignition_math6_vendor)

find_package(ament_cmake_core REQUIRED)
find_package(ament_cmake_vendor_package REQUIRED)

find_package(ignition-math6 6.9.2 QUIET)

ament_vendor(ignition_math6_vendor
  SATISFIED ${ignition-math6_FOUND}
  VCS_URL https://github.com/ignitionrobotics/ign-math.git
  VCS_VERSION ignition-math6_6.9.2
  PATCHES patches
  CMAKE_ARGS
    -DBUILD_DOCS:BOOL=OFF
  GLOBAL_HOOK
)

find_package(ament_cmake_test REQUIRED)
if(BUILD_TESTING)
  find_package(ament_cmake_lint_cmake REQUIRED)
  find_package(ament_cmake_copyright REQUIRED)
  find_package(ament_cmake_xmllint REQUIRED)

  ament_lint_cmake()
  ament_copyright()
  ament_xmllint()
endif()

ament_package()

ros2 launch gdb

ros2를 gdb를 사용할 수 있다(대박!). 먼저 ros2 run 옵션으로 사용하는 포스트를 찾았다. 이러면 node를 만들 때 전달한 파라미터를 모두 넣어줘야 하여 어렵고 불편하다. 다행히 ros가 죽을 때 어떤 파일을 사용했는지 알려줬다.

[ERROR] [robot_model_tutorial-1]: process has died [pid 1919, exit code -11, cmd '/home/ros2_test/install/hello_moveit/lib/hello_moveit/robot_model_tutorial --ros-args --params-file /tmp/launch_params_2myb0p1i --params-file /tmp/launch_params_q_86u0o7 --params-file /tmp/launch_params_2dnzqmp1'].

node를 실행할 때, tmp 디렉토리에 파일로 만들어 이를 args로 전달한다. 다음과 같이 ros run으로 gdb를 붙일 수 있다.

ros2 run --prefix 'gdb -ex run --args' hello_moveit robot_model_tutorial --ros-args --params-file /tmp/launch_params_2myb0p1i --params-file /tmp/launch_params_q_86u0o7 --params-file /tmp/launch_params_2dnzqmp1

파라미터 입력이 귀찮으면, launch 옵션에 prefix로 넣어 사용할 수 있다. 단 터미널을 새로 만들어야 한다. 왜 그런지는 잘 모르겠는데, 일단 되니까 패스.

<사용하는 lanch file>
from launch import LaunchDescription
from launch_ros.actions import Node
from moveit_configs_utils import MoveItConfigsBuilder


def generate_launch_description():
    moveit_config = MoveItConfigsBuilder("hello").to_moveit_configs()

    my_node = Node(
        package="hello_moveit",
        executable="robot_model_tutorial",
        output="screen",
        parameters=[
            moveit_config.robot_description,
            moveit_config.robot_description_semantic,
            moveit_config.robot_description_kinematics,
        ],
    )

    return LaunchDescription([my_node])
<디버그용 lanch file>
from launch import LaunchDescription
from launch_ros.actions import Node
from moveit_configs_utils import MoveItConfigsBuilder


def generate_launch_description():
    moveit_config = MoveItConfigsBuilder("hello").to_moveit_configs()

    my_node = Node(
        package="hello_moveit",
        executable="robot_model_tutorial",
        prefix ="xterm -e gdb run --args",
        output="screen",
        parameters=[
            moveit_config.robot_description,
            moveit_config.robot_description_semantic,
            moveit_config.robot_description_kinematics,
        ],
    )

    return LaunchDescription([my_node])

gdb 기본 사용법은 여기 정리되어 있다.

MoveIt2 tutorial 따라하기(setFromIK)

인터넷에 공개된 MoveIt2 tutorial을 따라하다 보면 많은 사실을 학습할 수 있다. 다만 그대로 따라하면 그 감동이 적어, 내가 마음대로 해보기로 했다. ABB가 인터넷에 공개한 ABB_IRB2400 urdf 파일로 시작했다. 내가 한 삽질을 여기에 끄적인다.

setup assistant로 urdf 파일을 로딩하고, move_group, kinematics solver, pose 등을 설정하여 움직이는 Manuplator를 프로젝트 이름 + moveit_config 디렉토리에 저장했다. 기본으로 제공된 demo.launch.py을 실행하여 Rviz를 열고, 마우스 버튼으로 robotModel, MarkArray, panel을 추가했다. Planning with Path Constraints 부분에서 막혔는데,

start_state.setFromIK(joint_model_group, start_pose2);

를 실행하면

No kinematics solver instantiated for group '%s

메세지를 내고 프로그램이 죽는다. RobotState Class를 찾아보니 인자로 제공된 JointModelGroup에서 SolverInstance를 얻지 못하면 error 처리한다.

01278 bool moveit::core::RobotState::setFromIK(const JointModelGroup* jmg, const geometry_msgs::Pose& pose,
01279                                          unsigned int attempts, double timeout,
01280                                          const GroupStateValidityCallbackFn& constraint,
01281                                          const kinematics::KinematicsQueryOptions& options)
01282 {
01283   const kinematics::KinematicsBaseConstPtr& solver = jmg->getSolverInstance();
01284   if (!solver)
01285   {
01286     logError("No kinematics solver instantiated for group '%s'", jmg->getName().c_str());
01287     return false;
01288   }
01289   return setFromIK(jmg, pose, solver->getTipFrame(), attempts, timeout, constraint, options);
01290 }
01291 

JointModelGroup은 moveit setup assistanct가 설정하는 듯 한데, 어떻게 설정하는지 정확하게 설명되어 있지 않았다. moveit setup assistant로 설정할 때 kinematics solver를 선택하는 항목이 있는데, 나는 제대로 선택했는데 문제가 있다.

제공된 panda_arm 예제가 어떻게 돌아가는지 다시 살펴 보기로 했다. moveit2_tutorial에 panda_arm으로 제공된 항목을 찾아 move_group에 해당하는 부분을 launch 했다. moveit2_tutorial을 설치하면 moveit_resources에 panda_move_config가 있는데 여기 demo.launch.py를 실행해 보았다.

root@rygen3600:/home/ros2_test#ros2 launch moveit_resources_panda_moveit_config demo.launch.py
...
[move_group-4] [INFO] [1690614580.226886350] [move_group.move_group]: 
[move_group-4] 
[move_group-4] ********************************************************
[move_group-4] * MoveGroup using: 
[move_group-4] *     - ApplyPlanningSceneService
[move_group-4] *     - ClearOctomapService
[move_group-4] *     - CartesianPathService
[move_group-4] *     - ExecuteTrajectoryAction
[move_group-4] *     - GetPlanningSceneService
[move_group-4] *     - KinematicsService
[move_group-4] *     - MoveAction
[move_group-4] *     - MotionPlanService
[move_group-4] *     - QueryPlannersService
[move_group-4] *     - StateValidationService
[move_group-4] ********************************************************
[move_group-4] 

parameter가 어떻게 제공되는지 알아보기 위해 param list를 확인해 보고, 의심되는 kinematics_solver 값을 확인했다. robot_description에 solver를 설정할 수 있는데, moveit setup assistant가 설정한 값으로 동작하고 있다.

root@rygen3600:/home/ros2_test/src/hello_moveit/debug# ros2 param get /move_group robot_description_kinematics.panda_arm.kinematics_solver
String value is: kdl_kinematics_plugin/KDLKinematicsPlugin

내가 만든 ABB 로봇은 해당 node가 없다. 이제 문제가 무엇인지 알았다. launch할 때 해당 값을 넣어줘야 하는데, 내가 빼먹고 넣어주지 않았다. 이제 panda_arm demo 파일을 보고, 어떻게 설정해야 하는지 알아봐야 한다.

panda demo를 자세히 보니, 노드를 만들어서 robot_description에 kinematics를 설정해 주었다. 나는 ros run으로 해당 프로그램만 실행해주어 kinematics_solver가 설정되지 않았다. 아래 형식으로 노드를 만들어 준다.

root@rygen3600:/home/ros2_test# cat src/hello_moveit/launch/my_demo.launch.py 
from launch import LaunchDescription
from launch_ros.actions import Node
from moveit_configs_utils import MoveItConfigsBuilder


def generate_launch_description():
    moveit_config = MoveItConfigsBuilder("hello").to_moveit_configs()

    # MoveGroupInterface demo executable
    move_group_demo = Node(
        name="my_move_group_interface",
        package="hello_moveit",
        executable="planning",
        output="screen",
        parameters=[
            moveit_config.robot_description,
            moveit_config.robot_description_semantic,
            moveit_config.robot_description_kinematics,
        ],
    )

    return LaunchDescription([move_group_demo])

이제 새로운 문제가 있다. orientation constraint를 설정하면 로봇이 제대로 움직이지 않는다. interactive marker는 쉽게 planning 하는데, 프로그램으로 하면 짧은 거리도 planning이 높은 확율로 실패한다.

docker 내부 gazebo를 원격으로 실행

그래픽 드라이버가 있는 PC를 거실로 옮기고, TV에 연결해서 사용했다. 사용할 때는 좋은데, 원격으로 터미널로 접속하여 gazebo를 실행하기 어려웠다.

172.30.1.71

  • 저사양 PC로 인터넷 겨우 됨.

172.30.1.9

  • 내부 nvidia 그래픽 카드 설치로 무거운 PC
  • 소음 발열 심함.
  • docker로 172.17.0.1로 gazebo를 실행.

목표는 172.30.1.71 pc로 ssh 터미널로 172.30.1.9에 접속한 뒤 docker로 실행중인 gazebo를 172.30.1.71 화면으로 띄우기다.

172.30.1.9에서 sshd_config를 다음과 같이 설정한다.

$cat /etc/ssh/sshd_config
...
#AllowAgentForwarding yes
#AllowTcpForwarding yes
#GatewayPorts no
X11Forwarding yes
X11DisplayOffset 10
X11UseLocalhost  yes
...

X11UseLocalhost no로 설정하면 xauth add 명령어가 실행되지 않는다.

172.30.1.71에서 ssh로 172.30.1.9로 접속한다.

$ cat ~/.ssh/config 

Host rygen3600
	Hostname 172.30.1.9
	ForwardX11 yes

docker container를 다음 옵션으로 실행한다.

nvidia-docker run -it -v /run/user/1000:/run/user/1000 -v /dev:/dev -v /tmp/.X11-unix:/tmp/.X11-unix:ro \
  -v /home/now0930/ros2/test:/home/ros2_test --name foxy\
  --privileged --ipc=host --shm-size=512m --net=host -e DISPLAY=$DISPLAY \
  -e XDG_RUNTIME_DIR=/run/user/1000 --runtime=nvidia \
  osrf/ros:foxy-desktop-custom

docker container에 접속하여 gazebo를 실행하면

x11 connection rejected because of wrong authentication.

에러가 뜬다. docker 외부 계정의 ~/.Xauthority 파일을 docker container root에 복사하면 gazebo가 실행된다.

빈 화면인데 22 프레임이 나온다.