
解决 ROS2 硬件节点 CtrlC 后资源未释放导致重启失败的问题这是一次ROS2封装硬件的实验使用香蕉板为开发板写了4个节点分别是button、led、servo、logic并通过launch.py启动所有节点。实验是发现第一次启动后节点程序开始运行topic能正常监听到。但当第一次按下Ctrl C后进程终止再次启动launch.py后程序报错报错显示前一次的进程在终止后并没有正常清理干净。通过检查代码发现问题finally:node.destroy_node()rclpy.shutdown() # ← 这里会抛异常因为 launch 已经关过了这个异常会导致进程非正常退出exit code 1。虽然 destroy_node() 在 shutdown() 前面已经执行了但进程崩溃时 Python 解释器可能跳过了 C 扩展层的资源清理导致 LGPIO 打开的 /dev/gpiochip0 句柄和引脚占用状态没有被内核完全回收。因此需要修改各节点的main及destroy_node()1修改maindef main(argsNone):rclpy.init(argsargs)node ButtonPublisher() # 其他节点同理改名字try:rclpy.spin(node)except KeyboardInterrupt:passfinally:node.destroy_node()# 关键修复避免重复 shutdown 抛异常try:if rclpy.ok():rclpy.shutdown()except Exception:pass2修改destory_node()def destroy_node(self):self.get_logger().info(‘Closing button GPIO…’)self.button.close()# 关键修复显式关闭 LGPIOFactory 的芯片句柄 try: if Device.pin_factory is not None: Device.pin_factory.close() except Exception: pass super().destroy_node()3修改完成重新编译运行测试能否正常终止程序进程被清理干净显示成功4再次运行看进程是否正常启动显示成功总结与建议本文详细分析了在 ROS2 中封装硬件节点时因CtrlC终止后资源未完全释放导致进程无法重启的问题。核心原因在于finally块中rclpy.shutdown()的重复调用引发异常导致进程非正常退出进而使得底层 GPIO 资源如/dev/gpiochip0句柄未被内核完全回收。解决思路总结如下主函数 (main) 优化在finally块中先调用node.destroy_node()然后通过try...except包裹并检查rclpy.ok()状态来安全地调用rclpy.shutdown()避免因launch已关闭而抛出异常。节点销毁 (destroy_node) 优化在自定义的destroy_node方法中除了关闭自定义硬件资源如button.close()还应显式尝试关闭底层引脚工厂如Device.pin_factory.close()确保所有硬件句柄被释放。预防此类问题的编程习惯建议资源释放顺序遵循“先关闭自定义资源再关闭框架资源”的原则。在 ROS2 节点中即先执行自定义的清理逻辑再调用父类的destroy_node。异常处理与状态检查在finally或清理函数中调用可能失败的系统或框架函数时应使用try...except进行包裹并优先检查相关状态如rclpy.ok()避免因异常导致清理流程中断。显式关闭句柄对于直接操作硬件、文件或网络等底层资源的库即使库提供了自动清理机制在程序退出前显式调用关闭/释放函数也是一个好习惯尤其是在可能发生异常退出的场景下。测试验证修改后务必进行“启动-终止-再启动”的完整循环测试并通过系统工具如lsof,gpioinfo或程序日志确认资源已被正确释放确保问题得到根治。通过遵循上述思路和习惯可以显著提高 ROS2 硬件节点乃至其他涉及底层资源操作程序的健壮性和可重启性。