Docker配合autowre可以看https://blog.csdn.net/weixin_60062799/article/details/149306141?spm=1001.2014.3001.5501,下面就是介绍autoware的一些启动前的工作,以及实车部署等相关操作,基本涵盖了全过程,实车部署部分可以从3.创建车辆和传感器描述部分查看。
1.Docker镜像备份到U盘
之前通过Docker成功编译了Autoware,具体可以参考:Ubuntu下Docker安装Autoware.universe(含GPU启动)-CSDN博客文章浏览阅读757次,点赞12次,收藏14次。详细记录Ubuntu系统下使用Docker安装autoware的详细步骤,不需要主机上安装任何编译环境,除了GPU的配置,其他所有工作均在容器内进行。https://blog.csdn.net/weixin_60062799/article/details/149306141?spm=1001.2014.3001.5501 安装autoware的时候已经下载好了镜像就没有必要在其他机器上在下载一遍。最简单的方式就是将下载好的镜像拷贝到U盘。
1.先使用docker images命令查看有哪些镜像
REPOSITORY TAG IMAGE ID CREATED SIZE
<none> <none> 8dc21956b017 11 hours ago 1.07GB
ghcr.io/autowarefoundation/autoware universe-devel-cuda-20250711 66e73df0500b 5 days ago 23.1GB
golang 1.19 80b76a6c918c 22 months ago 1.06GB
nvidia/opengl 1.0-glvnd-devel-ubuntu22.04 96334240cf5a 2 years ago 544MB
2.使用docker save命令
## 比如我这里要保存ghcr.io/autowarefoundation/autoware镜像
docker save -o autoware-universe-devel-cuda-20250711.tar ghcr.io/autowarefoundation/autoware:universe-devel-cuda-20250711
## 其他的也保存一下
docker save -o golang-1.19.tar golang:1.19
docker save -o opengl-1.0-glvnd-devel-ubuntu22.04.tar nvidia/opengl:1.0-glvnd-devel-ubuntu22.04
到指定目录去查找,然后复制到U盘里面。
3.从U盘导入Docker镜像
先将所有的打包镜像从U盘拷贝到主机上,然后使用:
docker load -i xxxxxxx.tar
加载完毕后使用docker images查看是否导入成功。
2.构建Autoware Build GUI(不是必须,而且构造比较麻烦)
看着构建吧,后面实际一点没用上。
在之前启动容器都带有一个参数–rm,导致每次退出容器后在里面下载的所有东西都没了。可以去掉那个该参数,使用下面命令主要是–name autoware_save,autoware_save是新容器的名字,里面会保存环境更改的环境。这里和下面使用rocker启动的有点突兀,说明一下原因,使用rocker最后会生成下面的命令,所以直接复制这段命令直接保存,下次启动使用这个命令。
docker run -it \\
–gpus all \\
–name autoware_save \\
-v /home/hong/autoware_main_ws/autoware:/home/hong/autoware_main_ws/autoware \\
-v /home/hong/autoware_main_ws/autoware_map:/home/hong/autoware_main_ws/autoware_map \\
-e DISPLAY \\
-e TERM \\
-e QT_X11_NO_MITSHM=1 \\
-e XAUTHORITY=/tmp/.dockerjv9fz9pb.xauth \\
-v /tmp/.dockerjv9fz9pb.xauth:/tmp/.dockerjv9fz9pb.xauth \\
-v /tmp/.X11-unix:/tmp/.X11-unix \\
-v /etc/localtime:/etc/localtime:ro \\
my_autoware_image:use-gpu
下次进入容器的时候之间诶使用命令,设置和上面的是一样的:
docker start -ai autoware_save
不想使用可以直接删除:
docker rm autoware_save
2.1写在前面
Autoware Build GUI 是 Autoware 提供的图形化工具,用于简化代码编译和构建流程,专为不熟悉命令行操作的开发者设计。不喜欢使用命令行的可以使用这个,运行完的界面如下:

实际使用过程中感觉不到有什么优势,之前使用容器构建过的包在这里编译会直接报错。之前编译过的包经过这个编译直接不能用了,可能需要执行sudo rm -rf install/ build/ log/,然后在编译吧,反正没有具体试过,之后还是使用下面的命令操作的:
colcon build –symlink-install –cmake-args -DCMAKE_BUILD_TYPE=Release
## 选择编译某一些包
colcon build –symlink-install –cmake-args -DCMAKE_BUILD_TYPE=Release –packages-select <package_name>
## 跳过某一些包
colcon build –symlink-install –cmake-args -DCMAKE_BUILD_TYPE=Release –packages-skip <package_name1> <package_name2>
2.2源码构建
Autoware Build GUI比较简单的是通过deb,不过没有找到,你们可以自己到官方参考链接找找:https://github.com/autowarefoundation/autoware-build-gui?tab=readme-ov-file#installation
下面通过源码构架,过程稍微来说比较繁琐:先进入到容器中,并挂载本地文件夹到容器中,没有镜像的可以看Ubuntu下Docker安装Autoware.universe(含GPU启动)-CSDN博客,和上面的链接是一样的:
# CPU版本
rocker -e LIBGL_ALWAYS_SOFTWARE=1 –x11 –user –volume $HOME/autoware_main_ws/autoware –volume $HOME/autoware_main_ws/autoware_map — ghcr.io/autowarefoundation/autoware:universe-devel-cuda-20250711
# GPU版本
rocker –nvidia –x11 –user –volume $HOME/autoware_main_ws/autoware –volume $HOME/autoware_main_ws/autoware_map — ghcr.io/autowarefoundation/autoware:universe-devel-cuda-20250711
首先安装依赖:
sudo apt update
sudo apt install libwebkit2gtk-4.1-0 libjavascriptcoregtk-4.1-0 libsoup-3.0-0 libsoup-3.0-common
## rust,按照提提示,一般默认的就可以,就是选择1
curl –proto '=https' –tlsv1.2 -sSf https://sh.rustup.rs/ | sh
## 找不到rust可以运行:
source $HOME/.cargo/env
## node 版本要求最好v18+,下面可能满足不了需求,最好使用下面的方式,这种方式不要使用
sudo apt update
sudo apt install nodejs
## node版本有问题,可以使用下面获取最新的:
curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash –
sudo apt install -y nodejs
接下来git源码,这里放到指定文件夹下。首先 切换到autoware 目录下,新建立一个文件夹:
## 根据自己的目录结构来切换
cd autoware_main_ws/autoware
mkdir AutowareBuildGUI
cd AutowareBuildGUI/
git clone https://github.com/leo-drive/autoware-build-gui.git
cd autoware-build-gui
pnpm i
容器内没有pnpm,需要安装,参考链接安装 | pnpm中文文档 | pnpm中文网:
wget -qO- https://get.pnpm.io/install.sh | ENV="$HOME/.bashrc" SHELL="$(which bash)" bash –
#安装完成后会提示,To start using pnpm, run:
source /home/hong/.bashrc
# 然后
pnpm i
# 成功后运行
pnpm tauri dev
2.3一些报错的解决方案
错误一:Error failed to get cargo metadata: No such file or directory (os error 2) ELIFECYCLE Command failed with exit code 1.
# 已经安装了rust,却找不到执行
source "$HOME/.cargo/env"
错误2:SyntaxError: Unexpected token '?' at wrapSafe (internal/modules/cjs/loader.js:915:16) at Module._compile (internal/modules/cjs/loader.js:963:27) at Object.Module._extensions..js (internal/modules/cjs/loader.js:1027:10) at Module.load (internal/modules/cjs/loader.js:863:32) at Function.Module._load (internal/modules/cjs/loader.js:708:14) at Module.require (internal/modules/cjs/loader.js:887:19) at Module.mod.require
## 先卸载旧版本
sudo apt remove –purge nodejs npm
## node版本有问题,可以使用下面获取最新的:
curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash –
sudo apt install -y nodejs
错误3:— stderr `PKG_CONFIG_ALLOW_SYSTEM_CFLAGS="1" "pkg-config" "–libs" "–cflags" "gdk-3.0" "gdk-3.0 >= 3.22"` did not exit successfully: exit status: 1 error: could not find system library 'gdk-3.0' required by the 'gdk-sys' crate
sudo apt update
sudo apt install -y libgtk-3-dev libwebkit2gtk-4.0-dev
错误4:— stderr Package libsoup-3.0 was not found in the pkg-config search path. Perhaps you should add the directory containing `libsoup-3.0.pc' to the PKG_CONFIG_PATH environment variable No package 'libsoup-3.0' found Package libsoup-3.0 was not found in the pkg-config search path. Perhaps you should add the directory containing `libsoup-3.0.pc' to the PKG_CONFIG_PATH environment variable No package 'libsoup-3.0' found
sudo apt update
sudo apt install -y libsoup-3.0-dev
错误5:— stderr Package webkit2gtk-4.1 was not found in the pkg-config search path. Perhaps you should add the directory containing `webkit2gtk-4.1.pc' to the PKG_CONFIG_PATH environment variable No package 'webkit2gtk-4.1' found Package webkit2gtk-4.1 was not found in the pkg-config search path. Perhaps you should add the directory containing `webkit2gtk-4.1.pc' to the PKG_CONFIG_PATH environment variable No package 'webkit2gtk-4.1' found
sudo apt update
sudo apt install -y \\
libwebkit2gtk-4.1-dev \\
libjavascriptcoregtk-4.1-dev \\
libgtk-3-dev \\
libsoup-3.0-dev
2.4使用
3.创建车辆和传感器描述
参考的官方教程:Overview – Autoware Documentation
实车部署必要条件是:线控接口(控制车辆)和激光雷达。其他的IMU、GNSS、相机都是可选的。
3.1创建和自定义您的 autoware 元存储库
我对这个的理解是相当于你自己基于autoware的主仓库复制了一份工程文件到你的账户下,你可以在你的仓库内修改配置文件,自己的车辆模型和传感器模型等等工作,下次使用的时候只需要进行git你的仓库的文件就可以快速配置,但是这个好像是Public,不能设置为私有的。下面只是简单的过一下流程,不使用这种方式。
按照官方的教程,第一部是创建和自定义autoware元存储库,这个有什么作用呢?在GitHub中,创建一个fork的作用是复制一个现有的仓库到你的个人账户下,这样你可以自由地修改实验代码。
首先访问https://github.com/autowarefoundation/autoware,然后创建一个新的fork:

创建过程演示,这里用官网的照片,这里只需要修改带*号的东西,然后Description看自己情况想不想改:

创建成功后如下,可以看到就是相当于把元原仓库工程复制一份到自己账户下:

3.2创建传感器模型
这里不使用autoware官方教程的方式,直接通过git autoware mian分支的源码进行构建。构建完成之后,先对原工程进行备份,方便恢复,然后在原工程的基础上进行修改。编译过程可以参考:Ubuntu下Docker安装Autoware.universe(含GPU启动)-CSDN博客

3.2.1根据车辆定义传感器模型
首先打开autoware/src/launcher/autoware_launch/sensor_kit目录,里面内容如下:

下面我们将根据sample_sensor_kit_launch 目录下的内容进行修改,创建描述自己车辆传感器信息的文件。
首先复制sample_sensor_kit_launch 目录,我起的名称是my_sensor_kit_launch,然后将该目录下的文件进行重命名:

然后 ,更改my_sensor_kit_description和my_sensor_kit_launch包的package.xml文件和CMakeLists.txt文件中的包名称:
my_sensor_kit_description中的package.xml文件修改:
# package.xml
原文件:<name>sample_sensor_kit_description</name>
<description>The sample_sensor_kit_description package</description>
修改后:<name>my_sensor_kit_description</name>
<description>The my_sensor_kit_description package</description>
# CMakeLists.txt
原文件:project(sample_sensor_kit_description)
修改:project(my_sensor_kit_description)
my_sensor_kit_launch中的package.xml文件修改:
# package.xml
原文件:<name>sample_sensor_kit_launch</name>
<description>The sample_sensor_kit_launch package</description>
修改后:<name>sample_sensor_kit_launch</name>
<description>The my_sensor_kit_launch package</description>
# CMakeLists.txt
原文件:project(sample_sensor_kit_description)
修改后:project(my_sensor_kit_description)
之后就是编译,先切换到包含src的那个目录(autoware/),然后执行,可以先看错误2,照着修改:
colcon build –symlink-install –cmake-args -DCMAKE_BUILD_TYPE=Release –packages-up-to my_sensor_kit_description my_sensor_kit_launch
错误1:报错找不到这两个包
是不是package.xml内容是不是都修改了。
错误2:ERROR:colcon:colcon build: Duplicate package names not supported: – common_sensor_launch: – launcher/autoware_launch/sensor_kit/my_sensor_kit_launch/common_sensor_launch – launcher/autoware_launch/sensor_kit/sample_sensor_kit_launch/common_sensor_launch
这里说明一下,其实我感觉保留原始的common_sensor_launch好一点,然后直接把原来的sample_sensor_kit_launch的common_sensor_launch删除,动的东西少一点。
my_sensor_kit_launch和sample_sensor_kit_launch有同样的包(common_sensor_launch),删除一个或者把某个包的名字改了(package.xml和CMakeLists.txt文件也要修改)。方便一点直接把sample_sensor_kit_launch给删除了,然后在build/ install/ log/中删除相应的包【不删也可以】。麻烦一点把名字给改了,然后修改my_sensor_kit_launch/package.xml文件,就是将原来的common_sensor_launch替换为my_common_sensor_launch。
<exec_depend>my_common_sensor_launch</exec_depend>
错误3:某个包编译出错
多尝试几次。不成功根据报错信息查看修改。这里没有遇到额外的情况。
3.2.2传感器坐标变换文件
主要目的是描述传感器坐标系,以及传感器的外参和 URDF 文件的描述。
my_sensor_kit_description包的文件夹结构为:
my_sensor_kit_description/
├─ config/
| |—imu_corrector.param.yaml
│ ├─ sensor_kit_calibration.yaml
│ └─ sensors_calibration.yaml
└─ urdf/
├─ sensor_kit.xacro
└─ sensors.xacro
下面将根据自己的车辆上面的传感器设计修改上面目录中config文件夹下的配置文件。
sensor_kit_calibration.yaml
根据自己的实际情况进行修改,这里只保留一个激光雷达(VLP16)和一个IMU。下面的传感器外参是相对于sensor_kit_base_link坐标系而言。sensor_kit_base_link可以自己定义看放在哪里,根据原文件可以看出sensor_kit_base_link在激光雷达处(仅仅旋转了一个角度),这里将sensor_kit_base_link和激光雷达坐标系设置为同一个。
注意:在这里改名称不会影响后面发布坐标系的名称!
sensor_kit_base_link:
velodyne_top_base_link:
x: 0.0
y: 0.0
z: 0.0
roll: 0.0
pitch: 0.0
yaw: 0.0
imu_link:
x: 0.17
y: 0.0
z: -0.12
roll: 0.0
pitch: 0.0
yaw: -1.57
sensors_calibration.yaml
该文件下描述车辆的基坐标系base_link和传感器的坐标系sensor_kit_base_link之间的变换关系。原文件这里的velodyne_rear_base_link应该是有另外一个激光雷达,这里不使用直接删除。关于这个base_link的位置后面在传感器的标定章节会进行讲解,但是注意的是base_link的x轴正方向必须和车辆前方朝向一致。至于为什么,因为autoware会根据输入车辆的速度信息做一个里程计,那里的base_link速度正方向对应于的x轴正方向,如果错误设置,会导致后面定位直接错误。
base_link:
sensor_kit_base_link:
x: 0.577
y: 0.0
z: 1.334
roll: 0.0
pitch: 0.0
yaw: 0.0
imu_corrector.param.yaml
在config中还有一个imu_corrector.param.yaml文件,用于IMU 传感器校准参数,可以根据自己传感器填写。一般是IMU和Lidar标定会输出这些结果。
sensor_kit.xacro
建立传感器的相应模型,添加传感器并从此文件中删除不必要的 xacro,这里使用一个激光雷达和一个IMU。
如果sensor_kit_calibration.yaml文件中的名称修改了,相应的${calibration['sensor_kit_base_link']['imu_link']['x']}也需要修改为对应的名字。
这个文件会被调用,发布坐标变换关系。具体名称为相应传感器<xacro>中的name,但是注意的是这里会生成两个坐标系。文件开头引入了VLP-16.urdf.xacro和imu.xacro文件,后面每个传感器会调用文件,并传入相应参数。雷达坐标系会多出一个名字为:${name}_base_link,IMU坐标系会多处一个名为:${name}_link。
<?xml version="1.0"?>
<robot xmlns:xacro="http://ros.org/wiki/xacro">
<xacro:macro name="sensor_kit_macro" params="parent x y z roll pitch yaw">
<xacro:include filename="$(find velodyne_description)/urdf/VLP-16.urdf.xacro"/>
<xacro:include filename="$(find imu_description)/urdf/imu.xacro"/>
<xacro:arg name="gpu" default="false"/>
<xacro:arg name="config_dir" default="$(find my_sensor_kit_description)/config"/> <!– 修改为自己创建的包 –>
<xacro:property name="sensor_kit_base_link" default="sensor_kit_base_link"/>
<!– 这里描述两个link之间的坐标关系,由第3行的params传递 –>
<joint name="${sensor_kit_base_link}_joint" type="fixed">
<origin rpy="${roll} ${pitch} ${yaw}" xyz="${x} ${y} ${z}"/>
<parent link="${parent}"/>
<child link="${sensor_kit_base_link}"/>
</joint>
<link name="${sensor_kit_base_link}">
<origin rpy="0 0 0" xyz="0 0 0"/>
</link>
<!– VLP16 –>
<xacro:property name="calibration" value="${xacro.load_yaml('$(arg config_dir)/sensor_kit_calibration.yaml')}"/>
<xacro:VLP-16 parent="sensor_kit_base_link" name="velodyne_top_base_link" topic="/points_raw" hz="10" samples="220" gpu="$(arg gpu)">
<origin
xyz="${calibration['sensor_kit_base_link']['velodyne_top_base_link']['x']}
${calibration['sensor_kit_base_link']['velodyne_top_base_link']['y']}
${calibration['sensor_kit_base_link']['velodyne_top_base_link']['z']}"
rpy="${calibration['sensor_kit_base_link']['velodyne_top_base_link']['roll']}
${calibration['sensor_kit_base_link']['velodyne_top_base_link']['pitch']}
${calibration['sensor_kit_base_link']['velodyne_top_base_link']['yaw']}"
/>
</xacro:VLP-16>
<!– imu –>
<xacro:imu_macro
name="imu_link"
parent="sensor_kit_base_link"
namespace=""
x="${calibration['sensor_kit_base_link']['imu_link']['x']}"
y="${calibration['sensor_kit_base_link']['imu_link']['y']}"
z="${calibration['sensor_kit_base_link']['imu_link']['z']}"
roll="${calibration['sensor_kit_base_link']['imu_link']['roll']}"
pitch="${calibration['sensor_kit_base_link']['imu_link']['pitch']}"
yaw="${calibration['sensor_kit_base_link']['imu_link']['yaw']}"
fps="100"
/>
</xacro:macro>
</robot>
sensors.xacro
将sensor_kit_base_link坐标系连接到base_link坐标系下,该文件调用了上面的sensor_kit文件,将参数传递给上面的文件,主要的作用是描述base_link和sensor_kit_base_link之间的坐标变换关系:
<?xml version="1.0"?>
<robot name="vehicle" xmlns:xacro="http://ros.org/wiki/xacro">
<xacro:arg name="config_dir" default="$(find my_sensor_kit_description)/config"/>
<xacro:property name="calibration" value="${xacro.load_yaml('$(arg config_dir)/sensors_calibration.yaml')}"/>
<!– sensor kit –>
<xacro:include filename="sensor_kit.xacro"/>
<xacro:sensor_kit_macro
parent="base_link"
x="${calibration['base_link']['sensor_kit_base_link']['x']}"
y="${calibration['base_link']['sensor_kit_base_link']['y']}"
z="${calibration['base_link']['sensor_kit_base_link']['z']}"
roll="${calibration['base_link']['sensor_kit_base_link']['roll']}"
pitch="${calibration['base_link']['sensor_kit_base_link']['pitch']}"
yaw="${calibration['base_link']['sensor_kit_base_link']['yaw']}"
/>
</robot>
3.2.3传感器驱动(my_sensor_kit_launch)
首先说在前面my_sensor_kit_launch和sample_sensor_kit_launch有同样的包(common_sensor_launch),简单点就是直接把sample_sensor_kit_launch备份然后删除,这样修改的比较少。麻烦点即使把my_sensor_kit_launch中的common_sensor_launch名字给该了,但是这样在多数配置文件中都需要修改名字。下面使用更改名字的方式介绍。
首先看一张图,各个传感器经过驱动获取传感器数据,然后在经过一些处理得到输出。common_sensor_launch主要是处理激光雷达的点云数据。

接下来看这一张图,有一个sensing.launch.xml文件,负责启动所有传感器,不需要的传感器这里可以去掉。之后是一些其他传感器启动文件,这些文件目录在my_sensor_kit_launch/launch下:

激光雷达驱动
首先注意激光雷达话题的坐标系要和上面的配置文件设置保持一致。如果动过common_sensor_launch包,需要定位到文件
my_sensor_kit_launch/my_common_sensor_launch/launch/nebula_node_container.launch.py
找到common_sensor_share_dir = get_package_share_directory("common_sensor_launch")中的common_sensor_launch修改为my_common_sensor_launch
如果是使用容器编译的Autoware,激光雷达使用的是以太网协议,首先把主机IP和激光雷达修改到同一网段下。这里用的是容器直接共享主机的网络栈(IP、端口、网卡等),启动容器的时候需要加上–network=host:
docker run –rm -it –gpus all –network=host -v /home/hong/autoware_main_ws/autoware:/home/hong/autoware_main_ws/autoware -v /home/hong/autoware_main_ws/autoware_map:/home/hong/autoware_main_ws/autoware_map -e DISPLAY -e TERM -e QT_X11_NO_MITSHM=1 -e XAUTHORITY=/tmp/.dockerjv9fz9pb.xauth -v /tmp/.dockerjv9fz9pb.xauth:/tmp/.dockerjv9fz9pb.xauth -v /tmp/.X11-unix:/tmp/.X11-unix -v /etc/localtime:/etc/localtime:ro my_autoware_image:use-gpu
my_sensor_kit_launch/my_sensor_kit_launch/launch/lidar.launch.xml里面需要修改为自己的包。
首先介绍nebula,该包在Autoware项目中的src/sensor_component/external/nebula目录下。
nebula简介:Nebula 是一个传感器驱动程序平台,旨在为尽可能广泛的设备提供统一的框架。 虽然它主要针对基于以太网的 LiDAR 传感器,但它的目标是轻松扩展以支持新的传感器和接口。 Nebula 与 ROS 2 配合使用,是 Autoware 项目推荐的传感器驱动程序。
github:https://github.com/tier4/nebula
文档:Nebula
查看支持的传感器:Supported sensors – Nebula。这里Velodyne LiDAR 16是支持的,已经经过实际测试,项目中使用的是VLP16,以此为例。
Velodyne Lidar传感器(16线), 按照教程使用sample_sensor_kit_launch模板,上文中已经更改为my_sensor_kit_launch 。打开my_sensor_kit_launch/launch/lidar.launch.xml文件按需修改,参照官网上看的明显一点,Creating a sensor model for Autoware – Autoware Documentation,下面介绍一下是如何调用lidar.launch.xml启动激光雷达的。
接下来测试nebula包能否能够启动雷达驱动,已经将Autoware全部包编译成功然后切换到工作空间下,在终端使用source ./install/setup.bash,接下来修改雷达默认的配置文件:src/sensor_component/external/nebula/nebula_ros/config/lidar/velodyne/VLP16.param.yaml,主要是修改host_ip、sensor_ip、data_port,然后执行命令:
ros2 launch nebula_ros velodyne_launch_all_hw.xml sensor_model:=VLP16
不同的激光雷达测试参考:Usage – Nebula。将命令中的launch文件和模型名替换即可。
启动驱动后,无明显错误。使用ros2 topic list查看雷达话题,使用ros2 topic echo /xxxx查看是否有数据输出。使用rviz2查看数据需要注意一点的是可能会包QoS相关的错误,需要在rviz中配置一下,当然还需要修改Fixed Frame:

测试nebula有数据输出即可。接下来介绍从my_sensor_kit_launch下的lidar.launch.xml启动,简单说明该文件的启动流程:
然后修改错误的地方:
# 2如果还是空,尝试从指定文件下读取参数
if not gp:
# 路径要写成绝对路径
with open("autoware/src/launcher/autoware_launch/vehicle/my_vehicle_launch/my_vehicle_description/config/vehicle_info.param.yaml", 'r') as f:
config = yaml.safe_load(f)
print(config)
for key, value in config.items():
if key == '/**' and 'ros__parameters' in value:
gp = value['ros__parameters']
break
## 第二处,定位到下面代码处,修改后的为:
ComposableNode(
package="nebula_ros",
plugin=sensor_make + "RosWrapper",
name=sensor_make.lower() + "_ros_wrapper_node",
parameters=[
{
"calibration_file": sensor_calib_fp,
"sensor_model": sensor_model,
"launch_hw": LaunchConfiguration("launch_driver"),
**create_parameter_dict(
"host_ip",
"sensor_ip",
"data_port",
"gnss_port",
"return_mode",
"min_range",
"max_range",
"frame_id",
"scan_phase",
"cloud_min_angle",
"cloud_max_angle",
"dual_return_distance_threshold",
"rotation_speed",
"packet_mtu_size",
"setup_sensor",
"udp_only",
"advanced_diagnostics",
"diag_span"
),
},
],
remappings=[
# cSpell:ignore knzo25
# TODO(knzo25): fix the remapping once nebula gets updated
("velodyne_points", "pointcloud_raw_ex"),
# ("robosense_points", "pointcloud_raw_ex"), #for robosense
# ("pandar_points", "pointcloud_raw_ex"), # for hesai
],
extra_arguments=[{"use_intra_process_comms": LaunchConfiguration("use_intra_process")}],
)
## 第三处,定位到add_launch_arg("sensor_model", description="sensor model name")
## 处添加以下内容
add_launch_arg("advanced_diagnostics", "False", description="advanced_diagnostics")
add_launch_arg("diag_span", "1000", description="diag_span")
add_launch_arg("return_mode", "Dual", description="return_mode")
修改完之后执行命令查看是否能够成功启动激光雷达,其中vehicle_mirror_param_file文件需要在外面传入,是之前配置的车辆镜子信息的文件:
ros2 launch my_sensor_kit_launch lidar.launch.xml vehicle_mirror_param_file:=./src/launcher/autoware_launch/vehicle/my_vehicle_launch/my_vehicle_description/config/mirror.param.yaml
这里的mirror.param.yaml文件见3.2.4章节创建车辆模型部分
激光雷达原有的话题被映射为xx/xx/pointcloud_raw_ex,查看是否能够输出数据,能够输出数据代表启动成功。
nebula_node_container.launch.py文件功能也在这里介绍一下:
这时候可以发现,只有一个话题下有激光雷达数据,其他话题下是没有数据的,而且终端下会报错没有base_link到激光雷达坐标系下的坐标变换。之前my_sensor_kit_description下的配置文件描述了各个传感器之间的坐标变换,但是这里没有加载,需要自己发布出来:
ros2 run tf2_ros static_transform_publisher 0 0.6 1.32 0 0 0 1 base_link velodyne_top_base_link
之后重新执行一下lidar.launch.xml文件,就能够看到数据。注意lidar.launch.xml后面的pointcloud_preprocessor别注释了,否则会影响部分功能。
错误1:Caught exception in launch (see debug for traceback): 'front_overhang',这个是因为autoware/src/launcher/autoware_launch/vehicle/my_vehicle_launch/my_vehicle_description/config/vehicle_info.param.yaml文件里的参数还没有写入到参数服务器中,找不到这个参数。
修改nebula_node_container.launch.py内容如下:
def get_vehicle_info(context):
# TODO(TIER IV): Use Parameter Substitution after we drop Galactic support
# https://github.com/ros2/launch_ros/blob/master/launch_ros/launch_ros/substitutions/parameter.py
gp = context.launch_configurations.get("ros_params", {})
# 如果还是空,尝试从文件中直接读取
if not gp:
with open("/home/Your Username/autoware/src/launcher/autoware_launch/vehicle/my_vehicle_launch/my_vehicle_description/config/vehicle_info.param.yaml", 'r') as f:
config = yaml.safe_load(f)
print(config)
for key, value in config.items():
if key == '/**' and 'ros__parameters' in value:
gp = value['ros__parameters']
break
if not gp:
gp = dict(context.launch_configurations.get("global_params", {}))
p = {}
p["vehicle_length"] = gp["front_overhang"] + gp["wheel_base"] + gp["rear_overhang"]
p["vehicle_width"] = gp["wheel_tread"] + gp["left_overhang"] + gp["right_overhang"]
p["min_longitudinal_offset"] = -gp["rear_overhang"]
p["max_longitudinal_offset"] = gp["front_overhang"] + gp["wheel_base"]
p["min_lateral_offset"] = -(gp["wheel_tread"] / 2.0 + gp["right_overhang"])
p["max_lateral_offset"] = gp["wheel_tread"] / 2.0 + gp["left_overhang"]
p["min_height_offset"] = 0.0
p["max_height_offset"] = gp["vehicle_height"]
return p
其他雷达驱动
还使用有另外的激光雷达,一般厂商会提供ROS驱动,注意下载对应Ros2 Humble版本的驱动在容器内进行编译:
colcon build –symlink-install –cmake-args -DCMAKE_BUILD_TYPE=Release –packages-select <package_name>
如果激光雷达使用的是以太网协议,首先把主机IP和激光雷达修改到同一网段下。这里用的是容器直接共享主机的网络栈(IP、端口、网卡等),启动容器的时候需要加上–network=host:
docker run –rm -it –gpus all –network=host -v /home/hong/autoware_main_ws/autoware:/home/hong/autoware_main_ws/autoware -v /home/hong/autoware_main_ws/autoware_map:/home/hong/autoware_main_ws/autoware_map -e DISPLAY -e TERM -e QT_X11_NO_MITSHM=1 -e XAUTHORITY=/tmp/.dockerjv9fz9pb.xauth -v /tmp/.dockerjv9fz9pb.xauth:/tmp/.dockerjv9fz9pb.xauth -v /tmp/.X11-unix:/tmp/.X11-unix -v /etc/localtime:/etc/localtime:ro my_autoware_image:use-gpu
my_sensor_kit_launch/my_sensor_kit_launch/launch/lidar.launch.xml里面需要修改为自己的包。同时还要尽可能实现上面nebula_node_container.launch.py包的功能,点云处理处理部分可以复用。
最后加入传感器驱动需要加入lidar.launch.xml中。
相机驱动
这里没有实际测试,只是在容器内读取到数据而已。
因为相机是免驱的,首先进入到容器内,需要把设备挂载到容器内:
# Executing command:
docker run –rm -it –device=/dev/video0 –device=/dev/video1 –gpus all -v /home/hong/autoware_main_ws/autoware:/home/hong/autoware_main_ws/autoware -v /home/hong/autoware_main_ws/autoware_map:/home/hong/autoware_main_ws/autoware_map -e DISPLAY -e TERM -e QT_X11_NO_MITSHM=1 -e XAUTHORITY=/tmp/.dockerjv9fz9pb.xauth -v /tmp/.dockerjv9fz9pb.xauth:/tmp/.dockerjv9fz9pb.xauth -v /tmp/.X11-unix:/tmp/.X11-unix -v /etc/localtime:/etc/localtime:ro 72b00f6820b7
这里就使用pyhton简单的写一个节点。该功能包在autoware/src/sensor_component/external目录下。
第一步:在该目录下创建ros包(终端需要在切换到该目录下)
ros2 pkg create fisheye_camera_driver –build-type ament_python –dependencies rclpy image_transport cv_bridge sensor_msgs opencv-python
第二步: 在fisheye_camera_driver/fisheye_camera_driver下创建驱动节点,创建一个py文件,编写驱动程序。
第三步:修改setup.py文件内容。
# 在setup()函数中添加:
entry_points={
'console_scripts': [
'fisheye_camera_node = fisheye_camera_driver.fisheye_camera_node:main',
],
},
关于配置文件的说明yaml
相机存在可以配置的参数,所以单独全部放入到一个yaml文件中去,在程序中读取。在程序中使用相对路径,这里编写了一段代码:
from ament_index_python.packages import get_package_share_directory
# 如果不是绝对路径,就从包路径拼接
if not os.path.isabs(config_file):
pkg_path = get_package_share_directory('fisheye_camera_driver')
config_file = os.path.join(pkg_path, 'config', config_file)
改写完成后编译该功能包,注意一点就是编译的时候最好切换到autoware目录下,不然会在子目录中重新创建出build等文件夹。
# 编译
colcon build –packages-select fisheye_camera_driver
# 运行
ros2 run fisheye_camera_driver fisheye_camera_node
相机配置camera.launch.xml
修改autoware/src/launcher/autoware_launch/sensor_kit/my_sensor_kit_launch/my_sensor_kit_launch/launch/camera.launch.xml 文件,如下:
<launch>
<arg name="launch_driver" default="true"/>
<arg name="camera_type" default="left"/>
<group>
<push-ros-namespace namespace="camera"/>
<group>
<push-ros-namespace namespace="fisheye_camera"/>
<node pkg="fisheye_camera_driver" exec="fisheye_camera_node" name="left_fisheye_camera" output="log"/>
</group>
</group>
</launch>
重新编译一下:
colcon build –symlink-install –cmake-args -DCMAKE_BUILD_TYPE=Release –packages-up-to my_common_sensor_launch my_sensor_kit_launch
然后运行ros2 launch my_sensor_kit_launch camera.launch.xml,查看是否已经发布相机图像。
报错: 打开设备失败
# 查看是否有权限,666是有的
stat -c "%a %n" /dev/video0
# 没有则开启权限
sudo chmod 666 /dev/video0
IMU驱动
这里imu话题名称使用imu_link,不知道为什么,修改为其他的名称似乎有点问题。然后IMU输出输出话题名字修改为/data,后面会包两个空间,最后话题名为/sensing/imu/data。
下载IMU厂商提供的驱动包,这里放到src/sensor_component/external目录下。选择该包进行编译。
修改 my_sensor_kit_launch/my_sensor_kit_launch/launch/imu.launch.xml 文件,添加自己的IMU驱动。
该文件中还会启动额外的两个节点,imu_corrector.launch.xml对原始 IMU 数据(imu_raw)进行处理和校正。gyro_bias_estimator.launch.xml利用车辆的运动状态(input_odom来自里程计)与原始 IMU 数据对比,估计陀螺仪偏置:
<group>
<push-ros-namespace namespace="openzen"/>
<node pkg="openzen_driver" exec="openzen_node" name="openzen_node" if="$(var launch_driver)">
<!– 参数设置 –>
<param name="sensor_name" value="IG148510057006E"/>
<param name="baudrate" value="115200"/>
</node>
</group>
<arg name="imu_raw_name" default="openzen/imu_raw"/>
<arg name="imu_corrector_param_file" default="$(find-pkg-share my_sensor_kit_description)/config/imu_corrector.param.yaml"/>
<include file="$(find-pkg-share autoware_imu_corrector)/launch/imu_corrector.launch.xml">
<arg name="input_topic" value="$(var imu_raw_name)"/>
<arg name="output_topic" value="imu_data"/>
<arg name="param_file" value="$(var imu_corrector_param_file)"/>
</include>
<include file="$(find-pkg-share autoware_imu_corrector)/launch/gyro_bias_estimator.launch.xml">
<arg name="input_imu_raw" value="$(var imu_raw_name)"/>
<arg name="input_odom" value="/localization/kinematic_state"/>
<arg name="imu_corrector_param_file" value="$(var imu_corrector_param_file)"/>
</include>
一般IMU是使用串口来通信的,所以这一部需要把IMU设备挂载到容器内,有两种方式:
然后进入容器内还要赋予权限:sudo chmod 666 /dev/ttyxxx
驱动没有问题,写入到imu.launch.xml文件中,注意,后面IMU矫正的最好不要注释,否则可能会出现问题。imu_raw_name需要修改为IMU发布的话题名称:
<arg name="imu_raw_name" default="data"/>
<arg name="imu_corrector_param_file" default="$(find-pkg-share my_sensor_kit_description)/config/imu_corrector.param.yaml"/>
<include file="$(find-pkg-share autoware_imu_corrector)/launch/imu_corrector.launch.xml">
<arg name="input_topic" value="$(var imu_raw_name)"/>
<arg name="output_topic" value="imu_data"/>
<arg name="param_file" value="$(var imu_corrector_param_file)"/>
</include>
<include file="$(find-pkg-share autoware_imu_corrector)/launch/gyro_bias_estimator.launch.xml">
<arg name="input_imu_raw" value="$(var imu_raw_name)"/>
<arg name="input_odom" value="/localization/kinematic_state"/>
<arg name="imu_corrector_param_file" value="$(var imu_corrector_param_file)"/>
</include>
其他的驱动保留
pass
sensing.launch.xml
最后就是在sensing.launch.xml文件内修改使用那些驱动,不使用的部分可以注释掉。但是下面的不要进行注释:
<include file="$(find-pkg-share autoware_vehicle_velocity_converter)/launch/vehicle_velocity_converter.launch.xml">
<arg name="input_vehicle_velocity_topic" value="/vehicle/status/velocity_status"/>
<arg name="output_twist_with_covariance" value="/sensing/vehicle_velocity_converter/twist_with_covariance"/>
</include>
番外:传感器话题传递讲解
点云话题传递讲解
my_sensor_kit_launch/my_common_sensor_launch/launch/nebula_node_container.launch.py,定位到上面文件,该文件有详细的传递过程。
VLP16激光雷达发布原始话题为pointcloud_raw_ex(实际会包几个空间名),经过第一次矫正得到一个话题self_cropped/pointcloud_ex,经过第二次矫正得到mirror_cropped/pointcloud_ex,经过第三次矫正得到rectified/pointcloud_ex。
到rectified/pointcloud_ex这里说一下,定位到文件中可以看到需要如下输入:
remappings=[
("~/input/twist", "/sensing/vehicle_velocity_converter/twist_with_covariance"),
("~/input/imu", "/sensing/imu/imu_data"),
("~/input/pointcloud", "mirror_cropped/pointcloud_ex"),
("~/output/pointcloud", "rectified/pointcloud_ex"),
],
第一个话题是哪里来的?打开sensing.launch.xml文件,拉到最后面就可以看到这个话题名了,但是/vehicle/status/velocity_status又是从哪里来呢?这个话题需要自己实现,主要作用是实时反馈车辆行驶速度,后面将到vehicle_interface再会具体讲解。
这里/sensing/vehicle_velocity_converter/twist_with_covariance没有话题,rectified/pointcloud_ex就没有数据,激光雷达点云传递过程就断开了,会影响后面感知功能。
经过第四次矫正会得到pointcloud_before_sync,到这里文件是结束了,但是传递过程远远没有结束。
定位到my_sensor_kit_launch/launch/pointcloud_preprocessor.launch.py文件,经过第六次矫正得到concatenated/pointcloud。找到该文件会发现找不到输入话题,因为输入话题被写入到配置文件中,定位到my_sensor_kit_launch/config/concatenate_and_time_sync_node.param.yaml,打开会发现如下结果:
input_topics: [
"/sensing/lidar/right/pointcloud_before_sync",
"/sensing/lidar/top/pointcloud_before_sync",
"/sensing/lidar/left/pointcloud_before_sync",
]
会发现里面有三个话题,/sensing/lidar/top/pointcloud_before_sync是经过sensing.launch.xml文件之后的结果,其实和nebula_node_container.launch.py里面的结果一致。上面的输入其他激光雷达话题不需要删,没有发布也不要删除,否则会有问题,不删除还能跑。
到这里输出的激光雷达的话题才是autoware最终使用的,也就是说中间任何一步断了,都会影响后续结果。
当然这里也可以用自己的点云数据,发布为/sensing/lidar/concatenated/pointcloud话题,但是还是要做好一些处理,比如滤除车辆周围的点云,还有车辆行驶过程中进行畸变矫正。
这个点云消息会在tier4_localization_component.launch.xml中使用,主要用于后面定位信息。
3.2.4创建车辆模型
在autoware/src/launcher/autoware_launch/vehicle路径下,将sample_vehicle_launch复制一份,这里改名为my_vehicle_launch,目录结构如下:

同时也需要将my_vehicle_description和my_vehicle_launch下的CMakeLists.txt和package.xml内容修改:
# package.xml主要修改
<name>my_vehicle_launch</name>
<description>The my_vehicle_launch package</description>
# CMakeLists.txt主要修改
project(my_vehicle_launch)
然后编译一下两个包:
colcon build –symlink-install –cmake-args -DCMAKE_BUILD_TYPE=Release –packages-up-to my_vehicle_description my_vehicle_launch
接下来修改:描述车辆尺寸文件, 车辆3D模型文件,车辆后视镜的参数文件, 模拟器模型参数和车辆的 URDF文件。
my_vehicle_description/config/mirror.param.yaml
该文件描述车辆镜子的参数,用于 PointCloudPreprocessor 中的 CropBox 过滤器,可以从激光雷达的点云数据中裁剪掉镜像部分。车上没有后视镜,这里都全部参数可以保持为0.0。下面图片展现了每个参数如何测量。

simulator_model.param.yaml
该文件主要是在模拟环境中关于车辆参数的配置。部分参数和注释如下:
/**:
ros__parameters:
simulated_frame_id: "base_link" # center of the rear axle.
origin_frame_id: "map" # map 坐标系通常是车辆在地图中定位时的基准坐标系
vehicle_model_type: "DELAY_STEER_ACC_GEARED" # options: IDEAL_STEER_VEL / IDEAL_STEER_ACC / IDEAL_STEER_ACC_GEARED / DELAY_STEER_ACC / DELAY_STEER_ACC_GEARED
initialize_source: "INITIAL_POSE_TOPIC" # options: ORIGIN / INITIAL_POSE_TOPIC
timer_sampling_time_ms: 25
add_measurement_noise: False # the Gaussian noise is added to the simulated results
vel_lim: 50.0 # limit of velocity km/h
vel_rate_lim: 7.0 # limit of acceleration
steer_lim: 1.0 # limit of steering angle, 车辆的转角限制?
steer_rate_lim: 5.0 # limit of steering angle change rate, 角速度限制
acc_time_delay: 0.1 # dead time for the acceleration input, 设置加速度输入的时间延迟
acc_time_constant: 0.1 # time constant of the 1st-order acceleration dynamics, 用于建模加速度响应的时间常数,表示加速过程的动态响应特性
steer_time_delay: 0.24 # dead time for the steering input, 模拟方向盘控制系统的响应延迟
steer_time_constant: 0.27 # time constant of the 1st-order steering dynamics, 模拟方向盘控制系统的动态响应,表示从输入到实际转角之间的时间延迟
steer_dead_band: 0.0 # dead band of the steering angle, 此参数用于模拟方向盘的死区,即当方向盘转动到一定程度之前,不会引起任何实际转向的情况。0.0 表示没有死区。
x_stddev: 0.0001 # x standard deviation for dummy covariance in map coordinate, 设置地图坐标系中 x 轴的标准差,单位通常是米(m)
y_stddev: 0.0001 # y standard deviation for dummy covariance in map coordinate, 设置地图坐标系中 y 轴的标准差,单位通常是米(m)
enable_road_slope_simulation: true # if true, slopes in the lanelet map are used to apply an extra acceleration to the ego vehicle, 是否启用道路坡度模拟
vehicle_info.param.yaml
该文件用于配置实车参数:
/**:
ros__parameters:
wheel_radius: 0.248 # The radius of the wheel, primarily used for dead reckoning. 轮子的半径,单位为米
wheel_width: 0.496 # The lateral width of a wheel tire, primarily used for dead reckoning. 轮胎的横向宽度,单位为米
wheel_base: 1.24 # between front wheel center and rear wheel center. 前轮中心和后轮中心之间的距离,单位为米
wheel_tread: 0.88 # between left wheel center and right wheel center. 左右轮中心之间的距离,单位为米
front_overhang: 0.3 # between front wheel center and vehicle front. 前轮中心和车辆前端之间的距离,单位为米
rear_overhang: 0.3 # between rear wheel center and vehicle rear. 后轮中心和车辆后端之间的距离,单位为米
left_overhang: 0.06 # between left wheel center and vehicle left. 左轮中心和车辆左端之间的距离,单位为米
right_overhang: 0.06 # between right wheel center and vehicle right. 右轮中心和车辆右端之间的距离,单位为米
vehicle_height: 1.35 # 车辆的高度,单位为米
max_steer_angle: 0.48869219 # [rad]. 车辆最大转向角,单位为弧度(rad)
vehicle.xacro
创建车辆的3D模型,主要作用是在RVIZ中能够显示车辆模型,使用是原文件中的mesh/lexus.dae文件。当然还要修改vehicle.xacro文件中的配置文件路径:
<xacro:property name="vehicle_info" value="${xacro.load_yaml('$(find my_vehicle_description)/config/vehicle_info.param.yaml')}"/>
配置完文件之后,可以重新编译一下包:
colcon build –symlink-install –cmake-args -DCMAKE_BUILD_TYPE=Release –packages-up-to my_vehicle_description my_vehicle_launch
完成传感器配置和车辆模型之后,可以加载看一下,首先没有编译过的可以编译一下总的包:colcon build –symlink-install –cmake-args -DCMAKE_BUILD_TYPE=Release,编译完后source ./install/setup.bash,然后直接运行:
ros2 launch autoware_launch planning_simulator.launch.xml map_path:=../autoware_map/sample-map-planning/ vehicle_model:=my_vehicle sensor_model:=my_sensor_kit vehicle_id:=my_vehicle
这里地图用之前下载好的,然后vehicle_model:=my_vehicle sensor_model:=my_sensor_kit用我们自己的包,然后vehicle_id:=my_vehicle,这个自己随便设置,设置缺失也没有任何问题。
如何实现车辆模型的缩放呢,可以打开my_vehicle_description/urdf/vehicle.xacro文件,修改:
<geometry>
<mesh filename="package://my_vehicle_description/mesh/lexus.dae" scale="0.5 0.5 0.5"/> <!– scale模型在三个轴上的缩放尺寸 –>
</geometry>
加载其他车辆模型(针对替换模型的需求)
去某一个提供模型的网站(买 3D 模型, CG 纹理和 3D 印刷。开自己的 3D 模型店!)上下载dae格式的模型。可以下载Smart forTwo 1999 4×4 VLE 免费 3D 模型 in 紧凑型轿车 3DExport,渲染图如下:

接下来的工作是把模型放入到工作空间空间下,我在my_vehicle_description新建了一个文件夹my_mesh,当然也可以放到mesh文件夹下,看个人喜好:

接下来重要的是在该文件夹下的CMakeLists.txt文件中将自己新建的文件夹添加进来,放入到mesh中的则不需要:
ament_auto_package(INSTALL_TO_SHARE
my_mesh
mesh
urdf
config
)
然后就是修改该文件夹下的urdf/vehicle.xacro文件,<origin xyz="" rpy=""/>主要作用是设置模型相对于base_link的位置,这里将base_link放到车后轮中间处(实际的base_link位置x轴需要和车辆前方保持一致),这里的具体值一开始都设置为0,然后查看模型位置,根据rviz修改相关的参数:
<?xml version="1.0"?>
<robot xmlns:xacro="http://ros.org/wiki/xacro">
<!– load parameter –>
<xacro:property name="vehicle_info" value="${xacro.load_yaml('$(find my_vehicle_description)/config/vehicle_info.param.yaml')}"/>
<!– vehicle body –>
<link name="base_link">
<visual>
<origin xyz="${vehicle_info['/**']['ros__parameters']['wheel_base']/2.0} 0 0" rpy="${pi/2.0} 0 ${pi}"/>
<geometry>
<mesh filename="package://my_vehicle_description/my_mesh/SmartForTwodae.dae" scale="1.0 1.0 1.0"/> <!– scale模型在三个轴上的缩放尺寸 –>
</geometry>
</visual>
</link>
</robot>
接下来需要重新编译一下,放入到mesh中也最好编译一下:
colcon build –symlink-install –cmake-args -DCMAKE_BUILD_TYPE=Release –packages-up-to my_vehicle_description my_vehicle_launch
然后在终端重新执行:
source ./install/setup.bash
ros2 launch autoware_launch planning_simulator.launch.xml map_path:=../autoware_map/sample-map-planning/ vehicle_model:=my_vehicle sensor_model:=my_sensor_kit vehicle_id:=my_vehicle
最终的效果如下:

3.2.5传感器的标定
下面标定算法对数据有要求,出结果的效果也比较差,因为自己使用容器和本机版本有问题,大部分标定算法都是使用Ros1的bag包,还需要从Ros2 转到Ros1,比较麻烦。
没办法了,拿出尺子量吧,这里使用的传感器只有imu和lidar,但是还需要选定一个base_link坐标系的位置,注意这里的base_link的x轴正方向只能和车辆前方朝向一致,然后base_link位置autoware官方是放在后轮中间接地处,坐标系在地面上但是跟着车动。具体可以看下面或者上文的my_vehicle_description/config/mirror.param.yaml处,有一张图片显示了base_link位置。选定好了base_link位置之后,然后确定雷达坐标系的相对于base_link的位置,然后imu和base_link的位置,量完了就可以。
确定完了参数之后需要写入到传感器坐标变换文件中,可以见3.2.2小节。
本节内容参考:Calibrating your sensors – Autoware Documentation
为了有效地发挥作用, 必须正确校准所有传感器以对齐其坐标系,并且必须使用 URDF 文件定义其位置(my_sensor_kit或作为 tf 启动文件)。
base_link和sensor_kit_base_link位置
该文件路径在:
autoware/src/launcher/autoware_launch/sensor_kit/my_sensor_kit_launch/my_sensor_kit_description/config/sensors_calibration.yaml
看官方如何定义的,首先官方给出了一个车辆的参数:

随后官方设置的参数为:
base_link:
sensor_kit_base_link:
x: 1.600000 # meter
y: 0.0
z: 1.421595 # 1.151595m + 0.270m
roll: 0.0
pitch: 0.0
yaw: 0.0
这里应该是把base_link放到后轮中间地平面上。然后sensor_kit_base_link放在雷达处,base_linkX轴正方向为车辆正前方。参照这样的格式进行设置就行。
前面在以下路径中定义了实车的参数,可以根据车连的机械图纸或自己手动测量确定,先确定和之间的相对位置
autoware/src/launcher/autoware_launch/vehicle/my_vehicle_launch/my_vehicle_description/config/vehicle_info.param.yaml
使用下面的标定工具有几个问题:1.教程不全,2.精度差,3.环境不同。下面的方法试了很多,效果不是很好,最后用车辆机械图纸+测量法,安装传感器的时候保证旋转矩阵尽量90度左右,这样得到一个大概值吧,节省时间。
TIER IV 的 CalibrationTools
这里开始进行传感器的外参标定,因为这里暂且就用到了激光雷达和IMU,所以暂时就对激光雷达和IMU进行外参标定。
如果仅仅标定lidar和imu,CalibrationTools 是不需要进行构建的,lidar和imu依赖其他的标定工具,当然也可以使用其他的工具,能够标定出结果就行。
首先我们先安装这个工具,这里在autoware/src/tier_iv_calibrationtools的目录进行相关操作,第一步骤还是进入到容器内,之前说过,启动容器使用–rm参数后,安装所有的依赖退出容器后全部不存在,所以这里使用别的参数启动:
docker run -it \\
–gpus all \\
–name autoware_tier_iv_calibrationtools \\
-v /home/hong/autoware_main_ws/autoware:/home/hong/autoware_main_ws/autoware \\
-v /home/hong/autoware_main_ws/autoware_map:/home/hong/autoware_main_ws/autoware_map \\
-e DISPLAY \\
-e TERM \\
-e QT_X11_NO_MITSHM=1 \\
-e XAUTHORITY=/tmp/.dockerjv9fz9pb.xauth \\
-v /tmp/.dockerjv9fz9pb.xauth:/tmp/.dockerjv9fz9pb.xauth \\
-v /tmp/.X11-unix:/tmp/.X11-unix \\
-v /etc/localtime:/etc/localtime:ro \\
my_autoware_image:use-gpu
wget和vcs这一步骤是可以不用在容器内进行操作的,最后一步骤在容器内进行操作,执行rosdep install之前可能还需要执行rosdep update和sudo apt-get update,最重要的是这一步需要科学上网,启动容器的时候可以使用参数–network=host和主机共享网络。
cd autoware/src/tier_iv_calibrationtools
wget https://raw.githubusercontent.com/tier4/CalibrationTools/tier4/universe/calibration_tools_autoware.repos
vcs import src < calibration_tools_autoware.repos
rosdep install -y –from-paths src –ignore-src –rosdistro $ROS_DISTRO
之后就是编译,因为之前已经编译过了autoware下的其他的包,所以这里不全部编译,而是编译该文件夹下的包,首先切到autoware目录下,和build install log文件夹在同一级目录内:
colcon build \\
–symlink-install \\
–packages-select $(colcon list –base-paths src/tier_iv_calibrationtools/src –names-only) \\
–cmake-args -DCMAKE_BUILD_TYPE=Release
编译过程中会出现一些包的报错,然后重新编译一下或许可以减少一些。我在编译的时候报错:
— stderr: camera_calibration /usr/lib/python3/dist-packages/setuptools/dist.py:723: UserWarning: Usage of dash-separated 'script-dir' will not be supported in future versions. Please use the underscore name 'script_dir' instead 因为这里暂且不使用相机外参标定,后面用到了再来解决。
激光雷达和地面的标定
标定文件路径如下:
autoware/src/tier_iv_calibrationtools/src/autoware/calibration_tools/calibrators/ground_plane_calibrator/launch/calibrator.launch.xml
当然也可以调用这个文件,里面有参数的描述,该文件调用的是上面的文件。
autoware/src/tier_iv_calibrationtools/src/autoware/calibration_tools/sensor_calibration_manager/launch/default_project/ground_plane_calibrator.launch.xml
打开文件看到如下内容,其中base_frame可以设置为base_link,lidar_frame就是雷达坐标系的名称,pointcloud_topic就是订阅的点云话题,其他参数可以根据描述进行更改:
<arg name="base_frame" default="base_link" description="Base frame"/>
<arg name="lidar_frame" default="velodyne" description="Lidar's frame"/>
<arg name="pointcloud_topic" default="/velodyne_points" description="Topic of the lidar to calibrate"/>
<arg name="max_inlier_distance" default="0.03" description="Maximum distance in meters to consider inliers in plane estimation"/>
<arg name="min_plane_points" default="500" description="Minimum number of points for an hypothesis for be considered valid"/>
<arg name="min_plane_points_percentage" default="20.0" description="Minimum percentage of the total points to consider an hypothesis valid"/>
<arg name="max_cos_distance" default="0.2" description="Maximum cosine distance between an hypothesis and the initial calibration for an hypothesis to be considered valid"/>
<arg name="max_iterations" default="500" description="Number of maximum iterations in the plane estimation algorithm"/>
<arg name="verbose" default="true" description="Verbose mode"/>
<arg name="overwrite_xy_yaw" default="false" description="Overwrite x, y, and yaw values with the initial calibration"/>
<arg name="filter_estimations" default="true" description="Flag to enable filtering estimations"/>
<arg name="use_crop_box_filter" default="true" description="Use an optional crop box filter to accelerate processing and ignore walls or other planes"/>
<arg name="crop_box_min_x" default="-50.0" description="Minimum x value of the crop box filter"/>
<arg name="crop_box_min_y" default="-50.0" description="Minimum y value of the crop box filter"/>
<arg name="crop_box_min_z" default="-50.0" description="Minimum z value of the crop box filter"/>
<arg name="crop_box_max_x" default="50.0" description="Maximum x value of the crop box filter"/>
<arg name="crop_box_max_y" default="50.0" description="Maximum y value of the crop box filter"/>
<arg name="crop_box_max_z" default="50.0" description="Maximum z value of the crop box filter"/>
<arg name="use_pca_rough_normal" default="false" description="Whether to use PCA or the initial calibration for normal plane estimation"/>
<arg name="rviz" default="true" description="Launch rviz"/>
在启动该节点之前需要发布两个坐标之间的相对变换关系,无旋转四元组可以设置为0 0 0 1,平移向量用尺子粗略测量,注意base_link要在上图的指定位置:
ros2 run tf2_ros static_transform_publisher x y z qx qy qz qw frame_id child_frame_id
固态激光雷达用这个似乎不太行。
激光雷达和IMU外参标定-OA-LICalib
autoware官网上使用的是OA-LICalib工具。这里也可以使用其他标定算法,只要能标出结果就可以。使用OA-LICalib经过测试有一个问题,就是使用实车采集的bag包标定的结果和测量的结果偏差很大,还有就是配置里面的参数变动对标定结果有很大影响,根据结果让AI修改几套参数,重新标定的结果也不是很准确。
校准工具是使用 ROS 1 开发的,与 ROS 2 不兼容。 因此,需要安装提供的一个具有 ROS 1 和所有必要软件包的 docker 镜像。工具使用的是Melodic,Noetic下没有经过实际测试。
数据采集
对于 Lidar-Imu 校准,需要一个包含IMU(X、Y、Z、滚动、俯仰、偏航)和雷达消息的 ROS 1 bag文件,消息格式为:sensor_msgs/PointCloud2 sensor_msgs/Imu。 为了获得校准过程的良好结果, 控制车辆画八字形和网格。

Ros2下录制多个话题的指令
ros2 bag record -o my_bag_name /topic1 /topic2 /topic3
在采集的时候可能在的Ros2下,使用以下方式可以将Ros2下的bag转为Ros1底下。如果使用的是标准的消息格式,直接使用以下命令:
# install bag converter tool (https://gitlab.com/ternaris/rosbags)
pip3 install rosbags
# 可以指定其他源下载:
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple rosbags
# convert bag
rosbags-convert <YOUR-SPLITTED-ROS2-BAG-FOLDER> –dst <OUTPUT-BAG-FILE>
# 注意第一个参数是文件夹
eg:rosbags-convert lidar_imu_202508131506/ –dst ros1_lidar_imu_202508131544.bag
注意一点的就是安装会出现WARNING: The script rosbags-convert is installed in '$HOME/.local/bin' which is not on PATH. 按住成功后需要将下面内容添加到~/.bashrc中。
export PATH="$HOME/.local/bin:$PATH"
如果包含非标准消息主题,还需拆分,请参考Lidar-Imu Calibration – Autoware Documentation
安装docker
一键安装:wget http://fishros.com/install -O fishros && . fishros
构建OA-LICalib
本节内容参考:【亲测免费】 OA-LICalib 项目常见问题解决方案-CSDN博客和Lidar-Imu Calibration – Autoware Documentation。因为本电脑没有melodic环境,所以使用容器进行构建,有melodic环境可以直接在主机上进行构建。
这里放到autoware_main_ws下,和autoware同一级目录,新建一个catkin_oa_calib/src目录,放在src目录下:
git clone https://github.com/leo-drive/OA-LICalib.git
git clone https://github.com/APRIL-ZJU/ndt_omp.git
## 上面还需要依赖rslidar_msgs
git clone https://github.com/RoboSense-LiDAR/rslidar_msg.git
之后就是构建docker镜像,如果是Melodic可以尝试一下本地构建:
cd OA-LICalib/docker
sudo docker build -t oalicalib .
构建完成后使用docker images查看会有oalicalib镜像,使用下面的指定运行容器,主要是更改第三个–volume处,挂载oalicalib的工程目录:
rocker –nvidia –x11 –user –volume $HOME/autoware_main_ws/ — oalicalib:latest
然后该指令会下载nvidia/opengl 1.0-glvnd-devel-ubuntu18.04镜像,会生成以下指令:
docker run –rm -it –gpus all -v /home/hong/autoware_main_ws:/home/hong/autoware_main_ws -e DISPLAY -e TERM -e QT_X11_NO_MITSHM=1 -e XAUTHORITY=/tmp/.dockergc05abut.xauth -v /tmp/.dockergc05abut.xauth:/tmp/.dockergc05abut.xauth -v /tmp/.X11-unix:/tmp/.X11-unix -v /etc/localtime:/etc/localtime:ro 7adde8b62968
接下来使用docker ps -a查看容器
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1c81d74e6725 7adde8b62968 "/ros_entrypoint.sh …" 5 seconds ago Up 5 seconds nifty_euler
使用docker commit 1c81d74e6725 oalicalib_use_gpu:latest保存,方便下次调用,可以正常进行可视化操作。
首先进入容器安装一些依赖:
sudo apt-get install ros-melodic-velodyne-msgs
sudo apt-get install libpcap-dev
sudo apt-get install ccache
sudo apt-get install libyaml-cpp-dev
sudo apt-get install libsuitesparse-dev
之后执行切换到OA-LICalib目录执行:./build_submodules.sh
进入容器后需要切换catkin_oa_calib目录下,然后继续编译:
catkin_make -DCATKIN_WHITELIST_PACKAGES="ndt_omp;rslidar_msgs"
catkin_make -DCATKIN_WHITELIST_PACKAGES="oa_licalib" 或 catkin_make
首先打开OA-LICalib/config/simu.yaml文件,更改参数,更改雷达和imu的topic,雷达模型LidarModel这里可以根据自己的雷达进行更改,选项有VLP_16_packet、VLP_16_points、VLP_32E_points、 VLS_128_points、Ouster_16_points、Ouster_32_points、Ouster_64_points、 Ouster_128_points、RS_16。如果使用的是velodyne 16,这里需要设置为VLP_16_points:
topic_lidar: /scan_sim # /velodyne_packets #
topic_imu: /imu_sim
# LidarModel: VLP_16_SIMU
LidarModel: VLP_16_points
还有修改bag相关的参数,主要是开始时间和结束时间,以及bag包的绝对路径:
segment_num: 1
selected_segment:
– {start_time: 0, end_time: 50, path_bag: autoware_main_ws/catkin_oa_calib/src/OA-LICalib/data/bag/simu_bag.bag}
如果是实车采集的plane_motion设置为true。
启动使用以下命令:
source ./devel/setup.bash
roslaunch oa_licalib li_calib.launch
激光雷达和IMU外参标定-lidar_IMU_calib
该包源码支持只支持了VLP-16,经过改进可以支持其他雷达,已知的有RS_Helios5515,提供源码,具体参考:ubuntu20.04使用浙大开源包lidar_IMU_calib_浙大标定-CSDN博客。 操作如下:
# 为了方便管理,放到auoware下
mkdir -p catkin_li_calib/src
cd catkin_li_calib/src
catkin_init_workspace
####下载源码###########
git clone https://github.com/APRIL-ZJU/lidar_IMU_calib
####同时下载ndt_omp,提供两种的方法,效果是一样的###
# 1.第一种
sudo apt install python3-wstool
wstool init
wstool merge lidar_IMU_calib/depend_pack.rosinstall
wstool update
# 2.第二种
git clone https://github.com/APRIL-ZJU/ndt_omp.git
# 如果没有安装velodyne-msgs,需安装
sudo apt-get install ros-noetic-velodyne-msgs
cd lidar_Imu_calib
./build_submodules.sh
cd ../..
catkin_make
source ./devel/setup.bash
结果在catkin_make的时候会报一大堆PCL的问题,推测应该是PCL版本的问题,因为ndt_omp依赖PCL1.7,而ubuntu20.04是pcl是1.10版本。之前构建OA-LICalib时候构建了一个容器,直接在容器内编译这个包,成功编译通过。
还有一个问题,这个记录的雷达话题格式需要为velodyne_msgs/VelodyneScan这种类型的,启动雷达使用rostopic list应该会出现/velodyne_packets,需要记录这种话题从才能进行标定。
修改licalib_gui.launch参数,然后启动:
roslaunch li_calib licalib_gui.launch
报错解读
如果使用sensor_msgs/PointCloud2的格式,那么可能报错:what(): : Can't use 2D indexing with a unorganized point cloud。这个问题出现lidar_IMU_calib/include/utils/vlp_common.h文件中,需要定位到函数:
void unpack_scan(const sensor_msgs::PointCloud2::ConstPtr &lidarMsg,
TPointCloud &outPointCloud)
具体的问题为temp_pc中的height为1,里面的points相当于1维数组,下面使用temp_pc.at(w,h)索引的会全部报错。
下面修改一版本,只保证能顺利通过该函数,但是出了该函数还是会报错:
// void unpack_scan(const sensor_msgs::PointCloud2::ConstPtr &lidarMsg,
// TPointCloud &outPointCloud) const {
// VPointCloud* temp_pc = new VPointCloud();
// pcl::fromROSMsg(*lidarMsg, *temp_pc);
// std::cout << *temp_pc << std::endl;
// int count = 0;
// outPointCloud.clear();
// outPointCloud.header = pcl_conversions::toPCL(lidarMsg->header);
// outPointCloud.height = temp_pc->height;
// outPointCloud.width = temp_pc->width;
// outPointCloud.is_dense = false;
// outPointCloud.resize(outPointCloud.height * outPointCloud.width);
// double timebase = lidarMsg->header.stamp.toSec();
// try {
// for (int h = 0; h < temp_pc->height; h++) {
// for (int w = 0; w < temp_pc->width; w++) {
// TPoint point;
// count++ ;
// point.x = temp_pc->points[w].x;
// point.y = temp_pc->points[w].y;
// point.z = temp_pc->points[w].z;
// point.intensity = temp_pc->points[w].intensity;
// point.timestamp = timebase;
// outPointCloud.points[w] = point;
// std::cout << count << std::endl;
// }
// }
// }
// catch (const std::exception &e) { // 捕获标准异常
// std::cout << "捕获到异常: " << e.what() << std::endl;
// }
// catch (…) { // 捕获所有其他异常
// std::cout << "捕获到未知类型异常" << std::endl;
// }
// // 使用完成后删除,防止内存泄漏
// delete temp_pc;
// temp_pc = nullptr; // 避免悬空指针
// std::cout << "—————————–" << std::endl;
// }
激光雷达和IMU外参标定-lidar_align
项目地址:GitHub – ethz-asl/lidar_align: A simple method for finding the extrinsic calibration between a 3D lidar and a 6-dof pose sensorA simple method for finding the extrinsic calibration between a 3D lidar and a 6-dof pose sensor – ethz-asl/lidar_align
https://github.com/ethz-asl/lidar_align 参考文章:激光雷达和IMU联合标定并运行LIOSAM_liosam imu标定-CSDN博客。
mkdir -p catkin_ethz_asl_calib/src
cd catkin_ethz_asl_calib/src
git clone https://github.com/ethz-asl/lidar_align.git
# 依赖
sudo apt-get install libnlopt-dev
cd ../..
catkin_make
原程序是里程计和雷达,如果标定雷达和IMU需要修改文件loader.cpp,修改内容如下:
bool Loader::loadTformFromROSBag(const std::string& bag_path, Odom* odom) {
rosbag::Bag bag;
try {
bag.open(bag_path, rosbag::bagmode::Read);
} catch (rosbag::BagException e) {
ROS_ERROR_STREAM("LOADING BAG FAILED: " << e.what());
return false;
}
// IMN
std::vector<std::string> types;
types.push_back(std::string("sensor_msgs/Imu"));
rosbag::View view(bag, rosbag::TypeQuery(types));
size_t imu_num = 0;
double shiftX=0,shiftY=0,shiftZ=0,velX=0,velY=0,velZ=0;
ros::Time time;
double timeDiff,lastShiftX,lastShiftY,lastShiftZ;
for (const rosbag::MessageInstance& m : view){
std::cout <<"Loading imu: \\e[1m"<< imu_num++<<"\\e[0m from ros bag"<<'\\r'<< std::flush;
sensor_msgs::Imu imu=*(m.instantiate<sensor_msgs::Imu>());
Timestamp stamp = imu.header.stamp.sec * 1000000ll +imu.header.stamp.nsec / 1000ll;
if(imu_num==1){
time=imu.header.stamp;
Transform T(Transform::Translation(0,0,0),Transform::Rotation(1,0,0,0));
odom->addTransformData(stamp, T);
}
else{
timeDiff=(imu.header.stamp-time).toSec();
time=imu.header.stamp;
velX=velX+imu.linear_acceleration.x*timeDiff;
velY=velX+imu.linear_acceleration.y*timeDiff;
velZ=velZ+(imu.linear_acceleration.z-9.801)*timeDiff;
lastShiftX=shiftX;
lastShiftY=shiftY;
lastShiftZ=shiftZ;
shiftX=lastShiftX+velX*timeDiff+imu.linear_acceleration.x*timeDiff*timeDiff/2;
shiftY=lastShiftY+velY*timeDiff+imu.linear_acceleration.y*timeDiff*timeDiff/2;
shiftZ=lastShiftZ+velZ*timeDiff+(imu.linear_acceleration.z-9.801)*timeDiff*timeDiff/2;
Transform T(Transform::Translation(shiftX,shiftY,shiftZ),
Transform::Rotation(imu.orientation.w,
imu.orientation.x,
imu.orientation.y,
imu.orientation.z));
odom->addTransformData(stamp, T);
}
}
// std::vector<std::string> types;
// types.push_back(std::string("geometry_msgs/TransformStamped"));
// rosbag::View view(bag, rosbag::TypeQuery(types));
// size_t tform_num = 0;
// for (const rosbag::MessageInstance& m : view) {
// std::cout << " Loading transform: \\e[1m" << tform_num++
// << "\\e[0m from ros bag" << '\\r' << std::flush;
// geometry_msgs::TransformStamped transform_msg =
// *(m.instantiate<geometry_msgs::TransformStamped>());
// Timestamp stamp = transform_msg.header.stamp.sec * 1000000ll +
// transform_msg.header.stamp.nsec / 1000ll;
// Transform T(Transform::Translation(transform_msg.transform.translation.x,
// transform_msg.transform.translation.y,
// transform_msg.transform.translation.z),
// Transform::Rotation(transform_msg.transform.rotation.w,
// transform_msg.transform.rotation.x,
// transform_msg.transform.rotation.y,
// transform_msg.transform.rotation.z));
// odom->addTransformData(stamp, T);
// }
if (odom->empty()) {
ROS_ERROR_STREAM("No odom messages found!");
return false;
}
return true;
}
之后编译,启动lidar_align.launch文件,启动之前更改一个参数bag_file,修改为自己bag包(里面记录激光雷达数据和IMU数据)。transforms_from_csv默认false,否则为从csv文件加载参数:
source ./devel/setup.bash
roslaunch lidar_align lidar_align.launch
3.3Vehicle interface
vehicle_interface的作用是将 Autoware 控制命令转换为针对于特定车辆的控制命令格式。autoware 的控制命令如下:
- 横向控制:转向轮偏角、转向轮转速
- 纵向控制:速度、加速度、加加速度
Autoware 发布的控制指令包括:速度控制、转向控制、车灯指令等等。随后,Vehicle interface将这些指令转换为执行动作,例如:电机与制动操作、方向盘控制、灯光控制等等。可以看一下这张图,熟悉一下工作流程: 
对于使用autoware实现自动驾驶的车辆需要满足一定的要求,autoware将其概括为两类:
| A | B |
| 可以通过目标速度或加速度在纵向方向上进行控制 | 可以通过特定的目标命令在纵向方向上进行控制(即制动和油门踏板) |
| 可以通过目标转向角在横向上进行控制(也可以使用转向速率) | 可以通过特定的目标命令在横向控制(即转向扭矩) |
| 车辆必须提供速度或加速度信息以及转向信息,如上述车辆状态主题中所述。 | 必须提供速度或加速度信息以及转向信息,如上述车辆状态主题中所述。 |
3.3.1实现vehicle_interface
这里参考官网教程Creating vehicle interface – Autoware Documentation。
首先创建一个工作目录,这里创建在autoware/src/launcher/autoware_launch/vehicle/下,注意要创建到该目录下终端需要先切换到该目录下,好像也可以指定参数:
ros2 pkg create –build-type ament_cmake my_vehicle_interface
包名为my_vehicle_interface,在该包中实现vehicle_interface,接收autoware.universe的控制指令然后发布车辆状态信息。
下面是autoware发布的一些话题:

下面是从vehicle_interface到autoware需要发布的一些话题:

下图描述了具体的关系:
下面实现my_vehicle_interface,需要在该包中创建包含这些主题的订阅者和发布者。首先在include/my_vehicle_interface下创建一个my_vehicle_interface.hpp在src目录下创建一个my_vehicle_interface.cpp。
其实可以先不用下,虽然官网上使用的是autoware_auto_msgs的消息,实际上autoware打开的是不一定是使用该消息,不过话题名称是一样的就对了。可以打开仿真环境使用ros2 topic list查看话题名称,使用ros2 topic info /xxxx查看话题类型。不是上面的类型可以不用看。
在此之前还需要处理一件事情,就是下载autoware自定义的消息格式,上面图片中的消息格式autoware_auto_msgs是没有的,tier4_autoware_msgs是有的(autoware/src/universe/external下),我将文件放到autoware/src/core下:
# autoware_auto_msgs
git clone https://github.com/tier4/autoware_auto_msgs.git
# tier4_autoware_msgs
# git clone https://github.com/tier4/tier4_autoware_msgs.git
接下来需要编译一下, 选择一个文件夹下的全部包:
# src/core/autoware_auto_msgs需要替换为真实的路径
colcon build \\
–symlink-install \\
–packages-select $(colcon list –base-paths src/core/autoware_auto_msgs –names-only) \\
–cmake-args -DCMAKE_BUILD_TYPE=Release
下面是我编写一个简单的vehicle_interface的简单示例,经过实际测试是没有问题的?
my_vehicle_interface.hpp
#pragma once
#include <rclcpp/rclcpp.hpp>
#include <geometry_msgs/msg/twist.hpp>
#include <std_msgs/msg/u_int8.hpp>
#include <std_msgs/msg/float32.hpp>
#include <std_msgs/msg/float64.hpp>
#include "can_analyzer/msg/ad103.hpp"
// #include <autoware_control_msgs/msg/ackermann_control_command.hpp>
#include <autoware_control_msgs/msg/control.hpp>
#include <autoware_vehicle_msgs/msg/gear_command.hpp>
#include <tier4_control_msgs/msg/gate_mode.hpp>
#include <tier4_vehicle_msgs/msg/vehicle_emergency_stamped.hpp>
#include <autoware_vehicle_msgs/msg/turn_indicators_command.hpp>
#include <autoware_vehicle_msgs/msg/hazard_lights_command.hpp>
#include <tier4_vehicle_msgs/msg/battery_status.hpp>
#include <autoware_vehicle_msgs/msg/control_mode_report.hpp>
#include <autoware_vehicle_msgs/msg/gear_report.hpp>
#include <autoware_vehicle_msgs/msg/hazard_lights_report.hpp>
#include <autoware_vehicle_msgs/msg/steering_report.hpp>
#include <autoware_vehicle_msgs/msg/turn_indicators_report.hpp>
// #include <autoware_vehicle_msgs/msg/velocity_report.hpp>
#include <autoware_vehicle_msgs/msg/velocity_report.hpp>
#include <sensor_msgs/msg/imu.hpp>
class my_vehicle_interface : public rclcpp::Node
{
public:
my_vehicle_interface();
private:
// from autoware
rclcpp::Subscription<autoware_control_msgs::msg::Control>::SharedPtr control_cmd_sub_; // 控制车辆的主要参数
rclcpp::Subscription<autoware_vehicle_msgs::msg::GearCommand>::SharedPtr gear_cmd_sub_; // 自动驾驶档位信息
rclcpp::Subscription<tier4_control_msgs::msg::GateMode>::SharedPtr current_gate_mode_sub_; // Autoware进行控制车辆模式(Auto、Stop等等)
rclcpp::Subscription<tier4_vehicle_msgs::msg::VehicleEmergencyStamped>::SharedPtr emergency_cmd_sub_; // Autoware处于紧急状况的时候会发送紧急信号
rclcpp::Subscription<autoware_vehicle_msgs::msg::TurnIndicatorsCommand>::SharedPtr turn_indicators_cmd_sub_; // 车辆的转向灯信号
rclcpp::Subscription<autoware_vehicle_msgs::msg::HazardLightsCommand>::SharedPtr hazard_lights_cmd_sub_; // 危险指示灯控制信号
// rclcpp::Subscription<tier4_vehicle_msgs::msg::ActuationCommandStamped>::SharedPtr actuation_cmd_sub_; // 针对B型车
// from vehicle
rclcpp::Publisher<tier4_vehicle_msgs::msg::BatteryStatus>::SharedPtr battery_charge_pub_; // 发布电池信息
rclcpp::Publisher<autoware_vehicle_msgs::msg::ControlModeReport>::SharedPtr control_mode_pub_; // 车辆当前的控制方式
rclcpp::Publisher<autoware_vehicle_msgs::msg::GearReport>::SharedPtr gear_status_pub_; // 车辆但前的档位信息
rclcpp::Publisher<autoware_vehicle_msgs::msg::HazardLightsReport>::SharedPtr hazard_lights_status_pub_; // 反馈车辆的为危险警示灯状态
rclcpp::Publisher<autoware_vehicle_msgs::msg::SteeringReport>::SharedPtr steering_status_pub_; // 反馈转向灯状态
rclcpp::Publisher<autoware_vehicle_msgs::msg::TurnIndicatorsReport>::SharedPtr turn_indicators_status_pub_; // 反馈转向信息
// rclcpp::Publisher<autoware_vehicle_msgs::msg::VelocityReport>::SharedPtr velocity_status_pub_; // 反馈车辆的速度状态
rclcpp::Publisher<autoware_vehicle_msgs::msg::VelocityReport>::SharedPtr velocity_status_pub_; // 反馈车辆的速度状态
// pub to vehicle
rclcpp::Publisher<geometry_msgs::msg::Twist>::SharedPtr cmd_vel_pub_; // 向车辆发送速度信息
rclcpp::Publisher<std_msgs::msg::UInt8>::SharedPtr control_mode_to_vehicle_pub_; // 发送是否自动驾驶的信息
rclcpp::Publisher<can_analyzer::msg::AD103>::SharedPtr AD103_Pub_; // 发布紧急制动
// sub from vehicle
rclcpp::Subscription<std_msgs::msg::UInt8>::SharedPtr control_mode_sub_; // 订阅获取车辆的驾驶模式
rclcpp::Subscription<std_msgs::msg::Float32>::SharedPtr steering_angle_sub_; // 订阅车端转向角度
rclcpp::Subscription<std_msgs::msg::Float64>::SharedPtr vehicle_velocity_sub_; // 订阅车端转向角度
rclcpp::Subscription<sensor_msgs::msg::Imu>::SharedPtr imu_sub_; // 订阅imu的话题
// turn indicators light
std_msgs::msg::UInt8::ConstSharedPtr current_control_mode_ptr_; // 当前的驾驶模式
std_msgs::msg::Float32::ConstSharedPtr current_steering_angle_ptr_;
std_msgs::msg::Float64::ConstSharedPtr vehicle_velocity_ptr_;
sensor_msgs::msg::Imu::ConstSharedPtr imu_ptr_;
// autoware command messages
autoware_control_msgs::msg::Control::ConstSharedPtr control_cmd_ptr_;
autoware_vehicle_msgs::msg::GearCommand::ConstSharedPtr gear_cmd_ptr_; // 自动驾驶档位信息
tier4_control_msgs::msg::GateMode::ConstSharedPtr current_gate_mode_ptr_; // Autoware进行控制车辆模式(Auto、Stop 等等)
tier4_vehicle_msgs::msg::VehicleEmergencyStamped::ConstSharedPtr emergency_cmd_ptr_; // Autoware 处于紧急状况的时候会发送紧急信号
autoware_vehicle_msgs::msg::TurnIndicatorsCommand::ConstSharedPtr turn_indicators_cmd_ptr_; // 车辆的转向灯信号
autoware_vehicle_msgs::msg::HazardLightsCommand::ConstSharedPtr hazard_lights_cmd_ptr_; // 危险指示灯控制信号
// callbacks
void callback_control_cmd(const autoware_control_msgs::msg::Control::ConstSharedPtr msg);
void callback_gear_cmd(const autoware_vehicle_msgs::msg::GearCommand::ConstSharedPtr msg);
void callback_gate_mode(const tier4_control_msgs::msg::GateMode::ConstSharedPtr msg);
void callback_emergency_cmd(const tier4_vehicle_msgs::msg::VehicleEmergencyStamped::ConstSharedPtr msg);
void callback_turn_indicators_cmd(const autoware_vehicle_msgs::msg::TurnIndicatorsCommand::ConstSharedPtr msg);
void callback_hazard_lights_cmd(const autoware_vehicle_msgs::msg::HazardLightsCommand::ConstSharedPtr msg);
void callback_current_control_mode(const std_msgs::msg::UInt8::ConstSharedPtr msg);
void callback_current_steering_angle(const std_msgs::msg::Float32::ConstSharedPtr msg);
void callback_vehicle_velocity(const std_msgs::msg::Float64::ConstSharedPtr msg);
void imuCallback(const sensor_msgs::msg::Imu::ConstSharedPtr msg);
// vehicle
void current_gate_mode_to_vehicle(tier4_control_msgs::msg::GateMode::ConstSharedPtr current_gate_mode_ptr_);
void control_command_to_vehicle(autoware_control_msgs::msg::Control::ConstSharedPtr control_cmd_ptr_);
void emergency_command_to_vehicle(tier4_vehicle_msgs::msg::VehicleEmergencyStamped::ConstSharedPtr emergency_cmd_ptr_);
void turn_indicators_command_to_vehicle(autoware_vehicle_msgs::msg::TurnIndicatorsCommand::ConstSharedPtr turn_indicators_cmd_ptr_);
void hazard_lights_command_to_vehicle( autoware_vehicle_msgs::msg::HazardLightsCommand::ConstSharedPtr hazard_lights_cmd_ptr_);
public:
void to_vehicle();
void to_autoware();
};
my_vehicle_interface.cpp
#include "my_vehicle_interface/my_vehicle_interface.hpp"
my_vehicle_interface::my_vehicle_interface()
: Node("my_vehicle_interface"),
current_control_mode_ptr_(nullptr),
current_steering_angle_ptr_(nullptr),
vehicle_velocity_ptr_(nullptr),
imu_ptr_(nullptr),
// autoware command messages
control_cmd_ptr_(nullptr),
gear_cmd_ptr_(nullptr),
current_gate_mode_ptr_(nullptr),
emergency_cmd_ptr_(nullptr),
turn_indicators_cmd_ptr_(nullptr),
hazard_lights_cmd_ptr_(nullptr)
{
/* subscribers */
using std::placeholders::_1;
// from autoware
control_cmd_sub_ = create_subscription<autoware_control_msgs::msg::Control>(
"/control/command/control_cmd", 1, std::bind(&my_vehicle_interface::callback_control_cmd, this, _1)); // 订阅控制命令
gear_cmd_sub_ = create_subscription<autoware_vehicle_msgs::msg::GearCommand>(
"/control/command/gear_cmd", 1, std::bind(&my_vehicle_interface::callback_gear_cmd, this, _1)); // 订阅换挡命令, 这里不需要
current_gate_mode_sub_ = create_subscription<tier4_control_msgs::msg::GateMode>(
"/control/current_gate_mode", 1, std::bind(&my_vehicle_interface::callback_gate_mode, this, _1)); // 订阅当前 Gate 模式
emergency_cmd_sub_ = create_subscription<tier4_vehicle_msgs::msg::VehicleEmergencyStamped>(
"/control/command/emergency_cmd", 1, std::bind(&my_vehicle_interface::callback_emergency_cmd, this, _1)); // 订阅紧急命令
turn_indicators_cmd_sub_ = create_subscription<autoware_vehicle_msgs::msg::TurnIndicatorsCommand>(
"/control/command/turn_indicators_cmd", 1, std::bind(&my_vehicle_interface::callback_turn_indicators_cmd, this, _1)); // 订阅转向灯命令
hazard_lights_cmd_sub_ = create_subscription<autoware_vehicle_msgs::msg::HazardLightsCommand>(
"/control/command/hazard_lights_cmd", 1, std::bind(&my_vehicle_interface::callback_hazard_lights_cmd, this, _1)); // 订阅危险指示灯命令
imu_sub_ = this->create_subscription<sensor_msgs::msg::Imu>(
"sensing/imu/data", 10,std::bind(&my_vehicle_interface::imuCallback, this, std::placeholders::_1)); // 订阅imu消息
// actuation_cmd_sub_ = create_subscription<tier4_vehicle_msgs::msg::ActuationCommandStamped>(
// "tier4_vehicle_msgs::msg::ActuationCommandStamped", 1, std::bind(&my_vehicle_interface::callback_actuation_cmd, this, _1)); // B型车
// to autoware
battery_charge_pub_ = create_publisher<tier4_vehicle_msgs::msg::BatteryStatus>(
"/vehicle/status/battery_status", rclcpp::QoS{1}); // 电池信息发布者
control_mode_pub_ = create_publisher<autoware_vehicle_msgs::msg::ControlModeReport>(
"/vehicle/status/control_mode", rclcpp::QoS{1}); // 控制模式发布者
gear_status_pub_ = create_publisher<autoware_vehicle_msgs::msg::GearReport>(
"/vehicle/status/gear_status", rclcpp::QoS{1}); // 档位状态发布者
hazard_lights_status_pub_ = create_publisher<autoware_vehicle_msgs::msg::HazardLightsReport>(
"/vehicle/status/hazard_lights_status", rclcpp::QoS{1}); // 危险指示灯状态发布者
steering_status_pub_ = create_publisher<autoware_vehicle_msgs::msg::SteeringReport>(
"/vehicle/status/steering_status", rclcpp::QoS{1}); // 转向状态发布者(方向盘角度等)
turn_indicators_status_pub_ = create_publisher<autoware_vehicle_msgs::msg::TurnIndicatorsReport>(
"/vehicle/status/turn_indicators_status", rclcpp::QoS{1}); // 转向灯状态发布者
velocity_status_pub_ = create_publisher<autoware_vehicle_msgs::msg::VelocityReport>(
"/vehicle/status/velocity_status", rclcpp::QoS{1}); // 速度状态发布者
// pub to vehicle
cmd_vel_pub_ = this->create_publisher<geometry_msgs::msg::Twist>("/cmd_vel", 1);
control_mode_to_vehicle_pub_ = this->create_publisher<std_msgs::msg::UInt8>("/control_mode", 1);
AD103_Pub_ = this->create_publisher<can_analyzer::msg::AD103>("/AD_103",1);
// sub from vehicle
control_mode_sub_ = this->create_subscription<std_msgs::msg::UInt8>(
"/vehicle/control_mode", 1, std::bind(&my_vehicle_interface::callback_current_control_mode, this, _1));
steering_angle_sub_ = this->create_subscription<std_msgs::msg::Float32>(
"/vehicle/steering_angle_cmd", 1, std::bind(&my_vehicle_interface::callback_current_steering_angle, this, std::placeholders::_1));
vehicle_velocity_sub_ = this->create_subscription<std_msgs::msg::Float64>(
"/curent_speed", 1, std::bind(&my_vehicle_interface::callback_vehicle_velocity, this, std::placeholders::_1));
}
void my_vehicle_interface::callback_control_cmd(const autoware_control_msgs::msg::Control::ConstSharedPtr msg)
{
control_cmd_ptr_ = msg;
}
void my_vehicle_interface::callback_gear_cmd(const autoware_vehicle_msgs::msg::GearCommand::ConstSharedPtr msg)
{
gear_cmd_ptr_ = msg;
}
void my_vehicle_interface::callback_gate_mode(const tier4_control_msgs::msg::GateMode::ConstSharedPtr msg)
{
current_gate_mode_ptr_ = msg;
}
void my_vehicle_interface::callback_emergency_cmd(const tier4_vehicle_msgs::msg::VehicleEmergencyStamped::ConstSharedPtr msg)
{
emergency_cmd_ptr_ = msg;
}
void my_vehicle_interface::callback_turn_indicators_cmd(const autoware_vehicle_msgs::msg::TurnIndicatorsCommand::ConstSharedPtr msg)
{
turn_indicators_cmd_ptr_ = msg;
}
void my_vehicle_interface::callback_hazard_lights_cmd(const autoware_vehicle_msgs::msg::HazardLightsCommand::ConstSharedPtr msg)
{
hazard_lights_cmd_ptr_ = msg;
}
void my_vehicle_interface::callback_current_control_mode(const std_msgs::msg::UInt8::ConstSharedPtr msg)
{
current_control_mode_ptr_ = msg;
}
void my_vehicle_interface::callback_current_steering_angle(const std_msgs::msg::Float32::ConstSharedPtr msg)
{
current_steering_angle_ptr_ = msg;
}
void my_vehicle_interface::callback_vehicle_velocity(const std_msgs::msg::Float64::ConstSharedPtr msg)
{
vehicle_velocity_ptr_ = msg;
}
void my_vehicle_interface::imuCallback(const sensor_msgs::msg::Imu::ConstSharedPtr msg)
{
imu_ptr_ = msg;
}
void my_vehicle_interface::to_vehicle()
{
// you should implement this structure according to your own vehicle design
control_command_to_vehicle(control_cmd_ptr_);
current_gate_mode_to_vehicle(current_gate_mode_ptr_);
emergency_command_to_vehicle(emergency_cmd_ptr_);
turn_indicators_command_to_vehicle(turn_indicators_cmd_ptr_);
hazard_lights_command_to_vehicle(hazard_lights_cmd_ptr_);
}
void my_vehicle_interface::to_autoware()
{
rclcpp::Time now = this->get_clock()->now();
// 1.上报车辆档位信息
// 车辆只有前进档、倒车挡和空档,这里直接上报原始控制命令
autoware_vehicle_msgs::msg::GearReport gear_report_msg;
if(gear_cmd_ptr_) // 接收到autoware消息才赋值
{
gear_report_msg.stamp = now;
gear_report_msg.report = gear_cmd_ptr_->command;
}
else
{
gear_report_msg.stamp = now;
gear_report_msg.report = 1; // 空档,起始的时候直接上报空档
}
gear_status_pub_->publish(gear_report_msg);
// 2.上报电池状态
tier4_vehicle_msgs::msg::BatteryStatus battery_report_msg;
battery_report_msg.stamp = now;
battery_report_msg.energy_level = 50.00;
battery_charge_pub_->publish(battery_report_msg);
// 3.上报当前车辆的控制模式
autoware_vehicle_msgs::msg::ControlModeReport control_mode_report_msg;
if(current_control_mode_ptr_) // 接收到autoware消息才赋值
{
control_mode_report_msg.stamp = now;
switch(current_control_mode_ptr_->data)
{
case 2:
control_mode_report_msg.mode = 1;
break;
case 5:
control_mode_report_msg.mode = 4;
break;
default:
control_mode_report_msg.mode = 0;
break;
}
}
else
{
control_mode_report_msg.stamp = now;
control_mode_report_msg.mode = 4; // 人类驾驶
}
control_mode_pub_->publish(control_mode_report_msg);
// 5.上报车辆转向信息
autoware_vehicle_msgs::msg::SteeringReport steering_angle_report_msg;
if(current_steering_angle_ptr_) // 接收到autoware消息才赋值
{
steering_angle_report_msg.stamp = now;
steering_angle_report_msg.steering_tire_angle = current_steering_angle_ptr_->data;
steering_status_pub_->publish(steering_angle_report_msg);
}
else
{
steering_angle_report_msg.stamp = now;
steering_angle_report_msg.steering_tire_angle = 0;
steering_status_pub_->publish(steering_angle_report_msg);
}
// 7.上报车辆的速度
autoware_vehicle_msgs::msg::VelocityReport velocity_report_msg;
velocity_report_msg.header.stamp = now;
velocity_report_msg.header.frame_id = "base_link";
velocity_report_msg.lateral_velocity = 0.0;
if(vehicle_velocity_ptr_) // 接收到autoware消息才赋值
{
velocity_report_msg.longitudinal_velocity = vehicle_velocity_ptr_->data;
if(imu_ptr_) velocity_report_msg.heading_rate = imu_ptr_->angular_velocity.z;
else velocity_report_msg.heading_rate = 0;
}
else
{
velocity_report_msg.longitudinal_velocity = 0.0;
velocity_report_msg.heading_rate = 0.0;
}
velocity_status_pub_->publish(velocity_report_msg);
}
void my_vehicle_interface::control_command_to_vehicle(autoware_control_msgs::msg::Control::ConstSharedPtr control_cmd_ptr_)
{
if (control_cmd_ptr_) // 接收到autoware消息才赋值
{
geometry_msgs::msg::Twist twist_msg;
twist_msg.linear.x = control_cmd_ptr_->longitudinal.velocity;
twist_msg.angular.z = control_cmd_ptr_->lateral.steering_tire_angle;
cmd_vel_pub_->publish(twist_msg);
}
}
void my_vehicle_interface::current_gate_mode_to_vehicle(tier4_control_msgs::msg::GateMode::ConstSharedPtr current_gate_mode_ptr_)
{
if(current_gate_mode_ptr_) // 接收到autoware消息才赋值
{
std_msgs::msg::UInt8 control_mode_msg;
switch(current_gate_mode_ptr_->data)
{
case 0:
control_mode_msg.data = 2;
break;
case 1:
control_mode_msg.data = 5; // 退出自驾
break;
}
control_mode_to_vehicle_pub_->publish(control_mode_msg);
}
}
void my_vehicle_interface::emergency_command_to_vehicle(tier4_vehicle_msgs::msg::VehicleEmergencyStamped::ConstSharedPtr emergency_cmd_ptr_)
{
if(emergency_cmd_ptr_ && emergency_cmd_ptr_->emergency)
{
can_analyzer::msg::AD103 emergency_command_msg;
emergency_command_msg.ad_brakedemand = 1; // 设置为 1 表示紧急模式
AD103_Pub_->publish(emergency_command_msg);
}
}
void my_vehicle_interface::turn_indicators_command_to_vehicle(autoware_vehicle_msgs::msg::TurnIndicatorsCommand::ConstSharedPtr turn_indicators_cmd_ptr_)
{
autoware_vehicle_msgs::msg::TurnIndicatorsReport turn_indicators_msg;
rclcpp::Time now = this->get_clock()->now();
turn_indicators_msg.stamp = now;
if (turn_indicators_cmd_ptr_)
{
switch(turn_indicators_cmd_ptr_->command)
{
case 1: // DISABLE
turn_indicators_msg.report = 1;
break;
case 2: // LEFT
turn_indicators_msg.report = 2;
break;
case 3: // RIGHT
turn_indicators_msg.report = 3;
break;
default:
return;
}
}
else{
turn_indicators_msg.report = 1;
}
turn_indicators_status_pub_->publish(turn_indicators_msg);
}
void my_vehicle_interface::hazard_lights_command_to_vehicle( autoware_vehicle_msgs::msg::HazardLightsCommand::ConstSharedPtr hazard_lights_cmd_ptr_)
{
autoware_vehicle_msgs::msg::HazardLightsReport hazard_lights_msg;
rclcpp::Time now = this->get_clock()->now();
hazard_lights_msg.stamp = now;
if(hazard_lights_cmd_ptr_)
{
switch(hazard_lights_cmd_ptr_->command)
{
case 1: // DISABLE
hazard_lights_msg.report = 1;
break;
case 2: // ENABLE
hazard_lights_msg.report = 2;
break;
default:
return;
}
}
else{
hazard_lights_msg.report = 1; // 失能
}
hazard_lights_status_pub_->publish(hazard_lights_msg);
}
main.cpp
之后main.cpp就是整合调用上面的程序
#include "rclcpp/rclcpp.hpp"
#include "my_vehicle_interface/my_vehicle_interface.hpp"
int main(int argc, char **argv)
{
rclcpp::init(argc, argv);
auto node = std::make_shared<my_vehicle_interface>();
// 使用多线程执行器保证回调和周期性任务都能执行
rclcpp::executors::MultiThreadedExecutor executor;
executor.add_node(node);
// 定时器:周期调用 to_vehicle() 和 to_autoware()
auto timer = node->create_wall_timer(
std::chrono::milliseconds(50), // 20Hz
[node]() {
node->to_vehicle(); // 将 Autoware 的指令发送给车辆
node->to_autoware(); // 将车辆状态回传给 Autoware
}
);
executor.spin();
rclcpp::shutdown();
return 0;
}
CMakeLists.txt文件
package.xml文件也要添加相应的包,这里不添加了。
cmake_minimum_required(VERSION 3.8)
project(my_vehicle_interface)
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
# 找依赖包
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)
find_package(geometry_msgs REQUIRED)
find_package(can_analyzer REQUIRED)
find_package(autoware_auto_control_msgs REQUIRED)
find_package(autoware_auto_vehicle_msgs REQUIRED)
find_package(tier4_control_msgs REQUIRED)
find_package(tier4_vehicle_msgs REQUIRED)
find_package(autoware_vehicle_msgs REQUIRED)
find_package(sensor_msgs REQUIRED)
find_package(autoware_control_msgs REQUIRED)
# 头文件路径
include_directories(
include
)
# 可执行文件
add_executable(vehicle_interface_node
src/main.cpp
src/my_vehicle_interface.cpp
)
# 链接依赖
ament_target_dependencies(vehicle_interface_node
rclcpp
ament_index_cpp
std_msgs
geometry_msgs
can_analyzer
autoware_auto_control_msgs
autoware_auto_vehicle_msgs
autoware_vehicle_msgs
tier4_control_msgs
tier4_vehicle_msgs
sensor_msgs
autoware_control_msgs
)
# 安装可执行文件
install(TARGETS
vehicle_interface_node
DESTINATION lib/${PROJECT_NAME}
)
# 安装头文件(如果你在 include 目录下有类声明)
install(DIRECTORY include/
DESTINATION include/
)
# 安装launch文件
install(
DIRECTORY launch
DESTINATION share/${PROJECT_NAME}/
)
if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
set(ament_cmake_copyright_FOUND TRUE)
set(ament_cmake_cpplint_FOUND TRUE)
ament_lint_auto_find_test_dependencies()
endif()
ament_package()
my_vehicle_interface.launch.xml
该文件在launch文件下,其中remap可将节点下的话题名替换为其他的,有需求可以修改。
<launch>
<node pkg="my_vehicle_interface" exec="my_vehicle_interface_node" name="my_vehicle_interface" output="screen">
<!– 控制命令话题 –>
<!– <remap from="/control/command/ackermann_control_command" to="/ackermann_cmd"/>
<remap from="/control/current_gate_mode" to="/gate_mode"/>
<remap from="/control/emergency_cmd" to="/emergency_stop"/>
<remap from="/vehicle/gear_cmd" to="/gear_command"/>
<remap from="/cmd_vel" to="/cmd_vel_stamped"/> –>
</node>
</launch>
my_vehicle_launch/launch/vehicle_interface.launch.xml
<?xml version="1.0" encoding="UTF-8"?>
<launch>
<arg name="vehicle_id" default="$(env VEHICLE_ID default)"/>
<include file="$(find-pkg-share my_vehicle_interface)/launch/my_vehicle_interface.launch.xml">
</include>
</launch>
autoware消息类型
下面是新的消息包!其实和老的相差不大。
具体可以查看https://github.com/tier4/autoware_auto_msgs/tree/tier4/main/autoware_auto_vehicle_msgs和https://github.com/tier4/tier4_autoware_msgs/blob/tier4/universe/tier4_control_msgs/msg/GateMode.msg,下面给出部分消息具体内容。
autoware_auto_control_msgs::msg::AckermannControlCommand
结构体定义如下,AckermannLateralCommand为横向控制命令,LongitudinalCommand为纵向控制命令。
struct AckermannControlCommand {
builtin_interfaces::msg::Time stamp;
autoware_auto_control_msgs::msg::AckermannLateralCommand lateral;
autoware_auto_control_msgs::msg::LongitudinalCommand longitudinal;
};
AckermannLateralCommand内容如下,stamp为时间戳,steering_tire_angle为目标转向轮的转角(rad),steering_tire_rotation_rate为期望的转向角速度(rad/s):
#include "builtin_interfaces/msg/Time.idl"
module autoware_auto_control_msgs {
module msg {
struct AckermannLateralCommand {
builtin_interfaces::msg::Time stamp;
float steering_tire_angle;
float steering_tire_rotation_rate;
};
};
};
LongitudinalCommand内容如下,stamp为时间戳,speed为速度(m/s),acceleration加速度(m/s^2),目标加加速度(m/s^3):
#include "builtin_interfaces/msg/Time.idl"
module autoware_auto_control_msgs {
module msg {
struct LongitudinalCommand {
builtin_interfaces::msg::Time stamp;
float speed;
float acceleration;
float jerk;
};
};
};
autoware_auto_vehicle_msgs::msg::GearCommand
GearCommand_Constants为档位常量,0为无效档位,1为空档,2为前进档, 2-18更具体的前进档位(用于某些带高级自动变速器的车型?),20倒档,21第二倒档,22驻车档位,23低速档,24第二低速档。
GearCommand中的command为设置的档位。
#include "builtin_interfaces/msg/Time.idl"
module autoware_auto_vehicle_msgs {
module msg {
module GearCommand_Constants {
const uint8 NONE = 0;
const uint8 NEUTRAL = 1;
const uint8 DRIVE = 2;
const uint8 DRIVE_2 = 3;
const uint8 DRIVE_3 = 4;
const uint8 DRIVE_4 = 5;
const uint8 DRIVE_5 = 6;
const uint8 DRIVE_6 = 7;
const uint8 DRIVE_7 = 8;
const uint8 DRIVE_8 = 9;
const uint8 DRIVE_9 = 10;
const uint8 DRIVE_10 = 11;
const uint8 DRIVE_11 = 12;
const uint8 DRIVE_12 = 13;
const uint8 DRIVE_13 = 14;
const uint8 DRIVE_14 = 15;
const uint8 DRIVE_15 = 16;
const uint8 DRIVE_16 = 17;
const uint8 DRIVE_17 = 18;
const uint8 DRIVE_18 = 19;
const uint8 REVERSE = 20;
const uint8 REVERSE_2 = 21;
const uint8 PARK = 22;
const uint8 LOW = 23;
const uint8 LOW_2 = 24;
};
struct GearCommand {
builtin_interfaces::msg::Time stamp;
@default (value=0)
uint8 command;
};
};
};
tier4_control_msgs::msg::GateMode
内容如下,AUTO代表Autoware 自动驾驶控制,EXTERNAL为外部接管控制(退出自驾),data为具体的控制方式:
uint8 AUTO = 0
uint8 EXTERNAL = 1
uint8 data
tier4_vehicle_msgs::msg::VehicleEmergencyStamped
是否是紧急状况,emergency字段是否为紧急状况。
builtin_interfaces/Time stamp
bool emergency
autoware_auto_vehicle_msgs::msg::TurnIndicatorsCommand
内容如下, TurnIndicatorsCommand_Constants列举了转向灯的枚举常量,0代表不做操作,1代表关闭所有转向灯,2使能坐转向灯,3使能右转向灯。command为具体的值,代表相应的操作。
#include "builtin_interfaces/msg/Time.idl"
module autoware_auto_vehicle_msgs {
module msg {
module TurnIndicatorsCommand_Constants {
const uint8 NO_COMMAND = 0;
const uint8 DISABLE = 1;
const uint8 ENABLE_LEFT = 2;
const uint8 ENABLE_RIGHT = 3;
};
struct TurnIndicatorsCommand {
builtin_interfaces::msg::Time stamp;
@default (value=0)
uint8 command;
};
};
};
autoware_auto_vehicle_msgs::msg::HazardLightsCommand
文件内容如下,HazardLightsCommand_Constants列举了危险指示灯的常量,0代表不做操作,1代表关闭危险指示灯,2代表打开危险指示灯。command为具体的值,代表相应的操作。
#include "builtin_interfaces/msg/Time.idl"
module autoware_auto_vehicle_msgs {
module msg {
module HazardLightsCommand_Constants {
const uint8 NO_COMMAND = 0;
const uint8 DISABLE = 1;
const uint8 ENABLE = 2;
};
struct HazardLightsCommand {
builtin_interfaces::msg::Time stamp;
@default (value=0)
uint8 command;
};
};
};
tier4_vehicle_msgs::msg::BatteryStatus
文件内容如下,energy_level代表电池容量(百分比?)。
builtin_interfaces/Time stamp
float32 energy_level
autoware_auto_vehicle_msgs::msg::ControlModeReport
文件内容如下, ControlModeReport_Constants列举了控制模式的常量(车辆发送到autoware的),0为无命令,1为全自动驾驶模式,2为仅自动转向,3为仅自动控制速度,4为手动模式(人类驾驶),5为系统未介入(脱离状态),6系统尚未准备好进入控制状态。ControlModeReport为车辆上报给autoware的具体操作。
#include "builtin_interfaces/msg/Time.idl"
module autoware_auto_vehicle_msgs {
module msg {
module ControlModeReport_Constants {
const uint8 NO_COMMAND = 0;
const uint8 AUTONOMOUS = 1;
const uint8 AUTONOMOUS_STEER_ONLY = 2;
const uint8 AUTONOMOUS_VELOCITY_ONLY = 3;
const uint8 MANUAL = 4;
const uint8 DISENGAGED = 5;
const uint8 NOT_READY = 6;
};
struct ControlModeReport {
builtin_interfaces::msg::Time stamp;
@default (value=0)
uint8 mode;
};
};
};
autoware_auto_vehicle_msgs::msg::GearReport
文件内容如下,GearReport_Constants为档位常量,0为未设置或未知档位,1代表空档,2代表前进档,3-19自动或手动前进的第2-第18挡,20为倒档,21第二倒档(其他车辆?),22为停车档,23为低速档,24为第二低速档。GearReport为车辆发送给autoware的具体指令。
#include "builtin_interfaces/msg/Time.idl"
module autoware_auto_vehicle_msgs {
module msg {
module GearReport_Constants {
const uint8 NONE = 0;
const uint8 NEUTRAL = 1;
const uint8 DRIVE = 2;
const uint8 DRIVE_2 = 3;
const uint8 DRIVE_3 = 4;
const uint8 DRIVE_4 = 5;
const uint8 DRIVE_5 = 6;
const uint8 DRIVE_6 = 7;
const uint8 DRIVE_7 = 8;
const uint8 DRIVE_8 = 9;
const uint8 DRIVE_9 = 10;
const uint8 DRIVE_10 = 11;
const uint8 DRIVE_11 = 12;
const uint8 DRIVE_12 = 13;
const uint8 DRIVE_13 = 14;
const uint8 DRIVE_14 = 15;
const uint8 DRIVE_15 = 16;
const uint8 DRIVE_16 = 17;
const uint8 DRIVE_17 = 18;
const uint8 DRIVE_18 = 19;
const uint8 REVERSE = 20;
const uint8 REVERSE_2 = 21;
const uint8 PARK = 22;
const uint8 LOW = 23;
const uint8 LOW_2 = 24;
};
struct GearReport {
builtin_interfaces::msg::Time stamp;
@default (value=0)
uint8 report;
};
};
};
autoware_auto_vehicle_msgs::msg::HazardLightsReport
文件内容如下,HazardLightsReport_Constants代表上报的危险指示灯常量,0为关闭,1为开启。report为上报的具体值。
#include "builtin_interfaces/msg/Time.idl"
module autoware_auto_vehicle_msgs {
module msg {
module HazardLightsReport_Constants {
const uint8 DISABLE = 1;
const uint8 ENABLE = 2;
};
struct HazardLightsReport {
builtin_interfaces::msg::Time stamp;
@default (value=0)
uint8 report;
};
};
};
autoware_auto_vehicle_msgs::msg::SteeringReport
文件内容如下,steering_tire_angle为车辆需要上报给autoware的车辆的转向角(rad)。
#include "builtin_interfaces/msg/Time.idl"
module autoware_auto_vehicle_msgs {
module msg {
@verbatim (language="comment", text=
" SteeringReport.msg")
struct SteeringReport {
builtin_interfaces::msg::Time stamp;
@verbatim (language="comment", text=
" Desired angle of the steering tire in radians left (positive)"
" or right (negative) of center (0.0)")
@default (value=0.0)
float steering_tire_angle;
};
};
};
autoware_auto_vehicle_msgs::msg::TurnIndicatorsReport
TurnIndicatorsReport_Constants为上报提示灯常量,1为关闭,2为左转向灯已开启,3为右转向灯已开启。report为上报具体的值。
#include "builtin_interfaces/msg/Time.idl"
module autoware_auto_vehicle_msgs {
module msg {
module TurnIndicatorsReport_Constants {
const uint8 DISABLE = 1;
const uint8 ENABLE_LEFT = 2;
const uint8 ENABLE_RIGHT = 3;
};
struct TurnIndicatorsReport {
builtin_interfaces::msg::Time stamp;
@default (value=0)
uint8 report;
};
};
};
autoware_auto_vehicle_msgs::msg::VelocityReport
文件内容如下,longitudinal_velocity为纵向速度(m/s),lateral_velocity为横向速度(即垂直于车头方向的速度,通常在侧风、打滑等情况下出现),heading_rate航向角变化率(即车辆偏航角的变化速度,通常是车辆绕 Z 轴的角速度,rad/s)
#include "std_msgs/msg/Header.idl"
module autoware_auto_vehicle_msgs {
module msg {
@verbatim (language="comment", text=
" VelocityReport.msg")
struct VelocityReport {
std_msgs::msg::Header header;
@default (value=0.0)
float longitudinal_velocity;
@default (value=0.0)
float lateral_velocity;
@default (value=0.0)
float heading_rate;
};
};
};
3.3.2车辆控制
这一步骤主要是实现车辆的纵向和横向控制,还有档位、转向灯等其他情况。整个包放到autoware/src/launcher/autoware_launch/vehicle目录下。实现自己车辆的线控功能,这里是通过my vehicle interface发布话题给该节点,该节点进而控制车辆,不是直接通过my vehicle interface控制车辆的。
还有一点是一般实现线控需要走CAN,本电脑下CAN被转为USB,所以这里只需要把对应的USB设备挂载到容器内就可以实现线控功能。挂载有两种方式:
然后进入容器内还要赋予权限:sudo chmod 666 /dev/ttyxxx
3.4编译
到这里大部分工作已经完成,接下来先编译自己的包。
colcon build –symlink-install –cmake-args -DCMAKE_BUILD_TYPE=Release –packages-select my_vehicle_interface my_vehicle_launch my_vehicle_description my_common_sensor_launch my_sensor_kit_launch
先跑一下
# 官方上教程为,实际上autoware_launch中无planning.launch.xml
ros2 launch autoware_launch planning.launch.xml map_path:=$HOME/autoware_map/sample-map-planning vehicle_model:=<YOUR-VEHICLE-NAME> sensor_model:=<YOUR-VEHICLE-NAME>_sensor_kit
# 使用下面可以进行
ros2 launch autoware_launch planning_simulator.launch.xml map_path:=../autoware_map/sample-map-planning/ vehicle_model:=my_vehicle sensor_model:=my_sensor_kit vehicle_id:=my_vehicle
4.绘制地图
本节参考教程:Available Open Source SLAM – Autoware Documentation
4.1建图
这里根据自己需求和传感器类型找开源的SLAM算法即可。这里使用的是 lidarslam-ros2,支持Lidar和IMU(可选)。这里以这个为例。
4.1.1调试LidarSlam-Ros2
在autoware目录下新建gmapping文件夹,然后在该文件下新建gmapping_ros2_ws/src文件夹,终端切换到gmapping_ros2_ws/src文件夹下执行
## 不再容器内执行也可以
git clone –recursive https://github.com/rsasaki0109/lidarslam_ros2
## 在容器内执行
cd gmapping_ros2_ws
rosdep update
sudo apt update
rosdep install –from-paths src –ignore-src -r -y
报错:rosdep update报老错误了,可以使用rosdepc工具,也可以使用其他方法。这里科学上网,打开vpn软件虚拟网卡功能即可(这里不能使用–network=host)
没有问题开始编译和测试:
colcon build –symlink-install –cmake-args -DCMAKE_BUILD_TYPE=Release
4.1.2测试
使用这个文件进行测试:
ros2 launch lidarslam lidarslam.launch.py
启动之前需要打开文件修改配置参数:
## 第一处修改remappings处,/input_cloud后面的话题需要修改为激光雷达的话题
mapping = launch_ros.actions.Node(
package='scanmatcher',
executable='scanmatcher_node',
parameters=[main_param_dir],
remappings=[('/input_cloud','/lidar/top/aw_points')],
output='screen'
)
## 第二处需要修改的地方是arguments处,需要标定base_link和雷达坐标系的变换关系
tf = launch_ros.actions.Node(
package='tf2_ros',
executable='static_transform_publisher',
arguments=['0','0.577','1.334','0','0','0.70710678','0.70710678','base_link','velodyne_top_base_link']
)
后面可以进一步修改这里面参数lidarslam/param/lidarslam.yaml。配置完成后:
ros2 launch lidarslam lidarslam.launch.py
然后播放bag包或者启动激光雷达驱动。
保存地图使用下面的命令,会保存在路径工具空间路径下gmapping_ros2_ws:
ros2 service call /map_save std_srvs/Empty
4.1.3使用imu
lidarslam/param/lidarslam.yaml中的use_imu设置为true,然后打开文件scanmatcher/src/scanmatcher_component.cpp,定位到imu_sub_可以看到订阅的imu话题,这里不需要在这里进行修改:
直接打开lidarslam.launch.py文件,将话题重映射:
mapping = launch_ros.actions.Node(
package='scanmatcher',
executable='scanmatcher_node',
parameters=[main_param_dir],
remappings=[('/input_cloud','/lidar/top/aw_points'),
('/imu','sensing/imu/data')],
output='screen'
)
然后重复执行上面的操作即可。
4.1.4保存环境
之前启动容器的时候使用–rm 参数退出了,容器里面新安装的包直接被带走了,下次启动的时候还需要重新安装,为了不重复进行操作,保存一下。
使用docker ps -a查看容器:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
82c230e49c01 my_autoware_image:use-gpu "/ros_entrypoint.sh …" 5 hours ago Up 5 hours upbeat_rhodes
使用docker commit 82c230e49c01 my_autoware_universe_devel_cuda:20250904 保存一个新的环境,下次加载这个即可。
4.2创建矢量地图
参考:Creating a vector map – Autoware Documentation
下面使用TIER IV 的矢量地图生成器,可以在浏览器上运行,不需要安装。如果是第一次使用该工具,请创建一个 TIER IV 帐户以使用矢量地图生成器工具。进入之后点击下方的Try it。
按照官方教程绘制矢量地图,下载osm文件,然后将点云地图的pcd文件和下载的osm文件放到一个文件夹下(一般autoware_map下新建立一个文件夹)。
5.启动Autoware
本节参考官方教程,详细见:Launch Autoware – Autoware Documentation
前面的工作做完且没有问题,到这里就是配置启动项了。打开配置文件,进行修改:
autoware/src/launcher/autoware_launch/autoware_launch/launch/autoware.launch.xml
防止报错,这里修改一些配置:
# 第一处是放入到挂载容器内的地址,否则该文件不会同步到本地
<arg name="data_path" default="$(env HOME)/autoware_main_ws/autoware/autoware_data" description="packages data and artifacts directory path"/>
# 第二出是添加参数pointcloud_container_name,否则会报错
<group if="$(var launch_sensing)">
<include file="$(find-pkg-share autoware_launch)/launch/components/tier4_sensing_component.launch.xml">
<arg name="pointcloud_container_name" value="$(var pointcloud_container_name)"/>
</include>
</group>
这里先不启用感知、规划和控制,先启动一下:
ros2 launch autoware_launch autoware.launch.xml vehicle_model:=my_vehicle sensor_model:=my_sensor_kit map_path:=../autoware_map/sample-map-planning/ \\
launch_perception:=false \\
launch_planning:=false \\
launch_control:=false
5.1加载自己的地图
进入容器:
docker run –rm -it –privileged –gpus all –network=host -v /home/hong/autoware_main_ws/autoware:/home/hong/autoware_main_ws/autoware -v /home/hong/autoware_main_ws/autoware_map:/home/hong/autoware_main_ws/autoware_map -e DISPLAY -e TERM -e QT_X11_NO_MITSHM=1 -e XAUTHORITY=/tmp/.dockerjv9fz9pb.xauth -v /tmp/.dockerjv9fz9pb.xauth:/tmp/.dockerjv9fz9pb.xauth -v /tmp/.X11-unix:/tmp/.X11-unix -v /etc/localtime:/etc/localtime:ro my_autoware_universe_devel_cuda:20250904
修改为自己地图的名字。
# 定位到下面
<arg name="lanelet2_map_file" default="lanelet2_map.osm" description="lanelet2 map file name"/>
<arg name="pointcloud_map_file" default="pointcloud_map.pcd" description="pointcloud map file name"/>
# 修改为自己的文件名,不需要加入路径,只要文件名
<arg name="lanelet2_map_file" default="<YOUR-LANELET-MAP-NAME>.osm" description="lanelet2 map file name"/>
<arg name="pointcloud_map_file" default="<YOUR-POINTCLOUD-MAP-NAME>.pcd" description="pointcloud map file name"/>
# eg
<arg name="lanelet2_map_file" default="my_lab_map.osm" description="lanelet2 map file name"/>
<arg name="pointcloud_map_file" default="map.pcd" description="pointcloud map file name"/>
5.2测试定位
5.2.1启动配置
启动autoware,下面修改了传入的map_path,禁用了感知、规划和控制模块,其他配置默认保持不变:
ros2 launch autoware_launch autoware.launch.xml vehicle_model:=my_vehicle sensor_model:=my_sensor_kit map_path:=../autoware_map/my-labs-map launch_perception:=false launch_planning:=false launch_control:=false
因为我这里就是用lidar和imu进行定位,所以上面的启动命令可能会有一些问题,这里添加了更加详细的参数。关于位置初始化的配置文件可以查看:
autoware/src/launcher/autoware_launch/autoware_launch/config/localization/pose_initializer.param.yaml
但是该文件中的类似于$(var gnss_enabled)这样的参数是从哪里加载进来的不知道,干脆在启动的时候手动设置加载进来吧,启动命令如下:
ros2 launch autoware_launch autoware.launch.xml vehicle_model:=my_vehicle sensor_model:=my_sensor_kit map_path:=../autoware_map/my-labs-map launch_perception:=false launch_planning:=false launch_control:=false ekf_enabled:=true gnss_enabled:=false yabloc_enabled:=false ndt_enabled:=true
5.2.2测试初始定位
启动后可以点击图像中的红框位置在地图上进行初始化,ndt算法会自动根据点云进行匹配,正确初始化:

但是这里可能可能会初始化不成功的问题,下面一步步排查。首先一定确定/sensing/lidar/concatenated/pointcloud话题有数据输出,才能进行下一步。
到autoware/src/universe/autoware_universe/launch/tier4_localization_launch目录下查看下面一张关于定位模块的图,下面的文件都在改目录下:

最主要的是上面的文件会调用pose_twist_estimator.launch.xml文件,然后该文件为调用util.launch.xml文件,打开util.launch.xml文件,可以看到:
<remap from="input" to="$(var input_pointcloud)"/>
input_pointcloud是我们在autoware_launch下的tier4_localization_component.launch.xml文件中设置的,为/sensing/lidar/concatenated/pointcloud。这里不是直接使用这个点云消息进行定位的,还会经过util.launch.xml文件处理三次才会得到最终定位使用的点云数据。
这三个算法的配置文件在autoware_launch/config/localization/ndt_scan_matcher下,可以根据util.launch.xml中的param对应的名称查看具体文件配置。下面说一下初始定位会出现的问题,为什么定位不到或者的定位到的位置乱飘。原因出现在:
<composable_node pkg="autoware_pointcloud_preprocessor" plugin="autoware::pointcloud_preprocessor::VoxelGridDownsampleFilterComponent" name="voxel_grid_downsample_filter">
<param from="$(var ndt_scan_matcher/pointcloud_preprocessor/voxel_grid_downsample_filter_param_path)"/>
<remap from="input" to="measurement_range/pointcloud"/>
<remap from="output" to="voxel_grid_downsample/pointcloud"/>
<extra_arg name="use_intra_process_comms" value="$(var use_intra_process)"/>
</composable_node>
因为这里错误的参数设置,导致经过这一步处理之后点云数量就剩下几十个了,肯定效果会很差,这里输出的点云话题为/localization/util/voxel_grid_downsample/pointcloud。解决这个问题定位到配置文件:
autoware/src/launcher/autoware_launch/autoware_launch/config/localization/ndt_scan_matcher/pointcloud_preprocessor/voxel_grid_filter.param.yaml
将里面的voxel_size改小,改为1.0左右或者更小。最后会经过一道下采样的工序,输出的点云话题名为:/localization/util/downsample/pointcloud,这个话题才是用来进行匹配定位的。
还有这里使用ndt进行定位,ndt的配置文件在,可以修改参数,得到更好的定位结果:
autoware/src/launcher/autoware_launch/autoware_launch/config/localization/ndt_scan_matcher/ndt_scan_matcher.param.yaml
5.2.3实时定位
实时定位需要一个车辆的速度消息,但是这个话题/localization/kinematic_state在车辆移动的时候输出为0,显然不对。这个为0,/sensing/vehicle_velocity_converter/twist_with_covariance话题肯定也是0,先而意见是自己的vehicle_interface没有向/vehicle/status/velocity_status话题发送车辆的速度消息,发布成功就可以实时定位了。
5.3整体调试
5.3.1下载Perception相关模型文件
后续的Planning和Perception相挂钩,所以Perception设置为false后面会有点问题。Perception需要一些模型文件,之前没有使用ansible 进行自动软件安装,所以这里需要手动下载。具体可以参考:https://github.com/autowarefoundation/autoware/tree/main/ansible/roles/artifacts
下面操作均在容器内进行!
sudo apt-get purge ansible
# Install pipx
sudo apt-get -y update
sudo apt-get -y install pipx
# Add pipx to the system PATH
python3 -m pipx ensurepath
# Install ansible
pipx install –include-deps –force "ansible==6.*"
# 上面慢的话使用
pipx install –include-deps –force "ansible==6.*" –pip-args="–index-url https://pypi.tuna.tsinghua.edu.cn/simple"
下载项目
cd ~/autoware # The root directory of the cloned repository
ansible-galaxy collection install -f -r "ansible-galaxy-requirements.yaml"
# 找不到ansible-galaxy的
source ~/.bashrc
playbook
ansible-playbook autoware.dev_env.download_artifacts -e "data_dir=$HOME/autoware_data" –ask-become-pass
5.3.2启动调试
这里在autoware.launch.xml感知模块设置了如下参数,使用lidar感知和不开启交通灯的识别:
<arg name="perception_mode" default="lidar" description="select perception mode. camera_lidar_radar_fusion, camera_lidar_fusion, lidar_radar_fusion, lidar, radar"/>
<arg name="traffic_light_recognition/use_high_accuracy_detection" default="false" description="enable to use high accuracy detection for traffic light recognition"/>
执行:
ros2 launch autoware_launch autoware.launch.xml vehicle_model:=my_vehicle sensor_model:=my_sensor_kit map_path:=../autoware_map/my-labs-map ekf_enabled:=true gnss_enabled:=false yabloc_enabled:=false ndt_enabled:=true




