跳转至

第三方中间件 ROS

注意

文件在【SimOne 安装路径】\SimOneAPI\ROS

SimOne 提供 ROS(Robot Operating System)中间件支持,通过 ROS API 将 SimOne 仿真平台与 ROS 生态连接,实现传感器数据的发布与车辆控制指令的订阅。开发者可在 ROS 环境中直接接收仿真场景的 GPS、真值、雷达等传感器数据,并通过 ROS Topic 下发控制指令驱动仿真主车,适用于基于 ROS 的自动驾驶算法开发与验证场景。

1. 数据交互流程概述

(1)获取仿真感知数据:ROS API 负责从 SimOne 仿真平台中收集感知数据。

(2)数据转换:收集到的仿真感知数据被转换成标准的 ROS 消息格式。

(3)发布消息到 ROS Topic:转换后的数据被发布到指定的 ROS 话题(Topic)上供进一步使用。

(4)驾驶算法订阅处理:驾驶算法模块订阅这些话题,并将数据传入规控/感知模块,进行算法训练和处理。

(5)发布控制消息:算法处理后,生成的控制消息被发布到仿真主车控制话题上。

(6)通信闭环完成控制:ROS API 捕捉这些控制消息,并将其作为参数传入 SimOne 仿真主车的动力学 Node。这样便驱动了仿真场景中主车的具体行动。

(7)算法验证和调试:驾驶算法可以选择订阅特定的话题消息来验证局部模块的准确性。此外,通过在 ROS 环境中建立一个闭环系统,可以实现对驾驶算法整体性能的综合验证。

2. 数据通信流程简图

3. 软件环境

  • 编程语言:C++

4. 编译环境

4.1 ROS Msg 消息转换

将 SimOne API 数据格式转为 ROS 可发布/订阅的标准数据格式。

(1)编写 ROS msg 消息文件

根据 SimOne API 接口数据,参照 C++数据类型与 ROS msg 数据类型的映射关系,编写对应的 ROS API msg 消息文件。

(2)创建 ROS 工程 Workspace

执行以下命令创建 ROS 工程 Workspace:

  mkdir -p ~/catkin_ws/src
  cd ~/catkin_ws/src
  catkin_init_workspace

(3)创建功能包

执行以下命令

  cd ~/catkin_ws/src
  catkin_create_pkg <package_name> roscpp std_msgs

(4)添加 msg 消息文件至功能包

将编写好的 msg 消息文件放入功能包的指定目录下:

  mkdir ~/catkin_ws/src/<package_name>/msg

(5)添加功能依赖

~/catkin_ws/src/<package_name>/package.xml 文件中添加功能依赖:

  <build_depend>message_generation</build_depend>
  <exec_depend>message_runtime</exec_depend>

(6)修改 CMakeLists.txt

~/catkin_ws/src/<package_name>/CMakeLists.txt 文件中添加必要的编译选项,以支持自定义消息的编译。

find_package(catkin REQUIRED COMPONENTS
    roscpp
    std_msgs
    message_generation
)

add_message_files(FILES
    <ROS API msg 消息文件 .msg>
)

generate_messages(DEPENDENCIES
    std_msgs
)

catkin_package(
    CATKIN_DEPENDS roscpp std_msgs message_runtime
)

(7)编译功能包

返回到 catkin_ws 目录并编译功能包:

  cd ~/catkin_ws
  catkin_make

(8)检查 C++头文件

检查 msg 文件生成的 C++头文件是否位于以下目录中:

  ~/catkin_ws/devel/include/<package_name>/

(9)将生成的 C++头文件整合至 ROS API 工程

将第(8)步生成的 C++头文件融入 ROS API 工程,以便在工程中使用自定义消息。

4.2 ROS 接口示例工程目录结构

ROS/
├── CMakeLists.txt   # 工程编译脚本
├── gen_make_debug.sh  # debug 工程编译环境生成脚本
├── gen_make_release.sh  # release 工程编译环境生成脚本
├── include     # 工程头文件
├── lib      # 工程依赖库
├── run      # ROS API 节点可执行程序生成目录
└── src      # ROS API 工程源文件

4.3 ROS 接口示例工程编译

编译 ROS 接口工程需要完成以下几个步骤:

(1)执行编译脚本

  • 对于调试模式,运行 gen_make_debug.sh 脚本。
  • 对于发布模式,运行 gen_make_release.sh 脚本。

(2)进入构建目录

  • 对于调试构建,请使用 cd build_debug 命令进入构建目录。
  • 对于发布构建,请使用 cd build_release 命令进入构建目录。

(3)开始构建项目

  • 在命令行中输入 make 命令开始构建过程。构建完成后,ROS API 节点程序将生成在 run/trans_node_ros 路径下。

5. 运行 ROS 通信节点

要启动并运行 ROS API 节点,请按照以下步骤操作:

(1)启动 ROS 核心

  • 在命令行中输入 roscore 来启动 ROS 核心服务。

(2)执行 ROS API 节点程序

  • 运行 trans_node_ros 程序。程序启动时,将根据 config.ini 配置文件中的参数来初始化设置。

  • config.ini 运行参数配置文件

[BridgeIO] Sim-One API 客户端连接设置
BridgeIO_IP=10.66.9.111 SimOne BridgeIO 节点 IP

[HostVehicle] Sim-One 仿真主车设置
Vehicle_ID=0 Sim-One 仿真主车 ID

[Sensor] Sim-One 传感器通信配置
IMG_IP=10.66.9.244 图像数据 UDP 接收端 IP
IMG_PORT=13944 图像数据 UDP 接收端 Port
PCD_IP=10.66.9.244 点云数据 UDP 接收端 IP
PCD_PORT=6699 点云数据 UDP 接收端 Port
PCD_PORT_INFO=7788 点云数据 UDP 接收端 InfoPort

[ROS] ROS 消息配置
GPS_Topic=/gps Gps 消息发布 Topic
GroundTruth_Topic=/ground_truth 感知物体真值消息发布 Topic
Image_Topic=/image 图像数据消息发布 Topic
PointCloud_Topic=/point_cloud 点云数据消息发布 Topic
Radar_Topic=/radar_detection 毫米波雷达数据消息发布 Topic
Sensor_Topic=/sensor_detection 目标及传感器真值数据消息发布 Topic
LaneInfo_Topic=/lane_info 感知车道/车道线数据消息发布 Topic
CTL_Topic=/control主车控制(油门/刹车/方向)数据消息订阅 Topic
POSE_CTL_Topic=/pose_control主车控制(离散点)数据消息订阅 Topic

6. 消息验证

6.1 运行 SimOne 测试 用例

(1)SimOne 仿真端操作

  • 启动 SimOne,新建测试 用例
  • 新建测试 主车(加载相关传感器,配置控制系统为 手动 / API 控制)
  • 运行 用例(选择 主车)

(2)ROS 端操作

  • 运行 SimOne ROS Bridge
  • 启动第三方算法

6.2 图像数据验证

  • 使用 rviz 可视化工具订阅 Image Topic,以显示实时的图像数据。

6.3 点云数据验证

  • 通过 rviz 订阅 PointCloud Topic,观察点云图像的实时更新。

6.4 结构化数据验证

  • 执行以下命令来配置环境并监听 ROS 话题以输出实时数据:
  source ~/catkin_ws/devel/setup.bash
  rostopic echo /gps

7. 接口

(1)通过 Sim-One API 获取仿真感知数据

主车 Gps 消息回调
bool SetGpsUpdateCB(void(*cb)(const char* mainVehicleId, SimOne_Data_Gps *pGps));
获取仿真感知物体真值回调
bool SetGroundTruthUpdateCB(void(*cb)(const char* mainVehicleId, SimOne_Data_Obstacle *pObstacle));
获取摄像头图像数据回调
bool SetStreamingImageUpdateCB(const  char* ip, unsigned short port, void(*cb)(SimOne_Streaming_Image *pImage));
获取激光雷达点云数据回调
bool SetStreamingPointCloudUpdateCB(const  char* ip, unsigned short port, unsigned short infoPort, void(*cb)(SimOne_Streaming_Point_Cloud *pPointCloud));
获取毫米波雷达目标信息回调
bool SetRadarDetectionsUpdateCB(void(*cb)(const char* mainVehicleId, const char* sensorId, SimOne_Data_RadarDetection *pDetections));
获取目标及传感器真知数据回调
bool SetSensorDetectionsUpdateCB(void(*cb)(const char* mainVehicleId, const char* sensorId, SimOne_Data_SensorDetections *pGroundtruth));
获取感知车道与车道线数据回调
bool GetSensorLaneInfo(const char* mainVehicleId, const char* sensorId, SimOne_Data_LaneInfo *pLaneInfo);

(2)通过 ROS Publisher 将感知消息发布到相应 Topic 上

发布Gps消息
pub_gps = handle_gps.advertise<msg_gen::gps>(gps_topic.c_str(), 1);
pub_gps_p->publish(gps_d);
发布仿真感知物体真值消息
pub_ground_truth = handle_ground_truth.advertise<msg_gen::obstacle>(ground_truth_topic.c_str(), 1);
pub_ground_truth_p->publish(obstacle_d);
发布摄像头图像数据消息
pub_image = handle_image.advertise<sensor_msgs::Image>(image_topic.c_str(), 1);
pub_image_p->publish(img_d);
发布激光雷达点云数据消息
pub_point_cloud = handle_point_cloud.advertise<sensor_msgs::PointCloud2>(point_cloud_topic.c_str(), 1);
pub_point_cloud_p->publish(point_cloud_d);
发布毫米波雷达目标信息
pub_radar = handle_radar.advertise<msg_gen::radardetection>(radar_topic.c_str(), 1);
pub_radar_p->publish(radar_detection_d);
发布目标及传感器真值消息
pub_sensor = handle_sensor.advertise<msg_gen::sensordetections>(sensor_topic.c_str(), 1);
pub_sensor_p->publish(sensor_detections_d);
发布感知车道/车道线消息
pub_laneinfo = handle_laneinfo.advertise<msg_gen::laneinfo>(lane_info_topic.c_str(), 1);
pub_laneinfo_p->publish(lane_info_d);

(3)通过 ROS Subscriber 订阅主车控制相关 Topic

订阅主车控制消息离散点控制
sub_ctl = handle_ctl.subscribe(ctl_topic.c_str(), 1, &ros_trans_node::rcv_ctl_cb, this);
订阅主车控制消息油门/刹车/方向控制
sub_pose_ctl = handle_pose_ctl.subscribe(pose_ctl_topic.c_str(), 1, &ros_trans_node::rcv_pose_ctl_cb, this);

(4)通过 Sim-One API 设置主车控制参数

根据离散点设置主车位置
SimOneAPI::SetPose(0, &pose_ctl);
设置主车控制参数
SimOneAPI::SetDrive(vehicle_id.c_str(), pCtrl.get());