原本我要节点发布一张图片,并在在rviz上显示, 图片是一副背景图再加一些点. 如果在运算的主程序内,每次都要发布整张图片,代价有点高.
所以决定将相应要绘制的点发布出来,再开辟一个node进行绘图处理与图片发布. ===> 这些点的个数是动态的,因此需要进行动态数组msg的发布
ROS Message types.
#landmark.msg uint8 id float32 x float32 y float32 theta
#odom.msg float32 x float32 y float32 theta
#robot.msg float32 x float32 y float32 theta
#systemState.msg odom odom robot robot_state landmark[] landmark
将msg文件编译生成相应的c++ 可以识别的头文件 详见: 创建一个 msg
#CMakeLists.txt find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs message_generation) add_message_files( FILES odom.msg robot.msg landmark.msg systemState.msg ) generate_messages( DEPENDENCIES std_msgs)
# package.xml <build_depend>message_generation</build_depend> <run_depend>message_runtime</run_depend>
说明: .h文件一般生成在 ~/catkin_ws/devel/include/你的包名/***.h
我的在 /home/yhzhao/gmapping_ws/devel/include/cv_slam/**.h
头文件添加
#include <cv_slam/odom.h> #include <cv_slam/landmark.h> #include <cv_slam/robot.h> #include <cv_slam/systemState.h> ros::Publisher systemStates_;
systemStates_ = ar_handle.advertise<cv_slam::systemState>("systemStates", 10); void QrSlam::pubSystemStates() { cv_slam::systemState sys_state; cv_slam::odom odom; odom.x = robot_odom_.x; odom.y = robot_odom_.y; odom.theta = robot_odom_.theta; cv_slam::robot robot; robot.x = miu_state_.at<float>(0); robot.y = miu_state_.at<float>(1); robot.theta = miu_state_.at<float>(2); vector<int> landmarkIDs = getLandmarksIndex(); for(int i=0; i<landmarkIDs.size(); i++) { cv_slam::landmark landmark; landmark.x = miu_state_.at<float>(3*i +3); landmark.y = miu_state_.at<float>(3*i +4); landmark.theta = miu_state_.at<float>(3*i +5); landmark.id = landmarkIDs.at(i); sys_state.landmark.push_back(landmark); } sys_state.odom = odom; sys_state.robot_state = robot; systemStates_.publish(sys_state); }
接收节点的设计: 全部代码见尾部.
#include <cv_slam/odom.h> #include <cv_slam/landmark.h> #include <cv_slam/robot.h> #include <cv_slam/systemState.h> ros::Publisher image_pub_ ; ros::Publisher state_pub_ ; Mat map_; Mat view_; Mat state_img_; <pre name="code" class="cpp">void chatterCallback(const cv_slam::systemStateConstPtr& msg) { cv_slam::odom odom = msg->odom; cv_slam::robot robot = msg->robot_state; vector<cv_slam::landmark> landmarks = msg->landmark; showOdom(map_, odom, CV_RGB(0,0,0)) ; showRobot(map_, robot, CV_RGB(0,0,255)); drawCoordinate(map_); map_.copyTo( view_ ); //避免历史重影 landmark showLandmarks(view_, landmarks, CV_RGB(0,0,0)); resize(view_, view_, map_size_); cv_bridge::CvImage cvi; sensor_msgs::Image ros_img; ros::Time time=ros::Time::now(); cvi.header.stamp = time; cvi.header.frame_id = "image"; cvi.encoding = "bgr8"; cvi.image = view_;//Mat cvi.toImageMsg(ros_img); image_pub_.publish(cvi.toImageMsg()); Mat state_show; state_img_.copyTo( state_show ); //避免历史重影 landmark showOdom(state_show, odom, CV_RGB(0,0,0),50,50); showRobot(state_show, robot, CV_RGB(0,0,0),300,50); cv_bridge::CvImage cvi_state; sensor_msgs::Image state_img; ros::Time time2=ros::Time::now(); cvi_state.header.stamp = time2; cvi_state.header.frame_id = "image"; cvi_state.encoding = "bgr8"; cvi_state.image = state_show; //Mat cvi_state.toImageMsg(state_img); state_pub_.publish(cvi_state.toImageMsg()); } int main(int argc, char **argv) { ros::init(argc, argv, "view"); ros::NodeHandle n; map_ = cv::imread("./OutPut/map/map.png", CV_LOAD_IMAGE_COLOR); //for display map_size_.width = map_.cols * map_scale_; map_size_.height = map_.rows * map_scale_; resize(map_, state_img_, Size(640,480)); ros::Subscriber sub = n.subscribe("/systemStates", 10, chatterCallback); image_pub_ = n.advertise<sensor_msgs::Image>("/view", 10); state_pub_ = n.advertise<sensor_msgs::Image>("/state_img", 10); ros::spin(); return 0; }
参考:
python 版 : http://answers.ros.org/question/60614/how-to-publish-a-vector-of-unknown-length-of-structs-in-ros/
c++ 版: http://stackoverflow.com/questions/15992990/how-would-you-publish-a-message-in-ros-of-a-vector-of-structs
CreatingMsgAndSrv : http://wiki.ros.org/cn/ROS/Tutorials/CreatingMsgAndSrv
=================================================
#include <ros/ros.h> #include <opencv2/opencv.hpp> #include <opencv/cv.h> #include <opencv/highgui.h> #include <image_transport/image_transport.h> #include <cv_bridge/cv_bridge.h> #include <sensor_msgs/image_encodings.h> //#include "../image_convert/image_converter.h" #include "ros/ros.h" #include "std_msgs/String.h" #include <cv_slam/odom.h> #include <cv_slam/landmark.h> #include <cv_slam/robot.h> #include <cv_slam/systemState.h> using namespace std; using namespace cv; void showOdom(cv::Mat& map, const cv_slam::odom rob_odom, const Scalar rgb); void showRobot(cv::Mat& map, const cv_slam::robot robot, const Scalar rgb); void drawCoordinate(cv::Mat& mat); std::string int2str(int num); void showLandmarks(cv::Mat& map, const vector<cv_slam::landmark> landmarks, const Scalar rgb); void chatterCallback(const cv_slam::systemStateConstPtr& msg); std::string float2str(float num); void showOdom(Mat image, const cv_slam::odom rob_odom, const Scalar rgb, const int x_coordinate, const int y_coordinate); void showRobot(Mat image, const cv_slam::robot rob_odom, const Scalar rgb, const int x_coordinate, const int y_coordinate); ros::Publisher image_pub_ ; ros::Publisher state_pub_ ; Mat map_; Mat view_; Mat state_img_; const int map_width_ = 2000;// = 150 ; const int map_height_ = 2000 ;// = 100 ; const int map_base_x_ = 1000;// = 150 ; const int map_base_y_ = 1000 ;// = 100 ; const float map_scale_ = 0.25; //缩放倍数 Size map_size_; //目标图像尺寸 int main(int argc, char **argv) { ros::init(argc, argv, "view"); ros::NodeHandle n; map_ = cv::imread("./OutPut/map/map.png", CV_LOAD_IMAGE_COLOR); //for display map_size_.width = map_.cols * map_scale_; map_size_.height = map_.rows * map_scale_; resize(map_, state_img_, Size(640,480)); ros::Subscriber sub = n.subscribe("/systemStates", 10, chatterCallback); image_pub_ = n.advertise<sensor_msgs::Image>("/view", 10); state_pub_ = n.advertise<sensor_msgs::Image>("/state_img", 10); ros::spin(); return 0; } void chatterCallback(const cv_slam::systemStateConstPtr& msg) { cv_slam::odom odom = msg->odom; cv_slam::robot robot = msg->robot_state; vector<cv_slam::landmark> landmarks = msg->landmark; showOdom(map_, odom, CV_RGB(0,0,0)) ; showRobot(map_, robot, CV_RGB(0,0,255)); drawCoordinate(map_); map_.copyTo( view_ ); //避免历史重影 landmark showLandmarks(view_, landmarks, CV_RGB(0,0,0)); resize(view_, view_, map_size_); cv_bridge::CvImage cvi; sensor_msgs::Image ros_img; ros::Time time=ros::Time::now(); cvi.header.stamp = time; cvi.header.frame_id = "image"; cvi.encoding = "bgr8"; cvi.image = view_;//Mat cvi.toImageMsg(ros_img); image_pub_.publish(cvi.toImageMsg()); Mat state_show; state_img_.copyTo( state_show ); //避免历史重影 landmark showOdom(state_show, odom, CV_RGB(0,0,0),50,50); showRobot(state_show, robot, CV_RGB(0,0,0),300,50); cv_bridge::CvImage cvi_state; sensor_msgs::Image state_img; ros::Time time2=ros::Time::now(); cvi_state.header.stamp = time2; cvi_state.header.frame_id = "image"; cvi_state.encoding = "bgr8"; cvi_state.image = state_show; //Mat cvi_state.toImageMsg(state_img); state_pub_.publish(cvi_state.toImageMsg()); } void showOdom(cv::Mat& map, const cv_slam::odom rob_odom, const Scalar rgb) { Point3d robot_pose; robot_pose.x = rob_odom.x; robot_pose.y = rob_odom.y; robot_pose.z = rob_odom.theta; const int ROBOT_DEFAULT_RADIUS = 2; const int ROBOT_DEFAULT_ARROW_LEN = 10; Point start, end; start.x = robot_pose.x + map_base_x_; start.y = robot_pose.y + map_base_y_; int thickness = 2; int lineType = 8; end.x = start.x + ROBOT_DEFAULT_ARROW_LEN * cos(robot_pose.z); end.y = start.y + ROBOT_DEFAULT_ARROW_LEN * sin(robot_pose.z); //display y convert .. start.y = map.rows - start.y; end.y = map.rows - start.y; circle(map,start,ROBOT_DEFAULT_RADIUS,rgb,0,lineType ); //line( map,start,end,rgb,thickness,lineType ); } void showRobot(cv::Mat& map, const cv_slam::robot robot, const Scalar rgb) { int temp_X = robot.x + map_base_x_; int temp_Y = robot.y + map_base_y_; temp_Y = map.rows - temp_Y ; cv::circle(map,Point( temp_X,temp_Y),2,rgb,1); //绘制 robot } void drawCoordinate(cv::Mat& mat) { std::string text ="Y"; cv::putText(mat,text,Point(20,20),CV_FONT_HERSHEY_COMPLEX,1,CV_RGB(0,0,255)); cv::line(mat,cv::Point(1,1),cv::Point(1,mat.rows),CV_RGB(255,0,0),1,8); text ="O"; cv::putText(mat,text,Point(20,mat.rows-20),CV_FONT_HERSHEY_COMPLEX,1,CV_RGB(0,0,255)); text ="X"; cv::putText(mat,text,Point(mat.cols-20,mat.rows-20),CV_FONT_HERSHEY_COMPLEX,1,CV_RGB(0,0,255)); cv::line(mat,cv::Point(1,mat.rows-1),cv::Point(mat.cols,mat.rows-1),CV_RGB(255,0,0),1,8); } void showLandmarks(cv::Mat& map, const vector<cv_slam::landmark> landmarks, const Scalar rgb) { for (int t = 0; t < landmarks.size(); t++) { cv_slam::landmark lanmark = landmarks.at(t); float X= lanmark.x + map_base_x_; float Y= lanmark.y + map_base_y_; Y = map.rows - Y ; cv::circle(map,Point( X,Y),2,rgb,2); //绘制mark位置 //cv::Mat landmark_cov = getCovMatrix(miu_state, 3+t*3, 4+t*3); //cv::Point2f center(X,Y); // drawEllipse(map, center, landmark_cov, CV_RGB(0, 150,0) ); std::string text = int2str(lanmark.id); cv::putText(map,text,Point(X,Y+20),CV_FONT_HERSHEY_COMPLEX, 2, CV_RGB(255, 0,0) ); } } void showOdom(Mat image, const cv_slam::odom odom, const Scalar rgb, const int x_coordinate, const int y_coordinate) { const int ROBOT_DEFAULT_RADIUS = 2; const int ROBOT_DEFAULT_ARROW_LEN = 30; Point3d robot_pose; robot_pose.x = odom.x; robot_pose.y = odom.y; robot_pose.z = odom.theta; //采用地图固定下 初始坐标系调整就可以 Point start, end; start.x = x_coordinate; start.y = y_coordinate; int thickness = 1; int lineType = 8; line( image,start,start+Point(500,0),CV_RGB(0,255,0),1,lineType ); // x轴 line( image,start,start+Point(0,500),CV_RGB(0,155,0),1,lineType ); // y轴 circle(image,start,ROBOT_DEFAULT_RADIUS,rgb,2,lineType ); end.x = start.x + ROBOT_DEFAULT_ARROW_LEN * cos(robot_pose.z); //放大5倍 end.y = start.y - ROBOT_DEFAULT_ARROW_LEN * sin(robot_pose.z); //display y convert line( image,start,end,rgb,thickness,lineType ); // 标记坐标信息 std::string text_id ="x: "+ float2str( robot_pose.x ); cv::putText(image, text_id, Point(50,150), CV_FONT_HERSHEY_COMPLEX, 1, CV_RGB(0,0,255)); std::string text1 ="y: "+ float2str( robot_pose.y ); cv::putText(image,text1, Point(50,200), CV_FONT_HERSHEY_COMPLEX, 1, CV_RGB(0,0,255)); std::string text2 ="z: "+ float2str( robot_pose.z*180/ 3.14159 ); cv::putText(image, text2, Point(50,250), CV_FONT_HERSHEY_COMPLEX, 1, CV_RGB(0,0,255)); } void showRobot(Mat image, const cv_slam::robot robot, const Scalar rgb, const int x_coordinate, const int y_coordinate) { const int ROBOT_DEFAULT_RADIUS = 2; const int ROBOT_DEFAULT_ARROW_LEN = 30; Point3d robot_pose; robot_pose.x = robot.x; robot_pose.y = robot.y; robot_pose.z = robot.theta; Point start, end; start.x = x_coordinate; start.y = y_coordinate; int thickness = 1; int lineType = 8; line( image,start,start+Point(500,0),CV_RGB(0,255,0),1,lineType ); // x轴 line( image,start,start+Point(0,500),CV_RGB(0,155,0),1,lineType ); // y轴 circle(image,start,ROBOT_DEFAULT_RADIUS,rgb,2,lineType ); end.x = start.x + ROBOT_DEFAULT_ARROW_LEN * cos( robot_pose.z ); end.y = start.y - ROBOT_DEFAULT_ARROW_LEN * sin( robot_pose.z ); //display y convert line( image,start,end,rgb,thickness,lineType ); // 标记坐标信息 std::string text_id ="x: "+ float2str( robot_pose.x ); cv::putText(image,text_id,Point(300,150),CV_FONT_HERSHEY_COMPLEX,1,CV_RGB(255,0,0)); std::string text1 ="y: "+ float2str( robot_pose.y ); cv::putText(image,text1,Point(300,200),CV_FONT_HERSHEY_COMPLEX,1,CV_RGB(255,0,0)); std::string text2 ="z: "+ float2str( robot_pose.z *180/ 3.14159); cv::putText(image,text2,Point(300,250),CV_FONT_HERSHEY_COMPLEX,1,CV_RGB(255,0,0)); } std::string int2str(int num) { std::stringstream ss; ss << num; std::string text = ss.str(); return text; } std::string float2str(float num) { std::stringstream ss; ss << num; std::string text = ss.str(); return text; }