-
Notifications
You must be signed in to change notification settings - Fork 0
ROS1 Cpp Interfaces
- I'm referring especially to the ROS1 simple node pattern, but if you need a class patter for the node, few updates are needed.
- a topic name as a macro
TOPIC_???replacing???with the uppercase name of the topic - for queue size for one topic,
Q_SZ_???replacing???with the uppercase name of the topic
suppose you want to use the message msg inside the package pkg. The topic is simply /top; the NodeHandle object is nh.
// include the message type (pkg::msg)
#include <pkg/msg.h>
// name of the topic and queue size
#define TOPIC_TOP "/top"
#define Q_SZ_TOP 1000Some remarks:
- the main call is
ros::Publisher p = nh.advertize<pkg::msg>( "topic", queue_sz )which takes as input the type of the message, the topic (asstd::string) and the queue size (usually 1000)
// define globally as pointer
ros::Publisher *pub_top;
// in the main function ...
TLOG( "Creating publisher " << LOGSQUARE( TOPIC_TOP ) << "..." );
ros::Publisher tpub_top = nh.advertise<pkg::msg>( TOPIC_TOP, Q_SZ_TOP );
pub_top = &tpub_top;
TLOG( "Creating publisher " << LOGSQUARE( TOPIC_TOP ) << "... OK" );the main call is pub_topic->publish( m ) which simply publishes the message through the topic.
pkg::msg m;
// fill in the message
// m.field = value;
pub_topic->publish( m );
// remember to use
// ros::spinOnce();// define globally
ros::Subscriber *sub_top;
pkg__msg last_msg; // a copy of the previously received message
// define the "callback"
void cbk_top( const pkg::msg::ConstPtr& pm )
{
// save the last message
last_msg = pm;
// ...
}
// in main() define the interface
TLOG( "subscribing to the topic " << LOGSQUARE( TOPIC_TOP ) << "..." );
ros::Subscriber tsub_top = nh.subscribe( TOPIC_TOP, Q_SZ_TOP, cbk_top );
sub_top = &tsub_top;
TLOG( "subscribing to the topic " << LOGSQUARE( TOPIC_TOP ) << "... OK" );☝️ The method must be public.
let's say the class my_class contains the methods cbk_top() you want to use as callback. The syntax changes a bit:
TLOG( "subscribing to the topic " << LOGSQUARE( TOPIC_TOP ) << "..." );
ros::Subscriber tsub_top = nh.subscribe( TOPIC_TOP, Q_SZ_TOP, &my_class::cbk_top, &my_class );
sub_top = &tsub_top;
TLOG( "subscribing to the topic " << LOGSQUARE( TOPIC_TOP ) << "... OK" );The function prototype is the same.
// import the message type (pkg::srv , pkg::srv::Request , pkg::srv::Response)
#include <pkg::srv.h>
// global definitions
#define SERVICE_SERV "/serv"// define globally a pointer to the service
ros::ServiceServer *srv_serv;
pkg::srv last_srv;
// prototype of the callback
bool cbk_serv( pkg::srv::Request& req, pkg::srv::Response& res )
{
// save the request
last_srv.request = req;
// elaborate the response
// res.field = value;
// ...
// save the response
last_srv.response = res;
return true;
}
// within the main function
OUTLOG( "Advertising service " << LOGSQUARE( SERVICE_SERV ) << "..." );
ros::ServiceServer tsrv_serv = nh.advertiseService( SERVICE_SERV, cbk_serv );
srv_serv = &tsrv_serv;
OUTLOG( "Advertising service " << LOGSQUARE( SERVICE_SERV ) << "... OK" );☝️ The method must be public.
not so different. my_class containts the method cbk_serv I want to use as callback. Instanciate the class, then open the service:
// within the main function
OUTLOG( "Advertising service " << LOGSQUARE( SERVICE_SERV ) << "..." );
ros::ServiceServer tsrv_serv = nh.advertiseService( SERVICE_SERV, &my_class::cbk_serv, &my_class );
srv_serv = &tsrv_serv;
OUTLOG( "Advertising service " << LOGSQUARE( SERVICE_SERV ) << "... OK" );// define also this
#define TIMEOUT_SERV 5 //seconds
// client definition
ros::ServiceClient *cl_serv;
// main function
TLOG( "Opening client " << LOGSQUARE( SERVICE_SERV ) << "..." );
ros::ServiceClient tcl_serv = nh.serviceClient<pkg::srv>( SERVICE_SERV );
if( !tcl_serv.waitForExistence( ros::Duration( TIMEOUT_SERV ) ) )
{
TERR( "unable to contact the server - timeout expired (" << TIMEOUT_SERV << "s) " );
return 0;
}
cl_serv = &tcl_serv;
TLOG( "Opening client " << LOGSQUARE( SERVICE_SERV ) << "... OK" );ROS1 clients are always blocking, unless you don't create a thread in charge of managing the request.
// service message (we're going to fill in the request only)
pkg::srv sm;
// populate the request
// sm.request.field = value;
// call the service (blocking)
if( !cl_serv->call( sm ) )
{
TERR( "unable to make a service request -- failed calling service "
<< LOGSQUARE( SERVICE_SERV )
<< (!cl.exists( ) ? " -- it seems not opened" : "") );
return 0;
}
// use the response
// sm.response.field🔗 see ros::service Namespace reference
This style allows to not create a handle for dealing with the service. Here are two ways to check if a service exists:
// === FIRST METHOD -- NON BLOCKING === //
// check if a service exists (non-blocking)
ros::service::exists( SERVICE_SERV )
{
// the service exists... do whatever you want inside here
}
// === SECOND METHOD -- BLOCKING === //
// wait for the existence of a service (blocking, at least for a given time)
if( !ros::service::waitForExistence( SERVICE_SERV, ros::Duration( TIMEOUT_SERV ) ) )
{
TERR( "unable to contact the server - timeout expired (" << TIMEOUT_SERV << "s) " );
return 0;
}
And here is how to call the service using bare calling style:
// service message (we're going to fill in the request only)
pkg::srv sm;
// populate the request
// sm.request.field = value;
// call the service (blocking)
if( !ros::service::call( SERVICE_SERV, sm ) )
{
TERR( "unable to make a service request -- failed calling service "
<< LOGSQUARE( SERVICE_SERV )
<< (!ros::service::exists( SERVICE_SERV )) ? " -- it seems not opened" : "") );
return 0;
}
// use the response
// sm.response.field🔗 see the official ROS Wiki
🔗 in particular, see the C++ interface for ROS parameter server
ros::param::set("/global_param", 5);
ros::param::set("relative_param", "my_string");
ros::param::set("bool_param", false);🔗 see the ROSparam command line
# set one value
rosparam set parameter_name value
# from file
rosparam load param-file.yamlLoading from YAML files the parameters is for sure the most interesting method. Here's a simple example of YAML parameters files, just to appreciate a bit of its syntax:
string: 'foo'
integer: 1234
float: 1234.5
boolean: true
list: [1.0, mixed list]
dictionary: {a: b, c: d}<!-- one parameter -->
<param name="somefloat1" value="3.14159" type="double" />
<!-- a YAML file -->
<param name="configfile" textfile="$(find roslaunch)/example.yaml" />ros::param::del("my_param");if( ros::param::has("my_param") )
{
// it exists... yuppie!
}ros::param::get("/global_name", value_reference);