欢迎光临
我们一直在努力

一文实现Docker+Autoware.universe实车部署

        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启动,简单说明该文件的启动流程:

  • 首先lidar.launch.xml首先会调用my_common_sensor_launch下velodyne_VLP16.launch.xml文件
  • velodyne_VLP16.launch.xml文件会调用同级目录下的nebula_node_container.launch.py
  • nebula_node_container.launch.py负责加载相关参数,启动雷达驱动,加载车辆的相关参数,以及进行点云的预处理。
  •         然后修改错误的地方:

  • lidar.launch.xml下删除(或者改成浮点型即可) <arg name="max_range" value="360"/>
  • nebula_node_container.launch.py增加内容 ## 第一处
    # 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")

  • 之后就是根据自己激光雷达参数修改配合,涉及lidar.launch.xml文件和nebula_node_container.launch.py文件,其中py文件中只需要着重修改add_launch_arg("return_mode", "Dual", description="return_mode")处。不清楚参数的可以参考nebula下默认的配置文件。
  •         修改完之后执行命令查看是否能够成功启动激光雷达,其中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文件功能也在这里介绍一下:

  • 裁剪掉车辆镜像区域以外的点云,根据上面传入的车辆镜子信息
  • 根据 IMU/速度数据纠正点云运动畸变
  • 滤除离群点(噪声)
  • 其他功能,可以打开文件自行查看
  •         这时候可以发现,只有一个话题下有激光雷达数据,其他话题下是没有数据的,而且终端下会报错没有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设备挂载到容器内,有两种方式:

  • 使用–privileged,所有的设备都挂载到容器下 docker run –rm -it –privileged –gpus all –network=host -v xxx
  • 使用–device=/dev/ttyUSB0,只有该设备 docker run –rm -it –gpus all –network=host –device=/dev/ttyUSB0 -v
  •         然后进入容器内还要赋予权限: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设备挂载到容器内就可以实现线控功能。挂载有两种方式:

  • 使用–privileged,所有的设备都挂载到容器下 docker run –rm -it –privileged –gpus all –network=host -v xxx
  • 使用–device=/dev/ttyUSB0,只有该设备 docker run –rm -it –gpus all –network=host –device=/dev/ttyUSBxxx -v
  •         然后进入容器内还要赋予权限: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

            下面操作均在容器内进行!

  •  Ansible 安装 # Remove apt installed ansible (In Ubuntu 22.04, ansible the version is old)
    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

     

    赞(0)
    未经允许不得转载:171主机测评 » 一文实现Docker+Autoware.universe实车部署
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址