std::thread子线程中ros::ok()无法响应Ctrl+C退出信号问题
问题根因
子线程中ros::ok()始终返回true、无法随Ctrl+C操作同步变false,核心原因有三个:
ros::init调用位置不对或者被自定义信号处理覆盖,Ctrl+C触发的SIGINT信号没有被ROS默认处理逻辑捕获,全局关闭状态没有更新- 创建
std::thread时传入的cameras、publishers参数是值传递,子线程持有这两个对象的独立拷贝,和主线程的ROS上下文完全隔离,就算主线程收到关闭信号,子线程内的ROS状态也不会同步 - 相机的
getRawImage()采集调用默认是永久阻塞模式,就算ros::ok()已经变为false,线程卡在阻塞采集调用里,根本走不到循环判断的位置,自然不会退出。
修复方案
- 主线程最开头必须调用
ros::init,不要自定义覆盖SIGINT信号处理函数,保证Ctrl+C触发时ROS能正常更新全局关闭状态 - 线程传参改用引用传递,保证子线程和主线程共享同一份ROS上下文、相机实例和发布者对象,避免拷贝导致的状态隔离
- 给相机采集接口设置合理超时,避免采集逻辑永久阻塞,保证线程能周期性执行
ros::ok()判断 ros::spin()退出后主动调用ros::shutdown()强制触发全局状态更新,等待所有子线程join完成后,再执行相机停流等清理操作,避免访问冲突。
修改后的线程函数代码
// 参数改为引用传递,避免值拷贝 void publish_camera_on_topic(std::vector<Camera>& cameras, const std::vector<ros::Publisher>& publishers, const int camera_index) { int frameSize; BYTE *imagePtr; int frame_id = 0; cv_bridge::CvImage img_bridge; sensor_msgs::Image img_msg; while (ros::ok()) { // 给采集接口加100ms超时,根据使用的相机SDK对应接口调整,超时直接返回空指针 imagePtr = cameras[camera_index].getRawImage(100); if (imagePtr == nullptr) { std::this_thread::sleep_for(std::chrono::milliseconds(10)); continue; } frameSize = cameras[camera_index].getFrameSize(); cameras[camera_index].createRGBImage(imagePtr, frameSize); unsigned char* pImage = cameras[camera_index].getImage(); if (pImage != NULL) { Mat image(cameras[camera_index].getMatSize(), CV_8UC3, pImage, Mat::AUTO_STEP); cameras[camera_index].releaseImage(); std_msgs::Header header; header.seq = frame_id; header.stamp = ros::Time::now(); img_bridge = cv_bridge::CvImage(header, sensor_msgs::image_encodings::RGB8, image); img_bridge.toImageMsg(img_msg); publishers[camera_index].publish(img_msg); } frame_id++; } std::cout << "ROS closing for thread of camera " << camera_index << " recieved." << std::endl; }
修改后的主程序逻辑
int main(int argc, char** argv) { // 主线程开头优先调用ros::init,默认会注册SIGINT处理逻辑 ros::init(argc, argv, "lumenera_camera_node"); ros::NodeHandle nh; // 保留原有相机初始化逻辑 std::vector<Camera> cameras; // ... 此处为原有相机初始化代码 ... // 创建图像发布者 std::vector<ros::Publisher> publishers; for (size_t i = 0; i < cameras.size(); i++) { char topic_name[200]; sprintf(topic_name, "/lumenera_camera_package/%d", i + 1); publishers.push_back(nh.advertise<sensor_msgs::Image>(topic_name, 10)); } // 启动子线程,用std::ref包装参数实现引用传递 std::vector<std::thread> thread_vector; for(size_t i = 0; i < cameras.size(); i++) { thread_vector.emplace_back(publish_camera_on_topic, std::ref(cameras), std::ref(publishers), i); } ros::spin(); // spin退出后主动触发全局关闭 ros::shutdown(); // 等待所有子线程安全退出 for (auto& t : thread_vector) { if (t.joinable()) { t.join(); } } // 子线程全部退出后再停止相机流 for(size_t i = 0; i < cameras.size(); i++) { cameras[i].stopStreaming(); } ROS_INFO("Node: [lumenera_camera_node] has been Ended."); return 0; }
注意:如果之前自行编写过SIGINT信号处理函数,必须删除,保证ROS默认的信号处理逻辑正常执行,否则
ros::ok()不会随Ctrl+C操作更新状态。采集超时时间根据实际需求设置即可,一般100ms以内的超时不会影响采集帧率,也能保证按下Ctrl+C后线程快速退出。
内容的提问来源于stack exchange,提问作者CVDE
相关产品推荐
相关产品推荐

