charge_manager 状态 1 自动录包功能部署说明

功能目标

新增一个独立的自动录包功能:

  • 监听 /charge_manager_state
  • 每次状态从非 1 变成 1 时,单独启动一次 rosbag record
  • 每次状态从 1 变成非 1 时,停止当前录包并生成 .bag
  • 录包目录总大小超过 10G 时,自动删除最老的 .bag / .bag.active 文件
  • 录制 topic:
    • /livox/lidar
    • /charge/left_rc
    • /charge/right_rc

这相当于新增一个状态触发录包功能,替代原来 record_lidar 二进制内部不透明的触发逻辑。

一键部署脚本

在机器人105上执行

cat > /tmp/install_auto_record_state1.sh <<'EOF'
#!/bin/bash
set -e


SCRIPT_DIR="/home/ysc/jy_cog/charge_manager_cpp/scripts"
LOG_DIR="/home/ysc/jy_cog/system/log/record_lidar_data"
AUTO_SCRIPT="$SCRIPT_DIR/auto_record_state1.sh"
START_SCRIPT="$SCRIPT_DIR/start_record_lidar.sh"
BACKUP_SCRIPT="$SCRIPT_DIR/start_record_lidar.sh.bak.$(date +%Y%m%d_%H%M%S)"


mkdir -p "$SCRIPT_DIR" "$LOG_DIR"


echo "[1/5] 写入自动录包脚本: $AUTO_SCRIPT"
cat > "$AUTO_SCRIPT" <<'EOS'
#!/bin/bash


RECORD_DIR="/home/ysc/jy_cog/system/log/record_lidar_data"
LIMIT_BYTES=$((10 * 1024 * 1024 * 1024))
KEEP_BYTES=$((8 * 1024 * 1024 * 1024))


mkdir -p "$RECORD_DIR"


record_pid=""
current_file=""


cleanup_old_bags() {
  local total old_file current_base
  total=$(du -sb "$RECORD_DIR" 2>/dev/null | awk '{print $1}')
  current_base="$(basename "$current_file" 2>/dev/null || true)"


  while [ -n "$total" ] && [ "$total" -gt "$LIMIT_BYTES" ]; do
    old_file=$(find "$RECORD_DIR" -maxdepth 1 -type f \
      \( -name "*.bag" -o -name "*.bag.active" \) \
      ! -name "${current_base}*" \
      -printf "%T@ %p\n" 2>/dev/null | sort -n | head -n 1 | cut -d' ' -f2-)


    [ -z "$old_file" ] && break


    echo "$(date '+%F %T') delete old bag: $old_file"
    rm -f "$old_file"


    total=$(du -sb "$RECORD_DIR" 2>/dev/null | awk '{print $1}')
    [ "$total" -le "$KEEP_BYTES" ] && break
  done
}


start_record() {
  if [ -n "$record_pid" ] && kill -0 "$record_pid" 2>/dev/null; then
    echo "$(date '+%F %T') rosbag already running pid: $record_pid"
    return
  fi


  cleanup_old_bags


  local ts
  ts=$(date '+%Y-%m-%d-%H-%M-%S')
  current_file="$RECORD_DIR/record_lidar_data_${ts}"


  echo "$(date '+%F %T') start record: $current_file"


  cd "$RECORD_DIR" || exit 1
  setsid rosbag record /livox/lidar /charge/left_rc /charge/right_rc \
    -o "$current_file" --tcpnodelay &


  record_pid=$!
  echo "$(date '+%F %T') rosbag pid: $record_pid"
}


stop_record() {
  if [ -n "$record_pid" ] && kill -0 "$record_pid" 2>/dev/null; then
    echo "$(date '+%F %T') stop record pid: $record_pid"


    kill -INT "-$record_pid" 2>/dev/null || kill -INT "$record_pid" 2>/dev/null || true


    for _ in $(seq 1 10); do
      kill -0 "$record_pid" 2>/dev/null || break
      sleep 1
    done


    if kill -0 "$record_pid" 2>/dev/null; then
      echo "$(date '+%F %T') rosbag still alive, send TERM: $record_pid"
      kill -TERM "-$record_pid" 2>/dev/null || kill -TERM "$record_pid" 2>/dev/null || true
    fi


    wait "$record_pid" 2>/dev/null || true
  fi


  record_pid=""
  current_file=""
  cleanup_old_bags
}


trap 'stop_record; exit 0' INT TERM EXIT


last_state=""


echo "$(date '+%F %T') auto_record_state1 started"


rostopic echo -p /charge_manager_state | while IFS=, read -r stamp state; do
  [ "$stamp" = "%time" ] && continue
  [ -z "$state" ] && continue


  if [ "$state" != "$last_state" ]; then
    echo "$(date '+%F %T') state change: $last_state -> $state"
  fi


  if [ "$state" = "1" ] && [ "$last_state" != "1" ]; then
    start_record
  fi


  if [ "$state" != "1" ] && [ "$last_state" = "1" ]; then
    stop_record
  fi


  last_state="$state"
done
EOS


chmod +x "$AUTO_SCRIPT"


echo "[2/5] 备份原 start_record_lidar.sh"
if [ -f "$START_SCRIPT" ]; then
  cp "$START_SCRIPT" "$BACKUP_SCRIPT"
  echo "backup: $BACKUP_SCRIPT"
fi


echo "[3/5] 替换 start_record_lidar.sh"
cat > "$START_SCRIPT" <<EOS
#!/bin/bash
mkdir -p "$LOG_DIR"
bash "$AUTO_SCRIPT" >> "$LOG_DIR/record_lidar.log" 2>&1 &
EOS
chmod +x "$START_SCRIPT"


echo "[4/5] 清理旧录包进程"
pkill -INT -f "rosbag record" 2>/dev/null || true
pkill -f "auto_record_state1.sh" 2>/dev/null || true
pkill -f "/record_lidar" 2>/dev/null || true
pkill -f "./record_lidar" 2>/dev/null || true


echo "[5/5] 部署完成"
echo
echo "请重启 charge_manager 或重新执行 charge.sh,让新的 start_record_lidar.sh 生效。"
echo
echo "验证命令:"
echo "  tail -f $LOG_DIR/record_lidar.log"
echo "  watch -n 1 'date; ps -ef | grep -E \"[a]uto_record_state1|[r]osbag\"; ls -lh $LOG_DIR'"
EOF


bash /tmp/install_auto_record_state1.sh

重启方式

部署完成后,需要重启 charge_manager,或者重新执行原来的启动入口,让 auto_record_lidar=true 分支重新拉起新的脚本。

如果不知道服务名,可以先查看:

ps -ef | grep -E "charge_manager|charge.sh|roslaunch" | grep -v grep

验证方式

开一个窗口看日志:

tail -f /home/ysc/jy_cog/system/log/record_lidar_data/record_lidar.log

开另一个窗口看进程和文件:

watch -n 1 'date; ps -ef | grep -E "[a]uto_record_state1|[r]osbag"; ls -lh /home/ysc/jy_cog/system/log/record_lidar_data/'

功能正常时,状态进入 1 会看到:

state change: 0 -> 1
start record: /home/ysc/jy_cog/system/log/record_lidar_data/record_lidar_data_...
rosbag pid: xxxx

状态离开 1 会看到:

state change: 1 -> 2
stop record pid: xxxx

目录里应出现新的 .bag 文件。录制过程中可能短暂出现 .bag.active,正常停止后会变成 .bag

验证 bag 内容

latest_bag=$(ls -t /home/ysc/jy_cog/system/log/record_lidar_data/*.bag | head -n 1)
rosbag info "$latest_bag"

应包含:

/livox/lidar
/charge/left_rc
/charge/right_rc

回滚方式

查看备份:

ls -lh /home/ysc/jy_cog/charge_manager_cpp/scripts/start_record_lidar.sh.bak.*

恢复最近备份:

latest_bak=$(ls -t /home/ysc/jy_cog/charge_manager_cpp/scripts/start_record_lidar.sh.bak.* | head -n 1)
cp "$latest_bak" /home/ysc/jy_cog/charge_manager_cpp/scripts/start_record_lidar.sh
chmod +x /home/ysc/jy_cog/charge_manager_cpp/scripts/start_record_lidar.sh
pkill -f auto_record_state1.sh
pkill -INT -f "rosbag record"

然后重启 charge_manager。