增加CPP

This commit is contained in:
2025-12-26 10:02:00 +08:00
parent 4961794bf5
commit ef6a98d473
22 changed files with 1027 additions and 83 deletions

0
cali_web.py Normal file
View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 MiB

After

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 MiB

After

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 MiB

After

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 MiB

After

Width:  |  Height:  |  Size: 2.5 MiB

731
lj360_manager.sh Executable file
View File

@@ -0,0 +1,731 @@
#!/bin/bash
# =============================================
# LJ360 全景监控服务管理脚本 - 增强版
# 基于步骤化提示、彩色输出、友好交互
# 适用于 RK3588 / Linux 本地或远程管理
# =============================================
# ---------------------------------------------
# 初始化设置
# ---------------------------------------------
# 遇到错误立即退出
set -e
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
PURPLE='\033[0;35m'
NC='\033[0m' # No Color
# 进度与状态打印函数
print_header() {
clear
echo -e "${PURPLE}"
echo "╔══════════════════════════════════════════════════════╗"
echo "║ LJ360 全景监控服务管理工具 (增强版) ║"
echo "║ RK3588 / Linux ║"
echo "╚══════════════════════════════════════════════════════╝"
echo -e "${NC}"
}
print_step() {
echo -e "${BLUE}[STEP $1/$2]${NC} $3"
}
print_success() {
echo -e "${GREEN}${NC} $1"
}
print_warning() {
echo -e "${YELLOW}${NC} $1"
}
print_error() {
echo -e "${RED}${NC} $1"
}
print_progress() {
echo -e "${CYAN}[进度]${NC} $1"
}
print_divider() {
echo -e "${BLUE}──────────────────────────────────────────────${NC}"
}
# ---------------------------------------------
# 全局配置
# ---------------------------------------------
TOTAL_STEPS=9
STEP=1
SERVICE_NAME="lj360.service"
WATCHDOG_SERVICE="lj360_watchdog.service"
# 获取当前用户
CURRENT_USER=$(whoami)
# 检查是否以 root 运行
if [ "$EUID" -eq 0 ]; then
print_error "❌ 请勿使用 root 用户运行此脚本。"
print_error "✅ 请使用以下命令运行(普通用户):"
print_error " bash $0"
exit 1
fi
# 项目目录
PROJECT_DIR="/home/$CURRENT_USER/LJ360"
print_header
print_success "🔧 当前用户: $CURRENT_USER"
print_success "📂 项目目录: $PROJECT_DIR"
echo ""
# ---------------------------------------------
# 主菜单逻辑与步骤化功能
# ---------------------------------------------
main_menu() {
while true; do
show_status
echo -e "${CYAN}🔧 请选择要执行的操作:${NC}"
print_divider
echo "1) 🛠️ 安装 LJ360 全景监控服务"
echo "2) 🛡️ 安装并启动看门狗服务"
echo "3) ▶️ 启动全景监控服务"
echo "4) ⏹️ 停止全景监控服务"
echo "5) 🔄 重启全景监控服务"
echo "6) 🔔 启用开机自启"
echo "7) 🚫 禁用开机自启"
echo "8) 📜 查看实时日志"
echo "9) 📊 查看系统状态"
echo "10) 🧹 卸载 LJ360 服务"
echo "0) 🚪 退出脚本"
print_divider
read -p "👉 请输入选项 [0-10]: " choice
case $choice in
1)
STEP=1
install_service
;;
2)
STEP=1
install_watchdog_service
;;
3)
STEP=1
start_service
;;
4)
STEP=1
stop_service
;;
5)
STEP=1
restart_service
;;
6)
STEP=1
enable_service
;;
7)
STEP=1
disable_service
;;
8)
STEP=1
view_logs
;;
9)
STEP=1
view_status
;;
10)
STEP=1
uninstall_service
;;
0)
print_success "👋 再见!脚本已退出。"
exit 0
;;
*)
print_error "❌ 无效选项,请重新输入 [0-10]"
sleep 2
;;
esac
done
}
# ---------------------------------------------
# 功能函数
# ---------------------------------------------
show_status() {
print_header
echo -e "${BLUE}【📊 当前服务状态】${NC}"
print_divider
# 检查主服务状态
if systemctl is-active --quiet $SERVICE_NAME 2>/dev/null; then
echo -e "${GREEN}● LJ360 服务正在运行 ✅${NC}"
else
echo -e "${RED}● LJ360 服务未运行 ❌${NC}"
fi
# 检查看门狗服务状态
if systemctl is-active --quiet $WATCHDOG_SERVICE 2>/dev/null; then
echo -e "${GREEN}● 看门狗服务正在运行 🛡️${NC}"
else
echo -e "${YELLOW}● 看门狗服务未运行 ⚠${NC}"
fi
# 检查开机自启状态
if systemctl is-enabled $SERVICE_NAME 2>/dev/null; then
echo -e "${GREEN}● 开机自启已启用 ✅${NC}"
else
echo -e "${YELLOW}● 开机自启未启用 ⚠${NC}"
fi
# 检查Web端口
echo ""
echo -e "${BLUE}【🌐 网络端口状态】${NC}"
print_divider
if netstat -tlnp 2>/dev/null | grep -q ":5000"; then
echo -e "${GREEN}● Web 服务端口 5000 已监听 ✅${NC}"
else
echo -e "${RED}● Web 服务端口 5000 未监听 ❌${NC}"
fi
echo ""
}
# --------------------------
# Step 1: 安装服务
# --------------------------
install_service() {
print_step $STEP $TOTAL_STEPS "安装 LJ360 全景监控服务"
# 检查项目目录是否存在
if [ ! -d "$PROJECT_DIR" ]; then
print_error "❌ 项目目录不存在: $PROJECT_DIR"
print_error "请确认项目路径是否正确"
read -p "🔘 按 Enter 继续..." -r
return
fi
# 检查web.py文件是否存在
if [ ! -f "$PROJECT_DIR/web.py" ]; then
print_error "❌ web.py 文件不存在: $PROJECT_DIR/web.py"
print_error "请确认项目文件是否正确"
read -p "🔘 按 Enter 继续..." -r
return
fi
# 创建启动脚本
local START_SCRIPT="$PROJECT_DIR/start_lj360.sh"
print_progress "🔧 创建启动脚本: $START_SCRIPT"
cat > "$START_SCRIPT" << EOF
#!/bin/bash
# LJ360 全景监控启动脚本
# 项目目录: $PROJECT_DIR
# 切换到项目目录
cd "$PROJECT_DIR"
# 设置环境变量
export DISPLAY=:0
export XAUTHORITY=/home/$CURRENT_USER/.Xauthority
export PYTHONPATH=$PROJECT_DIR:\$PYTHONPATH
# 日志文件
LOG_FILE="$PROJECT_DIR/lj360.log"
ERROR_LOG="$PROJECT_DIR/lj360_error.log"
echo "=========================================" >> "\$LOG_FILE"
echo "LJ360 启动时间: \$(date)" >> "\$LOG_FILE"
echo "=========================================" >> "\$LOG_FILE"
# 检查是否已有进程在运行
if pgrep -f "python3 web.py" > /dev/null; then
echo "\$(date): LJ360 应用已在运行,跳过启动" >> "\$LOG_FILE"
exit 0
fi
# 启动应用并记录输出
echo "\$(date): 启动 LJ360 web.py..." >> "\$LOG_FILE"
exec python3 web.py >> "\$LOG_FILE" 2>> "\$ERROR_LOG"
EOF
chmod +x "$START_SCRIPT"
chown "$CURRENT_USER:$CURRENT_USER" "$START_SCRIPT"
print_success "✅ 启动脚本已创建并授权"
# 创建 systemd 服务
print_progress "🔧 创建 systemd 服务文件: /etc/systemd/system/$SERVICE_NAME"
sudo tee /etc/systemd/system/$SERVICE_NAME > /dev/null << EOF
[Unit]
Description=LJ360 Surround View Service
After=network.target graphical.target
Wants=network.target graphical.target
[Service]
Type=simple
User=$CURRENT_USER
WorkingDirectory=$PROJECT_DIR
Environment="DISPLAY=:0"
Environment="XAUTHORITY=/home/$CURRENT_USER/.Xauthority"
Environment="PYTHONPATH=$PROJECT_DIR"
Environment="XDG_RUNTIME_DIR=/run/user/1000"
ExecStart=$PROJECT_DIR/start_lj360.sh
Restart=always
RestartSec=5
StandardOutput=journal
StandardError=journal
# 限制资源使用(可选)
# MemoryMax=1G
# CPUQuota=80%
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
print_success "✅ systemd 服务文件已部署 & 缓存已刷新"
STEP=$((STEP + 1))
read -p "🔘 按 Enter 继续..." -r
}
# --------------------------
# Step 2: 安装看门狗服务
# --------------------------
install_watchdog_service() {
print_step $STEP $TOTAL_STEPS "安装并启动看门狗服务"
# 创建看门狗脚本
local WATCHDOG_SCRIPT="$PROJECT_DIR/watchdog_lj360.sh"
print_progress "🔧 创建看门狗脚本: $WATCHDOG_SCRIPT"
cat > "$WATCHDOG_SCRIPT" << EOF
#!/bin/bash
# LJ360 看门狗服务脚本
# 项目目录: $PROJECT_DIR
APP_NAME="web.py"
CHECK_INTERVAL=10
LOG_FILE="$PROJECT_DIR/watchdog.log"
echo "=========================================" >> "\$LOG_FILE"
echo "看门狗服务启动时间: \$(date)" >> "\$LOG_FILE"
echo "=========================================" >> "\$LOG_FILE"
while true; do
# 检查主服务进程是否存在
if ! pgrep -f "python3 \$APP_NAME" > /dev/null; then
echo "\$(date): 检测到 LJ360 未运行,正在启动..." >> "\$LOG_FILE"
cd "$PROJECT_DIR"
# 设置环境变量
export DISPLAY=:0
export XAUTHORITY=/home/$CURRENT_USER/.Xauthority
export PYTHONPATH=$PROJECT_DIR:\$PYTHONPATH
# 启动应用
nohup python3 web.py >> "$PROJECT_DIR/lj360.log" 2>> "$PROJECT_DIR/lj360_error.log" &
echo "\$(date): 已启动 LJ360PID: \$!" >> "\$LOG_FILE"
# 等待应用启动
sleep 5
fi
sleep \$CHECK_INTERVAL
done
EOF
chmod +x "$WATCHDOG_SCRIPT"
chown "$CURRENT_USER:$CURRENT_USER" "$WATCHDOG_SCRIPT"
print_success "✅ 看门狗脚本已创建并授权"
# 创建看门狗 systemd 服务
print_progress "🔧 创建看门狗服务文件: /etc/systemd/system/$WATCHDOG_SERVICE"
sudo tee /etc/systemd/system/$WATCHDOG_SERVICE > /dev/null << EOF
[Unit]
Description=LJ360 Watchdog Service
After=network.target
Wants=network.target
[Service]
Type=simple
User=$CURRENT_USER
WorkingDirectory=$PROJECT_DIR
ExecStart=$PROJECT_DIR/watchdog_lj360.sh
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
print_success "✅ 看门狗服务文件已部署"
# 启用并启动看门狗服务
print_progress "🚀 启动看门狗服务"
sudo systemctl enable $WATCHDOG_SERVICE
sudo systemctl start $WATCHDOG_SERVICE
sleep 2
if systemctl is-active --quiet $WATCHDOG_SERVICE; then
print_success "✅ 看门狗服务启动成功"
else
print_warning "⚠️ 看门狗服务启动可能有问题,请查看日志"
fi
STEP=$((STEP + 1))
read -p "🔘 按 Enter 继续..." -r
}
# --------------------------
# Step 3: 启动服务
# --------------------------
start_service() {
print_step $STEP $TOTAL_STEPS "启动 LJ360 全景监控服务"
# 检查服务文件是否存在
if [ ! -f "/etc/systemd/system/$SERVICE_NAME" ]; then
print_error "❌ 服务未安装,请先安装服务"
read -p "🔘 按 Enter 继续..." -r
return
fi
print_progress "🚀 正在启动服务: $SERVICE_NAME"
sudo systemctl start "$SERVICE_NAME"
sleep 3
# 检查启动状态
if systemctl is-active --quiet "$SERVICE_NAME"; then
print_success "✅ 服务启动成功"
# 检查Web端口
sleep 2
if netstat -tlnp 2>/dev/null | grep -q ":5000"; then
local IP_ADDR=$(hostname -I | awk '{print $1}')
print_success "🌐 Web 服务已启动: http://$IP_ADDR:5000"
print_success "🌐 本地访问: http://localhost:5000"
fi
else
print_warning "⚠️ 服务启动可能有问题,请查看日志"
fi
STEP=$((STEP + 1))
read -p "🔘 按 Enter 继续..." -r
}
# --------------------------
# Step 4: 停止服务
# --------------------------
stop_service() {
print_step $STEP $TOTAL_STEPS "停止 LJ360 全景监控服务"
# 检查服务文件是否存在
if [ ! -f "/etc/systemd/system/$SERVICE_NAME" ]; then
print_error "❌ 服务未安装,请先安装服务"
read -p "🔘 按 Enter 继续..." -r
return
fi
print_progress "⏹️ 正在停止服务: $SERVICE_NAME"
sudo systemctl stop "$SERVICE_NAME"
sleep 2
# 清理可能的残留进程
print_progress "🧹 清理残留进程..."
pkill -f "python3 web.py" 2>/dev/null || true
# 检查停止状态
if ! systemctl is-active --quiet "$SERVICE_NAME"; then
print_success "✅ 服务停止成功"
else
print_warning "⚠️ 服务停止可能有问题"
fi
STEP=$((STEP + 1))
read -p "🔘 按 Enter 继续..." -r
}
# --------------------------
# Step 5: 重启服务
# --------------------------
restart_service() {
print_step $STEP $TOTAL_STEPS "重启 LJ360 全景监控服务"
# 检查服务文件是否存在
if [ ! -f "/etc/systemd/system/$SERVICE_NAME" ]; then
print_error "❌ 服务未安装,请先安装服务"
read -p "🔘 按 Enter 继续..." -r
return
fi
print_progress "🔄 正在重启服务: $SERVICE_NAME"
sudo systemctl restart "$SERVICE_NAME"
sleep 3
# 检查重启状态
if systemctl is-active --quiet "$SERVICE_NAME"; then
print_success "✅ 服务重启成功"
# 检查Web端口
sleep 2
if netstat -tlnp 2>/dev/null | grep -q ":5000"; then
local IP_ADDR=$(hostname -I | awk '{print $1}')
print_success "🌐 Web 服务已重启: http://$IP_ADDR:5000"
fi
else
print_warning "⚠️ 服务重启可能有问题,请查看日志"
fi
STEP=$((STEP + 1))
read -p "🔘 按 Enter 继续..." -r
}
# --------------------------
# Step 6: 启用开机自启
# --------------------------
enable_service() {
print_step $STEP $TOTAL_STEPS "启用开机自启"
# 检查服务文件是否存在
if [ ! -f "/etc/systemd/system/$SERVICE_NAME" ]; then
print_error "❌ 服务未安装,请先安装服务"
read -p "🔘 按 Enter 继续..." -r
return
fi
print_progress "🔔 正在启用开机自启: $SERVICE_NAME"
sudo systemctl enable "$SERVICE_NAME"
sleep 1
if systemctl is-enabled "$SERVICE_NAME"; then
print_success "✅ 开机自启已启用"
else
print_error "❌ 开机自启启用失败"
fi
STEP=$((STEP + 1))
read -p "🔘 按 Enter 继续..." -r
}
# --------------------------
# Step 7: 禁用开机自启
# --------------------------
disable_service() {
print_step $STEP $TOTAL_STEPS "禁用开机自启"
# 检查服务文件是否存在
if [ ! -f "/etc/systemd/system/$SERVICE_NAME" ]; then
print_error "❌ 服务未安装,请先安装服务"
read -p "🔘 按 Enter 继续..." -r
return
fi
print_progress "🚫 正在禁用开机自启: $SERVICE_NAME"
sudo systemctl disable "$SERVICE_NAME"
sleep 1
if ! systemctl is-enabled "$SERVICE_NAME"; then
print_success "✅ 开机自启已禁用"
else
print_error "❌ 开机自启禁用失败"
fi
STEP=$((STEP + 1))
read -p "🔘 按 Enter 继续..." -r
}
# --------------------------
# Step 8: 查看日志
# --------------------------
view_logs() {
print_step $STEP $TOTAL_STEPS "查看实时日志"
# 检查服务文件是否存在
if [ ! -f "/etc/systemd/system/$SERVICE_NAME" ]; then
print_error "❌ 服务未安装,请先安装服务"
read -p "🔘 按 Enter 继续..." -r
return
fi
print_progress "📜 请选择要查看的日志类型:"
echo "1) 📋 查看 systemd 服务日志"
echo "2) 📄 查看应用日志文件"
echo "3) ⚠️ 查看错误日志文件"
echo "4) 🛡️ 查看看门狗日志"
print_divider
read -p "👉 请选择 [1-4]: " log_choice
case $log_choice in
1)
print_progress "📜 正在打开 systemd 服务日志(按 Ctrl+C 退出)"
echo ""
sudo journalctl -u "$SERVICE_NAME" -f
;;
2)
print_progress "📄 正在查看应用日志文件: $PROJECT_DIR/lj360.log"
echo ""
if [ -f "$PROJECT_DIR/lj360.log" ]; then
tail -50 "$PROJECT_DIR/lj360.log"
echo ""
echo "按 Enter 查看更多,或按 Ctrl+C 退出..."
tail -f "$PROJECT_DIR/lj360.log"
else
print_error "❌ 日志文件不存在"
fi
;;
3)
print_progress "⚠️ 正在查看错误日志文件: $PROJECT_DIR/lj360_error.log"
echo ""
if [ -f "$PROJECT_DIR/lj360_error.log" ]; then
tail -50 "$PROJECT_DIR/lj360_error.log"
else
print_warning "⚠️ 错误日志文件不存在"
fi
;;
4)
print_progress "🛡️ 正在查看看门狗日志文件: $PROJECT_DIR/watchdog.log"
echo ""
if [ -f "$PROJECT_DIR/watchdog.log" ]; then
tail -50 "$PROJECT_DIR/watchdog.log"
else
print_warning "⚠️ 看门狗日志文件不存在"
fi
;;
*)
print_error "❌ 无效选项"
;;
esac
STEP=$((STEP + 1))
read -p "🔘 日志查看结束,按 Enter 继续..." -r
}
# --------------------------
# Step 9: 查看系统状态
# --------------------------
view_status() {
print_step $STEP $TOTAL_STEPS "查看详细系统状态"
echo -e "${CYAN}【📊 系统资源状态】${NC}"
print_divider
# CPU使用率
echo -e "${BLUE}● CPU 使用率:${NC}"
top -bn1 | grep "Cpu(s)" | awk '{print " " $2 "% user, " $4 "% system, " $8 "% idle"}'
# 内存使用
echo -e "${BLUE}● 内存使用:${NC}"
free -h | awk '/^Mem:/ {print " " $3 " / " $2 " (" $3/$2*100 "%) used"}'
# 磁盘空间
echo -e "${BLUE}● 磁盘空间:${NC}"
df -h /home | awk 'NR==2 {print " " $3 " / " $2 " (" $5 ") used"}'
echo ""
echo -e "${CYAN}【🔍 进程状态】${NC}"
print_divider
# LJ360相关进程
if pgrep -f "python3 web.py" > /dev/null; then
echo -e "${GREEN}● LJ360 进程正在运行 ✅${NC}"
ps aux | grep "python3 web.py" | grep -v grep | awk '{print " PID: " $2 ", CPU: " $3 "%, MEM: " $4 "%, CMD: " $11}'
else
echo -e "${RED}● LJ360 进程未运行 ❌${NC}"
fi
echo ""
echo -e "${CYAN}【📈 服务运行时间】${NC}"
print_divider
if systemctl is-active --quiet $SERVICE_NAME; then
UPTIME=$(systemctl show $SERVICE_NAME --property=ActiveEnterTimestamp | awk -F= '{print $2}')
echo -e "${GREEN}● 服务启动时间: $UPTIME${NC}"
fi
if systemctl is-active --quiet $WATCHDOG_SERVICE; then
WATCHDOG_UPTIME=$(systemctl show $WATCHDOG_SERVICE --property=ActiveEnterTimestamp | awk -F= '{print $2}')
echo -e "${GREEN}● 看门狗启动时间: $WATCHDOG_UPTIME${NC}"
fi
STEP=$((STEP + 1))
read -p "🔘 按 Enter 继续..." -r
}
# --------------------------
# Step 10: 卸载服务
# --------------------------
uninstall_service() {
print_step $STEP $TOTAL_STEPS "卸载 LJ360 全景监控服务"
echo -e "${RED}⚠ 警告:此操作将卸载 LJ360 全景监控服务!${NC}"
echo -n "✅ 确定要卸载吗?(y/N): "
read -r confirm
if [[ $confirm =~ ^[Yy]$ ]]; then
print_progress "⏹️ 停止服务..."
sudo systemctl stop "$SERVICE_NAME" 2>/dev/null || true
sudo systemctl stop "$WATCHDOG_SERVICE" 2>/dev/null || true
print_progress "🚫 禁用开机自启..."
sudo systemctl disable "$SERVICE_NAME" 2>/dev/null || true
sudo systemctl disable "$WATCHDOG_SERVICE" 2>/dev/null || true
print_progress "🗑️ 删除 systemd 服务文件..."
sudo rm -f /etc/systemd/system/"$SERVICE_NAME"
sudo rm -f /etc/systemd/system/"$WATCHDOG_SERVICE"
sudo systemctl daemon-reload
sudo systemctl reset-failed
print_progress "🗑️ 删除启动脚本..."
rm -f "$PROJECT_DIR/start_lj360.sh"
rm -f "$PROJECT_DIR/watchdog_lj360.sh"
print_progress "🔪 清理进程..."
pkill -f "python3 web.py" 2>/dev/null || true
pkill -f "watchdog_lj360.sh" 2>/dev/null || true
print_success "✅ LJ360 全景监控服务卸载完成"
print_warning "📝 日志文件保留在: $PROJECT_DIR/"
print_warning " - lj360.log"
print_warning " - lj360_error.log"
print_warning " - watchdog.log"
else
print_warning "❌ 取消卸载操作"
fi
STEP=$((STEP + 1))
read -p "🔘 按 Enter 继续..." -r
}
# ---------------------------------------------
# 启动主菜单
# ---------------------------------------------
# 检查是否在正确的目录运行
if [ ! -d "$PROJECT_DIR" ]; then
print_error "❌ 项目目录不存在: $PROJECT_DIR"
print_error "请确认项目路径是否正确,或者修改脚本中的 PROJECT_DIR 变量"
exit 1
fi
main_menu

View File

@@ -1,4 +1,4 @@
# mjpeg_streamer.py # mjpeg_streamer.pynano
import threading import threading
import time import time
from flask import Flask, Response, render_template, request, redirect, session, url_for from flask import Flask, Response, render_template, request, redirect, session, url_for

37
seial_test.py Normal file
View File

@@ -0,0 +1,37 @@
import serial
import time
# 配置串口(请根据实际情况修改设备名)
PORT = '/dev/ttyS0' # 或 '/dev/ttyUSB0'
BAUDRATE = 115200
try:
# 打开串口
ser = serial.Serial(PORT, BAUDRATE, timeout=1)
print(f"✅ 已打开串口 {PORT}")
# 发送数据
data_to_send = b'123456\n'
# 持续读取数据(注意:此时串口是打开的!)
print("📥 开始监听串口数据...")
while True:
ser.write(data_to_send)
print("📤 已发送数据")
try:
line = ser.readline()
if line:
print("接收到:", line.hex())
except serial.SerialException as e:
print("❌ 串口异常:", e)
break
time.sleep(1)
except serial.SerialException as e:
print(f"❌ 无法打开串口 {PORT}: {e}")
except KeyboardInterrupt:
print("\n🛑 用户中断")
finally:
if 'ser' in locals() and ser.is_open:
ser.close()
print("🔌 串口已关闭")

View File

@@ -12,7 +12,7 @@ shift_h = 100
# 标定图案与车辆之间在水平和垂直方向上的间隙大小 # 标定图案与车辆之间在水平和垂直方向上的间隙大小
inn_shift_w = 20 inn_shift_w = 20
inn_shift_h = 20 inn_shift_h = 30
# 拼接图像的总宽度/高度 # 拼接图像的总宽度/高度
total_w = 300 + 2 * shift_w total_w = 300 + 2 * shift_w
@@ -20,10 +20,10 @@ total_h = 350 + 2 * shift_h
# 计算车辆在全景图中的位置 # 计算车辆在全景图中的位置
xl = shift_w + 45 + inn_shift_w xl = shift_w + 55 + inn_shift_w
xr = total_w - xl xr = total_w - xl
print(xl, xr) print(xl, xr)
yt = shift_h + 60 + inn_shift_h yt = shift_h + 55 + inn_shift_h
yb = total_h - yt yb = total_h - yt
# -------------------------------------------------------------------- # --------------------------------------------------------------------
@@ -44,18 +44,18 @@ project_keypoints = {
"back": [(shift_w + 0, shift_h), "back": [(shift_w + 0, shift_h),
(shift_w + 300, shift_h), (shift_w + 300, shift_h),
(shift_w + 0, shift_h + 80), (shift_w + 0, shift_h + 70),
(shift_w + 300, shift_h + 80)], (shift_w + 300, shift_h + 70)],
"left": [(shift_h + 0, shift_w), "left": [(shift_h + 0, shift_w),
(shift_h + 350, shift_w), (shift_h + 350, shift_w),
(shift_h + 0, shift_w + 50), (shift_h + 0, shift_w + 40),
(shift_h + 350, shift_w + 50)], (shift_h + 350, shift_w + 40)],
"right": [(shift_h + 0, shift_w), "right": [(shift_h + 0, shift_w),
(shift_h + 350, shift_w), (shift_h + 350, shift_w),
(shift_h + 0, shift_w + 50), (shift_h + 0, shift_w + 40),
(shift_h + 350, shift_w + 50)] (shift_h + 350, shift_w + 40)]
} }
car_image = cv2.imread(os.path.join(os.getcwd(), "images", "car.png")) car_image = cv2.imread(os.path.join(os.getcwd(), "images", "car.png"))

BIN
test_dir/camera_v4l2 Executable file

Binary file not shown.

53
test_dir/camera_v4l2.cpp Normal file
View File

@@ -0,0 +1,53 @@
#include <opencv2/opencv.hpp>
#include <iostream>
int main() {
// 使用 V4L2 后端打开摄像头(设备索引 0 对应 /dev/video0
cv::VideoCapture cap(0, cv::CAP_V4L2);
cap.set(cv::CAP_PROP_FRAME_WIDTH, 1920);
cap.set(cv::CAP_PROP_FRAME_HEIGHT, 1080);
cap.set(cv::CAP_PROP_FOURCC, cv::VideoWriter::fourcc('Y','U','Y','V'));
// cap.set(cv::CAP_PROP_BUFFERSIZE, 1); // 减少缓冲,降低延迟
if (!cap.isOpened()) {
std::cerr << "❌ 无法打开摄像头 /dev/video0\n";
return -1;
}
// 可选:设置分辨率和格式(根据你的摄像头能力调整)
std::cout << "✅ 摄像头已打开,按 'q' 或 ESC 退出。\n";
cv::Mat frame;
while (true) {
cap >> frame; // 从摄像头读取一帧
if (frame.empty()) {
std::cerr << "⚠️ 读取帧失败,可能摄像头被占用或断开。\n";
break;
}
// 如果是 YUYV 格式2通道转换为 BGR 显示彩色
cv::Mat display_frame;
if (frame.channels() == 2) {
cv::cvtColor(frame, display_frame, cv::COLOR_YUV2BGR_YUYV);
} else {
display_frame = frame; // 假设已经是 BGR
}
// 显示图像
cv::imshow("Camera Feed (V4L2)", display_frame);
// 按 q 或 ESC 退出
int key = cv::waitKey(1) & 0xFF;
if (key == 'q' || key == 27) {
break;
}
}
cap.release();
cv::destroyAllWindows();
std::cout << "👋 已退出。\n";
return 0;
}

BIN
test_dir/check_opencv Executable file

Binary file not shown.

7
test_dir/v4l2.cpp Normal file
View File

@@ -0,0 +1,7 @@
#include <opencv2/opencv.hpp>
#include <iostream>
int main() {
std::cout << cv::getBuildInformation() << std::endl;
return 0;
}

BIN
test_dir/your_app Executable file

Binary file not shown.

190
web.py
View File

@@ -5,9 +5,65 @@ import sys
import os import os
import argparse import argparse
import numpy as np import numpy as np
import serial
import time
from surround_view import FisheyeCameraModel, BirdView from surround_view import FisheyeCameraModel, BirdView
import surround_view.param_settings as settings import surround_view.param_settings as settings
# 串口雷达配置
RADAR_SERIAL_PORT = '/dev/ttyS0'
RADAR_BAUDRATE = 115200 # 根据实际雷达配置调整
RADAR_TIMEOUT = 0.1
# 距离阈值配置单位mm
DISTANCE_THRESHOLD = 3000 # 距离过近阈值,可以根据需要调整
# 雷达ID与方向映射
RADAR_ID_MAP = {1: 'front', 2: 'left', 3: 'back', 4: 'right'}
def parse_radar_data(raw_data):
"""
解析雷达原始数据
数据格式: 0xA5 0x5A [雷达ID-距离高字节-距离低字节]... 0x5A 0xA5
"""
try:
# 查找帧头位置
frame_start = raw_data.find(b'\xA5\x5A')
if frame_start == -1:
return None
# 查找帧尾位置
frame_end = raw_data.find(b'\x5A\xA5', frame_start)
if frame_end == -1:
return None
# 提取完整帧数据
frame_data = raw_data[frame_start+2:frame_end]
# 检查数据长度是否正确每个雷达3字节 × 4雷达 = 12字节
if len(frame_data) != 12:
return None
# 解析每个雷达数据
radar_distances = {}
for i in range(4):
radar_id = frame_data[i*3]
if radar_id not in RADAR_ID_MAP:
continue
distance_high = frame_data[i*3+1]
distance_low = frame_data[i*3+2]
distance = (distance_high << 8) + distance_low
direction = RADAR_ID_MAP[radar_id]
radar_distances[direction] = distance
return radar_distances
except Exception as e:
print(f"[ERROR] 雷达数据解析失败: {e}")
return None
sys.path.append(os.path.dirname(__file__)) # 确保能导入 py_utils sys.path.append(os.path.dirname(__file__)) # 确保能导入 py_utils
from py_utils.coco_utils import COCO_test_helper from py_utils.coco_utils import COCO_test_helper
from py_utils.rknn_executor import RKNN_model_container # 假设使用 RKNN from py_utils.rknn_executor import RKNN_model_container # 假设使用 RKNN
@@ -269,6 +325,17 @@ class MultiCameraBirdView:
"right": False "right": False
} }
# 雷达数据
self.radar_distances = {
"front": 0,
"back": 0,
"left": 0,
"right": 0
}
self.radar_serial = None
self.radar_thread = None
self.radar_running = False
# === 新增YOLO 人体检测模型 === # === 新增YOLO 人体检测模型 ===
try: try:
self.yolo_model = RKNN_model_container(YOLO_MODEL_PATH, target='rk3588') self.yolo_model = RKNN_model_container(YOLO_MODEL_PATH, target='rk3588')
@@ -281,6 +348,9 @@ class MultiCameraBirdView:
self.shared_buffer = SharedFrameBuffer() # 👈 新增 self.shared_buffer = SharedFrameBuffer() # 👈 新增
# 初始化雷达串口
self._init_radar_serial()
def overlay_alert(self, birdview_img): def overlay_alert(self, birdview_img):
"""在鸟瞰图上叠加半透明红色预警区域""" """在鸟瞰图上叠加半透明红色预警区域"""
h, w = birdview_img.shape[:2] h, w = birdview_img.shape[:2]
@@ -369,6 +439,63 @@ class MultiCameraBirdView:
print(f"[ERROR] YOLO检测失败: {e}") print(f"[ERROR] YOLO检测失败: {e}")
return image, [], [] return image, [], []
def _init_radar_serial(self):
"""
初始化雷达串口并启动数据读取线程
"""
try:
self.radar_serial = serial.Serial(
port=RADAR_SERIAL_PORT,
baudrate=RADAR_BAUDRATE,
timeout=RADAR_TIMEOUT
)
print(f"✅ 已打开雷达串口 {RADAR_SERIAL_PORT}")
# 启动雷达数据读取线程
self.radar_running = True
self.radar_thread = threading.Thread(target=self._read_radar_data, daemon=True)
self.radar_thread.start()
print("[INFO] 雷达数据读取线程已启动")
except serial.SerialException as e:
print(f"❌ 无法打开雷达串口 {RADAR_SERIAL_PORT}: {e}")
self.radar_serial = None
except Exception as e:
print(f"[ERROR] 雷达初始化失败: {e}")
self.radar_serial = None
def _read_radar_data(self):
"""
雷达数据读取线程函数
"""
buffer = b''
while self.radar_running and self.radar_serial and self.radar_serial.is_open:
try:
# 读取可用数据
if self.radar_serial.in_waiting > 0:
data = self.radar_serial.read(self.radar_serial.in_waiting)
buffer += data
# 尝试解析数据
parsed_data = parse_radar_data(buffer)
if parsed_data:
print(f"[RADAR] 前:{parsed_data.get('front', 0):4d}cm 后:{parsed_data.get('back', 0):4d}cm 左:{parsed_data.get('left', 0):4d}cm 右:{parsed_data.get('right', 0):4d}cm")
# 更新雷达距离数据
self.radar_distances.update(parsed_data)
# 清空已解析的缓冲区
buffer = buffer[buffer.find(b'\x5A\xA5')+2:]
time.sleep(0.01) # 降低CPU占用
except serial.SerialException as e:
print(f"❌ 雷达串口异常: {e}")
break
except Exception as e:
print(f"[ERROR] 雷达数据读取失败: {e}")
time.sleep(0.1)
def _initialize_weights(self): def _initialize_weights(self):
try: try:
images = [os.path.join(os.getcwd(), "images", name + ".png") for name in self.names] images = [os.path.join(os.getcwd(), "images", name + ".png") for name in self.names]
@@ -405,6 +532,9 @@ class MultiCameraBirdView:
frame_count = 0 frame_count = 0
detection_interval = 3 # 每5帧进行一次检测避免性能问题 detection_interval = 3 # 每5帧进行一次检测避免性能问题
# 雷达触发视图切换的方向优先级(距离过近时优先显示哪个方向)
radar_priority = ['front', 'left', 'right', 'back']
while self.running: while self.running:
raw_frames = {} raw_frames = {}
processed_frames = [] processed_frames = []
@@ -414,7 +544,7 @@ class MultiCameraBirdView:
ret, frame = cap.read() ret, frame = cap.read()
raw_frames[name] = frame raw_frames[name] = frame
# self.shared_buffer.update(raw_frames[current_view])
p_frame = self.process_frame_once(frame, model) p_frame = self.process_frame_once(frame, model)
@@ -427,6 +557,18 @@ class MultiCameraBirdView:
self.birdview.make_white_balance() self.birdview.make_white_balance()
self.birdview.copy_car_image() self.birdview.copy_car_image()
# 检查雷达距离,自动切换视图
auto_switch_view = None
for direction in radar_priority:
distance = self.radar_distances.get(direction, 0)
if distance > 0 and distance < DISTANCE_THRESHOLD:
auto_switch_view = direction
break
if auto_switch_view:
current_view = auto_switch_view
print(f"[INFO] 雷达检测到{auto_switch_view}方向距离过近 ({self.radar_distances[auto_switch_view]}cm),自动切换视图")
# 获取单路图像(仅去畸变) # 获取单路图像(仅去畸变)
single_img = self.process_frame_undistort( single_img = self.process_frame_undistort(
raw_frames[current_view], raw_frames[current_view],
@@ -465,48 +607,22 @@ class MultiCameraBirdView:
bird_resized = cv2.resize(birdview_with_alert, (w_bird, h_display)) bird_resized = cv2.resize(birdview_with_alert, (w_bird, h_display))
single_resized = cv2.resize(single_img, (w_single, h_display)) single_resized = cv2.resize(single_img, (w_single, h_display))
display = np.hstack((bird_resized, single_resized)) display = np.hstack((bird_resized, single_resized))
self.shared_buffer.update(display)
# 在显示窗口上添加状态信息
# info_text = f"View: {current_view} | Persons detected: {len(person_boxes) if 'person_boxes' in locals() else 0}"
# cv2.putText(display, info_text, (10, 30),
# cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255, 255, 255), 2)
# 全屏显示 # 全屏显示
cv2.namedWindow('Video', cv2.WND_PROP_FULLSCREEN) cv2.namedWindow('Video', cv2.WND_PROP_FULLSCREEN)
cv2.setWindowProperty('Video', cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN) cv2.setWindowProperty('Video', cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN)
cv2.imshow("Video", display) cv2.imshow("Video", display)
key = cv2.waitKey(1) & 0xFF key = cv2.waitKey(1) & 0xFF
# if key == ord('q'):
# self.running = False
# break # 清理雷达资源
# elif key == ord('1'): self.radar_running = False
# current_view = "front" if self.radar_thread and self.radar_thread.is_alive():
# elif key == ord('2'): self.radar_thread.join(timeout=1.0)
# current_view = "back" if self.radar_serial and self.radar_serial.is_open:
# elif key == ord('3'): self.radar_serial.close()
# current_view = "left" print("🔌 雷达串口已关闭")
# elif key == ord('4'):
# current_view = "right"
# # 新增:预警控制
# elif key == ord('5'):
# self.alerts["front"] = True
# elif key == ord('6'):
# self.alerts["back"] = True
# elif key == ord('7'):
# self.alerts["left"] = True
# elif key == ord('8'):
# self.alerts["right"] = True
# elif key == ord('0'):
# # 清除所有预警
# for k in self.alerts:
# self.alerts[k] = False
# elif key == ord('d'):
# # 手动触发一次检测
# single_img, person_boxes, person_scores = self.detect_persons(single_img)
for cap in self.caps: for cap in self.caps:
cap.release() cap.release()

View File

@@ -4,14 +4,14 @@ camera_matrix: !!opencv-matrix
rows: 3 rows: 3
cols: 3 cols: 3
dt: d dt: d
data: [ 5.3042094228516248e+02, 0., 9.5131550663475389e+02, 0., data: [ 5.2332271672164040e+02, 0., 9.3044951906127255e+02, 0.,
5.3026569308499541e+02, 5.3695007291032755e+02, 0., 0., 1. ] 5.2236471571534673e+02, 5.4345769772632696e+02, 0., 0., 1. ]
dist_coeffs: !!opencv-matrix dist_coeffs: !!opencv-matrix
rows: 4 rows: 4
cols: 1 cols: 1
dt: d dt: d
data: [ -9.2062464083377798e-03, -6.3620029090856196e-03, data: [ -5.9498237920217710e-03, -1.0006293350185162e-03,
3.3735324773401221e-03, -1.0289639180500810e-03 ] 5.3026728708821542e-03, -4.5615699893810256e-03 ]
resolution: !!opencv-matrix resolution: !!opencv-matrix
rows: 2 rows: 2
cols: 1 cols: 1
@@ -21,10 +21,10 @@ project_matrix: !!opencv-matrix
rows: 3 rows: 3
cols: 3 cols: 3
dt: d dt: d
data: [ -2.2036739433214386e-01, -5.4728752801607983e-01, data: [ -3.9668895968930029e-01, -8.7369111224637641e-01,
4.0669019104227289e+02, -2.2188976198317060e-02, 5.0478302196710462e+02, -6.4056023932043557e-02,
-5.6384233390057015e-01, 3.2888161676725548e+02, -8.8811555523036101e-01, 3.8160313264225226e+02,
-8.4090277960475698e-05, -2.2171947701663608e-03, 1. ] -2.2060551352011550e-04, -3.5783374044860163e-03, 1. ]
scale_xy: !!opencv-matrix scale_xy: !!opencv-matrix
rows: 2 rows: 2
cols: 1 cols: 1
@@ -34,4 +34,4 @@ shift_xy: !!opencv-matrix
rows: 2 rows: 2
cols: 1 cols: 1
dt: f dt: f
data: [ -150., -100. ] data: [ -150., -300. ]

View File

@@ -4,14 +4,14 @@ camera_matrix: !!opencv-matrix
rows: 3 rows: 3
cols: 3 cols: 3
dt: d dt: d
data: [ 5.3382445956203196e+02, 0., 9.7253025945442369e+02, 0., data: [ 5.3803744412978585e+02, 0., 9.5032314180353455e+02, 0.,
5.3393792084343488e+02, 5.6249605531924215e+02, 0., 0., 1. ] 5.3783453341561233e+02, 5.3627020384755701e+02, 0., 0., 1. ]
dist_coeffs: !!opencv-matrix dist_coeffs: !!opencv-matrix
rows: 4 rows: 4
cols: 1 cols: 1
dt: d dt: d
data: [ -1.5749135021037808e-02, 2.9390620422222835e-03, data: [ -1.6431143641380805e-02, -7.4804038255179171e-03,
-4.3176357910129585e-03, 1.3296605027646462e-03 ] 9.8754634365867712e-03, -4.6136179081623443e-03 ]
resolution: !!opencv-matrix resolution: !!opencv-matrix
rows: 2 rows: 2
cols: 1 cols: 1
@@ -21,10 +21,10 @@ project_matrix: !!opencv-matrix
rows: 3 rows: 3
cols: 3 cols: 3
dt: d dt: d
data: [ -1.8474101274061588e-01, -5.4968582653196851e-01, data: [ -4.1308938086273067e-01, -1.2364750670379583e+00,
4.1680109927253847e+02, 9.7678250049304510e-03, 6.1409164370210317e+02, 1.5629889919431476e-02,
-4.9902821258073782e-01, 3.0304882941065989e+02, -1.2402139306547297e+00, 4.4475253192373538e+02,
4.3706314843734903e-05, -2.1601287734653030e-03, 1. ] 1.6633363942594040e-04, -4.9162230937539594e-03, 1. ]
scale_xy: !!opencv-matrix scale_xy: !!opencv-matrix
rows: 2 rows: 2
cols: 1 cols: 1
@@ -34,4 +34,4 @@ shift_xy: !!opencv-matrix
rows: 2 rows: 2
cols: 1 cols: 1
dt: f dt: f
data: [ -150., -100. ] data: [ -150., -300. ]

View File

@@ -4,14 +4,14 @@ camera_matrix: !!opencv-matrix
rows: 3 rows: 3
cols: 3 cols: 3
dt: d dt: d
data: [ 5.3576790643136087e+02, 0., 9.5266112763930198e+02, 0., data: [ 5.3234956441219367e+02, 0., 1.0016510548547229e+03, 0.,
5.3494293886479875e+02, 5.4089729625135715e+02, 0., 0., 1. ] 5.3229861100984783e+02, 5.4667741130363822e+02, 0., 0., 1. ]
dist_coeffs: !!opencv-matrix dist_coeffs: !!opencv-matrix
rows: 4 rows: 4
cols: 1 cols: 1
dt: d dt: d
data: [ -1.1422407725638895e-02, -1.2103818796148216e-02, data: [ -2.1940734474311458e-02, 2.5390922658033822e-02,
9.0774770002077006e-03, -2.8278270352926444e-03 ] -2.7187547373220645e-02, 9.2193950144527706e-03 ]
resolution: !!opencv-matrix resolution: !!opencv-matrix
rows: 2 rows: 2
cols: 1 cols: 1
@@ -21,10 +21,10 @@ project_matrix: !!opencv-matrix
rows: 3 rows: 3
cols: 3 cols: 3
dt: d dt: d
data: [ -2.0080056297490056e-01, -6.1187781235143224e-01, data: [ -4.9054850784465642e-01, -1.3514497357056201e+00,
4.5434494858537062e+02, -2.9556627352318810e-03, 7.0604177518052268e+02, 1.0522109557771796e-02,
-5.0502143174738201e-01, 3.0816263946657051e+02, -1.1178584461183012e+00, 4.4274468178801629e+02,
-1.8229281242150158e-05, -2.1645728959083397e-03, 1. ] 8.2723610754286312e-05, -4.9130349378573015e-03, 1. ]
scale_xy: !!opencv-matrix scale_xy: !!opencv-matrix
rows: 2 rows: 2
cols: 1 cols: 1
@@ -34,4 +34,4 @@ shift_xy: !!opencv-matrix
rows: 2 rows: 2
cols: 1 cols: 1
dt: f dt: f
data: [ -80., -100. ] data: [ -150., -300. ]

View File

@@ -4,14 +4,14 @@ camera_matrix: !!opencv-matrix
rows: 3 rows: 3
cols: 3 cols: 3
dt: d dt: d
data: [ 5.3402108990030604e+02, 0., 9.2598444295282172e+02, 0., data: [ 5.2209622748686536e+02, 0., 9.5144634774080509e+02, 0.,
5.3455325152709827e+02, 5.7771767919091610e+02, 0., 0., 1. ] 5.2226989398345188e+02, 5.3766700145639288e+02, 0., 0., 1. ]
dist_coeffs: !!opencv-matrix dist_coeffs: !!opencv-matrix
rows: 4 rows: 4
cols: 1 cols: 1
dt: d dt: d
data: [ -1.8724887233075402e-02, 6.4408558584901701e-03, data: [ -8.0625649755334261e-03, 1.9229042307457818e-02,
-5.2069636709412993e-03, 8.4815411645490968e-04 ] -1.6765217009295411e-02, 2.3943754181756584e-03 ]
resolution: !!opencv-matrix resolution: !!opencv-matrix
rows: 2 rows: 2
cols: 1 cols: 1
@@ -21,10 +21,10 @@ project_matrix: !!opencv-matrix
rows: 3 rows: 3
cols: 3 cols: 3
dt: d dt: d
data: [ -2.0592797222758674e-01, -6.8891188079230814e-01, data: [ -2.5531960850826479e-01, -8.5968191881593725e-01,
4.5964514365855666e+02, 2.1763027489573539e-02, 5.2529047158069864e+02, 2.8508251494131515e-02,
-6.3287857963783611e-01, 3.4166476009354983e+02, -6.9799661435239291e-01, 3.3028662032142546e+02,
1.3350446934563321e-04, -2.6387220529307219e-03, 1. ] 1.4612833761795866e-04, -3.1783223690618283e-03, 1. ]
scale_xy: !!opencv-matrix scale_xy: !!opencv-matrix
rows: 2 rows: 2
cols: 1 cols: 1
@@ -34,4 +34,4 @@ shift_xy: !!opencv-matrix
rows: 2 rows: 2
cols: 1 cols: 1
dt: f dt: f
data: [ -80., -100. ] data: [ -80., -200. ]