graph TB
subgraph "用户接口层"
A1[命令行接口 CLI]
A2[Docker容器接口]
A3[IDE集成接口]
A4[Web管理界面接口]
end
subgraph "脚本管理层"
A[Apollo Scripts Manager]
subgraph "构建脚本组"
B[Build Scripts]
B1[apollo_buildify.sh - 代码格式化]
B2[apollo_action.sh - 构建动作管理]
B3[apollo_clean.sh - 清理构建产物]
B4[apollo_format.sh - 代码格式检查]
B5[buildifier.sh - BUILD文件格式化]
B6[clang_format.sh - C++代码格式化]
B7[yapf.sh - Python代码格式化]
B8[apollo_lint.sh - 代码规范检查]
B9[apollo_ci.sh - 持续集成]
end
subgraph "运行时脚本组"
C[Runtime Scripts]
C1[env.sh - 环境初始化]
C2[apollo_base.sh - 基础函数库]
C3[bootstrap.sh - 系统引导]
C4[cyber_launch.sh - Cyber组件启动]
C5[模块启动脚本群组]
C51[dreamview.sh - 可视化界面]
C52[perception.sh - 感知模块]
C53[localization.sh - 定位模块]
C54[planning.sh - 规划模块]
C55[control.sh - 控制模块]
C56[canbus.sh - CAN总线通信]
C57[routing.sh - 路由模块]
C58[prediction.sh - 预测模块]
end
subgraph "测试脚本组"
D[Test Scripts]
D1[replay.sh - 数据回放]
D2[record_bag.sh - 数据记录]
D3[performance_test.sh - 性能测试]
D4[unit_test_runner.sh - 单元测试]
D5[integration_test.sh - 集成测试]
D6[functional_test.sh - 功能测试]
end
subgraph "工具脚本组"
E[Utility Scripts]
E1[device_setup.sh - 硬件设备配置]
E2[data_management.sh - 数据管理]
E3[configuration_tools.sh - 配置管理]
E4[model_download.sh - 模型下载]
E5[map_tools.sh - 地图工具]
E6[common_functions.sh - 通用函数库]
E7[log_analyzer.sh - 日志分析]
E8[diagnostic_tool.sh - 诊断工具]
end
subgraph "配置管理组"
F[Configuration Scripts]
F1[apollo_config.sh - 系统配置]
F2[switch_vehicle.sh - 车辆切换]
F3[install_scripts.sh - 安装脚本]
F4[environment_setup.sh - 环境设置]
F5[vehicle_calibrations.sh - 车辆标定]
F6[security_config.sh - 安全配置]
end
subgraph "运维脚本组"
G[Maintenance Scripts]
G1[monitor.sh - 系统监控]
G2[log_management.sh - 日志管理]
G3[data_cleaner.sh - 数据清理]
G4[ota.sh - 在线更新]
G5[health_check.sh - 健康检查]
G6[backup_restore.sh - 备份恢复]
end
subgraph "部署脚本组"
H[Deployment Scripts]
H1[apollo_deploy.sh - 部署脚本]
H2[remote_deploy.sh - 远程部署]
H3[package_builder.sh - 包构建]
H4[image_creator.sh - 镜像创建]
H5[container_manager.sh - 容器管理]
end
end
subgraph "底层支撑层"
J[操作系统层 - Linux Ubuntu]
K[Docker容器引擎]
L[Bazel构建系统]
M[Python运行时环境]
N[Bash Shell环境]
O[Protobuf编译器]
P[CMake构建工具]
end
subgraph "外部依赖"
Q[硬件驱动程序]
R[传感器设备]
S[网络服务]
T[云服务平台]
end
A1 --> A
A2 --> A
A3 --> A
A4 --> A
A --> B
A --> C
A --> D
A --> E
A --> F
A --> G
A --> H
B --> L
B --> P
B --> O
C --> N
C --> M
C --> K
D --> K
D --> J
E --> J
E --> Q
F --> J
F --> S
G --> J
G --> T
H --> K
H --> S
H --> T
J -.-> A
K -.-> A
L -.-> A
M -.-> A
N -.-> A
O -.-> A
P -.-> A
Q -.-> E
R -.-> E
S -.-> F
T -.-> H
function create_data_dir() {
local DATA_DIR="${APOLLO_ROOT_DIR}/data"
mkdir -p "${DATA_DIR}/log"
mkdir -p "${DATA_DIR}/bag"
mkdir -p "${DATA_DIR}/core"
}
创建必要的数据目录,包括日志、数据包和核心转储目录。
6.2. 设备初始化机制
6.2.1. 设备初始化流程
根据系统架构类型,脚本会调用不同的设备初始化函数:
bash复制代码
function setup_device() {
if [ "$(uname -s)" != "Linux" ]; then
info "Not on Linux, skip mapping devices."
return
fi
if [[ "${ARCH}" == "x86_64" ]]; then
setup_device_for_amd64
else
setup_device_for_aarch64
fi
}
6.2.2. x86_64 架构设备初始化
bash复制代码
function setup_device_for_amd64() {
# setup CAN device
local NUM_PORTS=8
for i in $(seq 0 $((${NUM_PORTS} - 1))); do
if [[ -e /dev/can${i} ]]; then
continue
elif [[ -e /dev/zynq_can${i} ]]; then
# soft link if sensorbox exist
sudo ln -s /dev/zynq_can${i} /dev/can${i}
else
break
# sudo mknod --mode=a+rw /dev/can${i} c 52 ${i}
fi
done
# Check Nvidia device
if [[ ! -e /dev/nvidia0 ]]; then
warning "No device named /dev/nvidia0"
fi
if [[ ! -e /dev/nvidiactl ]]; then
warning "No device named /dev/nvidiactl"
fi
if [[ ! -e /dev/nvidia-uvm ]]; then
warning "No device named /dev/nvidia-uvm"
fi
if [[ ! -e /dev/nvidia-uvm-tools ]]; then
warning "No device named /dev/nvidia-uvm-tools"
fi
if [[ ! -e /dev/nvidia-modeset ]]; then
warning "No device named /dev/nvidia-modeset"
fi
}
该函数初始化CAN设备节点,为自动驾驶车辆的通信做准备,同时检查NVIDIA GPU设备的存在。
6.2.3. aarch64 架构设备初始化
bash复制代码
function setup_device_for_aarch64() {
local can_dev="/dev/can0"
local socket_can_dev="can0"
if [ ! -e "${can_dev}" ]; then
warning "No CAN device named ${can_dev}. "
fi
if [[ -x "$(command -v ip)" ]]; then
if ! ip link show type can | grep "${socket_can_dev}" &> /dev/null; then
warning "No SocketCAN device named ${socket_can_dev}."
else
sudo modprobe can
sudo modprobe can_raw
sudo modprobe mttcan
sudo ip link set "${socket_can_dev}" type can bitrate 500000 sjw 4 berr-reporting on loopback off
sudo ip link set up "${socket_can_dev}"
fi
else
warning "ip command not found."
fi
}
6.3. 模块管理机制
6.3.1. 模块启动流程
模块启动的核心函数:
bash复制代码
function start_customized_path() {
MODULE_PATH=$1
MODULE=$2
shift 2
is_stopped_customized_path "${MODULE_PATH}" "${MODULE}"
if [ $? -eq 1 ]; then
# todo(zero): Better to move nohup.out to data/log/nohup.out
eval "nohup cyber_launch start ${APOLLO_ROOT_DIR}/modules/${MODULE_PATH}/launch/${MODULE}.launch &"
sleep 0.5
is_stopped_customized_path "${MODULE_PATH}" "${MODULE}"
if [ $? -eq 0 ]; then
ok "Launched module ${MODULE}."
return 0
else
error "Could not launch module ${MODULE}. Is it already built?"
return 1
fi
else
info "Module ${MODULE} is already running - skipping."
return 2
fi
}
6.3.2. 模块状态检查
bash复制代码
function is_stopped_customized_path() {
MODULE_PATH=$1
MODULE=$2
NUM_PROCESSES="$(pgrep -f "modules/${MODULE_PATH}/launch/${MODULE}.launch" | grep -cv '^1$')"
if [ "${NUM_PROCESSES}" -eq 0 ]; then
return 1
else
return 0
fi
}
该函数检查模块是否处于停止状态。
6.4. 构建系统实现
6.4.1. 构建目标确定
bash复制代码
function determine_build_targets() {
local targets_all
if [[ "$#" -eq 0 ]]; then
targets_all="$(python3 ${TOP_DIR}/scripts/find_all_package.py)"
echo "${targets_all}"
return
fi
for component in $@; do
local build_targets
if [ "${component}" = "cyber" ]; then
build_targets="cyber"
elif [[ -d "${TOP_DIR}/modules/${component}" ]]; then
build_targets="modules/${component}"
else
error "Directory ${TOP_DIR}/modules/${component} not found. Exiting ..."
exit 1
fi
if [ -z "${targets_all}" ]; then
targets_all="${build_targets}"
else
targets_all="${targets_all} ${build_targets}"
fi
done
echo "${targets_all}"
}
6.4.2. 构建参数处理
脚本使用getopts处理构建参数:
bash复制代码
while getopts "cdef:g:hij:mn:pt:uv" opt; do
case $opt in
c)
ACTION=clean
;;
d)
if [ -z "${SHORTHAND_TARGETS}" ]; then
SHORTHAND_TARGETS="all"
fi
USE_DBG=1
;;
e)
ENABLE_PROFILER=false
;;
f)
ADDTIONAL_OPTIONS="${ADDTIONAL_OPTIONS} --compilation_mode=${OPTARG}"
;;
g)
ADDTIONAL_OPTIONS="${ADDTIONAL_OPTIONS} --cxxopt=-g${OPTARG}"
;;
h)
usage
exit 0
;;
i)
USE_OPT=1
;;
j)
ADDTIONAL_OPTIONS="${ADDTIONAL_OPTIONS} -j${OPTARG}"
;;
m)
USE_GPU=0
;;
n)
ADDTIONAL_OPTIONS="${ADDTIONAL_OPTIONS} --jobs=${OPTARG}"
;;
p)
ACTION=build
;;
t)
if [ -z "${SHORTHAND_TARGETS}" ]; then
SHORTHAND_TARGETS="all"
fi
ADDTIONAL_OPTIONS="${ADDTIONAL_OPTIONS} --test_timeout=${OPTARG}"
;;
u)
USE_GPU=1
;;
v)
set -x
;;
\?)
echo "Invalid option: -$OPTARG" >&2
exit 1
;;
:)
echo "Option -$OPTARG requires an argument." >&2
exit 1
;;
esac
done
6.5. 配置管理脚本
6.5.1. 车辆配置切换
实现了车辆配置的动态切换:
bash复制代码
function switch_vehicle() {
local vehicle_id=$1
local vehicle_dir="${APOLLO_ROOT_DIR}/modules/calibration/data/${vehicle_id}"
if [ ! -d "${vehicle_dir}" ]; then
error "Invalid vehicle id: ${vehicle_id}. Directory does not exist: ${vehicle_dir}"
usage
fi
# Create symbolic links for calibration data
rm -rf ${APOLLO_ROOT_DIR}/modules/calibration/data/current
ln -s ${vehicle_dir} ${APOLLO_ROOT_DIR}/modules/calibration/data/current
ok "Successfully switched to vehicle: ${vehicle_id}"
}
7. 设计模式
7.1. 模板方法模式
bash复制代码
function start_customized_path() {
MODULE_PATH=$1
MODULE=$2
shift 2
is_stopped_customized_path "${MODULE_PATH}" "${MODULE}" # 检查状态
if [ $? -eq 1 ]; then # 算法骨架
eval "nohup cyber_launch start ${APOLLO_ROOT_DIR}/modules/${MODULE_PATH}/launch/${MODULE}.launch &"
sleep 0.5
is_stopped_customized_path "${MODULE_PATH}" "${MODULE}"
if [ $? -eq 0 ]; then
ok "Launched module ${MODULE}."
return 0
else
error "Could not launch module ${MODULE}. Is it already built?"
return 1
fi
else
info "Module ${MODULE} is already running - skipping."
return 2
fi
}
这个函数定义了启动模块的通用流程,但具体的模块名称和路径可以由子类(即具体的模块启动脚本)来定制。
7.2. 策略模式
在设备初始化中,Apollo Scripts使用了策略模式来处理不同架构的设备初始化:
bash复制代码
function setup_device() {
if [ "$(uname -s)" != "Linux" ]; then
info "Not on Linux, skip mapping devices."
return
fi
if [[ "${ARCH}" == "x86_64" ]]; then
setup_device_for_amd64 # x86_64策略
else
setup_device_for_aarch64 # aarch64策略
fi
}
function determine_build_targets() {
# ...
for component in $@; do
local build_targets
if [ "${component}" = "cyber" ]; then
build_targets="cyber"
elif [[ -d "${TOP_DIR}/modules/${component}" ]]; then
build_targets="modules/${component}"
else
error "Directory ${TOP_DIR}/modules/${component} not found. Exiting ..."
exit 1
fi
# ...
done
# ...
}