DroneMind/voice_drone_assistant/scripts/run_px4_offboard_one_terminal.sh
2026-04-14 09:59:51 +08:00

173 lines
5.2 KiB
Bash
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
# 单终端一键:按需 roscore → MAVROS 串口(MAVLink) → px4_ctrl_offboard_demo.py
#
# 用法(在仓库根目录):
# bash scripts/run_px4_offboard_one_terminal.sh
# bash scripts/run_px4_offboard_one_terminal.sh /dev/ttyACM0 921600
# bash scripts/run_px4_offboard_one_terminal.sh /dev/ttyACM0 921600 120
# 第三参 DEMO_MAX_SEC仅限制「px4_ctrl_offboard_demo.py」运行时长到时发 SIGTERM 结束
# demo随后脚本清理 MAVROS / 可选 roscore 并退出0 或不写表示不限制。
# 也可用环境变量 OFFBOARD_DEMO_MAX_SEC第三参优先
#
# 环境变量(可选):
# ROS_MASTER_URI 默认 http://127.0.0.1:11311
# ROS_HOSTNAME 默认 127.0.0.1
# OFFBOARD_PYTHON 默认优先用 conda env「yanshi」里的 python否则 python3
# OFFBOARD_DEMO_MAX_SEC 同第三参;整条脚本从头限时请用: timeout 600 bash 本脚本 ...
# ROCKET_OFFBOARD_DEMO_PY 显式指定 px4_ctrl_offboard_demo.py 路径(缺省时先 $ROOT/src/ 再 $ROOT/../src/
#
# 退出Ctrl+C 会结束 demo并停止本脚本拉起的 MAVROS若 roscore 由本脚本启动,会一并结束。
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
cd "$ROOT"
resolve_demo_py() {
if [[ -n "${ROCKET_OFFBOARD_DEMO_PY:-}" ]] && [[ -f "${ROCKET_OFFBOARD_DEMO_PY}" ]]; then
echo "$(cd "$(dirname "${ROCKET_OFFBOARD_DEMO_PY}")" && pwd)/$(basename "${ROCKET_OFFBOARD_DEMO_PY}")"
return
fi
local c1="$ROOT/src/px4_ctrl_offboard_demo.py"
if [[ -f "$c1" ]]; then
echo "$c1"
return
fi
local c2="$ROOT/../src/px4_ctrl_offboard_demo.py"
if [[ -f "$c2" ]]; then
echo "$(cd "$(dirname "$c2")" && pwd)/px4_ctrl_offboard_demo.py"
return
fi
echo "错误: 找不到 px4_ctrl_offboard_demo.py。已尝试:" >&2
echo " ROCKET_OFFBOARD_DEMO_PY=${ROCKET_OFFBOARD_DEMO_PY:-(未设置)}" >&2
echo " $c1" >&2
echo " $c2" >&2
exit 3
}
DEMO_PY="$(resolve_demo_py)"
source /opt/ros/noetic/setup.bash
export ROS_MASTER_URI="${ROS_MASTER_URI:-http://127.0.0.1:11311}"
export ROS_HOSTNAME="${ROS_HOSTNAME:-127.0.0.1}"
DEV="${1:-/dev/ttyACM0}"
BAUD="${2:-921600}"
DEMO_MAX_SEC="${3:-${OFFBOARD_DEMO_MAX_SEC:-0}}"
FCU_URL="${DEV}:${BAUD}"
if ! [[ "$DEMO_MAX_SEC" =~ ^[0-9]+$ ]]; then
echo "错误: 第三参demo 最长运行秒数)须为非负整数,当前=${DEMO_MAX_SEC}"
exit 2
fi
ROSCORE_PID=""
MAVROS_PID=""
WE_STARTED_ROSCORE=0
master_ok() {
timeout 3 rosnode list &>/dev/null
}
stop_mavros() {
pkill -f '/opt/ros/noetic/lib/mavros/mavros_node' 2>/dev/null || true
}
kill_children() {
if [[ -n "${MAVROS_PID:-}" ]] && kill -0 "$MAVROS_PID" 2>/dev/null; then
kill "$MAVROS_PID" 2>/dev/null || true
wait "$MAVROS_PID" 2>/dev/null || true
fi
if [[ "${WE_STARTED_ROSCORE}" -eq 1 ]] && [[ -n "${ROSCORE_PID:-}" ]] && kill -0 "$ROSCORE_PID" 2>/dev/null; then
kill "$ROSCORE_PID" 2>/dev/null || true
wait "$ROSCORE_PID" 2>/dev/null || true
fi
}
trap 'kill_children; exit 130' INT
trap 'kill_children; exit 143' TERM
resolve_python() {
if [[ -n "${OFFBOARD_PYTHON:-}" ]] && [[ -x "$OFFBOARD_PYTHON" ]]; then
echo "$OFFBOARD_PYTHON"
return
fi
for cand in \
"${HOME}/miniconda3/envs/yanshi/bin/python" \
"${HOME}/anaconda3/envs/yanshi/bin/python" \
"${HOME}/mambaforge/envs/yanshi/bin/python"; do
if [[ -x "$cand" ]]; then
echo "$cand"
return
fi
done
command -v python3
}
if ! master_ok; then
echo "[1/3] 启动 roscore ..."
roscore > /tmp/roscore_offboard_one_terminal.log 2>&1 &
ROSCORE_PID=$!
WE_STARTED_ROSCORE=1
for _ in $(seq 1 50); do
master_ok && break
sleep 0.2
done
if ! master_ok; then
echo "roscore 未起来,请查看: tail -40 /tmp/roscore_offboard_one_terminal.log"
kill_children
exit 1
fi
echo "roscore 已就绪 (pid=$ROSCORE_PID)"
else
echo "[1/3] 已有 ROS master跳过 roscore"
fi
echo "[2/3] 启动 MAVROS fcu_url=${FCU_URL}"
stop_mavros
sleep 1
roslaunch mavros px4.launch fcu_url:="$FCU_URL" > /tmp/mavros_offboard_one_terminal.log 2>&1 &
MAVROS_PID=$!
echo "等待飞控连接 (connected=true),超时 60s ..."
connected=0
for _ in $(seq 1 60); do
if timeout 4 rostopic echo /mavros/state -n 1 2>/dev/null | grep -qE 'connected: [Tt]rue'; then
connected=1
break
fi
sleep 1
done
if [[ "$connected" -ne 1 ]]; then
echo "仍未连上飞控。请检查串口与波特率,日志: tail -60 /tmp/mavros_offboard_one_terminal.log"
echo "可重试: bash $0 ${DEV} 57600"
kill_children
exit 1
fi
echo "MAVROS 已连接飞控"
PY="$(resolve_python)"
echo "[3/3] 使用 Python: $PY"
if [[ "$DEMO_MAX_SEC" -gt 0 ]]; then
echo "运行 $DEMO_PY${DEMO_MAX_SEC}s 后自动结束 demo 并退出本脚本(随后清理子进程)"
else
echo "运行 $DEMO_PY Ctrl+C 结束并将停止本脚本启动的 MAVROS"
fi
set +e
if [[ "$DEMO_MAX_SEC" -gt 0 ]]; then
timeout --signal=TERM --kill-after=8 "$DEMO_MAX_SEC" \
"$PY" "$DEMO_PY"
DEMO_EXIT=$?
if [[ "$DEMO_EXIT" -eq 124 ]]; then
echo "[timeout] 已到 ${DEMO_MAX_SEC}s已终止 px4_ctrl_offboard_demo.py退出码 124"
fi
else
"$PY" "$DEMO_PY"
DEMO_EXIT=$?
fi
set -e
kill_children
exit "$DEMO_EXIT"