1. 开始开发之前,你需要读什么?
后端服务使用 Python3,前端服务是 JavaScript/TypeScript React
本篇基于《分分钟搞定 OpenV2X 开发环境 - Beihai 版本》修改。
1.1 了解 OpenV2X 是什么?它适用于什么场景?
OpenV2X 致力于打造“车路协同”应用在“远边”(边缘云)或“近边”(路口智能计算盒子)场景中的开源解决方案(开源协议是 Apache 2.0)。在传统“云-边-端”三层架构中,OpenV2X 关注“远边侧”部分(5G 基站侧边缘云)和“近边侧”部分(边缘路口智能计算),致力于提供“路侧设备管理”和“路侧数据采集与智能分析”能力,以满足交管部门“智慧交通”管理需求,和辅助“车载智能”实现自动驾驶。
为什么有了“车载智能驾驶”解决方案还需要“路侧协同”方案?可以简单类比:在黑暗中车也能开,但有了路灯更安全。OpenV2X 在车路协同整体解决方案中,就扮演“路灯”的角色。
参考文档:
- 源代码:Github,同步镜像到 Gitee
- OpenV2X 官网:https://openv2x.org/
1.2 搭建 AIO 的开发基础环境
参考部署文档:Github,或者 Gitee,我们先搭建 AIO 的开发环境,然后去掉多余服务。
上述文档中选用了 CentOS 7.9,但因为:
- CentOS 7.9 会在 2024.06 结束支持
- CentOS 7.9 的基础组件偏旧,操作系统内核 / Docker / Python / Git 等基础组件都需要升级
- OpenV2X 服务可以从 Docker 启动
因此实际上我们可以选用其它操作系统也可以进行开发,本文选用 Ubuntu 22.04 系统。
搭建步骤如下:
1.2.1 准备 AIO 的 Ubuntu 机器
开一台 2Core/4G/40G 的服务器,安装 Ubuntu 22.04 系统
# 软链接,另 python 命令指向 python3
ln -s /usr/bin/python3 /usr/bin/python
# 安装 git review,方便后续提交代码 review
apt-get install git-review -y
查看基础组件版本
root@openv2x-test-master:~# cat /etc/os-release
PRETTY_NAME="Ubuntu 22.04.1 LTS"
...
root@openv2x-test-master:~# uname -a
Linux openv2x-test-master 5.15.0-43-generic #46-Ubuntu SMP Tue Jul 12 10:30:17 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
root@openv2x-test-master:~# python --version
Python 3.10.4
root@openv2x-test-master:~# pip --version
pip 22.0.2 from /usr/lib/python3/dist-packages/pip (python 3.10)
root@openv2x-test-master:~# git version
git version 2.34.1
root@openv2x-test-master:~# git-review --version
git-review version 2.2.0
1.2.2 安装 Docker 和 Docker-Compose
# 安装 docker 和 docker compose
apt install docker.io docker-compose -y
查看版本
root@openv2x-test-master:~# docker --version
Docker version 20.10.12, build 20.10.12-0ubuntu4
root@openv2x-test-master:~# docker-compose --version
docker-compose version 1.29.2, build unknown
1.2.3 安装 OpenV2X
rm -rf openv2x-aio-master.tar.gz && wget https://openv2x.oss-ap-southeast-1.aliyuncs.com/deploy/master/openv2x-aio-master.tar.gz
rm -rf src && tar zxvf openv2x-aio-master.tar.gz
cd src
export OPENV2X_EXTERNAL_IP=100.100.100.100
export OPENV2X_CENTER_IP=100.100.100.100
export OPENV2X_IS_CENTER=true
export OPENV2X_EMQX_ROOT=password
export OPENV2X_REGION=cn
export OPENV2X_ENABLE_DEMO_CAMERA=false
export OPENV2X_REDIS_ROOT=password
export OPENV2X_MARIADB_ROOT=password
export OPENV2X_ENABLE_GPU=false
export OPENV2X_ENDPOINT_HTTP_FLV=http://100.100.100.101:10101/live
export OPENV2X_ENABLE_DEMO_LIDAR=false
export OPENV2X_ENDPOINT_LIDAR=ws://${OPENV2X_EXTERNAL_IP}:8000/ws/127.0.0.1
export OPENV2X_MARIADB_DANDELION=password
bash ./install.sh
注意:
- 这里 100.100.100.100 是 OpenV2X 对外暴露的 IP 地址,是安装好 OpenV2X 之后,从 Web 页面访问的 URL 地址,不一定是该服务器的内网地址。
- 这里 http://100.100.100.101:10101/live 是 http flv 流的访问地址,不一定是内网地址
安装好之后,根据提示可以访问页面,用提示中的用户名/密码登录,确定登录成功。然后可以根据 OpenV2X 使用手册:Github,或 Gitee,验证功能正常。
1.2.4 删除 OpenV2X 应用服务,只保留基础组件
先看一下 OpenV2X 服务
root@99dev:~/src# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2a62b0a85dfd registry.cn-shanghai.aliyuncs.com/openv2x/lidar:latest "python3 udp_client.…" 2 minutes ago Up 2 minutes udp_client
54c98806c4e2 registry.cn-shanghai.aliyuncs.com/openv2x/lidar:latest "python3 udp_server.…" 2 minutes ago Up 2 minutes udp_server
a71c5fd8ba8b registry.cn-shanghai.aliyuncs.com/openv2x/lidar:latest "celery worker --app…" 2 minutes ago Up 2 minutes celery_worker
7b1e71e7c594 registry.cn-shanghai.aliyuncs.com/openv2x/roadmocker:latest "/docker-entrypoint.…" 3 minutes ago Up 2 minutes 0.0.0.0:6688->80/tcp, :::6688->80/tcp rse-simulator
937f076505a3 registry.cn-shanghai.aliyuncs.com/openv2x/omega:latest "/docker-entrypoint.…" 3 minutes ago Up 2 minutes omega
75cae64a51bb registry.cn-shanghai.aliyuncs.com/openv2x/dandelion:latest "run_service.sh" 3 minutes ago Up 2 minutes dandelion
76f1722c501d registry.cn-shanghai.aliyuncs.com/openv2x/lidar:latest "uvicorn main:app --…" 3 minutes ago Up 2 minutes lidar_websocket
16ad5e913289 registry.cn-shanghai.aliyuncs.com/openv2x/cerebrum:latest "sh /usr/local/bin/s…" 3 minutes ago Up 2 minutes cerebrum
01d497b72ebb registry.cn-shanghai.aliyuncs.com/openv2x/emqx:4.3.0 "/usr/bin/docker-ent…" 3 minutes ago Up 3 minutes 4369-4370/tcp, 5369/tcp, 0.0.0.0:1883->1883/tcp, :::1883->1883/tcp, 0.0.0.0:8081->8081/tcp, :::8081->8081/tcp, 0.0.0.0:8084->8084/tcp, :::8084->8084/tcp, 6369-6370/tcp, 0.0.0.0:8883->8883/tcp, :::8883->8883/tcp, 0.0.0.0:18083->18083/tcp, :::18083->18083/tcp, 11883/tcp, 0.0.0.0:15675->8083/tcp, :::15675->8083/tcp emqx
fe308fd009c7 registry.cn-shanghai.aliyuncs.com/openv2x/lal:latest "/lal/bin/lalserver …" 3 minutes ago Up 3 minutes 4433/tcp, 5544/tcp, 0.0.0.0:1935->1935/tcp, :::1935->1935/tcp, 8083-8084/tcp, 30000-30100/udp, 0.0.0.0:7001->8080/tcp, :::7001->8080/tcp lalserver
ae8e98c8e4ee registry.cn-shanghai.aliyuncs.com/openv2x/mariadb:10.5.5 "docker-entrypoint.s…" 3 minutes ago Up 3 minutes 0.0.0.0:3306->3306/tcp, :::3306->3306/tcp mariadb
1829992c5275 registry.cn-shanghai.aliyuncs.com/openv2x/redis:6.2.4-alpine "docker-entrypoint.s…" 3 minutes ago Up 3 minutes 0.0.0.0:6379->6379/tcp, :::6379->6379/tcp redis
其中基础服务包括:
- emqx:MQTT 服务器
- mariadb:数据库服务器
- redis:缓存服务器
业务服务包括:
- dandelion:RSE 设备管理服务
- cerebrum:RSE 数据处理服务
- rse-simulator:RSE 模拟器
- omega:控制面板
- hippocampus:RSTP 视频感知服务
- lidar:激光雷达感知服务
需要开发哪个模块,就 docker stop 对应模块的容器。
2. 开发文档
2.1 Dandelion 路侧设备管理模块
Dandelion 是 OpenV2X 设备管理模块,参考 OpenV2X 架构图:Github 或 Gitlab,在 All-In-One 场景中,Center 和 Edge 节点会部署在一起(Center Dandelion 和 Edge Dandelion 合二为一,即 Dandelion 混合模式)
参考 Dandelion 开发者文档:Github 或 Gitee:
我们尝试搭建 Dandelion 开发环境。
2.1.1 下载、查看和分析源码
在 1.2.4 章节中,我们保留了 MQTT / DB / Redis 三个基础服务,接下来,我们要从 Dandelion 开始启动和调试 OpenV2X 各个组件。
首先是 git clone 源代码(在之前的 Ubuntu 22.04 服务器上):
git clone git@github.com:open-v2x/dandelion.git
然后可以通过文本编辑工具(比如 VIM)查看源码。推荐用 VSCode 的 Remote SSH 插件,可以通过 SSH 直接查看和调试远程服务器上的 Python 代码。
2.1.2 启动服务
因为之前安装过服务,所以配置文件是现成的,在 /etc/dandelion/dandelion.conf,不需要处理。如果希望了解如何修改配置文件,可以参考:Github 或 Gitee
因为之前安装过服务,所以数据库也已经初始化完毕,可以通过 Mariadb 客户端连接到数据库,查看里面的表结构和内容,确认已经初始化完成。如果希望了解如何初始化数据库,可以参考:Github 或 Gitee
pip install tox
tox -e venv
source .tox/venv/bin/activate
uvicorn --reload --reload-dir dandelion --port 28300 --log-level debug dandelion.main:app --host 0.0.0.0
启动后,可以在 http://<external-IP>:28300/docs
看到此服务的 API,可以测试这些 API 是否正常。
也可以用 postman 或者 gabbi,跑全量的 API 测试。gabbi 测试用例(后续会整理到 github 代码仓库里)运行情况如下:
$ ls backend_apitest/*.yaml | xargs gabbi-run 100.100.100.100:28300 --
... ✓ gabbi-runner.area_create_user
... ✓ gabbi-runner.area_user_login
...
... ✓ gabbi-runner.user_login_delete_user
----------------------------------------------------------------------
Ran 4 tests in 1.131s
OK
全量测试通过,确定服务正常。
2.1.3 调试代码
如果要用 VSCode 进行调试,可以停止 uvicorn 进程。
python -m pip install virtualenv
python -m virtualenv .venv
source /root/dandelion/.venv/bin/activate
pip install -r requirements.txt
pip install -r test-requirements.txt
然后从 VSCode 选择 FastAPI 调试,会产生 .vscode/launch.json
{
"version": "0.2.0",
"configurations": [
{
"name": "Python: FastAPI",
"type": "python",
"request": "launch",
"module": "uvicorn",
"args": [
"dandelion.main:app",
"--host",
"0.0.0.0"
],
"jinja": true,
"justMyCode": true
}
]
}
这样 dandelion 会启动在 8000 端口:
(.venv) root@openv2x-test-master:~/dandelion# cd /root/dandelion ; /usr/bin/env /root/dandelion/.venv/bin/python /root/.vscode-server/extensions/ms-python.python-2022.12.1/pythonFiles/lib/python/debugpy/adapter/../../debugpy/launcher 36089 -- -m uvicorn dandelion.main:app --host 0.0.0.0
INFO: Started server process [17430]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
可以下断点,单步调试
2.1.4 提交 Patch
Github 上提交 PR,参考:Github 或 Gitee。
记得始终像 github 提交 PR,不要从向 gitee 提交。github 上的内容会自动更新到 gitee。
2.2 Celebrum 路侧设备信息处理模块
Cerebrum 是 OpenV2X 的“大脑”,负责收集和处理数据。
2.2.1 下载、查看和分析源码
参考 2.1.1 章节,下载 Celerum 代码
git clone git@github.com:open-v2x/cerebrum.git
2.2.2 搭建开发环境
搭建开发环境,我们有如下方法:
- 删除所有 OpenV2X 服务,只保留 3 个基础服务,参考 1.2.4 章节
- 只删除 Cerebrum 服务,保留其它所有服务
这里展示第 2 种方法(只删除 Cerebrum 服务,保留其它所有服务),操作步骤如下:
-
重新部署 OpenV2X,参考 1.2.3 章节
-
停止 Cerebrum 服务
docker stop cerebrum
然后用
docker ps
可以看到 cerebrum 容器确实停止了
2.2.3 启动服务
参考 Cerebrum 文档:Github 或 Gitlab
cd cerebrum
# 创建 python 虚拟环境
python3 -m virtualenv .venv
# 进入 python 虚拟环境
. .venv/bin/activate
# 安装依赖
pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements/algo.txt
pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt
pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple -r test-requirements.txt
pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements/bandit.txt
pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements/docstyle.txt
pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements/pep8.txt
pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements/typecheck.txt
查看 cerebrum 的部署配置,可以看到 cerebrum 靠环境变量完成配置
# cat /tmp/service/docker-compose-service.yaml | grep -i -E '^\s*cerebrum:\s*$' -A 20
cerebrum:
container_name: 'cerebrum'
image: 'openv2x/cerebrum:latest'
restart: 'always'
network_mode: 'host'
environment:
redis_host: '127.0.0.1'
mqtt_host: '127.0.0.1'
mysql_host: '127.0.0.1'
cloud_url: 'http://127.0.0.1:28300/api/v1'
mysql_user: 'root'
mysql_password: password
emqx_password: password
redis_password: password
volumes:
- '/etc/localtime:/etc/localtime'
我们可以将环境变量写进 .bashrc,然后重新进 shell,这样子进程都会继承环境变量。
export redis_host='127.0.0.1'
export mqtt_host='127.0.0.1'
export mysql_host='127.0.0.1'
export cloud_url='http://127.0.0.1:28300/api/v1'
export mysql_user='root'
export mysql_password=password
export emqx_password=password
export redis_password=password
然后运行服务:
python main.py
如果遇到错误 ImportError: libGL.so.1: cannot open shared object file: No such file or directory
:
(.venv) root@openv2x-test-master:~/cerebrum# python main.py
2022-08-30 08:05:43,754 INFO sqlalchemy.engine.Engine SELECT DATABASE()
2022-08-30 08:05:43,754 INFO sqlalchemy.engine.Engine [raw sql] {}
2022-08-30 08:05:43,755 INFO sqlalchemy.engine.Engine SELECT @@sql_mode
2022-08-30 08:05:43,755 INFO sqlalchemy.engine.Engine [raw sql] {}
2022-08-30 08:05:43,755 INFO sqlalchemy.engine.Engine SELECT @@lower_case_table_names
2022-08-30 08:05:43,755 INFO sqlalchemy.engine.Engine [raw sql] {}
2022-08-30 08:05:43,756 INFO sqlalchemy.engine.Engine BEGIN (implicit)
2022-08-30 08:05:43,758 INFO sqlalchemy.engine.Engine SELECT rsu.rsu_esn AS rsu_rsu_esn, rsu.location AS rsu_location, rsu.bias_x AS rsu_bias_x, rsu.bias_y AS rsu_bias_y, rsu.rotation AS rsu_rotation, rsu.reverse AS rsu_reverse, rsu.scale AS rsu_scale, rsu.lane_info AS rsu_lane_info
FROM rsu
2022-08-30 08:05:43,758 INFO sqlalchemy.engine.Engine [generated in 0.00011s] {}
2022-08-30 08:05:43,759 INFO sqlalchemy.engine.Engine ROLLBACK
Traceback (most recent call last):
File "/root/cerebrum/main.py", line 2, in <module>
from transform_driver.app import App
File "/root/cerebrum/transform_driver/app.py", line 21, in <module>
from pre_process_ai_algo.pre_process import Cfg
File "/root/cerebrum/pre_process_ai_algo/pre_process.py", line 21, in <module>
from pre_process_ai_algo.pipelines.fusion import Fusion
File "/root/cerebrum/pre_process_ai_algo/pipelines/fusion.py", line 17, in <module>
from pre_process_ai_algo.algo_lib import fusion
File "/root/cerebrum/pre_process_ai_algo/algo_lib/fusion/__init__.py", line 20, in <module>
from pre_process_ai_algo.algo_lib.fusion.algorithm import Hungarian
File "/root/cerebrum/pre_process_ai_algo/algo_lib/fusion/algorithm.py", line 22, in <module>
import cv2 # type: ignore
File "/root/cerebrum/.venv/lib/python3.10/site-packages/cv2/__init__.py", line 181, in <module>
bootstrap()
File "/root/cerebrum/.venv/lib/python3.10/site-packages/cv2/__init__.py", line 153, in bootstrap
native_module = importlib.import_module("cv2")
File "/usr/lib/python3.10/importlib/__init__.py", line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
ImportError: libGL.so.1: cannot open shared object file: No such file or directory
可以这样处理:
pip uninstall opencv-python
pip install opencv-python-headless
服务就可以正常启动了:
(.venv) root@openv2x-test-master:~/cerebrum# python main.py
2022-08-30 08:09:13,623 INFO sqlalchemy.engine.Engine SELECT DATABASE()
2022-08-30 08:09:13,623 INFO sqlalchemy.engine.Engine [raw sql] {}
2022-08-30 08:09:13,624 INFO sqlalchemy.engine.Engine SELECT @@sql_mode
2022-08-30 08:09:13,624 INFO sqlalchemy.engine.Engine [raw sql] {}
2022-08-30 08:09:13,624 INFO sqlalchemy.engine.Engine SELECT @@lower_case_table_names
2022-08-30 08:09:13,624 INFO sqlalchemy.engine.Engine [raw sql] {}
2022-08-30 08:09:13,625 INFO sqlalchemy.engine.Engine BEGIN (implicit)
2022-08-30 08:09:13,627 INFO sqlalchemy.engine.Engine SELECT rsu.rsu_esn AS rsu_rsu_esn, rsu.location AS rsu_location, rsu.bias_x AS rsu_bias_x, rsu.bias_y AS rsu_bias_y, rsu.rotation AS rsu_rotation, rsu.reverse AS rsu_reverse, rsu.scale AS rsu_scale, rsu.lane_info AS rsu_lane_info
FROM rsu
2022-08-30 08:09:13,627 INFO sqlalchemy.engine.Engine [generated in 0.00011s] {}
2022-08-30 08:09:13,628 INFO sqlalchemy.engine.Engine ROLLBACK
2022-08-30 08:09:14 | TRACE | Socket opened
2022-08-30 08:09:14 | TRACE | Watching socket for writability.
2022-08-30 08:09:14,295 INFO sqlalchemy.engine.Engine BEGIN (implicit)
2022-08-30 08:09:14,296 INFO sqlalchemy.engine.Engine SELECT system_config.mqtt_config AS system_config_mqtt_config, system_config.node_id AS system_config_node_id
FROM system_config
LIMIT %(param_1)s
2022-08-30 08:09:14,296 INFO sqlalchemy.engine.Engine [generated in 0.00015s] {'param_1': 1}
2022-08-30 08:09:14,297 INFO sqlalchemy.engine.Engine ROLLBACK
2022-08-30 08:09:14 | TRACE | misc_loop started
2022-08-30 08:09:14 | TRACE | Socket is writable, calling loop_write
2022-08-30 08:09:14 | TRACE | Stop watching socket for writability.
2022-08-30 08:09:14 | TRACE | Socket is readable, calling loop_read
2022-08-30 08:09:14 | TRACE | Watching socket for writability.
2022-08-30 08:09:14 | TRACE | Socket is writable, calling loop_write
2022-08-30 08:09:14 | TRACE | Stop watching socket for writability.
2022-08-30 08:09:14 | TRACE | Socket is readable, calling loop_read
2022-08-30 08:09:14 | TRACE | Socket is readable, calling loop_read
...
可以通过从 RSE 模拟器发送消息来验证服务是否正常,参考 Github 或 Gitee
也可以用脚本做功能和性能测试(脚本后续会上传到 github)
$ sh function_test.sh
====================================== test session starts =======================================
platform darwin -- Python 3.9.7, pytest-6.2.4, py-1.10.0, pluggy-0.13.1
rootdir: /Users/wuwenxiang/local/gitlab/v2x-apitest, configfile: pytest.ini
plugins: anyio-3.6.1, subtests-0.5.0, hypothesis-6.23.2, tavern-1.23.3, html-3.1.1, typeguard-2.13.3, schemathesis-3.10.1, cov-3.0.0, metadata-2.0.2
collected 5 items
function_test/dnp.tavern.yaml . [ 20%]
function_test/rsi.tavern.yaml . [ 40%]
function_test/rsm.tavern.yaml . [ 60%]
function_test/sds.tavern.yaml . [ 80%]
function_test/clc.tavern.yaml . [100%]
------- generated html file: file:///Users/wuwenxiang/local/gitlab/v2x-apitest/report.html -------
======================================= 5 passed in 2.95s ========================================
2.2.4 调试代码
参考 2.1.3 章节,通过 VSCode 进行调试。
2.2.5 提交 Patch
参考 2.1.4 章节
2.3 RoadMocker RSE 模拟器
RoadMocker RSE 是 OpenV2X 基于 MQTT 提供的一款 RSE 模拟器,用于帮助开源用户节约成本并进行开发调试
2.3.1 下载、查看和分析源码
参考 2.1.1 章节,下载 RoadMocker 代码
git clone git@github.com:open-v2x/roadmocker.git
然后可以通过文本编辑工具(比如 VIM)查看源码。
2.3.2 开发调试
具体操作可参看文档:Github 需要注意的是 ClientId 和 EdgeView 的 RSU 序列号,而不是 RSU 设备 ID.
2.3.3 提交 Patch
参考 2.1.4 章节.