【ROS2程序开机自启与看门狗】基于Systemd的进程守护与自动恢复机制

前言


1 开机自启与自动重启
1-1 创建启动脚本
  • 创建一个用于启动 ROS2 节点的 shell 脚本,用来统一配置环境并运行 ros2 run 命令。
bash 复制代码
nano /home/yourname/ros2_talker.sh
  • shell脚本中设置一些运行必须的环境变量
    • 注意这里使用 exec 直接替换 shell 进程运行 ROS2 节点,避免额外子进程导致信号不一致问题。
bash 复制代码
#!/bin/bash

# ROS2 环境
source /opt/ros/humble/setup.bash

# 如果你有工作空间(没有可以删掉这一行)
# source /home/yourname/ros2_ws/install/setup.bash

# 防止乱码 / 日志更稳定
export ROS_DOMAIN_ID=0
export RCUTILS_COLORIZED_OUTPUT=0

# 启动 demo talker
exec ros2 run demo_nodes_cpp talker
  • 给脚本添加可执行权限,使 systemd 或手动调用时可以直接运行。
bash 复制代码
chmod +x /home/yourname/ros2_talker.sh
1-2 创建 systemd 服务
  • 创建 systemd 服务文件,用于定义 ROS2 节点的启动方式与生命周期管理。
bash 复制代码
sudo nano /etc/systemd/system/ros2-talker.service
  • 内容如下:
INI 复制代码
[Unit]
Description=ROS2 Talker Demo
After=network.target

[Service]
Type=simple
User=yourname
WorkingDirectory=/home/yourname

ExecStart=/home/yourname/ros2_talker.sh

# 自动重启
Restart=always
RestartSec=2

# 日志统一进入 systemd journal
StandardOutput=journal
StandardError=journal

# ROS2 常用环境变量
Environment=ROS_DOMAIN_ID=0
Environment=RCUTILS_LOGGING_USE_STDOUT=1

[Install]
WantedBy=multi-user.target
  • [Unit]:定义服务基本信息,用于描述服务名称、启动依赖关系等基础元数据
    • `Description:服务的描述信息,用于在 systemctl status 中显示该服务的用途说明
    • After=network.target:指定该服务在网络服务启动完成之后再启动,确保 ROS2 运行时网络环境已就绪
  • [Service]:定义服务的核心运行参数,包括启动方式、执行用户、进程管理与重启策略
    • Type=simple:表示服务为简单类型,ExecStart 启动的进程即为主进程,不创建子守护进程
    • User:指定服务以XXX用户身份运行
    • WorkingDirectory:设置服务运行时的工作目录
  • ExecStart=:指定服务启动时执行的脚本路径
  • Restart=always:设置服务无论正常退出还是异常崩溃都会自动重启
    • RestartSec=2:设置重启前等待 2 秒
  • StandardOutput=journal:将标准输出重定向到 systemd journal 日志系统统一管理
  • StandardError=journal:将标准错误输出同样写入 journal,便于统一日志查看与调试
  • Environment=ROS_DOMAIN_ID=0:设置 ROS2 通信域 ID,确保节点在同一 DDS 网络中通信
  • Environment=RCUTILS_LOGGING_USE_STDOUT=1:强制 ROS2 日志输出到标准输出,避免日志分散或写入文件
  • [Install]:定义服务的安装与启动目标,用于 systemctl enable 时的挂载关系
    • WantedBy=multi-user.target:表示该服务会在系统进入多用户运行级别后自动启动(开机自启)

1-3 启动 + 设置开机自启
bash 复制代码
sudo systemctl daemon-reload
sudo systemctl enable ros2-talker.service
sudo systemctl start ros2-talker.service

1-4 查看运行状态
  • 查看服务当前运行状态,包括 PID、启动时间和日志摘要。
bash 复制代码
systemctl status ros2-talker.service

1-5 查看日志
  • 实时看日志
bash 复制代码
journalctl -u ros2-talker.service -f
  • 查看历史日志
bash 复制代码
journalctl -u ros2-talker.service

1-6 自重启测试
  • 我们先通过状态尝试获取本服务的pid
bash 复制代码
systemctl status ros2-talker
  • 然后我们手动尝试杀死这个进程,并观察这个进程是否会自己复活:
bash 复制代码
kill -9 9449

1-7 关闭服务
bash 复制代码
sudo systemctl stop ros2-talker.service

1-8 删除服务
bash 复制代码
sudo systemctl stop ros2-talker.service
sudo systemctl disable ros2-talker.service
sudo rm /etc/systemd/system/ros2-talker.service

2 看门狗

2-1 介绍
  • 看门狗Watchdog)是一种用于监控系统或程序运行状态的机制,当检测到异常、卡死或特定条件触发时,会自动执行恢复操作(如重启服务)
  • 一般分为几种:通常分为
    • 硬件看门狗(芯片级自动复位系统)
    • 系统看门狗systemd 等守护机制)
    • 应用看门狗(程序内部心跳检测)
    • 日志看门狗(通过日志内容判断异常)
  • 如果像这样无法修改程序源码的,可以做日志看门狗补全:

当无法在程序内部加入 heartbeat 或监控逻辑时,可以通过监听 systemd journal 或日志文件,基于关键字匹配的方式实现外部异常检测与自动重启机制

  • 值得说的是
    • Restart=always / Restart=on-failure 本质是 systemd 自带的进程级看门狗(Process Watchdog),但它只覆盖"进程层面",不等于完整看门狗体系。

2-2 看门狗脚本
  • 创建看门狗脚本文件,用于监听 ROS2 服务日志并在触发条件时执行自动重启逻辑。
bash 复制代码
nano /home/yourname/ros2_grep_watchdog.sh
  • 核心内容就是

实时监听 systemd 服务日志,并逐行读取输出内容,判断当前日志是否包含触发关键字,不匹配则跳过处理。

bash 复制代码
#!/bin/bash

SERVICE="ros2-talker.service"
PATTERN='Hello World: 50'

LAST_LINE=""
COOLDOWN=5
LAST_RESTART_TIME=0

journalctl -u "$SERVICE" -f --no-tail | while read -r line
do
    echo "$line"

    # 只匹配关键字
    if [[ "$line" != *"$PATTERN"* ]]; then
        continue
    fi

    # 去重:完全相同日志不重复触发
    if [[ "$line" == "$LAST_LINE" ]]; then
        continue
    fi

    LAST_LINE="$line"

    NOW=$(date +%s)

    # 冷却时间防抖
    if (( NOW - LAST_RESTART_TIME < COOLDOWN )); then
        echo "[WATCHDOG] cooldown active, skip restart"
        continue
    fi

    LAST_RESTART_TIME=$NOW

    echo "[WATCHDOG] trigger restart → $SERVICE"

    sudo systemctl restart "$SERVICE"
done

2-3 看门狗服务
  • 创建 systemd 服务文件,用于将 watchdog 脚本注册为系统守护进程。
bash 复制代码
sudo nano /etc/systemd/system/ros2-log-watchdog.service
  • 和上面一样,这里就不解释了
bash 复制代码
[Unit]
Description=ROS2 Log Watchdog
After=network.target

[Service]
ExecStart=/home/yourname/ros2_grep_watchdog.sh
Restart=always
RestartSec=1

User=yourname

[Install]
WantedBy=multi-user.target

2-4 权限添加
  • 由于看门狗服务在检测到特定关键词后重启服务时需要sudo权限,为了避免该服务在后台运行时候没法请求用户手动输入密码,我们需要配置免密权限来防止该服务执行失败。
  • 打开 sudoers 的独立配置文件,用于给特定用户添加受控的免密权限,避免直接修改 /etc/sudoers 导致系统风险。
bash 复制代码
sudo vim /etc/sudoers.d/ros2-watchdog
  • 允许用户 lzh 在不输入密码的情况下执行指定的 systemctl restart ros2-talker.service 命令,因为看门狗脚本是在 systemd 后台运行的,没有 TTY 交互能力,无法手动输入密码,如果不配置免密权限就会导致 watchdog 在触发重启时卡死或失败。
bash 复制代码
lzh ALL=(ALL) NOPASSWD: /bin/systemctl restart ros2-talker.service

2-5 启用服务
bash 复制代码
sudo systemctl daemon-reload
sudo systemctl enable ros2-log-watchdog
sudo systemctl start ros2-log-watchdog

2-6 观察日志
  • 通过观察日志我们可以看到,当检测到'Hello World: 50'时,看门狗服务会自动重启服务
bash 复制代码
journalctl -u ros2-log-watchdog.service -f

2-7 关闭服务
bash 复制代码
sudo systemctl stop ros2-log-watchdog.service

总结

  • 本文通过 systemd 实现 ROS2 节点的开机自启与自动重启,并结合日志监听脚本构建外部看门狗机制,在无法修改源码的情况下实现基础级运行监控与故障恢复。
  • 如有错误,欢迎指出!
  • 感谢支持!