[ROS2] 프로그래밍 기초(C++)
패키지 생성
$ ros2 pkg create [패키지 이름] --build-type [빌드 타입] --dependencies [의존하는 패키지1] [의존하는 패키지n]
dependencies를 설정하는 것은 패키지의 기능을 그대로 사용한다고 이해하면 된다.
패키지를 생성할 때 옵션으로 달아주지 않았다면 패키지를 생성한 후에도 package.xml에서 설정할 수 있다.
빌드 설정
(생성한 패키지에 helloworld_publisher, helloworld_subscriber 두 노드를 바탕으로 설명한다.)
패키지의 사용자 작업환경 디렉토리에서 CMakeLists.txt에 다음과 같이 추가를 해준다.
퍼블리셔 노드 작성
패키지의 사용자 작업환경 디렉토리에서 소스파일을 다음과 같이 작성한다.
#include <chrono>
#include <functional>
#include <memory>
#include <string>
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
using namespace std::chrono_literals;
class HelloworldPublisher : public rclcpp::Node
{
public:
HelloworldPublisher()
: Node("helloworld_publisher"), count_(0)
{
auto qos_profile = rclcpp::QoS(rclcpp::KeepLast(10)); // 클래스 생성자의 로컬 변수, 퍼블리셔를 생성할 때만 사용한다.
helloworld_publisher_ = this->create_publisher<std_msgs::msg::String>(
"helloworld", qos_profile); // 토픽의 타입, 토픽의 이름, qos 설정
timer_ = this->create_wall_timer(
1s, std::bind(&HelloworldPublisher::publish_helloworld_msg, this)); // 콜백함수를 설정, 1초마다 publish
}
private:
void publish_helloworld_msg() // 콜백함수
{
auto msg = std_msgs::msg::String();
msg.data = "Hello World: " + std::to_string(count_++);
RCLCPP_INFO(this->get_logger(), "Published message: '%s'", msg.data.c_str());
helloworld_publisher_->publish(msg);
}
rclcpp::TimerBase::SharedPtr timer_;
rclcpp::Publisher<std_msgs::msg::String>::SharedPtr helloworld_publisher_;
size_t count_;
};
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
auto node = std::make_shared<HelloworldPublisher>(); //HelloworldPublisher 타입의 객체를 생성하고, 이 객체를 가리키는 std::shared_ptr<T>를 반환
rclcpp::spin(node); // 노드를 실행
rclcpp::shutdown();
return 0;
}
자세한 설명은 책을 참고하도록 하자!
서브스크라이버 노드 작성
#include <functional>
#include <memory>
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
using std::placeholders::_1; //인수를 고정하는 place holder
class HelloworldSubscriber : public rclcpp::Node
{
public:
HelloworldSubscriber()
: Node("Helloworld_subscriber")
{
auto qos_profile = rclcpp::QoS(rclcpp::KeepLast(10));
helloworld_subscriber_ = this->create_subscription<std_msgs::msg::String>( // interface 지정
"helloworld", // 토픽 이름
qos_profile,
std::bind(&HelloworldSubscriber::subscribe_topic_message, this, _1)); // 콜백함수 지정 이 때 콜백함수가 msg를 첫번째 인자로 받기
// 때문에 _1 place holder를 지정한다.
}
private:
void subscribe_topic_message(const std_msgs::msg::String::SharedPtr msg) const
{
RCLCPP_INFO(this->get_logger(), "Received message: '%s'", msg->data.c_str());
}
rclcpp::Subscription<std_msgs::msg::String>::SharedPtr helloworld_subscriber_;
};
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
auto node = std::make_shared<HelloworldSubscriber>();
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
빌드
$ cd ~/workspace && colcon build --symlink-install
$ cd ~/workspace && colcon build --symlink-install --packages-select [패키지이름1][패키지이름N] #선택적으로 빌드를 한다.
실행
$ ros2 run my_first_ros_rclcpp_pkg helloworld_subscriber # tab을 눌러주면서 하면 빠르게 패키지와 노드를 선택할 수 있다.
$ ros2 run my_first_ros_rclcpp_pkg helloworld_publisher
실행을 하기 위해서는 setup script를 사용해야 한다.
~/.bashrc에 저장후 사용하거나 alias를 이용한다.
$ source ~/workspace/install/local_setup.bash
일반적으로는 underlay 개발환경의 setup.bash를 우선 소싱한 후 자신의 워크스페이스인 overlay 개발환경의 local_setup.bash를 소싱한다.
$ source /opt/ros/버전/setup.bash # underlay 개발환경 setup.bash 소싱
$ source ~/workspace/install/local_setup.bash # overlay 개발환경 setup.bash 소싱
# 위와 동일
$ source ~/workspace/install/setup.bash # 워크스페이스 뿐만 아니라 설치된 개발환경의 경로를 포함하도록 환경을 설정
ROS_DOMAIN_ID, Namespace
ROS_DOMAIN_ID
기본적으로 물리적으로 다른 네트워크를 쓴다면 독립적으로 작업할 수 있지만, 그럴 수 없다면 Domain을 다르게 설정한다.
같은 도메인에 있는 토픽들에 대해 퍼블리시 및 서브스크라이브를 할 수 있다.
$ export ROS_DOMAIN_ID=11 # 기본적으로 0~101의 정수를 사용한다.
Namespace
노드 이름에 Namespace를 붙혀주어 자신만의 네트워크를 그룹화 할 수 있다.
$ ros2 run turtlesim turtlesim_node --ros-args -r __ns:=/robot1
$ ros2 run turtlesim turtlesim_node --ros-args -r __ns:=/robot2
두 노드는 각 다른 Namespace에서 독립적으로 존재해 충돌하지 않는다.
댓글남기기