Basic knowledge learning
🌟 The difference between topic and service
- | topic of conversation | service |
---|---|---|
Synchronization | asynchronous | synchronization |
communication model | Publish / subscribe | Server / client |
Underlying protocol | ROSTCP/ROSUDP | ROSTCP/ROSUDP |
feedback mechanism | nothing | have |
buffer | have | nothing |
Real time | weak | strong |
Node relationship | Many to many | One to many |
Applicable scenario | data transmission | Logical processing |
Field ROS communication demo
1, Create a workspace
- Create a workspace named ros
mkdir -p ~/ros/src
- Enter the src folder where the workspace was created
cd ~/ros/src
- Generate workspace
catkin_init_workspace
- At this time, the workspace is empty and can be compiled
//Enter the workspace cd ~/ros/ //compile catkin_make
- Register the workspace in bash
//bash registration source devel/setup.bash //verification echo $ROS_PACKAGE_PATH //If you can see the file path of your workspace, it indicates that the workspace is created successfully
2, Create a ROS project package
- Switch to our workspace
cd ~/ros/src
- Using catkin_ create_ The PKG command creates a package called comm (Communication), which relies on std_msgs,roscpp,rospy
catkin_create_pkg comm std_msgs rospy roscpp
- Compile the project package in the workspace
//Enter the workspace cd ~/ros/ //compile catkin_make
3, Create transmitting and receiving nodes for communication
- Switch the directory to our comm project package
cd ~/ros/src/comm
- Enter src subdirectory
cd src
- Create a talker.cpp file in the src directory and write a Publisher node
touch talker.cpp
Open talker.cpp and replace the contents with the following:
#include "ros/ros.h" #include "std_msgs/String.h" #include <sstream> int main(int argc, char **argv) { ros::init(argc, argv, "talker"); ros::NodeHandle n; ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000); ros::Rate loop_rate(10); int count = 0; while (ros::ok()) { std_msgs::String msg; std::stringstream ss; ss << "hello world " << count; msg.data = ss.str(); ROS_INFO("%s", msg.data.c_str()); chatter_pub.publish(msg); ros::spinOnce(); loop_rate.sleep(); ++count; } return 0; }
- Create a listener.cpp file in the src directory and write a Subscriber node
touch listener.cpp
#include "ros/ros.h" #include "std_msgs/String.h" void chatterCallback(const std_msgs::String::ConstPtr& msg) { ROS_INFO("I heard: [%s]", msg->data.c_str()); } int main(int argc, char **argv) { ros::init(argc, argv, "listener"); ros::NodeHandle n; ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback); ros::spin(); return 0; }
- Edit the Cmakelist.txt file (Note: it is the CMakelist file under the comm project package)
//Add the following code at the end of the file include_directories(include ${catkin_INCLUDE_DIRS}) add_executable(talker src/talker.cpp) target_link_libraries(talker ${catkin_LIBRARIES}) add_dependencies(talker comm_generate_messages_cpp) add_executable(listener src/listener.cpp) target_link_libraries(listener ${catkin_LIBRARIES}) add_dependencies(listener comm_generate_messages_cpp)
Note: there should be no interval between the above adjacent codes, that is, do not leave a blank line. Be sure to be adjacent to each other, otherwise an error will be reported when compiling later
6. Switch the directory to the workspace directory and execute catkin_make run command
//Enter the workspace cd ~/ros/ //compile catkin_make
4, Correctness of test procedure
- Open a new terminal and start the ROS core program roscore
roscore
- Before starting to run our program, register the program at the original terminal
//Switch to workspace cd ~/ros //Program registration source ./devel/setup.bash
- Running the talker node
rosrun comm talker
- Create a new terminal and run the listener node
//Back to the workspace cd ~/ros //Program registration source ./devel/setup.bash //Run the listener node rosrun comm listener
Seeing the publish and subscribe of the two terminals proves successful!
Image ROS communication demo
The steps for creating a workspace are the same as for field communication
- image_publisher.cpp
#include <ros/ros.h> #include <image_transport/image_transport.h> #include <opencv2/highgui/highgui.hpp> #include <cv_bridge/cv_bridge.h> int main(int argc, char** argv) { ros::init(argc, argv, "image_publisher"); ros::NodeHandle nh; image_transport::ImageTransport it(nh); image_transport::Publisher pub = it.advertise("camera/image", 1); cv::Mat image = cv::imread(argv[1], CV_LOAD_IMAGE_COLOR); sensor_msgs::ImagePtr msg = cv_bridge::CvImage(std_msgs::Header(), "bgr8", image).toImageMsg(); ros::Rate loop_rate(5); while (nh.ok()) { pub.publish(msg); ros::spinOnce(); loop_rate.sleep(); } }
- image_subscrber.cpp
#include <ros/ros.h> #include <image_transport/image_transport.h> #include <opencv2/highgui/highgui.hpp> #include <cv_bridge/cv_bridge.h> void imageCallback(const sensor_msgs::ImageConstPtr& msg) { try { cv::imshow("view", cv_bridge::toCvShare(msg, "bgr8")->image); } catch (cv_bridge::Exception& e) { ROS_ERROR("Could not convert from '%s' to 'bgr8'.", msg->encoding.c_str()); } } int main(int argc, char **argv) { ros::init(argc, argv, "image_listener"); ros::NodeHandle nh; cv::namedWindow("view"); cv::startWindowThread(); image_transport::ImageTransport it(nh); image_transport::Subscriber sub = it.subscribe("camera/image", 1, imageCallback); ros::spin(); cv::destroyWindow("view"); }
- package.xml
<?xml version="1.0"?> <package format="2"> <name>ros_cv_image</name> <version>0.0.0</version> <description>The ros_cv_image package</description> <buildtool_depend>catkin</buildtool_depend> <build_depend>cv_bridge</build_depend> <build_depend>image_transport</build_depend> <build_depend>roscpp</build_depend> <build_depend>rospy</build_depend> <build_depend>std_msgs</build_depend> <build_depend>opencv2</build_depend> <build_export_depend>cv_bridge</build_export_depend> <build_export_depend>image_transport</build_export_depend> <build_export_depend>roscpp</build_export_depend> <build_export_depend>rospy</build_export_depend> <build_export_depend>std_msgs</build_export_depend> <build_export_depend>opencv2</build_export_depend> <exec_depend>cv_bridge</exec_depend> <exec_depend>image_transport</exec_depend> <exec_depend>roscpp</exec_depend> <exec_depend>rospy</exec_depend> <exec_depend>std_msgs</exec_depend> <exec_depend>opencv2</exec_depend> <!-- The export tag contains other, unspecified, tags --> <export> </export> </package>
- CMakeList.xml
cmake_minimum_required(VERSION 2.8.3) project(ros_cv_image) find_package(catkin REQUIRED COMPONENTS cv_bridge image_transport roscpp rospy std_msgs ) #Add this sentence find_package(OpenCV REQUIRED) catkin_package( # INCLUDE_DIRS include # LIBRARIES ros_cv_image # CATKIN_DEPENDS cv_bridge image_transport roscpp rospy std_msgs # DEPENDS system_lib ) #Add ${OpenCV_INCLUDE_DIRS} include_directories( ./include ${catkin_INCLUDE_DIRS} ${OpenCV_INCLUDE_DIRS} ) ## Add cmake target dependencies of the library ## as an example, code may need to be generated before libraries ## either from message generation or dynamic reconfigure # add_dependencies(${PROJECT_NAME} ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS}) ## Add cmake target dependencies of the executable ## same as for the library above # add_dependencies(${PROJECT_NAME}_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS}) ## Add folders to be run by python nosetests # catkin_add_nosetests(test) add_executable(image_publisher src/image_publisher) target_link_libraries(image_publisher ${catkin_LIBRARIES} ${OpenCV_LIBRARIES}) add_executable(image_subscriber src/image_subscriber) target_link_libraries(image_subscriber ${catkin_LIBRARIES} ${OpenCV_LIBRARIES})
⚠️ During running publisher, you need to add a parameter: rosrun comm publisher xxx.jpg
Video ROS communication demo
The steps for creating a workspace are the same as for field communication
- imagepub.cpp
#include<ros/ros.h> #include<cv_bridge/cv_bridge.h> #include<sensor_msgs/image_encodings.h> #include<image_transport/image_transport.h> #include<opencv2/opencv.hpp> #include<opencv2/highgui/highgui.hpp> #include<opencv2/imgproc/imgproc.hpp> #include<stdio.h> using namespace cv; using namespace std; int main(int argc, char** argv) { // ROS node initialization ros::init(argc, argv, "image_publisher"); ros::NodeHandle n; ros::Time time = ros::Time::now(); ros::Rate loop_rate(5); // Define node handle image_transport::ImageTransport it(n); image_transport::Publisher pub = it.advertise("video_image", 1); sensor_msgs::ImagePtr msg; // opencv ready to read video cv::VideoCapture video; video.open("/home/vlt/Pictures/1.mp4"); if( !video.isOpened() ) { ROS_INFO("Read Video failed!\n"); return 0; } Mat frame; int count = 0; while(1) { video >> frame; if( frame.empty() ) break; count++; msg = cv_bridge::CvImage(std_msgs::Header(), "bgr8", frame).toImageMsg(); pub.publish(msg); ROS_INFO( "read the %dth frame successfully!", count ); loop_rate.sleep(); ros::spinOnce(); } video.release(); return 0; }
- imagesub.cpp
#include<ros/ros.h> #include<cv_bridge/cv_bridge.h> #include<sensor_msgs/image_encodings.h> #include<image_transport/image_transport.h> #include<opencv2/opencv.hpp> #include<opencv2/highgui/highgui.hpp> #include<opencv2/imgproc/imgproc.hpp> #include<stdio.h> #include<math.h> #include<vector> void imageCalllback(const sensor_msgs::ImageConstPtr& msg) { ROS_INFO("Received \n"); try{ cv::imshow( "video", cv_bridge::toCvShare(msg, "bgr8")->image ); cv::waitKey(30); } catch( cv_bridge::Exception& e ) { ROS_ERROR( "Could not convert from '%s' to 'bgr8'.", msg->encoding.c_str() ); } } int main(int argc, char** argv) { ros::init(argc, argv, "image_listener"); ros::NodeHandle n; cv::namedWindow("video"); cv::startWindowThread(); image_transport::ImageTransport it(n); image_transport::Subscriber sub = it.subscribe( "video_image", 1, imageCalllback ); ros::spin(); cv::destroyWindow("video"); return 0; }
- package.xml
<?xml version="1.0"?> <package format="2"> <name>ros_cv_image</name> <version>0.0.0</version> <description>The ros_cv_image package</description> <buildtool_depend>catkin</buildtool_depend> <build_depend>cv_bridge</build_depend> <build_depend>image_transport</build_depend> <build_depend>roscpp</build_depend> <build_depend>rospy</build_depend> <build_depend>sensor_msgs</build_depend> <build_depend>std_msgs</build_depend> <build_depend>opencv2</build_depend> <build_export_depend>cv_bridge</build_export_depend> <build_export_depend>image_transport</build_export_depend> <build_export_depend>roscpp</build_export_depend> <build_export_depend>rospy</build_export_depend> <build_export_depend>sensor_msgs</build_export_depend> <build_export_depend>std_msgs</build_export_depend> <build_export_depend>opencv2</build_export_depend> <exec_depend>cv_bridge</exec_depend> <exec_depend>image_transport</exec_depend> <exec_depend>roscpp</exec_depend> <exec_depend>rospy</exec_depend> <exec_depend>std_msgs</exec_depend> <exec_depend>opencv2</exec_depend> <!-- The export tag contains other, unspecified, tags --> <export> </export> </package>
- CMakeList.xml
cmake_minimum_required(VERSION 2.8.3) project(ros_cv_image) find_package(catkin REQUIRED COMPONENTS cv_bridge image_transport roscpp rospy sensor_msgs std_msgs ) #Add this sentence find_package(OpenCV REQUIRED) catkin_package( # INCLUDE_DIRS include # LIBRARIES ros_cv_image # CATKIN_DEPENDS cv_bridge image_transport roscpp rospy std_msgs # DEPENDS system_lib ) #Add ${OpenCV_INCLUDE_DIRS} include_directories( ./include ${catkin_INCLUDE_DIRS} ${OpenCV_INCLUDE_DIRS} ) ## Add cmake target dependencies of the library ## as an example, code may need to be generated before libraries ## either from message generation or dynamic reconfigure # add_dependencies(${PROJECT_NAME} ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS}) ## Add cmake target dependencies of the executable ## same as for the library above # add_dependencies(${PROJECT_NAME}_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS}) ## Add folders to be run by python nosetests # catkin_add_nosetests(test) add_executable(imagepub src/imagepub.cpp) target_link_libraries(imagepub ${catkin_LIBRARIES} ${OpenCV_LIBRARIES}) add_executable(imagesub src/imagesub.cpp) target_link_libraries(imagesub ${catkin_LIBRARIES} ${OpenCV_LIBRARIES})
Multi machine ROS communication
Configure ip and hostname
sudo gedit /etc/hosts
//Add the ip and hostname between the two hosts to be communicated, for example: 192.168.3.172 hero2 192.168.3.203 ivip-To-be-filled-by-O-E-M
Two or more hosts need to add ip and hostname
Reference link:
[1] ROS learning [2] - ROS communication programming (topic programming) in Ubuntu 16.04 link.
[2] ROS write, subscribe and publish pictures link.
[3] Subscribe and publish image messages in videos in ROS link.
[4] ROS official tutorial: link.