QT Console 프로그램을 ROS 2.0으로 빌드하기

|

ROS 2.0 with QT

ROS 2.0에서 QT 콘솔 프로그램을 빌드하는 방법에 대해 포스팅합니다.

먼저 QT 콘솔 샘플 코드입니다.

main.cpp

#include <QCoreApplication>
#include <QDebug>

int main(int argc, char *argv[]) {
  QCoreApplication a(argc, argv);

  qDebug() << "snowdeer QT Console";

  return a.exec();
}


CMakeLists.txt

cmake_minimum_required(VERSION 3.5)
project(qt_console)

set(REQUIRED_QT_VERSION 5.9.0)

# Default to C++14
if(NOT CMAKE_CXX_STANDARD)
  set(CMAKE_CXX_STANDARD 14)
endif()

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic -fPIC)
endif()

set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)

find_package(Qt5Core REQUIRED)
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)

add_executable(${PROJECT_NAME} main.cpp)

ament_target_dependencies(${PROJECT_NAME} rclcpp Qt5Core)

install(TARGETS ${PROJECT_NAME}
  DESTINATION lib/${PROJECT_NAME})

ament_package()

Simple Message Publisher 및 Subscriber 예제 (Node 클래스 상속)

|

간단하게 메시지를 발행/수신하는 예제 코드는 다음과 같습니다.

snow_publisher_using_class

package.xml

<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="2">
  <name>snow_publisher</name>
  <version>0.4.0</version>
  <description>Snow ROS Message Publisher</description>
  <maintainer email="snowdeer0314@gmail.com">snowdeer</maintainer>
  <license>Apache License 2.0</license>

  <buildtool_depend>ament_cmake</buildtool_depend>

  <build_depend>rclcpp</build_depend>
  <build_depend>std_msgs</build_depend>

  <exec_depend>rclcpp</exec_depend>
  <exec_depend>std_msgs</exec_depend>
  
  <export>
    <build_type>ament_cmake</build_type>
  </export>
</package>


CMakeLists.txt

cmake_minimum_required(VERSION 3.5)
project(snow_publisher_using_class)

# Default to C++14
if(NOT CMAKE_CXX_STANDARD)
  set(CMAKE_CXX_STANDARD 14)
endif()

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic)
endif()

find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)

add_executable(${PROJECT_NAME} main.cpp)
ament_target_dependencies(${PROJECT_NAME} rclcpp std_msgs)

install(TARGETS ${PROJECT_NAME}
  DESTINATION lib/${PROJECT_NAME})

ament_package()


main.cpp

#include <cstdio>

#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"

using namespace std;
using namespace std::chrono_literals;

class SnowPublisher : public rclcpp::Node {
public:
  SnowPublisher()
  : Node("minimal_publisher") {
    mCount = 0;
    mPublisher = this->create_publisher<std_msgs::msg::String>("snowdeer_channel");
    mTimer = this->create_wall_timer(
      1000ms, std::bind(&SnowPublisher::publish_message, this));
  }

private:
  int mCount;
  rclcpp::TimerBase::SharedPtr mTimer;
  rclcpp::Publisher<std_msgs::msg::String>::SharedPtr mPublisher;
  
  void publish_message();
 
};

void SnowPublisher::publish_message() {
  mCount++;

  auto message = std_msgs::msg::String();
  message.data = "Hello, SnowDeer! " + std::to_string(mCount);

  RCLCPP_INFO(this->get_logger(), "Publishing: '%s'", message.data.c_str())
  mPublisher->publish(message);
}

int main(int argc, char * argv[]) {
  
  printf("This is SnowDeer's ROS Message Publisher.\n");

  rclcpp::init(argc, argv);
  rclcpp::spin(std::make_shared<SnowPublisher>());
  rclcpp::shutdown();

  return 0;
}


snow_subscriber_using_class

package.xml

<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="2">
  <name>snow_subscriber</name>
  <version>0.4.0</version>
  <description>Snow ROS Message Subscriber</description>
  <maintainer email="snowdeer0314@gmail.com">snowdeer</maintainer>
  <license>Apache License 2.0</license>

  <buildtool_depend>ament_cmake</buildtool_depend>

  <build_depend>rclcpp</build_depend>
  <build_depend>std_msgs</build_depend>

  <exec_depend>rclcpp</exec_depend>
  <exec_depend>std_msgs</exec_depend>
  
  <export>
    <build_type>ament_cmake</build_type>
  </export>
</package>


CMakeLists.txt

cmake_minimum_required(VERSION 3.5)
project(snow_subscriber_using_class)

# Default to C++14
if(NOT CMAKE_CXX_STANDARD)
  set(CMAKE_CXX_STANDARD 14)
endif()

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic)
endif()

find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)

add_executable(${PROJECT_NAME} main.cpp)
ament_target_dependencies(${PROJECT_NAME} rclcpp std_msgs)

install(TARGETS ${PROJECT_NAME}
  DESTINATION lib/${PROJECT_NAME})

ament_package()


main.cpp

#include <cstdio>

#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"

using namespace std;
using std::placeholders::_1;

class SnowSubscriber : public rclcpp::Node {
public:
  SnowSubscriber()
  : Node("snowdeer_msg_subscriber") {
    mSubscriber = this->create_subscription<std_msgs::msg::String>(
      "snowdeer_channel", std::bind(&SnowSubscriber::receive_message, this, _1));
  }

private:
  rclcpp::Subscription<std_msgs::msg::String>::SharedPtr mSubscriber;

  void receive_message(const std_msgs::msg::String::SharedPtr msg);
  
};

void SnowSubscriber::receive_message(const std_msgs::msg::String::SharedPtr msg) {
  RCLCPP_INFO(this->get_logger(), "Received Message: '%s'", msg->data.c_str())
}

int main(int argc, char * argv[]) {
  
  printf("This is SnowDeer's ROS Message Subscriber.\n");

  rclcpp::init(argc, argv);
  rclcpp::spin(std::make_shared<SnowSubscriber>());
  rclcpp::shutdown();

  return 0;
}

Simple Message Publisher 및 Subscriber 예제

|

간단하게 메시지를 발행/수신하는 예제 코드는 다음과 같습니다.

snowdeer_publisher

package.xml

<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="2">
  <name>snow_publisher</name>
  <version>0.4.0</version>
  <description>Snow ROS Message Publisher</description>
  <maintainer email="snowdeer0314@gmail.com">snowdeer</maintainer>
  <license>Apache License 2.0</license>

  <buildtool_depend>ament_cmake</buildtool_depend>

  <build_depend>rclcpp</build_depend>
  <build_depend>std_msgs</build_depend>

  <exec_depend>rclcpp</exec_depend>
  <exec_depend>std_msgs</exec_depend>
  
  <export>
    <build_type>ament_cmake</build_type>
  </export>
</package>


CMakeLists.txt

cmake_minimum_required(VERSION 3.5)
project(snow_publisher)

# Default to C++14
if(NOT CMAKE_CXX_STANDARD)
  set(CMAKE_CXX_STANDARD 14)
endif()

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic)
endif()

find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)

add_executable(${PROJECT_NAME} main.cpp)
ament_target_dependencies(${PROJECT_NAME} rclcpp std_msgs)

install(TARGETS ${PROJECT_NAME}
  DESTINATION lib/${PROJECT_NAME})

ament_package()


main.cpp

#include <cstdio>

#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"

using namespace std;
using namespace std::chrono_literals;

int main(int argc, char * argv[]) {
  
  printf("This is SnowDeer's ROS Message Publisher.\n");

  rclcpp::init(argc, argv);
  auto node = rclcpp::Node::make_shared("snowdeer_msg_publisher");
  auto publisher = node->create_publisher<std_msgs::msg::String>("snowdeer_topic");
  auto message = std::make_shared<std_msgs::msg::String>();
  auto publish_count = 0;

  rclcpp::WallRate loop_rate(1000ms);

  while (rclcpp::ok()) {
    message->data = "Hello, SnowDeer! " + std::to_string(publish_count++);

    RCLCPP_INFO(node->get_logger(), "Publishing: '%s'", message->data.c_str())
    publisher->publish(message);
    
    rclcpp::spin_some(node);

    loop_rate.sleep();
  }

  rclcpp::shutdown();
  
  return 0;
}


snowdeer_subscriber

package.xml

<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="2">
  <name>snow_subscriber</name>
  <version>0.4.0</version>
  <description>Snow ROS Message Subscriber</description>
  <maintainer email="snowdeer0314@gmail.com">snowdeer</maintainer>
  <license>Apache License 2.0</license>

  <buildtool_depend>ament_cmake</buildtool_depend>

  <build_depend>rclcpp</build_depend>
  <build_depend>std_msgs</build_depend>

  <exec_depend>rclcpp</exec_depend>
  <exec_depend>std_msgs</exec_depend>
  
  <export>
    <build_type>ament_cmake</build_type>
  </export>
</package>


CMakeLists.txt

cmake_minimum_required(VERSION 3.5)
project(snow_subscriber)

# Default to C++14
if(NOT CMAKE_CXX_STANDARD)
  set(CMAKE_CXX_STANDARD 14)
endif()

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic)
endif()

find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)

add_executable(${PROJECT_NAME} main.cpp)
ament_target_dependencies(${PROJECT_NAME} rclcpp std_msgs)

install(TARGETS ${PROJECT_NAME}
  DESTINATION lib/${PROJECT_NAME})

ament_package()


main.cpp

#include <cstdio>

#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"

using namespace std;

rclcpp::Node::SharedPtr node = nullptr;

void callback_from_topic(const std_msgs::msg::String::SharedPtr msg) {
  RCLCPP_INFO(node->get_logger(), "Received Message: '%s'", msg->data.c_str())
}

int main(int argc, char * argv[]) {
  
  printf("This is SnowDeer's ROS Message Subscriber.\n");

  rclcpp::init(argc, argv);
  node = rclcpp::Node::make_shared("snowdeer_msg_subscriber");

  auto subscription = node->create_subscription<std_msgs::msg::String>
      ("snowdeer_topic", callback_from_topic);

  rclcpp::spin(node);
  rclcpp::shutdown();

  node = nullptr;

  return 0;
}

Hello, ROS 예제 코드

|

패키지 구성

ROS 2.0의 각 노드의 패키지 구성은 다음과 같습니다.

  • package.xml
  • CMakeLists.txt
  • 소스 코드들

package.xml은 안드로이드의 manifest.xml과 같은 역할을 하며, 노드의 이름이나 라이센스, 설명 등의 내용이 들어있습니다.

CMakeLists.txt는 ROS2 만의 독특한 명령어가 조금 들어있긴 하지만 기본적으로 일반적인 CMakeLists 파일과 거의 유사합니다.

‘Hello, ROS’의 예제 코드는 다음과 같습니다. ROS적인 요소는 아예 들어있지 않지만 ament를 이용해서 빌드를 할 수 있다는 데 의의를 가질 수 있을 것 같습니다.


package.xml

<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="2">
  <name>hello_ros</name>
  <version>0.4.0</version>
  <description>Snow ROS Message Publisher using class</description>
  <maintainer email="snowdeer0314@gmail.com">snowdeer</maintainer>
  <license>Apache License 2.0</license>

  <buildtool_depend>ament_cmake</buildtool_depend>

  <build_depend>rclcpp</build_depend>

  <exec_depend>rclcpp</exec_depend>
  
  <export>
    <build_type>ament_cmake</build_type>
  </export>
</package>


CMakeLists.txt

cmake_minimum_required(VERSION 3.5)
project(snow_publisher)

# Default to C++14
if(NOT CMAKE_CXX_STANDARD)
  set(CMAKE_CXX_STANDARD 14)
endif()

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic)
endif()

find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)

add_executable(${PROJECT_NAME} main.cpp)
ament_target_dependencies(${PROJECT_NAME} rclcpp std_msgs)

install(TARGETS ${PROJECT_NAME}
  DESTINATION lib/${PROJECT_NAME})

ament_package()


main.cpp

#include <cstdio>

using namespace std;

int main(int argc, char * argv[]) {
  
  printf("Hello ROS. I am snowdeer.\n");

  return 0;
}

ROS2 간단한 명령어 모음

|

ROS 2.0의 실행 명령어는 ros2 입니다. ROS 1.0 버전에서의 명령어 사용법과 꽤 차이가 있습니다. 간단한 사용법을 알아놓는 편이 좋습니다.

ros2 명령어 사용법

도움말

간단한 도움말을 보기 위해서는 --help 옵션을 넣으면 됩니다.

$ ros2 --help


프로그램 실행방법

run 옵션을 이용해서 프로그램을 실행할 수 있습니다. 하지만 경험상 그냥 실행 파일만 실행해도 잘 실행이 되더군요.

ros2 run demo_nodes_cpp talker -t chatter2


Message Publish

특정 Topic으로 메시지를 발행하기 위해서는 topic pub 옵션을 사용하면 됩니다. 아래 예제는 snowdeer_topic이라는 이름의 Topic에 문자열 메시지를 보내는 예제입니다.

$ ros2 topic pub /snowdeer_topic std_msgs/String "data: Hello, snowdeer."


Message Subscribe

특정 Topic의 메시지를 수신하기 위해서는 topic echo 옵션을 사용하면 됩니다.

$ ros2 topic echo /snowdeer_topic


Topic 리스트 조회

$ ros2 topic list


Node 리스트 조회

$ ros2 node list