
ROS2를 활용하여 로봇을 제어하기 위해서는 다양한 노드와 패키지를 구성해야 하며, 실제 로봇 하드웨어와 ROS2를 연결하는 경우에는
ros2_control을 활용할 수 있습니다.이번에는 실제 로봇과 ROS2 사이에서 데이터를 주고받기 위한 Hardware Interface 패키지를 구성하는 과정을 정리해보고자 합니다.
ROS2 패키지를 생성하는 기본적인 방법부터
package.xml, CMakeLists.txt 설정과 Hardware Interface를 Plugin으로 등록하기 위한 기본 구조까지 하나씩 살펴보겠습니다.ROS2 패키지 생성
먼저 ROS2 Workspace의
src 디렉터리로 이동한 후 새로운 패키지를 생성합니다.ros2 pkg create --build-type ament_cmake <package_name>
예를 들어
yj_hardware라는 이름의 패키지를 생성한다면 다음과 같이 작성할 수 있습니다.ros2 pkg create --build-type ament_cmake yj_hardware
여기서
--build-type은 해당 패키지에서 사용할 빌드 시스템을 지정합니다.ROS2에서는 주로 다음 두 가지 방식을 사용합니다.
ament_cmake: C/C++ 기반 ROS2 패키지
ament_python: Python 기반 ROS2 패키지
이번 Hardware Interface는 C++로 구현하기 때문에
ament_cmake를 사용합니다.패키지를 생성하면 기본적으로 다음과 같은 구조가 생성됩니다.
yj_hardware/ ├── CMakeLists.txt ├── include/ ├── package.xml └── src/
이후 Hardware Interface를 구현하면서
launch, config, urdf 등의 디렉터리와 plugin 설정 파일을 추가하게 됩니다.package.xml 설정
package.xml은 ROS2 패키지의 기본 정보와 해당 패키지에서 사용하는 Dependency(의존성) 를 정의하는 파일입니다.Hardware Interface 패키지에서는 다음과 같이 구성할 수 있습니다.
<?xml version="1.0"?> <?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?> <package format="3"> <name>yj_hardware</name> <version>0.0.0</version> <description>ROS2 control hardware package</description> <maintainer email="715jin@naver.com">yejin</maintainer> <license>TODO: License declaration</license> <buildtool_depend>ament_cmake</buildtool_depend> <depend>rclcpp</depend> <depend>hardware_interface</depend> <depend>pluginlib</depend> <depend>controller_manager</depend> <depend>joint_state_broadcaster</depend> <depend>joint_trajectory_controller</depend> <test_depend>ament_lint_auto</test_depend> <test_depend>ament_lint_common</test_depend> <export> <build_type>ament_cmake</build_type> </export> </package>
각 Dependency의 역할은 다음과 같습니다.
ament_cmake
ROS2 C++ 패키지를 빌드하기 위한 기본 빌드 시스템입니다.
<buildtool_depend>ament_cmake</buildtool_depend>
rclcpp
ROS2에서 C++ 노드를 작성하기 위한 기본 라이브러리입니다.
Node, Publisher, Subscriber뿐만 아니라 ROS2의 시간 및 통신과 관련된 여러 기능을 사용할 수 있습니다.
<depend>rclcpp</depend>
hardware_interface
ros2_control에서 실제 로봇 하드웨어와 데이터를 주고받기 위해 사용하는 Hardware Interface 관련 기능을 제공합니다.<depend>hardware_interface</depend>
Hardware Interface 클래스를 작성할 때 핵심적으로 사용되는 패키지입니다.
pluginlib
작성한 Hardware Interface 클래스를 ROS2에서 Plugin 형태로 동적으로 불러오기 위해 사용합니다.
<depend>pluginlib</depend>
Hardware Interface를 작성한 뒤 별도의 XML 파일에 Plugin 정보를 등록하고,
controller_manager에서 해당 클래스를 불러오는 구조로 사용됩니다.controller_manager
ros2_control에서 Controller와 Hardware Interface를 관리하는 역할을 합니다.<depend>controller_manager</depend>
대표적으로 다음 노드를 실행할 때 사용됩니다.
ros2_control_node
joint_state_broadcaster
로봇의 Joint Position, Velocity 등의 상태 정보를 ROS2 Topic으로 전달하기 위해 사용합니다.
<depend>joint_state_broadcaster</depend>
일반적으로
/joint_states Topic을 통해 현재 Joint 상태를 확인할 수 있습니다.joint_trajectory_controller
목표 Joint Position 또는 Trajectory를 입력하여 로봇의 Joint를 제어하기 위해 사용합니다.
<depend>joint_trajectory_controller</depend>
이후 Controller 설정 YAML 파일에서 사용할 Controller를 지정하게 됩니다.
CMakeLists.txt 설정
CMakeLists.txt는 C++ Source Code를 어떤 방식으로 컴파일하고 설치할 것인지 정의하는 파일입니다.Hardware Interface에서는 작성한 C++ 코드를 Shared Library 형태로 만들어 Plugin으로 사용할 수 있도록 구성합니다.
Hardware Interface Library 생성
add_library(yj_hardware SHARED src/yj_hardware.cpp )
위 코드는
src/yj_hardware.cpp
파일을 컴파일하여
yj_hardware라는 공유 라이브러리(Shared Library) 를 생성한다는 의미입니다.Linux 환경에서는 최종적으로
.so 형태의 라이브러리가 생성됩니다.예를 들어 다음과 같은 형태입니다.
libyj_hardware.so
ros2_control에서는 이 라이브러리를 Plugin 형태로 불러와 Hardware Interface를 사용하게 됩니다.Hardware Plugin 등록
Hardware Interface 클래스를 작성했다고 해서
ros2_control에서 자동으로 해당 클래스를 찾을 수 있는 것은 아닙니다.작성한 Hardware Interface가 어떤 클래스인지 알려주기 위해 Plugin Description XML 파일을 작성해야 합니다.
예를 들어 다음과 같은 파일을 사용할 수 있습니다.
yj_hardware.xml
그리고
CMakeLists.txt에서 다음과 같이 등록합니다.pluginlib_export_plugin_description_file( hardware_interface yj_hardware.xml )
첫 번째 인자인
hardware_interface
는 해당 Plugin이 어느 패키지의 Plugin Interface를 사용하는지 나타냅니다.
두 번째 인자인
yj_hardware.xml
에는 실제 Hardware Interface 클래스 정보가 정의되어 있습니다.
즉,
Hardware Interface C++ Class ↓ Shared Library ↓ Plugin XML ↓ pluginlib ↓ ros2_control
과 같은 구조로 Hardware Interface를 불러오게 됩니다.
파일 설치 설정
ROS2 패키지는 빌드한 이후
install 디렉터리를 기준으로 실행됩니다.따라서 Source Code뿐만 아니라 Header, Plugin XML, Launch, YAML, URDF 등의 파일도
install 공간으로 복사하도록 설정해야 합니다.Library 설치
install( TARGETS yj_hardware DESTINATION lib )
앞에서 생성한
yj_hardware 공유 라이브러리를install/<package_name>/lib/
영역에 설치합니다.
Header 파일 설치
install( DIRECTORY include/ DESTINATION include )
include 디렉터리에 작성한 Header 파일들을 Install 공간으로 복사합니다.Plugin XML 설치
install( FILES yj_hardware.xml DESTINATION share/${PROJECT_NAME} )
Hardware Plugin 정보를 가지고 있는 XML 파일을 패키지의 Share Directory로 복사합니다.
${PROJECT_NAME}에는 CMakeLists.txt에서 정의한 패키지 이름이 들어갑니다.예를 들어 패키지 이름이
yj_hardware라면 다음 위치에 설치됩니다.install/yj_hardware/share/yj_hardware/
Launch / Config / URDF 설치
install( DIRECTORY launch config urdf DESTINATION share/${PROJECT_NAME} )
Hardware Interface를 실행하면서 사용하는
launch/ config/ urdf/
디렉터리도 함께 Install 공간으로 복사합니다.
각 디렉터리는 일반적으로 다음과 같은 역할을 합니다.
launch/ └─ ROS2 Node 및 Controller 실행 설정 config/ └─ Controller 설정 YAML 파일 urdf/ └─ Robot Model 및 ros2_control 설정
이렇게 Install 공간으로 복사해두면 Python Launch 파일 등에서 다음과 같은 방법으로 패키지 경로를 가져올 수 있습니다.
from ament_index_python.packages import get_package_share_directory package_path = get_package_share_directory('yj_hardware')
이를 이용하면 절대 경로를 직접 입력하지 않고도 패키지 내부의
config, urdf, launch 등의 파일에 접근할 수 있습니다.패키지 구조
위의 설정을 모두 적용하면 Hardware Interface 패키지는 대략 다음과 같은 구조를 가지게 됩니다.
yj_hardware/ │ ├── CMakeLists.txt ├── package.xml ├── yj_hardware.xml │ ├── include/ │ └── yj_hardware/ │ └── yj_hardware.hpp │ ├── src/ │ └── yj_hardware.cpp │ ├── launch/ │ └── hardware.launch.py │ ├── config/ │ └── controllers.yaml │ └── urdf/ └── robot.urdf.xacro
각각의 파일들이 독립적으로 동작하는 것이 아니라,
URDF ↓ ros2_control 설정 ↓ Hardware Interface ↓ Plugin ↓ Controller Manager ↓ Controller
형태로 연결되어 실제 로봇을 제어하게 됩니다.
이번에는 ROS2에서
ros2_control Hardware Interface를 사용하기 위한 첫 단계로 패키지를 생성하고 기본적인 파일을 구성하는 방법을 정리해보았습니다.처음에는
package.xml, CMakeLists.txt, Plugin XML 등 여러 파일을 동시에 설정해야 하기 때문에 구조가 복잡하게 느껴질 수 있습니다. 하지만 각각의 파일이 어떤 역할을 담당하는지 이해하면 전체적인 구조를 조금 더 쉽게 파악할 수 있습니다.특히 Hardware Interface 패키지는 단순한 ROS2 Node와 달리 작성한 C++ 클래스를 공유 라이브러리로 생성하고, 이를 Plugin으로 등록하여
ros2_control에서 불러오는 과정이 필요합니다.다음에는 실제 Hardware Interface 클래스를 구현하면서
read(), write() 등의 함수를 통해 ROS2와 실제 로봇 사이에서 Joint 정보를 어떻게 주고받는지 조금 더 구체적으로 정리해보겠습니다.ROS2의 구조가 처음에는 복잡하게 느껴질 수 있지만, 하나씩 직접 구성해보면서 각 파일의 역할을 이해해 나가면 전체적인 흐름을 훨씬 쉽게 이해할 수 있습니다. 😊
추가적인 내용이나 잘못된 부분이 있다면 계속해서 수정하며 정리해보도록 하겠습니다!