更新 run_all.sh

This commit is contained in:
administrator 2026-05-07 03:09:52 +00:00
parent 6431727f4a
commit 7c4ff06d3b
1 changed files with 347 additions and 345 deletions

View File

@ -1,345 +1,347 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail set -euo pipefail
export DISPLAY=:1
# Unified launcher for TurtleBot3 office workflow:
# - Terminal A: simulation / bridge / (optional SLAM) / RViz # Unified launcher for TurtleBot3 office workflow:
# - Terminal B: Nav2 # - Terminal A: simulation / bridge / (optional SLAM) / RViz
# - Optional C: explore_lite # - Terminal B: Nav2
# - Optional websocket + gzweb-demo frontend # - Optional C: explore_lite
# # - Optional websocket + gzweb-demo frontend
# Modes: #
# mapping : online SLAM mapping # Modes:
# localization : map-based localization/navigation # mapping : online SLAM mapping
# # localization : map-based localization/navigation
# Mapping mode interactive commands: #
# s : save map and switch to localization mode # Mapping mode interactive commands:
# q : stop all # s : save map and switch to localization mode
# # q : stop all
# Examples: #
# ./run_all.sh mapping --explore --gzweb # Examples:
# ./run_all.sh localization --gzweb --map /abs/path/office_map.yaml # ./run_all.sh mapping --explore --gzweb
# ./run_all.sh localization --gzweb --map /abs/path/office_map.yaml
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
MODE="${1:-mapping}" ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
shift || true MODE="${1:-mapping}"
shift || true
ROBOT_MODEL="${ROBOT_MODEL:-waffle}"
USE_SIM_TIME="${USE_SIM_TIME:-true}" ROBOT_MODEL="${ROBOT_MODEL:-waffle}"
GZ_PARTITION="${GZ_PARTITION:-tb3_office_gz}" USE_SIM_TIME="${USE_SIM_TIME:-true}"
WORLD_PATH_DEFAULT="${ROOT}/turtlebot3_simulations/turtlebot3_gazebo/worlds/office_gz_dartsim.sdf" GZ_PARTITION="${GZ_PARTITION:-tb3_office_gz}"
MAP_YAML_DEFAULT="${ROOT}/turtlebot3_simulations/turtlebot3_gazebo/map/office_map.yaml" WORLD_PATH_DEFAULT="${ROOT}/turtlebot3_simulations/turtlebot3_gazebo/worlds/office_gz_dartsim.sdf"
MAP_BASENAME_DEFAULT="${ROOT}/turtlebot3_simulations/turtlebot3_gazebo/map/office_map" MAP_YAML_DEFAULT="${ROOT}/turtlebot3_simulations/turtlebot3_gazebo/map/office_map.yaml"
SLEEP_NAV2="${SLEEP_NAV2:-18}" MAP_BASENAME_DEFAULT="${ROOT}/turtlebot3_simulations/turtlebot3_gazebo/map/office_map"
EXPLORE_WAIT_TIMEOUT="${EXPLORE_WAIT_TIMEOUT:-420}" SLEEP_NAV2="${SLEEP_NAV2:-18}"
EXPLORE_WAIT_TIMEOUT="${EXPLORE_WAIT_TIMEOUT:-420}"
WORLD_PATH="${WORLD_PATH:-$WORLD_PATH_DEFAULT}"
MAP_YAML="${NAV2_MAP_PATH:-$MAP_YAML_DEFAULT}" WORLD_PATH="${WORLD_PATH:-$WORLD_PATH_DEFAULT}"
MAP_BASENAME="${MAP_BASENAME:-$MAP_BASENAME_DEFAULT}" MAP_YAML="${NAV2_MAP_PATH:-$MAP_YAML_DEFAULT}"
MAP_BASENAME="${MAP_BASENAME:-$MAP_BASENAME_DEFAULT}"
WITH_EXPLORE=false
WITH_GZWE`B`=false WITH_EXPLORE=false
NO_RVIZ=false WITH_GZWE=false
# 与 --gzweb 同用:只起 gz websocket(9002) 等资源,不起 office 内 npm run dev页面用镜像内 /opt/gzwebfoundation NO_RVIZ=false
NO_GZWEB_VITE=false # 与 --gzweb 同用:只起 gz websocket(9002) 等资源,不起 office 内 npm run dev页面用镜像内 /opt/gzwebfoundation
NO_GZWEB_VITE=false
while [[ $# -gt 0 ]]; do
case "$1" in
--explore) WITH_EXPLORE=true; shift ;; while [[ $# -gt 0 ]]; do
--gzweb) WITH_GZWEB=true; shift ;; case "$1" in
--no-gzweb-vite) NO_GZWEB_VITE=true; shift ;; --explore) WITH_EXPLORE=true; shift ;;
--no-rviz) NO_RVIZ=true; shift ;; --gzweb) WITH_GZWEB=true; shift ;;
--map) MAP_YAML="$2"; shift 2 ;; --no-gzweb-vite) NO_GZWEB_VITE=true; shift ;;
--world) WORLD_PATH="$2"; shift 2 ;; --no-rviz) NO_RVIZ=true; shift ;;
--help|-h) --map) MAP_YAML="$2"; shift 2 ;;
cat <<'EOF' --world) WORLD_PATH="$2"; shift 2 ;;
Usage: --help|-h)
./run_all.sh mapping [--explore] [--gzweb] [--no-gzweb-vite] [--no-rviz] [--world PATH] cat <<'EOF'
./run_all.sh localization [--gzweb] [--no-gzweb-vite] [--no-rviz] [--map PATH] [--world PATH] Usage:
./run_all.sh mapping [--explore] [--gzweb] [--no-gzweb-vite] [--no-rviz] [--world PATH]
--no-gzweb-vite 需与 --gzweb 同时使用:不启动 workspace 内 gzweb-demo 的 Vite浏览器用 /opt/gzweb8000 ./run_all.sh localization [--gzweb] [--no-gzweb-vite] [--no-rviz] [--map PATH] [--world PATH]
EOF
exit 0 --no-gzweb-vite 需与 --gzweb 同时使用:不启动 workspace 内 gzweb-demo 的 Vite浏览器用 /opt/gzweb8000
;; EOF
*) exit 0
echo "[run_all] unknown argument: $1" >&2 ;;
exit 1 *)
;; echo "[run_all] unknown argument: $1" >&2
esac exit 1
done ;;
esac
if [[ "$MODE" != "mapping" && "$MODE" != "localization" ]]; then done
echo "[run_all] mode must be mapping or localization" >&2
exit 1 if [[ "$MODE" != "mapping" && "$MODE" != "localization" ]]; then
fi echo "[run_all] mode must be mapping or localization" >&2
exit 1
if [[ ! -f "$WORLD_PATH" ]]; then fi
echo "[run_all] world not found: $WORLD_PATH" >&2
exit 1 if [[ ! -f "$WORLD_PATH" ]]; then
fi echo "[run_all] world not found: $WORLD_PATH" >&2
exit 1
if [[ "$MODE" == "localization" && ! -f "$MAP_YAML" ]]; then fi
echo "[run_all] map yaml not found: $MAP_YAML" >&2
exit 1 if [[ "$MODE" == "localization" && ! -f "$MAP_YAML" ]]; then
fi echo "[run_all] map yaml not found: $MAP_YAML" >&2
exit 1
set +u fi
source /opt/ros/humble/setup.bash
set +u
# 仅在 install 目录缺失时构建(避免每次启动都重复构建,同时修复未 source ROS 环境导致的 ament_cmake 找不到) source /opt/ros/humble/setup.bash
if [[ ! -d "${ROOT}/install" ]]; then
colcon build --symlink-install # 仅在 install 目录缺失时构建(避免每次启动都重复构建,同时修复未 source ROS 环境导致的 ament_cmake 找不到)
fi if [[ ! -d "${ROOT}/install" ]]; then
colcon build --symlink-install
source "${ROOT}/install/setup.bash" fi
set -u
export TURTLEBOT3_MODEL="$ROBOT_MODEL" source "${ROOT}/install/setup.bash"
set -u
LOG_DIR="${ROOT}/logs/run_all_$(date +%Y%m%d_%H%M%S)" export TURTLEBOT3_MODEL="$ROBOT_MODEL"
mkdir -p "$LOG_DIR"
mkdir -p "$(dirname "$MAP_BASENAME")" LOG_DIR="${ROOT}/logs/run_all_$(date +%Y%m%d_%H%M%S)"
mkdir -p "$LOG_DIR"
PIDS=() mkdir -p "$(dirname "$MAP_BASENAME")"
start_bg() { PIDS=()
local name="$1"; shift
local logfile="${LOG_DIR}/${name}.log" start_bg() {
echo "[run_all] starting ${name} ..." local name="$1"; shift
"$@" >"$logfile" 2>&1 & local logfile="${LOG_DIR}/${name}.log"
local pid=$! echo "[run_all] starting ${name} ..."
PIDS+=("$pid") "$@" >"$logfile" 2>&1 &
echo "[run_all] ${name} pid=${pid}, log=${logfile}" local pid=$!
} PIDS+=("$pid")
echo "[run_all] ${name} pid=${pid}, log=${logfile}"
cleanup() { }
echo "[run_all] stopping all processes..."
for pid in "${PIDS[@]:-}"; do cleanup() {
kill "$pid" 2>/dev/null || true echo "[run_all] stopping all processes..."
done for pid in "${PIDS[@]:-}"; do
} kill "$pid" 2>/dev/null || true
trap cleanup EXIT INT TERM done
}
wait_for_topic() { trap cleanup EXIT INT TERM
local topic="$1"
local timeout="${2:-60}" wait_for_topic() {
local i=0 local topic="$1"
while (( i < timeout )); do local timeout="${2:-60}"
if ros2 topic list 2>/dev/null | grep -Fxq "${topic}"; then local i=0
return 0 while (( i < timeout )); do
fi if ros2 topic list 2>/dev/null | grep -Fxq "${topic}"; then
sleep 1 return 0
((i+=1)) fi
done sleep 1
return 1 ((i+=1))
} done
return 1
wait_for_port() { }
local port="$1"
local timeout="${2:-30}" wait_for_port() {
local i=0 local port="$1"
while (( i < timeout )); do local timeout="${2:-30}"
if ss -ltn 2>/dev/null | grep -q ":${port} "; then local i=0
return 0 while (( i < timeout )); do
fi if ss -ltn 2>/dev/null | grep -q ":${port} "; then
sleep 1 return 0
((i+=1)) fi
done sleep 1
return 1 ((i+=1))
} done
return 1
wait_for_explore_stop_log() { }
local logfile="$1"
local timeout="${2:-900}" wait_for_explore_stop_log() {
local i=0 local logfile="$1"
while (( i < timeout )); do local timeout="${2:-900}"
if [[ -f "$logfile" ]] && grep -Eq "All frontiers traversed/tried out, stopping\\.|Exploration stopped\\." "$logfile"; then local i=0
return 0 while (( i < timeout )); do
fi if [[ -f "$logfile" ]] && grep -Eq "All frontiers traversed/tried out, stopping\\.|Exploration stopped\\." "$logfile"; then
sleep 1 return 0
((i+=1)) fi
done sleep 1
return 1 ((i+=1))
} done
return 1
wait_for_explore_stop_log_with_timer() { }
local logfile="$1"
local timeout="${2:-900}" wait_for_explore_stop_log_with_timer() {
local start_ts local logfile="$1"
start_ts="$(date +%s)" local timeout="${2:-900}"
local i=0 local start_ts
while (( i < timeout )); do start_ts="$(date +%s)"
if [[ -f "$logfile" ]] && grep -Eq "All frontiers traversed/tried out, stopping\\.|Exploration stopped\\." "$logfile"; then local i=0
return 0 while (( i < timeout )); do
fi if [[ -f "$logfile" ]] && grep -Eq "All frontiers traversed/tried out, stopping\\.|Exploration stopped\\." "$logfile"; then
if (( i % 10 == 0 )); then return 0
local now_ts elapsed fi
now_ts="$(date +%s)" if (( i % 10 == 0 )); then
elapsed=$(( now_ts - start_ts )) local now_ts elapsed
echo "[run_all] explore elapsed: ${elapsed}s" now_ts="$(date +%s)"
fi elapsed=$(( now_ts - start_ts ))
sleep 1 echo "[run_all] explore elapsed: ${elapsed}s"
((i+=1)) fi
done sleep 1
return 1 ((i+=1))
} done
return 1
kill_port_listeners() { }
local port="$1"
local pids kill_port_listeners() {
pids="$(ss -ltnp 2>/dev/null | grep ":${port} " | sed -E 's/.*pid=([0-9]+).*/\1/' | sort -u || true)" local port="$1"
if [[ -n "${pids}" ]]; then local pids
echo "[run_all] port ${port} already in use, killing: ${pids}" pids="$(ss -ltnp 2>/dev/null | grep ":${port} " | sed -E 's/.*pid=([0-9]+).*/\1/' | sort -u || true)"
kill ${pids} 2>/dev/null || true if [[ -n "${pids}" ]]; then
sleep 1 echo "[run_all] port ${port} already in use, killing: ${pids}"
fi kill ${pids} 2>/dev/null || true
} sleep 1
fi
show_rviz_arg="show_rviz:=true" }
if [[ "$NO_RVIZ" == true ]]; then
show_rviz_arg="show_rviz:=false" show_rviz_arg="show_rviz:=true"
fi if [[ "$NO_RVIZ" == true ]]; then
show_rviz_arg="show_rviz:=false"
if [[ "$MODE" == "mapping" ]]; then fi
start_bg "A_sim" \
ros2 launch turtlebot3_gazebo turtlebot3_office_gz.launch.py \ if [[ "$MODE" == "mapping" ]]; then
robot_model:="$ROBOT_MODEL" \ start_bg "A_sim" \
use_sim_time:="$USE_SIM_TIME" \ ros2 launch turtlebot3_gazebo turtlebot3_office_gz.launch.py \
start_slam:=true \ robot_model:="$ROBOT_MODEL" \
"$show_rviz_arg" \ use_sim_time:="$USE_SIM_TIME" \
gz_partition:="$GZ_PARTITION" \ start_slam:=true \
gz_world_name:=default \ "$show_rviz_arg" \
world:="$WORLD_PATH" gz_partition:="$GZ_PARTITION" \
else gz_world_name:=default \
start_bg "A_sim" \ world:="$WORLD_PATH"
ros2 launch turtlebot3_gazebo turtlebot3_office_gz.launch.py \ else
robot_model:="$ROBOT_MODEL" \ start_bg "A_sim" \
use_sim_time:="$USE_SIM_TIME" \ ros2 launch turtlebot3_gazebo turtlebot3_office_gz.launch.py \
start_slam:=false \ robot_model:="$ROBOT_MODEL" \
"$show_rviz_arg" \ use_sim_time:="$USE_SIM_TIME" \
gz_partition:="$GZ_PARTITION" \ start_slam:=false \
gz_world_name:=default \ "$show_rviz_arg" \
world:="$WORLD_PATH" gz_partition:="$GZ_PARTITION" \
fi gz_world_name:=default \
world:="$WORLD_PATH"
# --gzwebgz launch(9002) + 可选 office 内 gzweb-demo 的 Vite5173 fi
# --gzweb --no-gzweb-vite不起 office Vite页面用 foundation 已在 /opt/gzweb:8000 起的实例(见 run_all_fix.sh
if [[ "$WITH_GZWEB" == true ]]; then # --gzwebgz launch(9002) + 可选 office 内 gzweb-demo 的 Vite5173
export GZ_PARTITION # --gzweb --no-gzweb-vite不起 office Vite页面用 foundation 已在 /opt/gzweb:8000 起的实例(见 run_all_fix.sh
OFFICE_MODEL_ROOT="${ROOT}/turtlebot3_simulations/turtlebot3_gazebo/models/turtlebot3_office" if [[ "$WITH_GZWEB" == true ]]; then
RESOURCE_PATHS=( export GZ_PARTITION
"${HOME}/.gazebo/models" OFFICE_MODEL_ROOT="${ROOT}/turtlebot3_simulations/turtlebot3_gazebo/models/turtlebot3_office"
"${ROOT}/turtlebot3_simulations/turtlebot3_gazebo/models" RESOURCE_PATHS=(
"${OFFICE_MODEL_ROOT}" "${HOME}/.gazebo/models"
) "${ROOT}/turtlebot3_simulations/turtlebot3_gazebo/models"
for d in "${OFFICE_MODEL_ROOT}"/*/materials/scripts "${OFFICE_MODEL_ROOT}"/*/materials/textures; do "${OFFICE_MODEL_ROOT}"
[[ -d "${d}" ]] && RESOURCE_PATHS+=("${d}") )
done for d in "${OFFICE_MODEL_ROOT}"/*/materials/scripts "${OFFICE_MODEL_ROOT}"/*/materials/textures; do
RESOURCE_PATH_JOINED="$(IFS=:; echo "${RESOURCE_PATHS[*]}")" [[ -d "${d}" ]] && RESOURCE_PATHS+=("${d}")
export GZ_SIM_RESOURCE_PATH="${RESOURCE_PATH_JOINED}" done
# Keep compatibility with components resolving textures via legacy lookup vars. RESOURCE_PATH_JOINED="$(IFS=:; echo "${RESOURCE_PATHS[*]}")"
export GZ_FILE_PATH="${RESOURCE_PATH_JOINED}" export GZ_SIM_RESOURCE_PATH="${RESOURCE_PATH_JOINED}"
export GAZEBO_RESOURCE_PATH="${RESOURCE_PATH_JOINED}" # Keep compatibility with components resolving textures via legacy lookup vars.
export GZ_FILE_PATH="${RESOURCE_PATH_JOINED}"
if [[ "$NO_GZWEB_VITE" != true ]]; then export GAZEBO_RESOURCE_PATH="${RESOURCE_PATH_JOINED}"
if [[ ! -d "${ROOT}/gzweb-demo/gzweb-demo/node_modules/gzweb" ]]; then
echo "[run_all] gzweb demo dependencies missing, running npm install once..." if [[ "$NO_GZWEB_VITE" != true ]]; then
bash -lc "cd '${ROOT}/gzweb-demo/gzweb-demo' && npm install" if [[ ! -d "${ROOT}/gzweb-demo/gzweb-demo/node_modules/gzweb" ]]; then
fi echo "[run_all] gzweb demo dependencies missing, running npm install once..."
else bash -lc "cd '${ROOT}/gzweb-demo/gzweb-demo' && npm install"
echo "[run_all] --no-gzweb-vite: skipping office gzweb-demo npm install; use /opt/gzweb (port 8000) for UI." fi
fi else
echo "[run_all] --no-gzweb-vite: skipping office gzweb-demo npm install; use /opt/gzweb (port 8000) for UI."
kill_port_listeners 9002 fi
kill_port_listeners 5173
kill_port_listeners 9002
echo "[run_all] waiting for /clock before websocket..." kill_port_listeners 5173
if wait_for_topic "/clock" 25; then
start_bg "D_websocket" gz launch "${ROOT}/websocket.sdf" echo "[run_all] waiting for /clock before websocket..."
if [[ "$NO_GZWEB_VITE" != true ]]; then if wait_for_topic "/clock" 25; then
start_bg "E_gzweb_frontend" bash -lc "cd '${ROOT}/gzweb-demo/gzweb-demo' && npm run dev:host" start_bg "D_websocket" gz launch "${ROOT}/websocket.sdf"
echo "[run_all] office gzweb-demo Vite: http://localhost:5173" if [[ "$NO_GZWEB_VITE" != true ]]; then
fi start_bg "E_gzweb_frontend" bash -lc "cd '${ROOT}/gzweb-demo/gzweb-demo' && npm run dev:host"
else echo "[run_all] office gzweb-demo Vite: http://localhost:5173"
echo "[run_all] WARN: /clock not detected in time; starting websocket (and optional frontend) anyway." fi
start_bg "D_websocket" gz launch "${ROOT}/websocket.sdf" else
if [[ "$NO_GZWEB_VITE" != true ]]; then echo "[run_all] WARN: /clock not detected in time; starting websocket (and optional frontend) anyway."
start_bg "E_gzweb_frontend" bash -lc "cd '${ROOT}/gzweb-demo/gzweb-demo' && npm run dev:host" start_bg "D_websocket" gz launch "${ROOT}/websocket.sdf"
fi if [[ "$NO_GZWEB_VITE" != true ]]; then
fi start_bg "E_gzweb_frontend" bash -lc "cd '${ROOT}/gzweb-demo/gzweb-demo' && npm run dev:host"
fi
if wait_for_port 9002 12; then fi
echo "[run_all] websocket server ready on ws://localhost:9002"
else if wait_for_port 9002 12; then
echo "[run_all] ERROR: websocket server failed on 9002; check ${LOG_DIR}/D_websocket.log" echo "[run_all] websocket server ready on ws://localhost:9002"
fi else
fi echo "[run_all] ERROR: websocket server failed on 9002; check ${LOG_DIR}/D_websocket.log"
fi
echo "[run_all] waiting ${SLEEP_NAV2}s before starting Nav2..." fi
sleep "$SLEEP_NAV2"
echo "[run_all] waiting ${SLEEP_NAV2}s before starting Nav2..."
if [[ "$MODE" == "mapping" ]]; then sleep "$SLEEP_NAV2"
start_bg "B_nav2" ros2 launch nav2_bringup navigation_launch.py use_sim_time:="$USE_SIM_TIME"
else if [[ "$MODE" == "mapping" ]]; then
start_bg "B_nav2" \ start_bg "B_nav2" ros2 launch nav2_bringup navigation_launch.py use_sim_time:="$USE_SIM_TIME"
ros2 launch nav2_bringup bringup_launch.py \ else
use_sim_time:="$USE_SIM_TIME" \ start_bg "B_nav2" \
map:="$MAP_YAML" \ ros2 launch nav2_bringup bringup_launch.py \
slam:=false use_sim_time:="$USE_SIM_TIME" \
fi map:="$MAP_YAML" \
slam:=false
if [[ "$WITH_EXPLORE" == true && "$MODE" == "mapping" ]]; then fi
start_bg "C_explore" ros2 launch explore_lite explore.launch.py use_sim_time:="$USE_SIM_TIME"
last_index=$(( ${#PIDS[@]} - 1 )) if [[ "$WITH_EXPLORE" == true && "$MODE" == "mapping" ]]; then
EXPLORE_PID="${PIDS[$last_index]}" start_bg "C_explore" ros2 launch explore_lite explore.launch.py use_sim_time:="$USE_SIM_TIME"
EXPLORE_LOG="${LOG_DIR}/C_explore.log" last_index=$(( ${#PIDS[@]} - 1 ))
fi EXPLORE_PID="${PIDS[$last_index]}"
EXPLORE_LOG="${LOG_DIR}/C_explore.log"
echo fi
echo "[run_all] started. Mode: ${MODE}"
echo "[run_all] logs: ${LOG_DIR}" echo
echo echo "[run_all] started. Mode: ${MODE}"
echo "[run_all] logs: ${LOG_DIR}"
if [[ "$MODE" == "mapping" ]]; then echo
if [[ "$WITH_GZWEB" == true ]]; then
if [[ "$NO_GZWEB_VITE" == true ]]; then if [[ "$MODE" == "mapping" ]]; then
echo "[run_all] UI: foundation /opt/gzweb on port 8000 (no office Vite)." if [[ "$WITH_GZWEB" == true ]]; then
elif wait_for_port 5173 25; then if [[ "$NO_GZWEB_VITE" == true ]]; then
echo "[run_all] office gzweb-demo ready: http://localhost:5173" echo "[run_all] UI: foundation /opt/gzweb on port 8000 (no office Vite)."
else elif wait_for_port 5173 25; then
echo "[run_all] WARN: office gzweb frontend not ready yet, check ${LOG_DIR}/E_gzweb_frontend.log" echo "[run_all] office gzweb-demo ready: http://localhost:5173"
fi else
fi echo "[run_all] WARN: office gzweb frontend not ready yet, check ${LOG_DIR}/E_gzweb_frontend.log"
fi
if [[ "${WITH_EXPLORE}" == true && -n "${EXPLORE_PID:-}" ]]; then fi
echo "[run_all] exploration is running (pid=${EXPLORE_PID}), waiting for stop signal (timeout=${EXPLORE_WAIT_TIMEOUT}s)..."
if wait_for_explore_stop_log_with_timer "${EXPLORE_LOG}" "${EXPLORE_WAIT_TIMEOUT}"; then if [[ "${WITH_EXPLORE}" == true && -n "${EXPLORE_PID:-}" ]]; then
echo "[run_all] detected exploration stop from log, finalizing explore process..." echo "[run_all] exploration is running (pid=${EXPLORE_PID}), waiting for stop signal (timeout=${EXPLORE_WAIT_TIMEOUT}s)..."
else if wait_for_explore_stop_log_with_timer "${EXPLORE_LOG}" "${EXPLORE_WAIT_TIMEOUT}"; then
echo "[run_all] WARN: explore stop not detected within timeout; forcing stop and saving current map." echo "[run_all] detected exploration stop from log, finalizing explore process..."
fi else
kill "${EXPLORE_PID}" 2>/dev/null || true echo "[run_all] WARN: explore stop not detected within timeout; forcing stop and saving current map."
wait "${EXPLORE_PID}" 2>/dev/null || true fi
echo "[run_all] explore_lite stopped, now saving map." kill "${EXPLORE_PID}" 2>/dev/null || true
fi wait "${EXPLORE_PID}" 2>/dev/null || true
echo "[run_all] explore_lite stopped, now saving map."
if [[ "${WITH_EXPLORE}" == true ]]; then fi
echo "[run_all] auto-saving map to ${MAP_BASENAME}.yaml/.pgm ..."
ros2 run nav2_map_server map_saver_cli -f "$MAP_BASENAME" --ros-args -p use_sim_time:="$USE_SIM_TIME" if [[ "${WITH_EXPLORE}" == true ]]; then
echo "[run_all] map saved successfully:" echo "[run_all] auto-saving map to ${MAP_BASENAME}.yaml/.pgm ..."
echo " - ${MAP_BASENAME}.yaml" ros2 run nav2_map_server map_saver_cli -f "$MAP_BASENAME" --ros-args -p use_sim_time:="$USE_SIM_TIME"
echo " - ${MAP_BASENAME}.pgm" echo "[run_all] map saved successfully:"
echo "[run_all] mapping workflow completed, exiting now." echo " - ${MAP_BASENAME}.yaml"
exit 0 echo " - ${MAP_BASENAME}.pgm"
fi echo "[run_all] mapping workflow completed, exiting now."
exit 0
cat <<EOF fi
[run_all] Mapping mode notes:
- RViz 'Localization: inactive' is normal during SLAM mapping. cat <<EOF
- You did not enable --explore, so auto-save/auto-exit is disabled. [run_all] Mapping mode notes:
- Press Ctrl+C when you want to stop all. - RViz 'Localization: inactive' is normal during SLAM mapping.
EOF - You did not enable --explore, so auto-save/auto-exit is disabled.
wait - Press Ctrl+C when you want to stop all.
else EOF
echo "[run_all] Localization mode: RViz Localization should become active after initial pose." wait
echo "[run_all] Keep this terminal running; Ctrl+C to stop all." else
wait echo "[run_all] Localization mode: RViz Localization should become active after initial pose."
fi echo "[run_all] Keep this terminal running; Ctrl+C to stop all."
wait
fi