UI에서 topic subscribe가 안 되어서, 구글링 중 발견한 내용(https://robotics.stackexchange.com/questions/100861/using-qt-designer-and-ros2-together-for-a-gui)을 참고해서 아래와 같이 MultiThreadedExecutor를 사용해서 해결했었다.
def main():
rp.init()
executor = MultiThreadedExecutor()
app = QApplication(sys.argv)
myWindows = WindowClass()
myWindows.show()
pi_cam_subscriber = PiCamSubscriber(myWindows)
executor.add_node(pi_cam_subscriber)
emergency_status_subscriber = EmergencySubscriber(myWindows)
executor.add_node(emergency_status_subscriber)
robot_status_subscriber = RobotStatusSubscriber(myWindows)
executor.add_node(robot_status_subscriber)
task_queue_subscriber = TaskQueueSubscriber(myWindows)
executor.add_node(task_queue_subscriber)
cctv_video_subscriber = CctvVideoSubscriber(myWindows)
executor.add_node(cctv_video_subscriber)
amcl_subscriber = AmclSubscriber()
executor.add_node(amcl_subscriber)
path_subscriber = PathSubscriber()
executor.add_node(path_subscriber)
done_task_subscriber = DoneTaskSubscriber()
executor.add_node(done_task_subscriber)
thread = Thread(target=executor.spin)
thread.start()
이렇게 작성하고 나서는 deadlock도 발생하지 않는 것으로 보였고 Topic pub/sub에 문제가 없었기 때문에 멀티스레드를 잘 썼다고 생각했는데...
면접 질문을 받고 왠지 찜찜해서 알아보니까 (https://discourse.ros.org/t/how-to-use-callback-groups-in-ros2/25255)
요약하면, SingleThreadedExecutor
는 말 그대로 싱글스레드로 실행되지만, MultiThreadedExecutor
는 callbackgroup 설정에 따라 싱글스레드일 수도 있고, 멀티스레드일 수도 있다는 것.
callbackgroup은 따로 설정하지 않으면 MutuallyExclusiveCallbackGroup
이며, 이 경우 executor의 callback은 하나씩 실행된다.
병렬로 실행되게 하려면 ReentrantCallbackGroup
을 적용해야 한다. deadlock이 발생하지 않은 건 사실상 싱글스레드로 실행되고 있었기 때문이고, 멀티스레드를 적용하려면 callback_group을 따로 설정해 줬어야 하고, deadlock을 방지하는 방법도 고려해야 했을 것이다.
ROS2 공식문서(https://docs.ros2.org/foxy/api/rclpy/api/execution_and_callbacks.html#module-rclpy.callback_groups)를 보면 callback을 thread pool 안에서 실행한다는 내용이 있다.
그렇기 때문에 SingleThreadedExecutor에서는 아예 subscribe를 못했던 이슈가 MultiThreadedExecutor를 사용했을 때 해결된 것으로 보인다. 다만 여러 노드가 병렬로 동작한 것은 아니므로 MultiThread로 개발했다고 말할 수는 없다.