ROS theory and practice - 3 - ROS communication programming

ROS project development process

Create workspace - > create function package - > create source code (Python / C + +) - > configure Compilation Rules - > compile and run

Workspace

src: source space
Build: build space [intermediate file]
Development: executable space
Install: install space

Detect environment variables

laniakea@laniakea-virtual-machine >>> echo $ROS_PACKAGE_PATH
/home/laniakea/learn_GY0/catkin_ws/src:/opt/ros/melodic/share

Observe catkin_ lib folder in devel under WS
Place the compiled executable
. sh and bash setting script for environment variables

Placing function packs under src

Function package creation

catkin_create_pkg <package_name> [depend1] [depend2] [depend3]

Dependent-n: options on which the function pack depends

std_ Msgs: message interface defined in ROS standard
std_ Srvs: service interface defined in ROS standard


When you see a folder with cmakelists Txt and package XML, it must be a function package

  <build_depend>message_generation</build_depend> <!-- Compile dependency -->
  <exec_depend>message_runtime</exec_depend> <!-- Run dependency -->

ROS Topic communication programming

Publisher

  • Initialize ROS node;
  • Register the node information with the ROS Master, including the published topic name and the message type in the topic;
  • Create message data;
  • Circularly publish messages according to a certain frequency.

string_publisher.cpp

/**
 * This routine will publish chatter topic, and the message type is String
 */

#include <sstream>
#include "ros/ros.h"
#include "std_msgs/String.h"

int main(int argc, char **argv)
{
    // ROS node initialization
    ros::init(argc, argv, "string_publisher");

    // Create node handle
    ros::NodeHandle n;

    // Create a Publisher, publish topic named chatter, and the message type is std_msgs::String
    // "chatter" topic name
    // Buffer size
    // Create Publisher from handle
    ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);

    // Set the frequency of the cycle
    // 10 times per second at 10Hz
    ros::Rate loop_rate(10);

    int count = 0;
    while (ros::ok())
    {
        // Initialize std_msgs::String type message
        std_msgs::String msg;
        std::stringstream ss;
        ss << "hello world " << count;
        msg.data = ss.str();

        // Release news
        ROS_INFO("%s", msg.data.c_str());
        chatter_pub.publish(msg);

        // Delay according to cycle frequency
        loop_rate.sleep(); // 100ms
        // If the above program is executed for 10ms, it only sleeps for 90ms
        ++count;
    }
    return 0;
}

subscriber

  • Initialize ROS node;
  • Subscribe to topics needed;
  • Loop waiting for topic message, received
  • Enter the callback function after the message;
  • Complete the message processing in the callback function.

string_subscriber.cpp

/**
 * This routine will subscribe to chatter topic, and the message type is String
 */

#include "ros/ros.h"
#include "std_msgs/String.h"

// After receiving the subscribed message, it will enter the message callback function - > data processing
// const constant pointer 
// std_msgs::String message type

void chatterCallback(const std_msgs::String::ConstPtr& msg)
{
    // Print the received message
    ROS_INFO("I heard: [%s]", msg->data.c_str());
}

int main(int argc, char **argv)
{
    // Initialize ROS node
    ros::init(argc, argv, "string_subscriber");

    // Create node handle
    ros::NodeHandle n;

    // Create a Subscriber, subscribe to topic named chatter, and register the callback function chatterCallback
    // The buffer of Subscriber is not necessarily the same as that of Publisher
    // Because you don't know the sending frequency of Publisher, you need to query all the time, so you need a callback function
    ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);

    // Loop waiting callback function
    ros::spin();
    return 0;
}

Configure cmakelists txt

  • Set the code to be compiled and the generated executable file;
  • Set up link library;
add_executable(string_publisher src/string_publisher.cpp)
add_executable(string_subscriber src/string_subscriber.cpp)

target_link_libraries(string_publisher ${catkin_LIBRARIES})
target_link_libraries(string_subscriber ${catkin_LIBRARIES})

Custom message interface

tree                 
.
├── CMakeLists.txt
├── include
│   └── learning_communication
├── msg # Store message definition file
│   └── PersonMsg.msg
├── package.xml
└── src
    ├── string_publisher.cpp
    └── string_subscriber.cpp

4 directories, 5 files

PersonMsg.msg

string name
uint8 age
uint8 sex

uint8 unknown = 0
uint8 male = 1
uint8 female = 2

In package Add in XML

  <build_depend>message_generation</build_depend>
  <exec_depend>message_runtime</exec_depend>

At cmakelists Txt

...
# Find dependent packages
find_package(catkin REQUIRED COMPONENTS  
  roscpp
  rospy
  std_msgs
  std_srvs
  message_ generation # Dynamic message generation function package
)
...
# Dynamically generate header file
add_message_files(FILES PersonMsg.msg)
generate_messages(DEPENDENCIES std_msgs)
...
catkin_package(
#  INCLUDE_DIRS include
#  LIBRARIES learning_communication
   CATKIN_DEPENDS roscpp rospy std_msgs std_srvs message_runtime
#  DEPENDS system_lib
)

A personmsg. Will be generated in devel H header file

person_publisher.cpp

/**
 * This routine will subscribe to / person_info topic, custom message type, learning_communication::PersonMsg
 */

#include <ros/ros.h>
#include "learning_communication/PersonMsg.h"

// After receiving the subscribed message, it will enter the message callback function
void personInfoCallback(const learning_communication::PersonMsg::ConstPtr& msg)
{
    // Print the received message
    ROS_INFO("Subcribe Person Info: name:%s  age:%d  sex:%d", 
			 msg->name.c_str(), msg->age, msg->sex);
}

int main(int argc, char **argv)
{
    // Initialize ROS node
    ros::init(argc, argv, "person_subscriber");

    // Create node handle
    ros::NodeHandle n;

    // Create a Subscriber named / person_info topic, register the callback function personInfoCallback
    ros::Subscriber person_info_sub = n.subscribe("/person_info", 10, personInfoCallback);

    // Loop waiting callback function
    ros::spin();

    return 0;
}

person_subscriber.cpp

/**
 * This routine will subscribe to / person_info topic, custom message type, learning_communication::PersonMsg
 */

#include <ros/ros.h>
#include "learning_communication/PersonMsg.h"

// After receiving the subscribed message, it will enter the message callback function
void personInfoCallback(const learning_communication::PersonMsg::ConstPtr& msg)
{
    // Print the received message
    ROS_INFO("Subcribe Person Info: name:%s  age:%d  sex:%d", 
			 msg->name.c_str(), msg->age, msg->sex);
}


int main(int argc, char **argv)
{
    // Initialize ROS node
    ros::init(argc, argv, "person_subscriber");

    // Create node handle
    ros::NodeHandle n;

    // Create a Subscriber named / person_info topic, register the callback function personInfoCallback
    ros::Subscriber person_info_sub = n.subscribe("/person_info", 10, personInfoCallback);

    // Loop waiting callback function
    ros::spin();

    return 0;
}

At cmakelists Txt

...
add_executable(person_publisher src/person_publisher.cpp)
add_executable(person_subscriber src/person_subscriber.cpp)
...
# Path to dynamically generate header file
add_dependencies(person_publisher ${PROJECT_NAME}_gencpp)
add_dependencies(person_subscriber ${PROJECT_NAME}_gencpp)
...
target_link_libraries(person_publisher ${catkin_LIBRARIES})
target_link_libraries(person_subscriber ${catkin_LIBRARIES})

ROS Service communication programming

$rossrv show std_srvs/SetBool   
bool data # request send
--- # Distinguish the contents of request and response
# response feedback
bool success
string message

Implementation server

  • Initialize ROS node;
  • Create a Server instance;
  • Wait for the service request circularly and enter the callback function;
  • Complete the processing of the service function in the callback function and feed back the response data.

string_client.cpp

/**
 * This routine will request print_string service, std_srvs::SetBool
 */

#include "ros/ros.h"
#Include "std_srvs / setbool. H" / / standard service interface definition in ROS

int main(int argc, char **argv)
{
    // ROS node initialization
    ros::init(argc, argv, "string_client");

    // Create node handle
    ros::NodeHandle n;

    // Create a client, and the service message type is std_srvs::SetBool
    ros::ServiceClient client = n.serviceClient<std_srvs::SetBool>("print_string");

    // Create STD_ Srvs:: service message of type setbool
    // Encapsulate the service message of the client
    std_srvs::SetBool srv;
    srv.request.data = true;

    // Issue a service request and wait for the response result
    // client.call(srv) blocks the function until the service gives a response
    if (client.call(srv))
    {
        ROS_INFO("Response : [%s] %s", srv.response.success?"True":"False", 
									   srv.response.message.c_str());
    }
    else
    {
        ROS_ERROR("Failed to call service print_string");
        return 1;
    }
    return 0;
}

Implementation client

  • Initialize ROS node;
  • Create a Client instance;
  • Publish service request data;
  • Wait for the response result after Server processing.

string_server.cpp

/**
 * This routine will provide print_string service, std_srvs::SetBool
 */

#include "ros/ros.h"
#include "std_srvs/SetBool.h"

// service callback function, input parameter req, output parameter res
bool print(std_srvs::SetBool::Request  &req,
         std_srvs::SetBool::Response &res)
{
    // Print string
    if(req.data)
	{
		ROS_INFO("Hello ROS!");
		res.success = true;
		res.message = "Print Successully";
	}
	else
	{
		res.success = false;
		res.message = "Print Failed";
	}

    return true;
}


int main(int argc, char **argv)
{
    // ROS node initialization
    ros::init(argc, argv, "string_server");

    // Create node handle
    ros::NodeHandle n;response 

    // Create a file called print_string server, register the callback function print()
    // Once a service enters (a node sends a service request), it enters the print callback function
    ros::ServiceServer service = n.advertiseService("print_string", print);

    // Loop waiting callback function
    ROS_INFO("Ready to print hello string.");
    ros::spin();

    return 0;
}

At cmakelists Txt

...
add_executable(string_client src/string_client.cpp)
add_executable(string_server src/string_server.cpp)
...
target_link_libraries(string_client ${catkin_LIBRARIES})
target_link_libraries(string_server ${catkin_LIBRARIES})

Custom service

In learning_ Create srv folder under communication folder
Customization file personsrv srv

string name
uint8  age
uint8  sex

uint8 unknown = 0
uint8 male    = 1
uint8 female  = 2
---
string result

person_client1

/**
 * This routine will request / show_person service, service data type learning_communication::PersonSrv
 */

#include <ros/ros.h>
#include "learning_communication/PersonSrv.h"

int main(int argc, char** argv)
{
    // Initialize ROS node
	ros::init(argc, argv, "person_client");

    // Create node handle
	ros::NodeHandle node;

    // After discovering the / spawn service, create a service client and connect to the service named / spawn
	ros::service::waitForService("/show_person");
	ros::ServiceClient person_client = node.serviceClient<learning_communication::PersonSrv>("/show_person");

    // Initialize learning_ Request data of communication:: person
	learning_communication::PersonSrv srv;
	srv.request.name = "Tom";
	srv.request.age  = 20;
	// In the srv file --- the above will generate the request namespace, and the following will generate the response namespace
	srv.request.sex  = learning_communication::PersonSrv::Request::male;

    // Request service call
	ROS_INFO("Call service to show person[name:%s, age:%d, sex:%d]", 
			 srv.request.name.c_str(), srv.request.age, srv.request.sex);

	// call(srv) sends a service request
	// Wait for the result of the server response
	person_client.call(srv);

	// Display service call results
	ROS_INFO("Show person result : %s", srv.response.result.c_str());

	return 0;
};

person_server1

/**
 * This routine will execute / show_person service, service data type learning_communication::PersonSrv
 */

#include <ros/ros.h>
#include "learning_communication/PersonSrv.h"

// service callback function, input parameter req, output parameter res
bool personCallback(learning_communication::PersonSrv::Request  &req,
         			learning_communication::PersonSrv::Response &res)
{
    // Display request data
    ROS_INFO("Person: name:%s  age:%d  sex:%d", req.name.c_str(), req.age, req.sex);

	// Set feedback data
	res.result = "OK";

    return true;
}

int main(int argc, char **argv)
{
    // ROS node initialization
    ros::init(argc, argv, "person_server");

    // Create node handle
    ros::NodeHandle n;

    // Create a file named / show_ The server of person registers the callback function personCallback
    ros::ServiceServer person_service = n.advertiseService("/show_person", personCallback);

    // Loop waiting callback function
    ROS_INFO("Ready to show person informtion.");
    ros::spin();

    return 0;
}
add_service_files(
  FILES
  PersonSrv.srv
)
...
add_dependencies(person_client1 ${PROJECT_NAME}_gencpp)
add_dependencies(person_server1 ${PROJECT_NAME}_gencpp)
...
target_link_libraries(person_client1 ${catkin_LIBRARIES})
target_link_libraries(person_server1 ${catkin_LIBRARIES})

Keywords: ROS

Added by CtrlAltDel on Sun, 30 Jan 2022 01:07:45 +0200