编译
脚本
v3_gaosi_img_pose_flag.sh
#!/bin/bash #外部给与执行权限 #sudo chmod +x run_ros_nodes.sh # 定义 ROS 安装路径 #安装时候添加到系统路径了 不需要每次都source ROS_SETUP="/opt/ros/noetic/setup.bash" # 定义工作目录路径 自己的工程没有加到系统路径,每次需要source WORKSPACE_DIR="/home/r9000k/v2_project/gaosi_slam/ros/ros_cgg" conda_envs="/home/r9000k/anaconda3" # 修改2-1 自己的conda 安装路径 #ROS_cv_briage_dir="/home/r9000k/v1_software/opencv/catkin_ws_cv_bridge/devel/setup.bash" # 修改2-2 自己编译的cv_briage包节点,貌似不用也行 制定了依赖opencv3.4.9 而非自带4.2 #echo $ROS_cv_briage_dir conda_envs_int=$conda_envs"/etc/profile.d/conda.sh" # 不用改 conda自带初始化文件 echo $conda_envs_int conda_envs_bin=$conda_envs"/envs/gaussian_splatting/bin" # 不用改 conda自带python安装位置 脚本中需要指定是conda特定的环境python而不是系统默认的 echo $conda_envs_bin ROS_SETUP="/opt/ros/noetic/setup.bash" #不用改 安装时候添加到系统路径了 不需要每次都source 这里留着 #指定目录 # 启动 ROS Master echo "Starting ROS 总结点..." gnome-terminal -- bash -c "\ cd $WORKSPACE_DIR; source devel/setup.bash; \ roscore; \ exec bash" # 等待 ROS Master 启动 sleep 3 # 运行 C++ 发布节点 # echo "Running C++ 发布节点..." # gnome-terminal -- bash -c "\ # cd $WORKSPACE_DIR; source devel/setup.bash; \ # rosrun gaosi_img_pose_flag image_pose_flag_publisher; \ # exec bash" # echo "Running C++ 订阅节点..." # gnome-terminal -- bash -c "\ # cd $WORKSPACE_DIR; source devel/setup.bash; \ # rosrun gaosi_img_pose_flag image_pose_flag_subscriber; \ # exec bash" # 运行python节点 python_DIR="${WORKSPACE_DIR}/src/gaosi_img_pose_flag/src" echo "Running python image_pose_publisher发布节点..." gnome-terminal -- bash -c " \ cd $WORKSPACE_DIR; source devel/setup.bash; \ source $conda_envs_int; \ conda activate gaussian_splatting ; \ cd $python_DIR; \ /usr/bin/python3 image_pose_publisher.py; \ exec bash" # 运行python节点 echo "Running python image_pose_subscriber订阅节点..." gnome-terminal -- bash -c " \ cd $WORKSPACE_DIR; source devel/setup.bash; \ source $conda_envs_int; \ conda activate gaussian_splatting ; \ cd $python_DIR; \ /usr/bin/python3 image_pose_subscriber.py; \ exec bash"
包
1 创建自定义消息
ImagePose.msg
# ImagePose.msg sensor_msgs/Image image std_msgs/Float64 flag geometry_msgs/Pose pose
CMakeLists.txt
cmake_minimum_required(VERSION 3.0.2) project(gaosi_img_pose_flag) find_package(catkin REQUIRED COMPONENTS roscpp geometry_msgs sensor_msgs cv_bridge message_filters # 消息同步 image_transport std_msgs # 自定义消息 message_generation # 自定义消息 ) # 自定义消息 add_message_files( FILES ImagePose.msg ) # 自定义消息 generate_messages( DEPENDENCIES std_msgs sensor_msgs geometry_msgs ) find_package(OpenCV REQUIRED) find_package(Boost REQUIRED COMPONENTS filesystem) find_package(Eigen3 REQUIRED) catkin_package( CATKIN_DEPENDS roscpp geometry_msgs sensor_msgs cv_bridge std_msgs message_runtime DEPENDS Boost ) include_directories( ${catkin_INCLUDE_DIRS} ${OpenCV_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS} "/usr/local/include/eigen3" ) # 编译发布节点 add_executable(image_pose_flag_publisher src/image_pose_flag_publisher.cpp) # 自定义消息引用 add_dependencies(image_pose_flag_publisher ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS}) target_link_libraries(image_pose_flag_publisher ${catkin_LIBRARIES} ${OpenCV_LIBRARIES} ${Boost_LIBRARIES} ) add_executable(image_pose_flag_subscriber src/image_pose_flag_subscriber.cpp) add_dependencies(image_pose_flag_subscriber ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS}) target_link_libraries(image_pose_flag_subscriber ${catkin_LIBRARIES} ${OpenCV_LIBRARIES})
package.xml
<?xml version="1.0"?> <package format="2"> <name>gaosi_img_pose_flag</name> <version>0.0.1</version> <description> A package to publish and subscribe to images and GPS data using ROS. </description> <!-- Maintainer of the package --> <maintainer email="your_email@example.com">Your Name</maintainer> <!-- License of the package --> <license>MIT</license> <!-- Build tool required to build this package --> <buildtool_depend>catkin</buildtool_depend> <!-- Dependencies of the package during build and runtime --> <build_depend>roscpp</build_depend> <build_depend>sensor_msgs</build_depend> <build_depend>cv_bridge</build_depend> <build_depend>eigen</build_depend> <build_depend>geometry_msgs</build_depend> <build_depend>message_filters</build_depend> <build_depend>image_transport</build_depend> <!--自定义消息 --> <build_depend>message_generation</build_depend> <build_depend>message_runtime</build_depend> <build_depend>std_msgs</build_depend> <exec_depend>roscpp</exec_depend> <exec_depend>sensor_msgs</exec_depend> <exec_depend>cv_bridge</exec_depend> <exec_depend>eigen</exec_depend> <exec_depend>geometry_msgs</exec_depend> <exec_depend>message_filters</exec_depend> <exec_depend>image_transport</exec_depend> <!--自定义消息 --> <exec_depend>message_generation</exec_depend> <exec_depend>message_runtime</exec_depend> <exec_depend>std_msgs</exec_depend> <!-- Declare additional dependencies required for building this package --> <build_export_depend>roscpp</build_export_depend> <build_export_depend>sensor_msgs</build_export_depend> <build_export_depend>cv_bridge</build_export_depend> <build_export_depend>eigen</build_export_depend> <build_export_depend>geometry_msgs</build_export_depend> <build_export_depend>message_filters</build_export_depend> <build_export_depend>image_transport</build_export_depend> <!--自定义消息 --> <build_export_depend>message_generation</build_export_depend> <build_export_depend>message_runtime</build_export_depend> <build_export_depend>std_msgs</build_export_depend> <!-- Export information, can be used by other packages --> <export> <!-- Export any specific information here --> </export> </package>
image_pose_publisher.py
#!/usr/bin/env python import rospy from sensor_msgs.msg import Image from std_msgs.msg import Float64 from geometry_msgs.msg import Pose from gaosi_img_pose_flag.msg import ImagePose # 更换为你包的名字 from cv_bridge import CvBridge import numpy as np import cv2 class ImagePosePublisher: def __init__(self): # Initialize node rospy.init_node('image_pose_publisher', anonymous=True) # Initialize publishers self.pub = rospy.Publisher('image_pose_topic', ImagePose, queue_size=10) # Create a bridge between OpenCV and ROS self.bridge = CvBridge() # Create or load a sample image self.image = np.zeros((480, 640, 3), dtype=np.uint8) # Black image # Set flag self.flag = Float64() self.flag.data = 1.23 # Example double value # Set pose self.pose = Pose() self.pose.position.x = 1.0 self.pose.position.y = 2.0 self.pose.position.z = 3.0 self.pose.orientation.x = 0.0 self.pose.orientation.y = 0.0 self.pose.orientation.z = 0.0 self.pose.orientation.w = 1.0 # Set publish rate self.rate = rospy.Rate(10) # 10 Hz self.publish() def publish(self): while not rospy.is_shutdown(): msg = ImagePose() # Convert OpenCV image to ROS image message msg.image = self.bridge.cv2_to_imgmsg(self.image, encoding="bgr8") msg.flag = self.flag msg.pose = self.pose # Publish message self.pub.publish(msg) self.rate.sleep() if __name__ == '__main__': try: ImagePosePublisher() except rospy.ROSInterruptException: pass
image_pose_subscriber.py
image_pose_subscriber.py 例子0 工程多线程加速版本
#include <ros/ros.h> #include <gaosi_img_pose_flag/ImagePose.h> // 更换为你包的名字 #include <queue> #include <mutex> #include <thread> #include <iostream> // Global variables std::queue<gaosi_img_pose_flag::ImagePose::ConstPtr> data_queue; std::mutex queue_mutex; // Callback function to handle incoming messages void callback(const gaosi_img_pose_flag::ImagePose::ConstPtr& msg) { std::lock_guard<std::mutex> lock(queue_mutex); data_queue.push(msg); } // Thread function to process the queue void processQueue() { while (ros::ok()) { std::queue<gaosi_img_pose_flag::ImagePose::ConstPtr> local_queue; { std::lock_guard<std::mutex> lock(queue_mutex); std::swap(local_queue, data_queue); // Safely access the queue } while (!local_queue.empty()) { auto msg = local_queue.front(); local_queue.pop(); // Process the message ROS_INFO("Processing flag: %.2f", msg->flag.data); ROS_INFO("Processing pose: Position(%.2f, %.2f, %.2f), Orientation(%.2f, %.2f, %.2f, %.2f)", msg->pose.position.x, msg->pose.position.y, msg->pose.position.z, msg->pose.orientation.x, msg->pose.orientation.y, msg->pose.orientation.z, msg->pose.orientation.w); } // Optional: Sleep to avoid busy waiting std::this_thread::sleep_for(std::chrono::milliseconds(100)); } } void spinThread() { ros::spin();// 处理回调函数积累消息 } int main(int argc, char** argv) { ros::init(argc, argv, "image_pose_processor"); ros::NodeHandle nh; // Initialize the subscriber ros::Subscriber sub = nh.subscribe("image_pose_topic", 10, callback); // Create a thread to process the queue std::thread processing_thread(processQueue); // Start a thread to run ros::spin() std::thread spin_thread(spinThread); ros::Rate rate(1);// Hz 频率 while (ros::ok()) { // Call ros::spinOnce() to process callbacks ros::spinOnce(); // Process the queue //processQueue(); // Sleep for the rest of the loop period rate.sleep(); } //ros::spin(); // Join the processing thread before exiting if (processing_thread.joinable()) { processing_thread.join(); } // Join the spin thread before exiting if (spin_thread.joinable()) { spin_thread.join(); } return 0; }
image_pose_subscriber.py 例子1 简单版本
#!/usr/bin/env python import rospy from sensor_msgs.msg import Image from std_msgs.msg import Float64 from geometry_msgs.msg import Pose from gaosi_img_pose_flag.msg import ImagePose # 更换为你包的名字 from cv_bridge import CvBridge import cv2 class ImagePoseSubscriber: def __init__(self): # Initialize node rospy.init_node('image_pose_subscriber', anonymous=True) # Initialize subscriber self.sub = rospy.Subscriber('image_pose_topic', ImagePose, self.callback) # Create a bridge between OpenCV and ROS self.bridge = CvBridge() # Create an OpenCV window cv2.namedWindow("Received Image") def callback(self, msg): try: # Convert ROS image message to OpenCV image cv_image = self.bridge.imgmsg_to_cv2(msg.image, desired_encoding="bgr8") cv2.imshow("Received Image", cv_image) cv2.waitKey(30) except Exception as e: rospy.logerr("Failed to convert image: %s", str(e)) rospy.loginfo("Received flag: %.2f", msg.flag.data) rospy.loginfo("Received pose: Position(%.2f, %.2f, %.2f), Orientation(%.2f, %.2f, %.2f, %.2f)", msg.pose.position.x, msg.pose.position.y, msg.pose.position.z, msg.pose.orientation.x, msg.pose.orientation.y, msg.pose.orientation.z, msg.pose.orientation.w) def run(self): rospy.spin() if __name__ == '__main__': try: subscriber = ImagePoseSubscriber() subscriber.run() except rospy.ROSInterruptException: pass
image_pose_subscriber.py 例子2 类内部单独线程处理版本
#include <ros/ros.h> #include <sensor_msgs/Image.h> #include <std_msgs/Float64.h> #include <geometry_msgs/Pose.h> #include <cv_bridge/cv_bridge.h> #include <opencv2/opencv.hpp> #include <gaosi_img_pose_flag/ImagePose.h> // 更换为你包的名字 #include <queue> #include <mutex> #include <thread> class ImagePoseSubscriber { public: ImagePoseSubscriber() { // Initialize subscriber sub_ = nh_.subscribe("image_pose_topic", 10, &ImagePoseSubscriber::callback, this); // Start processing thread processing_thread_ = std::thread(&ImagePoseSubscriber::processQueue, this); } ~ImagePoseSubscriber() { // Join the thread before destruction if (processing_thread_.joinable()) { processing_thread_.join(); } } private: ros::NodeHandle nh_; ros::Subscriber sub_; std::queue<gaosi_img_pose_flag::ImagePose::ConstPtr> data_queue_; std::mutex queue_mutex_; std::thread processing_thread_; bool stop_thread_ = false; void callback(const gaosi_img_pose_flag::ImagePose::ConstPtr& msg) { std::lock_guard<std::mutex> lock(queue_mutex_); data_queue_.push(msg); } void clearQueue() { std::lock_guard<std::mutex> lock(queue_mutex_); std::queue<gaosi_img_pose_flag::ImagePose::ConstPtr> empty; std::swap(data_queue_, empty); } void processQueue() { while (!stop_thread_) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Adjust as needed std::queue<gaosi_img_pose_flag::ImagePose::ConstPtr> local_queue; { std::lock_guard<std::mutex> lock(queue_mutex_); // 通过交换对象内部的数据成员来完成操作。这个过程不涉及对数据的实际拷贝。 std::swap(local_queue, data_queue_); } while (!local_queue.empty()) { auto msg = local_queue.front();//它返回队列中第一个元素的引用,但不移除它。 local_queue.pop();//移除队列中第一个元素 try { // Convert ROS image message to OpenCV image cv::Mat cv_image = cv_bridge::toCvCopy(msg->image, "bgr8")->image; cv::imshow("Received Image", cv_image); cv::waitKey(30); } catch (cv_bridge::Exception& e) { ROS_ERROR("cv_bridge exception: %s", e.what()); } ROS_INFO("Received flag: %.2f", msg->flag.data); ROS_INFO("Received pose: Position(%.2f, %.2f, %.2f), Orientation(%.2f, %.2f, %.2f, %.2f)", msg->pose.position.x, msg->pose.position.y, msg->pose.position.z, msg->pose.orientation.x, msg->pose.orientation.y, msg->pose.orientation.z, msg->pose.orientation.w); } } } }; int main(int argc, char** argv) { ros::init(argc, argv, "image_pose_subscriber"); ImagePoseSubscriber image_pose_subscriber; ros::spin(); return 0; }
image_pose_subscriber.py 例子3-1 类外部(主线程)单独线程处理版本
#include <ros/ros.h> #include <gaosi_img_pose_flag/ImagePose.h> // 更换为你包的名字 #include <queue> #include <mutex> #include <thread> class ImagePoseSubscriber { public: ImagePoseSubscriber() { // Initialize subscriber sub_ = nh_.subscribe("image_pose_topic", 10, &ImagePoseSubscriber::callback, this); } // Function to access the queue from outside std::queue<gaosi_img_pose_flag::ImagePose::ConstPtr> getQueueSnapshot() { std::lock_guard<std::mutex> lock(queue_mutex_); return data_queue_; // Return a copy of the queue } // Optionally, you can also provide a function to clear the queue void clearQueue() { std::lock_guard<std::mutex> lock(queue_mutex_); std::queue<gaosi_img_pose_flag::ImagePose::ConstPtr> empty; std::swap(data_queue_, empty); } private: ros::NodeHandle nh_; ros::Subscriber sub_; std::queue<gaosi_img_pose_flag::ImagePose::ConstPtr> data_queue_; std::mutex queue_mutex_; void callback(const gaosi_img_pose_flag::ImagePose::ConstPtr& msg) { std::lock_guard<std::mutex> lock(queue_mutex_); data_queue_.push(msg); } }; void processQueue(ImagePoseSubscriber& subscriber) { while (ros::ok()) { std::queue<gaosi_img_pose_flag::ImagePose::ConstPtr> queue_snapshot = subscriber.getQueueSnapshot(); while (!queue_snapshot.empty()) { auto msg = queue_snapshot.front(); queue_snapshot.pop(); // Process the message (e.g., display image or log pose data) // This is just an example of processing ROS_INFO("Processing flag: %.2f", msg->flag.data); ROS_INFO("Processing pose: Position(%.2f, %.2f, %.2f), Orientation(%.2f, %.2f, %.2f, %.2f)", msg->pose.position.x, msg->pose.position.y, msg->pose.position.z, msg->pose.orientation.x, msg->pose.orientation.y, msg->pose.orientation.z, msg->pose.orientation.w); } // Optionally, add a sleep to prevent busy waiting std::this_thread::sleep_for(std::chrono::milliseconds(100)); } } int main(int argc, char** argv) { ros::init(argc, argv, "image_pose_processor"); // Initialize ImagePoseSubscriber ImagePoseSubscriber image_pose_subscriber; // Create a thread to process the queue std::thread processing_thread(processQueue, std::ref(image_pose_subscriber)); // Spin ROS ros::spin(); // Join the processing thread before exiting if (processing_thread.joinable()) { processing_thread.join(); } return 0; }
image_pose_subscriber.py 例子3-2 非类外部(主线程)单独线程处理版本
#include <ros/ros.h> #include <gaosi_img_pose_flag/ImagePose.h> // 更换为你包的名字 #include <queue> #include <mutex> #include <thread> #include <iostream> // Global variables std::queue<gaosi_img_pose_flag::ImagePose::ConstPtr> data_queue; std::mutex queue_mutex; // Callback function to handle incoming messages void callback(const gaosi_img_pose_flag::ImagePose::ConstPtr& msg) { std::lock_guard<std::mutex> lock(queue_mutex); data_queue.push(msg); } // Thread function to process the queue void processQueue() { while (ros::ok()) { std::queue<gaosi_img_pose_flag::ImagePose::ConstPtr> local_queue; { std::lock_guard<std::mutex> lock(queue_mutex); std::swap(local_queue, data_queue); // Safely access the queue } while (!local_queue.empty()) { auto msg = local_queue.front(); local_queue.pop(); // Process the message ROS_INFO("Processing flag: %.2f", msg->flag.data); ROS_INFO("Processing pose: Position(%.2f, %.2f, %.2f), Orientation(%.2f, %.2f, %.2f, %.2f)", msg->pose.position.x, msg->pose.position.y, msg->pose.position.z, msg->pose.orientation.x, msg->pose.orientation.y, msg->pose.orientation.z, msg->pose.orientation.w); } // Optional: Sleep to avoid busy waiting std::this_thread::sleep_for(std::chrono::milliseconds(100)); } } int main(int argc, char** argv) { ros::init(argc, argv, "image_pose_processor"); ros::NodeHandle nh; // Initialize the subscriber ros::Subscriber sub = nh.subscribe("image_pose_topic", 10, callback); // Create a thread to process the queue std::thread processing_thread(processQueue); // Spin ROS ros::spin(); // Join the processing thread before exiting if (processing_thread.joinable()) { processing_thread.join(); } return 0; }
image_pose_flag_publisher.cpp
#include <ros/ros.h> #include <sensor_msgs/Image.h> #include <std_msgs/Float64.h> #include <geometry_msgs/Pose.h> #include <cv_bridge/cv_bridge.h> #include <opencv2/opencv.hpp> #include <gaosi_img_pose_flag/ImagePose.h> // 更换为你包的名字 class ImagePosePublisher { public: ImagePosePublisher() { // Initialize publisher pub_ = nh_.advertise<gaosi_img_pose_flag::ImagePose>("image_pose_topic", 10); // Load or create a sample image image_ = cv::Mat::zeros(480, 640, CV_8UC3); // Black image // Set flag flag_.data = 1.23; // Example double value // Set pose pose_.position.x = 1.0; pose_.position.y = 2.0; pose_.position.z = 3.0; pose_.orientation.x = 0.0; pose_.orientation.y = 0.0; pose_.orientation.z = 0.0; pose_.orientation.w = 1.0; // Set publish rate ros::Rate loop_rate(10); // 10 Hz while (ros::ok()) { gaosi_img_pose_flag::ImagePose msg; // Convert OpenCV image to ROS image message msg.image = *cv_bridge::CvImage(std_msgs::Header(), "bgr8", image_).toImageMsg(); msg.flag = flag_; msg.pose = pose_; // Publish message pub_.publish(msg); ros::spinOnce(); loop_rate.sleep(); } } private: ros::NodeHandle nh_; ros::Publisher pub_; cv::Mat image_; std_msgs::Float64 flag_; geometry_msgs::Pose pose_; }; int main(int argc, char** argv) { ros::init(argc, argv, "image_pose_publisher"); ImagePosePublisher image_pose_publisher; return 0; }
image_pose_flag_subscriber.cpp
#include <ros/ros.h> #include <sensor_msgs/Image.h> #include <std_msgs/Float64.h> #include <geometry_msgs/Pose.h> #include <cv_bridge/cv_bridge.h> #include <opencv2/opencv.hpp> #include <gaosi_img_pose_flag/ImagePose.h> // 更换为你包的名字 class ImagePoseSubscriber { public: ImagePoseSubscriber() { // Initialize subscriber sub_ = nh_.subscribe("image_pose_topic", 10, &ImagePoseSubscriber::callback, this); } private: ros::NodeHandle nh_; ros::Subscriber sub_; void callback(const gaosi_img_pose_flag::ImagePose::ConstPtr& msg) { try { // Convert ROS image message to OpenCV image cv::Mat cv_image = cv_bridge::toCvCopy(msg->image, "bgr8")->image; cv::imshow("Received Image", cv_image); cv::waitKey(30); } catch (cv_bridge::Exception& e) { ROS_ERROR("cv_bridge exception: %s", e.what()); } ROS_INFO("Received flag: %.2f", msg->flag.data); ROS_INFO("Received pose: Position(%.2f, %.2f, %.2f), Orientation(%.2f, %.2f, %.2f, %.2f)", msg->pose.position.x, msg->pose.position.y, msg->pose.position.z, msg->pose.orientation.x, msg->pose.orientation.y, msg->pose.orientation.z, msg->pose.orientation.w); } }; int main(int argc, char** argv) { ros::init(argc, argv, "image_pose_subscriber"); ImagePoseSubscriber image_pose_subscriber; ros::spin(); return 0; }