第三方中间件 ROS

1. 数据交互流程概述

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

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

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

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

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

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

  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
  1. 创建功能包

执行以下命令

  cd ~/catkin_ws/src
  catkin_create_pkg <package_name> roscpp std_msgs
  1. 添加 msg 消息文件至功能包

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

  mkdir ~/catkin_ws/src/<package_name>/msg
  1. 添加功能依赖

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

  <build_dependmessage_generation</build_depend<exec_dependmessage_runtime</exec_depend
  1. 修改 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
)
  1. 编译功能包

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

  cd ~/catkin_ws
  catkin_make
  1. 编译整个工作空间

为确保整个工作空间构建成功,再次执行:

  cd ~/catkin_ws
  catkin_make
  1. 检查 C++头文件

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

  ~/catkin_ws/devel/include/<package_name>/
  1. 将生成的 C++头文件整合至 ROS API 工程

将第 9 步生成的 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);
  1. 通过 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);
  1. 通过 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);
  1. 通过 Sim-One API 设置主车控制参数

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