1. 安装

这个版本的变化还挺大的,之前的版本的CalibrationTools会读取vehicle_model:=sample_vehicle sensor_model:=sample_sensor_kit,但是新版的不会读取这些tf信息。他可以不依赖autoware,其实现在autoware的作用就是来发布tf坐标,然后bag提供topic数据,并给到CalibrationTools完成标定

1.1 系统要求

  • Ubuntu 22.04
  • ROS2 Humble

1.2 与 Autoware 一起安装

在安装 Autoware 之后(请参见 源代码安装 页面),执行以下命令:

cd autoware
wget https://raw.githubusercontent.com/tier4/CalibrationTools/tier4/universe/calibration_tools_autoware.repos
vcs import src < calibration_tools.repos
rosdep install -y --from-paths src --ignore-src --rosdistro $ROS_DISTRO
colcon build --symlink-install --cmake-args -DCMAKE_BUILD_TYPE=Release

2. 可用工具

2.1 外部校准工具

名称 校准的传感器 特征类型 校准类型 文档 教程
地面平面校准器 基础激光雷达 地面 滚转、俯仰、z N/A N/A
交互式相机-激光雷达校准器 相机-激光雷达 手动对应 完整姿态 N/A N/A
激光雷达-激光雷达 2D 校准器 激光雷达-激光雷达 自然特征 x, y, 偏航 N/A N/A
基于映射的校准器(激光雷达-激光雷达) 激光雷达-激光雷达 自然特征 完整姿态 链接 链接
基于映射的校准器(基础激光雷达) 基础激光雷达 自然特征和地面 滚转、俯仰和z N/A N/A
标记雷达-激光雷达校准器 雷达-激光雷达 标记 x, y, 偏航 链接 链接
基于标签的 PnP 校准器 相机-激光雷达 标记 完整姿态 链接 链接
基于标签的 SfM 校准器 相机-激光雷达-基础 标记 完整姿态 链接 链接

2.2 内部校准工具

名称 校准的传感器 特征类型 校准类型 文档 教程
相机内部参数校准器 相机内部参数 校准板 OpenCV 相机模型 N/A N/A

3. 设计

传感器校准工具库提供了多种内部和外部校准的替代方案。尽管如此,本文档的其余部分仅关注外部校准,因为相机内部校准是一个直接且易于理解的过程

外部校准过程的架构由两个实体组成:校准器节点本身和传感器校准管理器(可能会使用其他节点,但它们并不直接参与校准过程)。接下来,我们将详细说明这些元素的角色。

3.1 校准器节点

校准器节点是一个常规节点,实施了 ExtrinsicCalibrator 服务:

---
tier4_calibration_msgs/CalibrationResult[] results

其中 CalibrationResult 包含帧之间的变换、状态标志以及用于评估和调试目的的可选分数和文本消息。

geometry_msgs/TransformStamped transform_stamped
bool success
float32 score
std_msgs/String message

该设计旨在尽可能将校准器节点及其内部逻辑与特定校准用例的细节和使用的 tf 结构解耦。校准器的代码对其特定任务以外的所有内容都是无关的,服务请求甚至不包含要校准的帧。这样,所有特定于用例的关注点都在 sensor_calibration_manager 包中实现的节点和启动器配置期间指定,从而实现了高水平的关注点分离和代码重用。

3.2 传感器校准管理器

尽管可以通过启动校准器节点(这涉及非平凡的参数化)并使用 CLIros2 service call ...)的服务接口直接执行校准过程,但强烈建议使用 sensor_calibration_manager 包来自动化该过程
sensor_calibration_manager 实现了一个用户界面,允许用户选择特定的 projectcalibrator 组合,确保所需的 tf 和服务可用,并处理/保存校准结果。

3.2.1 项目和校准器

在 TIER IV,我们目前运行多个使用各种类型传感器的项目。然而,我们并不为每个特定项目创建校准器节点,而是重用相同的代码,仅修改参数和辅助节点。为此,在 sensor_calibration_manager 包中,我们引入了 projectscalibrators 的概念。在此上下文中,project 由一系列 calibrators 组成(注意在此上下文中 calibrators 与校准器节点不同),同一校准器(语义上)可以属于多个项目。

以下是涉及此方案的文件示例:

cd sensor_calibration_manager/sensor_calibration_manager/calibrators && find .
./projectA/
./projectA/calibratorA.py
./projectA/calibratorB.py
./projectA/__init__.py
./projectB/
./projectB/calibratorA.py
./projectB/calibratorB.py
./projectB/__init__.py
./projectC/
./projectC/calibratorC.py
./projectC/__init__.py
./__init__.py

在这里插入图片描述

在此示例中,校准器文件夹位于 sensor_calibration_manager 包内,每个 project 在其自己的文件夹中组织(例如,projectAprojectBprojectC),并且在每个 project 文件夹内,一个或多个 calibrators 通过 Python 文件(例如,calibratorA.pycalibratorB.pycalibratorC.py)表示。

  • 注意某些 calibrators 在多个 project 中存在。这基本上意味着该 calibrator 可以在多个 projects 中使用,尽管它有自己的一组配置文件(稍后会详细说明)。例如,我们希望使用 基于标签的 PnP 校准器 方法来校准公交车和机器人出租车。
  • __init__.py 文件用于在 sensor_calibration_manager 包中注册 projectscalibrators。如何编写这些文件将在 集成 部分中解释。
3.2.2 校准器接口

校准器接口是 sensor_calibration_manager 包中校准过程的表示。它指定其 projectcalibrator 名称、在校准过程中所需的 tf 以及校准器节点应返回的预期帧。

根据前面的示例,calibratorA.py 文件可以实现如下:

@CalibratorRegistry.register_calibrator(
    project_name="projectA", calibrator_name="calibratorA"
)
class CalibratorA(CalibratorBase):
    required_frames = ["calibration_parent_frame", "calibration_child_frame", "auxiliar_frame"]

    def __init__(self, ros_interface: RosInterface, **kwargs):
        super().__init__(ros_interface)

        self.add_calibrator(
            service_name="the_name_of_the_calibration_service",
            expected_calibration_frames=[
                FramePair(parent="calibration_parent_frame", child="calibration_child_frame"),
            ],
        )

在这里插入图片描述

除了指定 required_framesservices_name,校准器接口还可用于在必要时对校准结果进行后处理,以符合机器人框架约定和其他项目特定要求。

例如,摄像头-激光雷达校准返回从 optical_linklidar 框架本身的 tf。然而,在大多数情况下,配置文件中更倾向于使用 camera_link 而非 optical_linkcamera_link 的坐标轴不同),对于某些激光雷达,集成商更愿意使用他们的 base_link 或足迹(与车辆的 base_link 不同),因为这使他们能够更好地与 CAD 文件配合使用。

在 TIER IV,大多数传感器安装在一个称为 sensor_kit 的结构中,所有与传感器校准相关的 tf 通常都以此框架为起点或终点(例如,从 base_linksensor_kit 或从 sensor_kitlidar_base_link)。

特别是对于摄像头-激光雷达的情况,在我们大多数项目中,表示摄像头-激光雷达校准的 tf 是从 sensor_kit_base_linkcameraX/camera_link。为了将校准器返回的 tfcameraX/camera_optical_linklidar)转换为我们需要保存的格式,后处理步骤可以如下实现:

# 取自 sensor_calibration_manager/sensor_calibration_manager/calibrators/xx1/tag_based_pnp_calibrator.py
def post_process(self, calibration_transforms: Dict[str, Dict[str, np.array]]):
    optical_link_to_lidar_transform = calibration_transforms[
        f"{self.camera_name}/camera_optical_link"
    ]["velodyne_top"]
    sensor_kit_to_lidar_transform = self.get_transform_matrix(
        "sensor_kit_base_link", "velodyne_top"
    )
    camera_to_optical_link_transform = self.get_transform_matrix(
        f"{self.camera_name}/camera_link", f"{self.camera_name}/camera_optical_link"
    )
    sensor_kit_camera_link_transform = np.linalg.inv(
        camera_to_optical_link_transform
        @ optical_link_to_lidar_transform
        @ np.linalg.inv(sensor_kit_to_lidar_transform)
    )

    result = {
        "sensor_kit_base_link": {
            f"{self.camera_name}/camera_link": sensor_kit_camera_link_transform
        }
    }
    return result

*注意:在此示例中,sensor_kit_to_lidar_transform 被假定为已知且固定,因为它对应于先前的激光雷达-激光雷达校准结果或是一个硬编码值。

3.2.3 启动文件

校准器接口并未实现任何涉及节点的 ROS 逻辑。该过程的这一部分由 sensor_calibration_manager 包调用的常规启动文件实现。

根据前面的示例,启动器结构如下:

launch/
launch/projectA/
launch/projectA/calibratorA.launch.xml
launch/projectA/calibratorB.launch.xml
launch/projectB/
launch/projectB/calibratorA.launch.xml
launch/projectB/calibratorB.launch.xml
launch/projectC/
launch/projectC/calibratorC.launch.xml

*注意:具有值 project_name="projectA"calibrator_name="calibratorA"calibrator interface 将启动 launch/projectA/calibratorA.launch.xml

启动文件可以包含带有和不带有默认参数的参数,这些参数将自动转换为可配置的用户界面,以便用户在启动时进行设置。需要注意的一点是,校准器接口中指定的服务必须由启动文件中的某个节点提供。

*注意:启动文件中定义的参数值可以通过 kwargs 访问到 calibration interface

整体流程为
sensor_calibration_manager.py使用这个操作来调用注册机

        self.calibrator = CalibratorRegistry.create_calibrator(
            project_name,
            calibrator_name,
            ros_interface=self.ros_interface,
            **context.launch_configurations,
        ) 

然后在calibrator_registry.py实现注册机

    @classmethod
    def create_calibrator(cls, project_name: str, calibrator_name: str, **kwargs) -> CalibratorBase:
        """
        Create the executor using a factory pattern.

        This method gets the appropriate Executor class from the registry
        and creates an instance of it, while passing in the parameters
        given in ``kwargs``.

        Args:
            name (str): The name of the executor to create.

        Returns
        -------
            An instance of the executor that is created.

        """
        if project_name not in cls.registry or calibrator_name not in cls.registry[project_name]:
            cls.logger.error(
                f"Calibrator project={project_name} name={calibrator_name} does not exist"
            )
            return None

        exec_class = cls.registry[project_name][calibrator_name]
        executor = exec_class(**kwargs)
        return executor

并对于每一个调用完成注册,比如sensor_calibration_manager/sensor_calibration_manager/calibrators/rdv/tag_based_pnp_calibrator.py

    project_name="rdv", calibrator_name="tag_based_pnp_calibrator" ) ```
    然后就是将class实现放在CalibratorBase中,完成调用
3.2.4 启动传感器校准管理器

要使用校准管理器,请执行以下命令(在配置 ROS 工作区后):

ros2 run sensor_calibration_manager sensor_calibration_manager

将显示以下窗口:

然后,用户必须选择 projectcalibrator 的组合并按下 Continue,这将显示以下菜单:

在这里插入图片描述

…详情请参照古月居

  • 创建一个名为 my_new_calibrator_package 的 ROS2 包。节点本身需要是一个具有至少两个线程的多线程执行器的一部分。这是因为校准服务调用仅在校准过程完成后返回。
  • package.xml 中添加对 tier4_calibration_msgs 包的依赖,以使用校准服务。
  • 在节点的头文件中添加一个校准服务。
  • 在大多数情况下,还需要创建一个专门用于服务的组。

在头文件中:

...
rclcpp::Service<tier4_calibration_msgs::srv::ExtrinsicCalibrator>::SharedPtr service_server_;
rclcpp::CallbackGroup::SharedPtr srv_callback_group_
...

在源文件中:

...
// 服务服务器在专用线程中运行,因为这是一个阻塞调用
srv_callback_group_ = create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);

service_server_ = this->create_service<tier4_calibration_msgs::srv::ExtrinsicCalibrator>(
  "extrinsic_calibration_service_name",
  std::bind(
    &MyNewCalibratorPackage::requestReceivedCallback, this, std::placeholders::_1,
    std::placeholders::_2),
  rmw_qos_profile_services_default, srv_callback_group_);
  ...

此外,用户必须实现 requestReceivedCallback 以符合接口要求。

Logo

技术共进,成长同行——讯飞AI开发者社区

更多推荐