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