학습 내용
과제 설명
< 송신 노드와 수신 노드에서 누락 없이 모두 잘 도착하는가? >
노드 1(Sender)에서 노드 2(Receiver)로 숫자 1-100을 100개의 토픽에 담아 전송할 때, 화면에 100개의 숫자가 모두 출력되는지 확인해 본다. 우선 C++ 파일 2개(sender_serial.cpp, receiver_serial.cpp)와 launch 파일 1개(sr_serial.launch)를 만든다. 이후 숫자를 보내면서 누락된 토픽이 있는지 확인한다. 중간보다는 맨 처음과 끝에서 누락이 있는지 확인한다. 이때 누락을 막기 위해 받는 노드를 먼저 실행시킨 뒤 보내는 노드를 실행시켜 결과를 확인한다. 만약 이 방법이 누락을 해결하지 못한다면 어떻게 해결해야 할지 생각해 본다.
과제 수행
먼저 과제를 위해 필요한 파일들을 생성한다. 생성 위치는 다음과 같다.
파일 생성 위치
xycar_ws
├─ build
├─ devel
└─ src
├─ CMakeLists.txt
└─ msg_send
├─ CMakeLists.txt
├─ launch
│ ├─ m_send.launch
│ └─ sr_serial.launch (new!)
└─ src
├─ teacher.cpp
├─ student.cpp
├─ sender_serial.cpp (new!)
└─ receiver_serial.cpp (new!)
동작을 위한 소스 코드는 다음과 같다. 소스 코드는 C++로 작성하였다.
sender_serial.cpp
#include <ros/ros.h>
#include <std_msgs/Int32.h>
int main (int argc, char **argv) {
ros::init(argc, argv, "sender_serial");
ros::NodeHandle nh;
ros::Publisher pub = nh.advertise<std_msgs::Int32>("my_topic", 10);
ros::Rate rate(2);
int count = 1;
while (ros::ok()) {
std_msgs::Int32 msg;
msg.data = count;
pub.publish(msg);
count = count + 1;
rate.sleep();
}
return 0;
}
+) 코드의 nh.advertise() 내부의 10은 queue size를 지정한 것이며, queue size는 발행되는 메시지를 얼마나 가지고 있을 것인가와 관련된 변수이다. 이 값은 데이터의 종류와 양에 따라 적절히 설정하는 것이 좋다(Python에서는 알아서 설정해 준다). 너무 크거나 적으면 메모리 낭비나 데이터 손실이 야기될 수 있다.
receiver_serial.cpp
#include <ros/ros.h>
#include <std_msgs/Int32.h>
void callback (const std_msgs::Int32::ConstPtr& count) {
ROS_INFO("%d", count->data);
}
int main (int argc, char **argv) {
ros::init(argc, argv, "receiver_serial");
ros::NodeHandle nh;
ros::Subscriber sub = nh.subscribe("my_topic", 10, callback);
ros::spin();
return 0;
}
sr_serial.launch
<launch>
<node pkg="msg_send" type="sender_serial" name="sender_serial_node">
</node>
<node pkg="msg_send" type="receiver_serial" name="receiver_serial_node" output="screen">
</node>
</launch>
+) 앞선 예제와 마찬가지로 msg_send의 CMakeLists.txt에 조건들을 추가해줘야 한다.
https://leevision.tistory.com/80
[ROS 튜토리얼] ROS 노드 통신 프로그래밍
학습 내용 메시지를 통한 노드 간 통신 Teacher(Publisher)가 "my_topic"이라는 토픽을 Student(Subscriber)에게 전송하면 Student가 이 화면에 출력하는 토픽 통신 시스템을 구현해 보자. 언어는 C++를 사용한다.
leevision.tistory.com
누락을 방지하기 위해 수신 노드(receiver)를 먼저 실행시킨 뒤 송신 노드(sender)를 실행해 보자. 실행 순서는 다음과 같다. 3개의 터미널을 켜서 각각 roscore, receiver_serial.cpp, sender_serial.cpp를 실행한다. 이때 순서는 roscore 후 receiver_serial을 먼저 실행하고 sender_serial을 실행시켜 출력을 확인한다.
동작 결과, receiver는 첫 번째 숫자인 1을 받지 못하고 2부터 받아오는 것을 알 수 있다. 즉, 토픽의 누락이 발생하였다.
왜 이런 일이 발생한 걸까?
과제 #1의 문제 상황은 Publisher와 Subscriber의 동기화 문제라고 볼 수 있다. 즉 둘 사이의 실질적인 통신구축에 지연시간이 존재하는 것이다. 위의 시간차를 두고 송수신하는 방법은 실질적으로 누락을 해결하진 못한다. 시간차를 둔다고 하더라도 수신 노드가 수신할 준비가 되지 않았는데 토픽을 전송한다면 수신 노드에서 이를 받지 못하고 잃어버릴 가능성이 있는 것이다.
따라서 이를 해결하기 위해서는 수신 노드에게 확실히 수신할 준비가 되었는지 확인하는 절차가 필요하다. 이는 노드가 등록이 됐는지 확인하는 getNumPublishers()와 getNumSubscribers()가 있다. (Python에서는 get_num_connections() 함수를 이용한다) 단, 주의해야 할 점은 ros::Publisher Class에 getNumSubscribers()가 정의되어 있고 ros::Subscriber Class에 getNumPublishers()가 정의되어 있다.
// Returns the number of subscribers that are currently connected to this Publisher.
int num_connections = pub.getNumSubscribers();
// Returns the number of subscribers that are currently connected to this Subscriber.
int num_connections = pub.getNumPublishers();
여기서는 보내는 노드(Publisher) 쪽에서 getNumSubscribers()를 사용하여 받을 노드(Subcriber)가 준비가 되었는지 확인한다. getNumSubscribers()은 현재 publisher와 연결되어 있는 subscriber 노드의 수를 반환한다. 즉, getNumSubscribers()의 반환 값이 0이 아닐 때 메시지 전송을 시작한다면 토픽 누락을 해결할 수 있다.
sender_serial.cpp
#include <ros/ros.h>
#include <std_msgs/Int32.h>
int main (int argc, char **argv) {
ros::init(argc, argv, "sender_serial");
ros::NodeHandle nh;
ros::Publisher pub = nh.advertise<std_msgs::Int32>("my_topic", 10);
ros::Rate rate(2);
int count = 1;
// 확인한 subscribers가 0이 아닌, 즉 수신 노드가 준비 되었을 때 while을 빠져나온다.
while (pub.getNumSubscribers() == 0)
count = 1;
while (ros::ok()) {
std_msgs::Int32 msg;
msg.data = count;
pub.publish(msg);
count = count + 1;
rate.sleep();
}
return 0;
}
+) 이때 1:N 통신에서도 pub.getNumSubscribers()의 반환 값을 이용할 수 있다. 간단히 while 조건문을 N이 아닌 경우로 바꾸면 된다.
코드를 수정한 후 다시 실행하였을 때, 토픽 누락 없이 제대로 동작하는 것을 알 수 있다.
결과 정리
이번 실습을 통해 노드 통신 과정에서 데이터의 누락이 발생한 문제 상황에 대해 원인과 해결 방법에 대해 학습하였다. 노드와 노드 사이의 동기화 문제로 인해 지연시간이 발생하게 되고 이 때문에 토픽이 누락되는 문제가 발생할 수 있다. 이를 해결하기 위해, 전송받을 노드가 상태를 확인하고 이를 만족하였을 때 전송하는 방법을 사용할 수 있다. C++에서는 getNumSubscribers() 함수를 사용하면 현재 publisher와 연결된 subscriber의 수를 확인할 수 있고 원하는 수의 subscriber가 확인이 되었을 때, 메시지 전송을 시작하도록 구현하면 된다.
'Study > ROS' 카테고리의 다른 글
[ROS 튜토리얼] 과제 #3. 도착하는 데이터를 미처 처리하지 못하면 어떻게 되는가? (0) | 2023.10.04 |
---|---|
[ROS 튜토리얼] 과제 #2. 데이터 크기에 따른 전송속도는 어떻게 되는가? (1) | 2023.10.03 |
[ROS 튜토리얼] ROS 통신 중 발생하는 다양한 문제 상황과 해결 방법 (1) | 2023.10.03 |
[ROS 튜토리얼] ROS 노드 통신 프로그래밍 (0) | 2023.09.27 |
[ROS 튜토리얼] ROS 기초 실습 (1) 거북이 토픽 전송 (0) | 2023.09.25 |