개요
에서 사용자 지정 msg 및 srv 인터페이스를 만드는 방법을 배웠습니다.
가장 좋은 방법은 전용 인터페이스 패키지에서 인터페이스를 선언하는 것이지만 때로는 하나의 패키지에서 인터페이스를 모두 선언, 생성 및 사용하는 것이 편리할 수 있습니다.
인터페이스는 현재 CMake 패키지에서만 정의할 수 있음을 기억하십시오. 그러나 CMake 패키지에 Python 라이브러리와 노드가 있을 수 있으므로( 사용 ) 하나의 패키지에서 인터페이스와 Python 노드를 함께 정의할 수 있습니다. 여기서는 단순성을 위해 CMake 패키지와 C++ 노드를 사용합니다.
이 자습서는 msg 인터페이스 유형에 초점을 맞추지만 여기서 단계는 모든 인터페이스 유형에 적용할 수 있습니다.
실습
1. 패키지 생성
작업 공간 src
디렉토리에서 more_interfaces
패키지를 만들고 그 안에 msg 파일용 디렉토리를 만드십시오.
Copy ros2 pkg create --build-type ament_cmake more_interfaces
mkdir more_interfaces/msg
2. 메시지 파일 생성
내부에 AddressBook.msg
새 파일을 만들고 다음 코드를 붙여넣어 개인에 대한 정보를 전달하는 메시지more_interfaces/msg
를 만듭니다.
Copy uint8 PHONE_TYPE_HOME=0
uint8 PHONE_TYPE_WORK=1
uint8 PHONE_TYPE_MOBILE=2
string first_name
string last_name
string phone_number
uint8 phone_type
이 메시지는 다음 필드로 구성됩니다.
phone_type: 여러 명명된 상수 값이 정의된 uint8 유형
다음으로 msg 파일이 C++, Python 및 기타 언어의 소스 코드로 변환되었는지 확인해야 합니다.
2.1 msg 파일 빌드
package.xml
다음 줄을 열고 추가합니다.
Copy <buildtool_depend>rosidl_default_generators</buildtool_depend>
<exec_depend>rosidl_default_runtime</exec_depend>
<member_of_group>rosidl_interface_packages</member_of_group>
빌드 시에는 rosidl_default_generators
가 필요 하지만 런타임에는 rosidl_default_runtime가
필요 합니다.
CMakeLists.txt
다음 줄을 열고 추가합니다.
파일에서 msg/srv 메시지 코드를 생성하는 패키지를 찾습니다.
Copy find_package(rosidl_default_generators REQUIRED)
생성하려는 메시지 목록을 선언합니다.
Copy set(msg_files
"msg/AddressBook.msg"
)
.msg 파일을 수동으로 추가하면 다른 .msg 파일을 추가한 후 CMake가 프로젝트를 재구성해야 할 때를 알 수 있습니다.
메시지를 생성합니다.
Copy rosidl_generate_interfaces(${PROJECT_NAME}
${msg_files}
)
또한 메시지 런타임 종속성을 내보내는지 확인하십시오.
Copy ament_export_dependencies(rosidl_default_runtime)
이제 msg 정의에서 소스 파일을 생성할 준비가 되었습니다. 아래의 4단계에서 모두 함께 수행하므로 지금은 컴파일 단계를 건너뛸 것입니다.
2.2 다중 인터페이스 설정
set
을 사용하여 CMakeLists.txt
모든 인터페이스를 깔끔하게 나열 할 수 있습니다 .
Copy set(msg_files
"msg/Message1.msg"
"msg/Message2.msg"
# etc
)
set(srv_files
"srv/Service1.srv"
"srv/Service2.srv"
# etc
)
다음과 같이 한 번에 모든 목록을 생성합니다.
Copy rosidl_generate_interfaces(${PROJECT_NAME}
${msg_files}
${srv_files}
)
3. 동일한 패키지의 인터페이스 사용
이제 이 메시지를 사용하는 코드 작성을 시작할 수 있습니다.
more_interfaces/src 아래에 publish_address_book.cpp
라는 파일을 만들고 다음 코드를 붙여 넣습니다.
Copy #include <chrono>
#include <memory>
#include "rclcpp/rclcpp.hpp"
#include "more_interfaces/msg/address_book.hpp"
using namespace std::chrono_literals;
class AddressBookPublisher : public rclcpp::Node
{
public:
AddressBookPublisher()
: Node("address_book_publisher")
{
address_book_publisher_ =
this->create_publisher<more_interfaces::msg::AddressBook>("address_book", 10);
auto publish_msg = [this]() -> void {
auto message = more_interfaces::msg::AddressBook();
message.first_name = "John";
message.last_name = "Doe";
message.phone_number = "1234567890";
message.phone_type = message.PHONE_TYPE_MOBILE;
std::cout << "Publishing Contact\nFirst:" << message.first_name <<
" Last:" << message.last_name << std::endl;
this->address_book_publisher_->publish(message);
};
timer_ = this->create_wall_timer(1s, publish_msg);
}
private:
rclcpp::Publisher<more_interfaces::msg::AddressBook>::SharedPtr address_book_publisher_;
rclcpp::TimerBase::SharedPtr timer_;
};
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<AddressBookPublisher>());
rclcpp::shutdown();
return 0;
}
3.1 코드 설명
새로 만든 AddressBook.msg
의 헤더를 포함합니다.
Copy #include "more_interfaces/msg/address_book.hpp"
AddressBook
노드와 게시자를 만듭니다.
Copy using namespace std::chrono_literals;
class AddressBookPublisher : public rclcpp::Node
{
public:
AddressBookPublisher()
: Node("address_book_publisher")
{
address_book_publisher_ =
this->create_publisher<more_interfaces::msg::AddressBook>("address_book");
주기적으로 메시지를 게시하는 콜백을 만듭니다.
Copy auto publish_msg = [this]() -> void {
나중에 게시할 AddressBook
메시지 인스턴스를 만듭니다 .
Copy auto message = more_interfaces::msg::AddressBook();
AddressBook
필드를 채웁니다.
Copy message.first_name = "John";
message.last_name = "Doe";
message.phone_number = "1234567890";
message.phone_type = message.PHONE_TYPE_MOBILE;
마지막으로 주기적으로 메시지를 보냅니다.
Copy std::cout << "Publishing Contact\nFirst:" << message.first_name <<
" Last:" << message.last_name << std::endl;
this->address_book_publisher_->publish(message);
초 마다 publish_msg
함수를 호출하는 1초 타이머를 만듭니다 .
Copy timer_ = this->create_wall_timer(1s, publish_msg);
3.2 게시자 빌드
CMakeLists.txt
에서 이 노드에 대한 새 대상을 생성해야 합니다.
Copy find_package(rclcpp REQUIRED)
add_executable(publish_address_book src/publish_address_book.cpp)
ament_target_dependencies(publish_address_book rclcpp)
install(TARGETS
publish_address_book
DESTINATION lib/${PROJECT_NAME})
3.3 인터페이스에 대한 링크
동일한 패키지에서 생성된 메시지를 사용하려면 다음 CMake 코드를 사용해야 합니다.
Copy rosidl_target_interfaces(publish_address_book
${PROJECT_NAME} "rosidl_typesupport_cpp")
이렇게 하면 생성된 관련 C++ 코드를 찾고 AddressBook.msg
대상이 이에 대해 링크할 수 있습니다.
사용 중인 인터페이스가 독립적으로 빌드된 다른 패키지에 있는 경우 이 단계가 필요하지 않다는 것을 알 수 있습니다. 이 CMake 코드는 인터페이스가 정의된 패키지와 동일한 패키지에서 인터페이스를 사용하려는 경우에만 필요합니다.
4. 실행
작업 공간의 루트로 돌아가서 패키지를 빌드합니다.
Copy cd ~/ros2_ws
colcon build --packages-up-to more_interfaces
그런 다음 작업 영역을 소싱하고 게시자를 실행합니다.
Copy source install/local_setup.bash
ros2 run more_interfaces publish_address_book
publish_address_book.cpp
에서 설정한 값을 포함하여 정의한 메시지를 전달하는 게시자가 표시되어야 합니다.
address_book
메시지가 토픽에 게시되고 있는지 확인하려면 다른 터미널을 열고 작업 공간을 소싱하고 다음을 호출하십시오 .topic echo
Copy source install/setup.bash
ros2 topic echo /address_book
5. 기존 인터페이스 정의 사용
새 인터페이스 정의에서 기존 인터페이스 정의를 사용할 수 있습니다. 예를 들어, Contact.msg
라는 이름의 기존 ROS2 패키지rosidl_tutorials_msgs
에 속하는 메시지가 있다고 가정해 보겠습니다 . AddressBook.msg
그 정의가 이전의 맞춤형 인터페이스와 동일하다고 가정합니다 .
이 경우 (노드 가 있는 패키지의 인터페이스AddressBook.msg
)를 유형 ( 별도 의 패키지 의 인터페이스 )으로 정의할 수 있습니다. 다음과 같이 AddressBook.msg
type 의 Contact
배열 로 정의할 수도 있습니다 .
Copy rosidl_tutorials_msgs/Contact[] address_book
이 메시지를 생성하려면 다음에서 Contact.msg's
패키지 에 대한 종속성을 선언해야 합니다.
Copy <build_depend>rosidl_tutorials_msgs</build_depend>
<exec_depend>rosidl_tutorials_msgs</exec_depend>
그리고 CMakeLists.txt
:
Copy find_package(rosidl_tutorials_msgs REQUIRED)
rosidl_generate_interfaces(${PROJECT_NAME}
${msg_files}
DEPENDENCIES rosidl_tutorials_msgs
)
에 Contact.msg
추가할 수 있으려면 게시자 노드에 헤더를 포함해야 합니다 .contactsaddress_book
Copy #include "rosidl_tutorials_msgs/msg/contact.hpp"
콜백을 다음과 같이 변경할 수 있습니다.
Copy auto publish_msg = [this]() -> void {
auto msg = std::make_shared<more_interfaces::msg::AddressBook>();
{
rosidl_tutorials_msgs::msg::Contact contact;
contact.first_name = "John";
contact.last_name = "Doe";
contact.phone_number = "1234567890";
contact.phone_type = message.PHONE_TYPE_MOBILE;
msg->address_book.push_back(contact);
}
{
rosidl_tutorials_msgs::msg::Contact contact;
contact.first_name = "Jane";
contact.last_name = "Doe";
contact.phone_number = "4254242424";
contact.phone_type = message.PHONE_TYPE_HOME;
msg->address_book.push_back(contact);
}
std::cout << "Publishing address book:" << std::endl;
for (auto contact : msg->address_book) {
std::cout << "First:" << contact.first_name << " Last:" << contact.last_name <<
std::endl;
}
address_book_publisher_->publish(*msg);
};
이러한 변경 사항을 빌드하고 실행하면 예상대로 정의된 메시지와 위에 정의된 메시지 배열이 표시됩니다.