You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 19:03:49