TF 树梳理与变换管理:YAML/JSON 外参加载、图路径与环检测、直接关系重写、局部求逆、标定链路替换 UI、变换矩阵计算
Project description
tftree-manager
当前版本:0.0.12
TF 树梳理与变换管理:从 YAML 目录或 mdrive4 JSON 目录加载/保存外参与顺序,构建无向图、路径查找、环检测、变换矩阵计算。
安装
pip install tftree-manager
功能特性
- 从
yaml_dir/extrinsics/*.yaml与order_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_axes:compute_axes=FLU、projection_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_axesmetadata 时,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_ground、at128p_front->vrf_ground、ins->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_axesmetadata 时,D03 不推测相机轴系,compute与projection返回一致。 - 下游 C01/C03/C05/F_cursor_skill 等业务侧只选择
compute或projection语义,不自行做 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->B与B->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_frames或prune_frames命中的旧边,按 update YAML 顺序追加新的直接边。 - 写出重写后的
extrinsics/*.yaml与order_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.yaml的direct_relations_meta,不写入单条 extrinsic YAML。link_nodes长度必须 >= 2,首节点等于source_frame,尾节点等于target_frame。- 未提供
replace_frames时默认删除target_frame相关旧边;也可通过 API/CLI 额外传入prune_frames。 - 重写后会校验不存在
A->B与B->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_edge、path、yaml_dir、saved、cancelled、direct_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 操作:
- 布局支持
auto、tree、star,右侧按钮可即时切换;auto对vrf_ground星型图使用中心环绕,否则使用左根右展树形布局。 - 鼠标滚轮按当前位置缩放,右键按住拖拽平移画布。
- 左键点击路径边可选中;非路径边不可选。
- 左键依次点击两个直接相邻节点时,若两点之间的边属于标定路径,会自动选中该边;非相邻节点或非路径相邻边只更新节点高亮,不触发替换。
保存策略:
- 默认直接覆盖
yaml_dir/extrinsics/*.yaml与order_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-json与load_from_mdrive4_json需要) - PySide6(仅
prune-ui图形界面需要,可通过pip install tftree-manager[ui]安装)
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
648d43cd6447cd8b338b4fd99a8fc3324e65ce5f1983cac5033eec9b2ea79e82
|
|
| MD5 |
3b14de4e3e203877e543ffad4ae3f28d
|
|
| BLAKE2b-256 |
f1ef24466dc8ede417a864c06d81eb81f84a08d8b3be462d90a4a56f3cda1233
|
File details
Details for the file tftree_manager-0.0.12-py3-none-any.whl.
File metadata
- Download URL: tftree_manager-0.0.12-py3-none-any.whl
- Upload date:
- Size: 39.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.19
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1d2ec7c464612c734c7dbe2c85fd1a9f817aecb11c80b494fc178919003b18e5
|
|
| MD5 |
12335e0113d7e981c6c1dfbf0c7d4814
|
|
| BLAKE2b-256 |
ad0b0bc941da03dba282ac585ed5b1ff9e0ae6568b239aa7415331b01313ab9f
|