How can I add a subscriber in C++ in ROS - c++

So I am trying to add a subscriber to a specific topic.
The purpose of the subscriber is to get range messages from the pi_sonar topic and use it in the code.
This is the code here:
line Follower code
so, if I wanted to add the sonar messages, should it look like this:
void turtlebot::range_sub('pacakge name of sonars'::range msg){
turtlebot::rng = msg.range;
}
based on what I was able to understand here I mean…
Is that correct?
I am gonna try it once I have my hands on the robot

You can follow this tutorial, it explains exactly what you are looking for,
#include <ros/ros.h>
#include <sensor_msgs/Range.h>
void sonarCallback(const sensor_msgs::Range::ConstPtr& msg)
{
ROS_INFO("Sonar Seq: [%d]", msg->header.seq);
ROS_INFO("Sonar Range: [%f]", msg->range);
}
int main(int argc, char **argv)
{
ros::init(argc, argv, "infrared_listener");
ros::NodeHandle n;
ros::Subscriber sub = n.subscribe("sensor/sonar0", 1000, sonarCallback);
ros::spin();
return 0;
}
Basically, you have to create ros::Subscriber whose callback, i.e., sonarCallback, is listening to incoming messages where you can implement your logic what to do with the sonar sensor reading, Please go through the link I shared and if something not clear, update the question accordingly

Related

ZeroMQ PubSub using inproc sockets hangs forever

I'm adapting a tcp PubSub example to using inproc with multithread. It ends up hanging forever.
My setup
macOS Mojave, Xcode 10.3
zmq 4.3.2
The source code reeproducing the issue:
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <thread>
#include "zmq.h"
void hello_pubsub_inproc() {
void* context = zmq_ctx_new();
void* publisher = zmq_socket(context, ZMQ_PUB);
printf("Starting server...\n");
int pub_conn = zmq_bind(publisher, "inproc://*:4040");
void* subscriber = zmq_socket(context, ZMQ_SUB);
printf("Collecting stock information from the server.\n");
int sub_conn = zmq_connect(subscriber, "inproc://localhost:4040");
sub_conn = zmq_setsockopt(subscriber, ZMQ_SUBSCRIBE, 0, 0);
std::thread t_pub = std::thread([&]{
const char* companies[2] = {"Company1", "Company2"};
int count = 0;
for(;;) {
int which_company = count % 2;
int index = (int)strlen(companies[0]);
char update[12];
snprintf(update, sizeof update, "%s",
companies[which_company]);
zmq_msg_t message;
zmq_msg_init_size(&message, index);
memcpy(zmq_msg_data(&message), update, index);
zmq_msg_send(&message, publisher, 0);
zmq_msg_close(&message);
count++;
}
});
std::thread t_sub = std::thread([&]{
int i;
for(i = 0; i < 10; i++) {
zmq_msg_t reply;
zmq_msg_init(&reply);
zmq_msg_recv(&reply, subscriber, 0);
int length = (int)zmq_msg_size(&reply);
char* value = (char*)malloc(length);
memcpy(value, zmq_msg_data(&reply), length);
zmq_msg_close(&reply);
printf("%s\n", value);
free(value);
}
});
t_pub.join();
// Give publisher time to set up.
sleep(1);
t_sub.join();
zmq_close(subscriber);
zmq_close(publisher);
zmq_ctx_destroy(context);
}
int main (int argc, char const *argv[]) {
hello_pubsub_inproc();
return 0;
}
The result
Starting server...
Collecting stock information from the server.
I've also tried adding this before joining threads to no avail:
zmq_proxy(publisher, subscriber, NULL);
The workaround: Replacing inproc with tcp fixes it instantly. But shouldn't inproc target in-process usecases?
Quick research tells me that it couldn't have been the order of bind vs. connect, since that problem is fixed in my zmq version.
The example below somehow tells me I don't have a missing shared-context issue, because it uses none:
ZeroMQ Subscribers not receiving message from Publisher over an inproc: transport class
I read from the Guide in the section Signaling Between Threads (PAIR Sockets) that
You can use PUB for the sender and SUB for the receiver. This will correctly deliver your messages exactly as you sent them and PUB does not distribute as PUSH or DEALER do. However, you need to configure the subscriber with an empty subscription, which is annoying.
What does it mean by an empty subscription?
Where am I doing wrong?
You can use PUB for the sender and SUB for the receiver. This will correctly deliver your messages exactly as you sent them and PUB does not distribute as PUSH or DEALER do. However, you need to configure the subscriber with an empty subscription, which is annoying.
Q : What does it mean by an empty subscription?
This means to set ( configure ) a subscription, driving a Topic-list message-delivery filtering, using an empty subscription string.
Q : Where am I doing wrong?
Here :
// sub_conn = zmq_setsockopt(subscriber, ZMQ_SUBSCRIBE, 0, 0); // Wrong
sub_conn = zmq_setsockopt(subscriber, ZMQ_SUBSCRIBE, "",0); // Empty string
Doubts also here, about using a proper syntax and naming rules :
// int pub_conn = zmq_bind(publisher, "inproc://*:4040");
int pub_conn = zmq_bind(publisher, "inproc://<aStringWithNameMax256Chars>");
as inproc:// transport-class does not use any kind of external stack, but maps the AccessPoint's I/O(s) onto 1+ memory-locations ( a stack-less, I/O-thread not requiring transport-class ).
Given this, there is nothing like "<address>:<port#>" being interpreted by such (here missing) protocol, so the string-alike text gets used as-is for identifying which Memory-location are the message-data going to go into.
So, the "inproc://*:4040" does not get expanded, but used "literally" as a named inproc:// transport-class I/O-Memory-location identified as [*:4040] ( Next, asking a .connect()-method of .connect( "inproc://localhost:4040" ) will, and must do so, lexically miss the prepared Memory-location: ["*:4040"] as the strings do not match
So this ought fail to .connect() - error-handling might be silent, as since the versions +4.x there is not necessary to obey the historical requirement to first .bind() ( creating a "known" named-Memory-Location for inproc:// ) before one may call a .connect() to get it cross-connected with an "already existing" named-Memory-location, so the v4.0+ will most probably not raise any error on calling and creating a different .bind( "inproc://*:4040" ) landing-zone and next asking a non-matching .connect( "inproc://localhost:4040" ) ( which does not have a "previously prepared" landing-zone in an already existing named-Memory-location.

Multithreading behaviour with ROS AsyncSpinner

I am trying to understand how the AsyncSpinner from ROS really works because I may have something misunderstood. You can find a similar question here.
As seen here its definition mentions:
Asynchronous spinner: spawns a couple of threads (configurable) that
will execute callbacks in parallel while not blocking the thread that
called it. The start/stop method allows to control when the callbacks
start being processed and when it should stop.
And in the official documentation here the AsyncSpinning is also remarked as a type of multi-threading Spinning.
So, said that, I have a very simple example with a publisher and subscriber with an AsyncSpinner to test the multi-threading behavior.
#include "ros/ros.h"
#include "std_msgs/String.h"
int main(int argc, char **argv)
{
ros::init(argc, argv, "publisher");
ros::NodeHandle nh;
ros::Publisher chatter_pub = nh.advertise<std_msgs::String>("chatter", 1000);
ros::Rate loop_rate(10);
while (ros::ok())
{
std_msgs::String msg;
msg.data = "hello world";
chatter_pub.publish(msg);
ros::spinOnce();
loop_rate.sleep();
}
return 0;
}
And the subscriber where the spinner is defined and used:
#include "ros/ros.h"
#include "std_msgs/String.h"
#include <boost/thread.hpp>
int count = 0;
void chatterCallback(const std_msgs::String::ConstPtr& msg)
{
count++;
ROS_INFO("Subscriber %i callback: I heard %s", count, msg->data.c_str());
sleep(1);
}
int main(int argc, char **argv)
{
ros::init(argc, argv, "subscriber");
ros::NodeHandle nh;
ros::Subscriber sub = nh.subscribe("chatter", 1000, chatterCallback);
ros::AsyncSpinner spinner(boost::thread::hardware_concurrency());
ros::Rate r(10);
spinner.start();
ros::waitForShutdown();
return 0;
}
When I run both programs I get the following output:
[ INFO] [1517215527.481856914]: Subscriber 1 callback: I heard hello world
[ INFO] [1517215528.482005146]: Subscriber 2 callback: I heard hello world
[ INFO] [1517215529.482204798]: Subscriber 3 callback: I heard hello world
As you can see the callback runs every second and no other callbacks are being called in parallel. I know that the global callback queue is being fulfilled because if I stop the publisher, the subscriber will keep popping the accumulated messages from the queue.
I know I should not block a callback but in the definition above is remarked that this will not stop the thread where it was called and I guess neither the others created by the spinner. Am I blocking the next callbacks just because I'm blocking the callback? Is there something I did misunderstood? I am bit confused and not able to demonstrate that the callbacks are running in parallel. Maybe you have another example?
Short answer:
ROS callbacks are threadsafe by default. This means a registered callback can only be processed by one thread and concurrent calls are disabled. A second thread is not able to access the same callback at the same time.
If you register a second callback, you will see the spinner working like expected and multiple threads are calling your callbacks at the same time.
ros::Subscriber sub1 = nh.subscribe("chatter", 1000, chatterCallback);
ros::Subscriber sub2 = nh.subscribe("chatter", 1000, chatterCallback);
Extended answer:
An async spinner tries to call available callbacks in the callback queue as fast as the rate allows. If the callback is already in process (by an other thread) the CallResult is TryAgain. This means a new attempt will be started later on.
The implementation of this lock uses the variable allow_concurrent_callbacks_ which means this behaviour is optional.
Solution:
It is possible to allow concurrent calls by setting the correct SubscribeOptions.allow_concurrent_callbacks which is false by default. Therefore you need to define your own SubscribeOptions. Here is the code you need to subscribe and allow concurrent callback calls:
ros::SubscribeOptions ops;
ops.template init<std_msgs::String>("chatter", 1000, chatterCallback);
ops.transport_hints = ros::TransportHints();
ops.allow_concurrent_callbacks = true;
ros::Subscriber sub = nh.subscribe(ops);

Using UDP transport data to Non-ROS computer over UDP

I want to transport the data from a ROS computer to the Non-ROS computer over the UDP.
To specify my works now :
I have a LiDAR(Sick TiM561) and I can launch it by the ROS computer successfully, and I can use the "Subscriber" to get the data of the LiDAR now using roscpp which is the c++ code in the ROS. Now,I need to transport the LiDAR's data to the microbox which is a MATLAB-based computer (this PC that you can build your program in it by MATLAB). But I'm not familiar in the c++ code, could someone can suggest me or direct me how to modify the code as below and add it into my roscpp code? Thanks in advance!
For the structure, the Micro box is the client, ROS PC is server.
This is the code of my roscpp:
#include "ros/ros.h"
#include "std_msgs/String.h"
#include "sensor_msgs/LaserScan.h"
void laser_msg_Callback(const sensor_msgs::LaserScan::ConstPtr& scan)
{
for (int x=0;x< scan->ranges.size();x++)
{
ROS_INFO("I heard: [%f]", scan->ranges[x]);
}
}
int main(int argc, char **argv)
{
ros::init(argc, argv, "sick_listener");
ros::NodeHandle n;
ros::Subscriber sub = n.subscribe("scan", 811, laser_msg_Callback);
ros::spin();
return 0;
}
And this code maybe is what I need:
UdpClient client;
public IPAddress serverIP = IPAddress.Parse("140.124.35.1");
public Form1()
{
InitializeComponent();
client = new UdpClient();
}
public void SendData()
{
client.Connect(serverIP, 3000);
byte[] data = Encoding.ASCII.GetBytes("Hi, I'm new client.");
client.Send(data, data.Length);
DoListening();
}
public void DoListening()
{
IPEndPoint adress = new IPEndPoint(serverIP, 3000);
byte[] receivedbytes = client.Receive(ref adress);
string recieved = Encoding.ASCII.GetString(receivedbytes);
MessageBox.Show("Recieved: " + recieved);
}
private void button1_Click(object sender, EventArgs e)
{
SendData();
}
I'd think a possible way to go for you is to check out the rossereal implementations. I'm not familiar with development for microbox, probably none of the rosserial supported platforms works with it out of the box, but I thought it's what the rosserial for: to provide a ros-compliant protocol for communicating a ros server with non-ros devices. For that your ros-nodes' code don't even need to know if the client's publishers and/or subscribers are regular ros nodes running on a ros-running computer or they are mimicked by a rosserial client on a non-ros device.
When I used it, the efficiency of rosserial_windows was good for me. I was able to control a rather high resolution camera attached to a windows computer connected to a network with several ros running computers to transfer image messages from the camera with a satisfiable rate.
If none of the available rosserial implementations are compatible with microbox then you may want to check out the rosserial's "Adding Support for New Hardware" and the existing code for rosserial_embeddedlinux to implement the rosserial-like connection thru udp.

Running C++ application reacting to outside changes

I have a C++ application that will be running constantly. It is listening for messages from a wireless module.
#include <stdlib.h>
#include <stdio.h>
struct payload {
char node[16];
char message
};
...
int main(int argc, char** argv) {
mesh.setNodeID(0); //irrelevant
mesh.begin(); //irrelevant
while(1){
mesh.update(); //irrelevant
mesh.DHCP(); //irrelevant
while(network.available()) {
struct payload received;
mesh.read(header, &received, sizeof(received)); //irrelevant
}
//below code goes here
}
And I want to be able to also send messages from this system.
I am currently reading a line from file:
//pseudo code
if (!fileEmpty) {
line = readLine();
struct payload send;
send.node = //splitted line
send.message = //splitted line
mesh.write(header, &send, sizeof(send));
And I split the line using strtok and assign the parts to a struct.
But there should be a better way.
I can't split the code for sending in different file (called with arguments) because there is some problem with the wireless module when I am listening and sending messages simultaneously. Assuming I splitted the code in two different files, I can kill the listening program when sending and then run it again, but this seems like a bad way of doing things.
So I am out of ideas.

ZeroMQ Pub Sending Empty String

I've got a simple C++ PUB and python SUB set up, with intent to have C++ side built as a simple DLL eventually. I've had some prior experience with a similar set up with python on both sides, and no issues. I am, however a total C++ noob.
My C++ code:
#define ZMQ_EXPORT
#include "stdafx.h"
#include "zmq.hpp"
int _tmain(int argc, _TCHAR* argv[]) {
zmq::context_t context(1);
zmq::socket_t publisher(context, ZMQ_PUB);
publisher.bind("tcp://*:6666");
zmq::message_t message(5);
memcpy(message.data(), "Hello", 5);
while(true) {
Sleep(500);
publisher.send(message);
}
return 0;
}
Result from python SUB script on recv_multipart():
['']
I am confident it is otherwise working, though I think there's a flaw with how I am doing the memcpy.
I'm thinking your missing the whole 'subscription' part of pub/sub
You need to give the PUB message some sort of message filter. This also means that your SUB needs to do the setsockopt to be able to receive messages.
You're given example shows that you in fact do not have a message filter for your PUB message (or rather your "Hello" IS your message filter and the data message is infact an empty string).