Skip to main content

TF 树梳理与变换管理:YAML/JSON 外参加载、图路径与环检测、直接关系重写、局部求逆、标定链路替换 UI、变换矩阵计算

Project description

tftree-manager

当前版本:0.0.12

TF 树梳理与变换管理:从 YAML 目录或 mdrive4 JSON 目录加载/保存外参与顺序,构建无向图、路径查找、环检测、变换矩阵计算。

安装

pip install tftree-manager

功能特性

  • yaml_dir/extrinsics/*.yamlorder_manifest.yaml 加载/保存外参与顺序
  • 从 mdrive4 JSON 目录读取 source_frame->root_frame 外参并导出 tftree YAML
  • 无向图构建、BFS 路径查找、有向环检测
  • 沿路径累积 4x4 变换矩阵、从根节点计算到所有可达节点
  • 更新变换、求逆、删除有向边(remove_transform)、重排顺序并写回 YAML
  • 相邻直接链路求逆(invert_adjacent_relation):传入两个节点,自动识别当前真实方向并翻转保存
  • 直接关系重写(rewrite_direct_relations):删除被更新相机旧链路,按外部 YAML 声明方向写入新直接边,并在 order_manifest.yaml 记录原始计算链路 link_nodes
  • 标定链路替换(replace_link_for_calibration / launch_prune_ui):输入新标定外参,在当前唯一路径上选择一条既有链接更新,拓扑不变
  • 输出模式(compute / projection):graph 内部只存 compute,projection 仅在 get_transform(..., output_mode="projection") 输出时派生
  • 可选 Flask API 服务与命令行入口

快速开始

from tftree_manager import TFTreeManager

manager = TFTreeManager("/path/to/yaml_dir")
manager.load_from_yaml()

# 所有坐标系
frames = manager.get_all_frames()

# 两节点间变换矩阵,默认 compute,用于尺寸链/保存/写回
T_compute = manager.get_transform("lidar", "camera")

# lidar2cam 显示、PnP、投影使用 projection
T_projection = manager.get_transform("lidar", "camera", output_mode="projection")

# 从根节点计算到所有节点
transforms = manager.compute_all_transforms_from_root("world")

# 检测环
cycles = manager.detect_cycles()

# 保存
manager.save_to_yaml()

mdrive4 JSON 输入

V0.0.12 中 TFTreeManager.load_from_mdrive4_json(input_dir, root_frame="ego") 消费 mdrive4_json.load_numpy_calibration(),并把 mdrive4-json camera record 的格式轴系差异限制在 D03 输出模式边界。

规则:

  • 复用 mdrive4_json.load_numpy_calibration 读取 JSON,按 mdrive4 JSON 原始语义解释为 sensor_id->ego,不在本库做求逆。
  • Python API 默认 root_frame="ego" 仅为兼容旧调用;官方一致性用法请显式传 root_frame="vrf_ground"。这里的 vrf_ground 是后轴中心接地点,不等同 vrf
  • JSON 主 frame 优先来自 frame_id,缺失时回退文件名 stem;camera_id/文件名 alias 不会写入 tftree 边。
  • JSON RPY 按“度制 ZYX”(Rz(yaw) * Ry(pitch) * Rx(roll))转换为四元数 [qx, qy, qz, qw]
  • mdrive4-json camera 原始外参按 FLU 表达;tftree graph 内部保存 compute/FLU,不在加载时改写矩阵。
  • camera metadata 写入 order_manifest.yaml.frame_axescompute_axes=FLUprojection_axes=RDF。保存为 YAML 后再次加载仍可派生 projection。
  • get_transform(..., output_mode="compute") 用于标定文件、尺寸链、保存和 JSON 写回;get_transform(..., output_mode="projection") 用于显示、PnP、lidar2cam 投影。
  • load_from_mdrive4_json(..., camera_frame_axes=None) 不记录 camera axes metadata,仅用于 raw 诊断。
  • PB/YAML 缺少 frame_axes metadata 时,compute/projection 一致,不做轴系转换;非 camera JSON 外参不做轴系转换。
  • JSON 写回只允许由 D03 执行 compute -> JSON FLU 序列化;业务侧不要自行拼 camera JSON RPY/translation。
  • V0.0.6 的 JSON 入口输出过 ego->sensor_id;需要旧方向语义时继续使用 V0.0.6。

Python 示例:

from tftree_manager import TFTreeManager

manager = TFTreeManager("/path/to/yaml_output")
manager.load_from_mdrive4_json("/path/to/json_dir", root_frame="vrf_ground")
T_projection = manager.get_transform("at128p_front", "front_120", output_mode="projection")
manager.save_to_yaml()

CLI:

tftree-manager load-json /path/to/json_dir --output-dir /path/to/yaml_output
tftree-manager load-json /path/to/json_dir --output-dir /path/to/yaml_output --root-frame vrf_ground

官方 root 输出边示例:front_30->vrf_groundat128p_front->vrf_groundins->vrf_ground

输出模式合同

V0.0.12 固化两个稳定输出模式:

  • compute:默认模式,用于标定文件、尺寸链、direct/manual link 存储、PB/YAML/JSON 写回。
  • projection:用于 lidar2cam 显示、PnP、图像投影。JSON camera 的 projection 轴系为 OpenCV/RDF。

合同边界:

  • graph 内部只存 compute,包括从 JSON 加载后的 camera FLU 外参、direct relation、manual link 和 YAML 工作区。
  • projection 不落盘为 graph 状态,只在 get_transform(..., output_mode="projection")convert_transform_mode(...)input_mode="projection" 的 D03 API 边界派生/归一。
  • JSON camera 的 compute 轴系固定为 FLU;JSON camera 的 projection 轴系固定为 OpenCV/RDF。
  • PB/YAML 无 order_manifest.yaml.frame_axes metadata 时,D03 不推测相机轴系,computeprojection 返回一致。
  • 下游 C01/C03/C05/F_cursor_skill 等业务侧只选择 computeprojection 语义,不自行做 FLU/RDF 转换。

API:

from tftree_manager import OUTPUT_MODE_COMPUTE, OUTPUT_MODE_PROJECTION, TFTreeManager

manager = TFTreeManager("/path/to/yaml_or_json_derived")
manager.load_from_yaml()

T_compute = manager.get_transform("at128p_front", "front_120")
T_projection = manager.get_transform(
    "at128p_front",
    "front_120",
    output_mode=OUTPUT_MODE_PROJECTION,
)

T_compute_round_trip = manager.convert_transform_mode(
    "at128p_front",
    "front_120",
    T_projection,
    from_mode=OUTPUT_MODE_PROJECTION,
    to_mode=OUTPUT_MODE_COMPUTE,
)

PB/YAML 默认接口示例:

from tftree_manager import OUTPUT_MODE_PROJECTION, TFTreeManager

manager = TFTreeManager("/path/to/pb_parsed_yaml")
manager.load_from_yaml()

# 不传 output_mode 等价 compute,保持 PB/YAML 标定语义。
T_pb_compute = manager.get_transform("lidar", "camera")

# 统一调用链路可显式传 projection;无 frame_axes metadata 时结果仍与 compute 一致。
T_pb_projection = manager.get_transform(
    "lidar",
    "camera",
    output_mode=OUTPUT_MODE_PROJECTION,
)

转换公式:

T_target_projection_from_source_projection = R_target_projection_from_target_compute @ T_target_compute_from_source_compute @ R_source_compute_from_source_projection

upsert_direct_transform(..., input_mode="projection") 可接收投影矩阵,D03 会先转换为 compute 后存储。

业务侧禁止项:

  • 不要自行实现 FLU/RDF 或 OpenCV/RDF camera 轴系转换。
  • 不要直接调用 tftree_manager.frame_axes.convert_transform_source_axes;该函数是 D03 内部边界实现,不是业务 API。
  • 不要保留“调用新 API 失败后捕获 TypeError 再走旧矩阵语义”的 fallback;版本不满足时应直接升级/失败。
  • 不要手写 JSON camera 写回序列化;JSON 写回必须走 D03,以保证 compute -> JSON FLU 一致。

相邻链路局部求逆

V0.0.6 新增 TFTreeManager.invert_adjacent_relation(left, right, output_dir=None, save=True)

规则:

  • 只允许两节点之间存在一条直接边;多跳路径不允许求逆。
  • 若实际存在 left->right,则翻转为 right->left;若实际存在 right->left,则翻转为 left->right
  • 缺失节点、非相邻节点、同时存在 A->BB->A 都会抛出 TFTreeValidationError
  • 推荐指定 output_dir,避免覆盖原始 YAML 工作目录。
from tftree_manager import TFTreeManager

manager = TFTreeManager("/path/to/yaml_dir")
manager.load_from_yaml()
result = manager.invert_adjacent_relation("at128p_front", "vrf", output_dir="/path/to/inverted_yaml")
print(result.original_edge, result.output_edge)

直接关系重写

V0.0.4 新增 TFTreeManager.rewrite_direct_relations(update_yaml_path, prune_frames=None, output_dir=None, save=True),用于处理 PB 解析后的 YAML 工作目录。C04 负责产出 IPM 自动计算后的直接关系 YAML,pb_calibration 继续负责 PB parse/build。

本库已提供的模板能力:

  • 读取 direct_relations.yaml,校验 direct_relations 格式、重复边、反向重复边与无向闭环。
  • 删除 replace_framesprune_frames 命中的旧边,按 update YAML 顺序追加新的直接边。
  • 写出重写后的 extrinsics/*.yamlorder_manifest.yaml,并在 direct_relations_meta 保留追溯元数据。
  • 提供 Python API 与 CLI 两种触发方式。

调用方需要单独开发的接口/适配层:

  • 直接关系 YAML 生成接口:业务侧根据 IPM、标定结果或其它计算结果生成 direct_relations,本库不负责计算外参。
  • PB 工作目录准备接口:业务侧负责调用 pb_calibration parse,准备包含 extrinsics/order_manifest.yaml 的 YAML 工作目录。
  • 重写触发接口:业务侧可封装 Python API 或 CLI,把 YAML 工作目录、update YAML、输出目录与错误处理接入 C04 或其它流程。
  • PB 回写接口:业务侧继续调用 pb_calibration build,把重写后的 YAML 转回 PB;本库不直接读写 PB。

输入 YAML 格式:

direct_relations:
  - source_frame: at128p_front
    target_frame: front_120
    link_nodes: [at128p_front, vrf, front_120]
    translation: {x: 0.0, y: 0.0, z: 0.0}
    rotation: {qx: 0.0, qy: 0.0, qz: 0.0, qw: 1.0}
    replace_frames: [front_120]

规则:

  • source_frame -> target_frame 是最终写入 extrinsics/*.yaml 的方向,不自动反转。
  • link_nodes 仅作为原始计算链路元数据保存到 order_manifest.yamldirect_relations_meta,不写入单条 extrinsic YAML。
  • link_nodes 长度必须 >= 2,首节点等于 source_frame,尾节点等于 target_frame
  • 未提供 replace_frames 时默认删除 target_frame 相关旧边;也可通过 API/CLI 额外传入 prune_frames
  • 重写后会校验不存在 A->BB->A 反向重复边,且无无向闭环;允许森林结构。

推荐接入策略:

  • 有输出隔离需求时使用 output_dir,避免覆盖 pb_calibration parse 生成的原始 YAML。
  • 单相机更新时 replace_frames 默认填目标相机 frame 即可;显式写出更利于排查。
  • 多相机更新时每条 direct relation 都显式写 replace_frames,避免误删无关外参。
  • 捕获到 TFTreeValidationError 时不要继续执行 PB build,应保留原 YAML 并把错误详情返回给上层流程。

Python 示例:

from tftree_manager import TFTreeManager

manager = TFTreeManager("/path/to/yaml_dir")
manager.load_from_yaml()
result = manager.rewrite_direct_relations(
    "/path/to/direct_relations.yaml",
    output_dir="/path/to/rewritten_yaml",
)
print(result.added_relations[0].link_nodes)

命令行

# 以 YAML 目录为参数,加载并打印树与环信息
tftree-manager /path/to/yaml_dir

# 按 direct_relations YAML 重写直接边;默认写回 yaml_dir
tftree-manager rewrite-direct /path/to/yaml_dir /path/to/direct_relations.yaml

# 输出到新目录,不修改原 YAML 目录
tftree-manager rewrite-direct /path/to/yaml_dir /path/to/direct_relations.yaml --output-dir /path/to/rewritten_yaml

# 读取 mdrive4 JSON 并导出 YAML
tftree-manager load-json /path/to/json_dir --output-dir /path/to/yaml_output

# 自动识别 A/B 的直接边真实方向并求逆;推荐输出到新目录
tftree-manager invert-edge /path/to/yaml_dir at128p_front vrf --output-dir /path/to/inverted_yaml

# 额外删除指定 frame 相关旧边
tftree-manager rewrite-direct /path/to/yaml_dir /path/to/direct_relations.yaml --prune-frame front_120

# 启动 Flask API 服务(默认 0.0.0.0:5000)
tftree-manager-server

环境变量 TFTREE_DATA_DIR 可指定 API 默认数据目录(默认 ./data)。

标定链路替换 UI

V0.0.7 中 prune-ui 的语义是“标定外参链路替换”:标定程序传入新外参 source_frame->target_frame 后,tftree 找到当前树中连接两端点的路径;若两端点直接相邻则自动更新该边;若是多跳路径,则打开 PySide6 UI,只允许用户选择路径上的一条既有链接进行替换。UI 只读取/保存 PB parse 后的 YAML 工作目录,不直接生成 PB,也不会新增或删除边。

其它程序接入

其它 Python 程序应把 launch_prune_ui 当作稳定公共入口;tftree_manager.prune_ui.window.PruneWindow 是当前内部 Qt 实现,暂不承诺作为可嵌入组件的稳定 API。调用方只需要准备三类输入:PB parse 后的 YAML 工作目录、新标定得到的 Transform 或 4x4 矩阵、以及这条新外参的 source_frame/target_frame

from pathlib import Path

import numpy as np

from tftree_manager import LinkReplacementResult, Transform, launch_prune_ui


def replace_calibrated_link(yaml_dir: Path, selected_edge: str | None = None) -> LinkReplacementResult:
    T_new = np.eye(4)
    T_new[:3, 3] = [1.2, 0.0, 0.3]
    calibrated = Transform.from_matrix(T_new, "at128p_front", "front_120")

    return launch_prune_ui(
        yaml_dir=str(yaml_dir),
        source_frame="at128p_front",
        target_frame="front_120",
        calibrated_transform=calibrated,
        selected_edge=selected_edge,
        save=True,
        output_dir=None,
        title="标定链路替换",
        layout_mode="auto",
    )

接入行为:

  • selected_edge 不为空时走非 GUI 自动化路径,不需要安装 PySide6。
  • source_frame/target_frame 直接相邻时自动更新唯一边,也不需要安装 PySide6。
  • 多跳路径且未传 selected_edge 时才打开 PySide6 UI;图形界面依赖通过 pip install "tftree-manager[ui]" 安装。
  • 返回值固定为 LinkReplacementResult,可检查 replaced_edgepathyaml_dirsavedcancelleddirect_adjacent

Python API:

from tftree_manager import Transform, launch_prune_ui

result = launch_prune_ui(
    yaml_dir="/path/to/yaml_dir",
    source_frame="at128p_front",
    target_frame="front_120",
    calibrated_transform=Transform.from_matrix(T_new, "at128p_front", "front_120"),
    title="标定链路替换",
    layout_mode="auto",
)
print(result.replaced_edge)

非 GUI API:

from tftree_manager import TFTreeManager

manager = TFTreeManager("/path/to/yaml_dir")
manager.load_from_yaml()
result = manager.replace_link_for_calibration(
    "at128p_front",
    "front_120",
    T_new,
    selected_edge="vrf->front_120",
)

CLI:

tftree-manager prune-ui /path/to/yaml_dir \
  --update-yaml /path/to/new_calibration.yaml \
  --layout auto

# 自动化/测试入口:不打开 UI,直接更新选中路径边并覆盖 YAML
tftree-manager prune-ui /path/to/yaml_dir \
  --update-yaml /path/to/new_calibration.yaml \
  --selected-edge 'vrf->front_120' \
  --layout tree

UI 操作:

  • 布局支持 autotreestar,右侧按钮可即时切换;autovrf_ground 星型图使用中心环绕,否则使用左根右展树形布局。
  • 鼠标滚轮按当前位置缩放,右键按住拖拽平移画布。
  • 左键点击路径边可选中;非路径边不可选。
  • 左键依次点击两个直接相邻节点时,若两点之间的边属于标定路径,会自动选中该边;非相邻节点或非路径相邻边只更新节点高亮,不触发替换。

保存策略:

  • 默认直接覆盖 yaml_dir/extrinsics/*.yamlorder_manifest.yaml,不自动备份。
  • 保存前校验拓扑不存在反向重复边和无向闭环。
  • 只更新被选中的一条既有链接,保留原 key 方向与边顺序,calib_json/ 等辅助目录不参与修改。

依赖

  • Python >= 3.10
  • numpy >= 1.20.0
  • PyYAML >= 5.4.0
  • Flask >= 2.0.0(API 与 server 入口需要)
  • mdrive4-json >= 0.0.1(load-jsonload_from_mdrive4_json 需要)
  • PySide6(仅 prune-ui 图形界面需要,可通过 pip install tftree-manager[ui] 安装)

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

tftree_manager-0.0.12.tar.gz (39.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tftree_manager-0.0.12-py3-none-any.whl (39.1 kB view details)

Uploaded Python 3

File details

Details for the file tftree_manager-0.0.12.tar.gz.

File metadata

  • Download URL: tftree_manager-0.0.12.tar.gz
  • Upload date:
  • Size: 39.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.19

File hashes

Hashes for tftree_manager-0.0.12.tar.gz
Algorithm Hash digest
SHA256 648d43cd6447cd8b338b4fd99a8fc3324e65ce5f1983cac5033eec9b2ea79e82
MD5 3b14de4e3e203877e543ffad4ae3f28d
BLAKE2b-256 f1ef24466dc8ede417a864c06d81eb81f84a08d8b3be462d90a4a56f3cda1233

See more details on using hashes here.

File details

Details for the file tftree_manager-0.0.12-py3-none-any.whl.

File metadata

File hashes

Hashes for tftree_manager-0.0.12-py3-none-any.whl
Algorithm Hash digest
SHA256 1d2ec7c464612c734c7dbe2c85fd1a9f817aecb11c80b494fc178919003b18e5
MD5 12335e0113d7e981c6c1dfbf0c7d4814
BLAKE2b-256 ad0b0bc941da03dba282ac585ed5b1ff9e0ae6568b239aa7415331b01313ab9f

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page