Tf_tree_terminal

Hi everyone,

here a small package that I created : tf_tree_terminal.

A lightweight ROS 2 utility to visualize the Coordinate Transform (TF) tree directly in the terminal with a folder-style structure.

I found it a bit cumbersome to launch rqt-tf-tree to debug or log my tf-tree, so I made this little tool to display it in the terminal like the “Tree” command for directories under Linux.

ros2 run tf_tree_terminal show

Example output :

--- Terminal TF Tree ---
map
└── odom
    └── base_footprint
        └── base_link
            ├── camera_link
            ├── chassis_link
            │   ├── front_axle
            │   └── rear_axle
            └── velodyne_link
------------------------
27 Likes

Great!I love this idea!

I would love to see it integrated as ROS 2 CLI verb.

Something like “ros2 tf tree”

3 Likes

In fact I have added a simpler command in the latest release :

tf-tree

Or, to save the output as txt file

tf-tree my-robot.txt

https://github.com/Tanneguydv/tf_tree_terminal/releases/tag/1.0.1

I think this might actually be possible as the ros2 command is meant to be extendible.

1 Like

That’s awesome, I would say 90% of cases when I need to inspect a tf tree it’s when I’m connected headless over ssh and then I have to sftp the pdf over which is super annoying. :grinning_face_with_smiling_eyes:

I agree, it would definitely make sense to turn it into a PR for GitHub - ros2/ros2cli: ROS 2 command line interface tools

4 Likes

This is close to a wishlist I’ve had for some time now (essentially a view_frames tree view of the /robot_description that is easily usable on a headless machine/ssh session and shows joint names). It looks like the current iteration only shows the link names - in my use cases having the joints connecting them shown is also very useful for verifying I have my ros2_controllers (which claim joints) set up correctly for my planning groups (which people typically think of in terms of links). Right now I just do ros2 topic echo --once --field data --full-length /robot_description | head -n -1 and then do some ctrl+f.

Having a headless friendly version of view_frames is still very helpful though. My 2 cents!

2 Likes

Creating this binding for ros2cli would be a great candidate to be integrated as a standard tool into the geometry packages potentially replacing view_frames as the recommended quick debugging tool. And then view_frames could become more about the details and including extra information not captured in the CLI version.

3 Likes

Totally agree, I wish to complete the tool with more feedbacks for users for debugging processes before creating a PR!

1 Like

I added some more debug features, for example now, when running tf-tree -p mobile on a fake mobile Robot I get :



░▀█▀░█▀▀░░░░░▀█▀░█▀▄░█▀▀░█▀▀
░░█░░█▀▀░▄▄▄░░█░░█▀▄░█▀▀░█▀▀
░░▀░░▀░░░░░░░░▀░░▀░▀░▀▀▀░▀▀▀
TF-TREE CLI DEBUGGER

[INFO] [1767483009.498731809] [tf_tree_cli_helper]: Mode: REP 105 (Mobile)
[INFO] [1767483009.499220178] [tf_tree_cli_helper]: Single-shot mode: Buffering (3s)…

— TF SNAPSHOT: 2026-01-03 23:30:12 —
🔗 Link: base_link [STATIC]
├── 🔗 Link: base_footprint [STATIC] [STATIC]
│   ⚙️  Joint: to_base_footprint [TF: robot_state_publisher | JointState: joint_state_publisher]
├── 🔗 Link: camera_link [STATIC] [STATIC]
│   ⚙️  Joint: to_camera_link [TF: robot_state_publisher | JointState: joint_state_publisher]
│   └── 🔗 Link: camera_link_optical [STATIC] [STATIC]
│       ⚙️  Joint: to_camera_link_optical [TF: robot_state_publisher | JointState: joint_state_publisher]
├── 🔗 Link: left_wheel [12.3 Hz] [LIVE: 81.5ms]
│   ⚙️  Joint: to_left_wheel [TF: robot_state_publisher | JointState: joint_state_publisher]
└── 🔗 Link: right_wheel [12.3 Hz] [LIVE: 81.6ms]
⚙️  Joint: to_right_wheel [TF: robot_state_publisher | JointState: joint_state_publisher]
└── 🔗 Link: test_orphan [STATIC] [STATIC]
⚙️  Joint: to_test_orphan [TF: robot_state_publisher | JointState: joint_state_publisher]
✅ /joint_states topic

— COMPLIANCE DIAGNOSTIC —
❌ Recommendation: map
❌ Recommendation: odom
✅ Recommendation: base_link

I wished to link a “diagnostic” to the tf-tree discovery, according to REP 105 for mobile and REP 199 for arm robots.

I still doubt about the utility to specify the publisher for TF as it will always be emit by robot_state_publisher. The main idea is to be able to get the same insights as Rviz2 about joint_states not being published :

This state :

Equals this output for tf-tree –profile mobile :

░▀█▀░█▀▀░░░░░▀█▀░█▀▄░█▀▀░█▀▀
░░█░░█▀▀░▄▄▄░░█░░█▀▄░█▀▀░█▀▀
░░▀░░▀░░░░░░░░▀░░▀░▀░▀▀▀░▀▀▀
TF-TREE CLI DEBUGGER

[INFO] [1767558554.199176504] [tf_tree_cli_helper]: Mode: REP 105 (Mobile)
[INFO] [1767558554.200081643] [tf_tree_cli_helper]: Single-shot mode: Buffering (3s)…

— TF SNAPSHOT: 2026-01-04 20:29:17 —
🔥 ALERT: 2 DISJOINTED TREES!
└─ Roots: base_link, right_wheel

🔗 Link: base_link [STATIC]
├── 🔗 Link: base_footprint [STATIC] [STATIC]
│   ⚙️  Joint: to_base_footprint [TF: robot_state_publisher | JointState: ❌ none]
└── 🔗 Link: camera_link [STATIC] [STATIC]
⚙️  Joint: to_camera_link [TF: robot_state_publisher | JointState: ❌ none]
└── 🔗 Link: camera_link_optical [STATIC] [STATIC]
⚙️  Joint: to_camera_link_optical [TF: robot_state_publisher | JointState: ❌ none]
🔗 Link: right_wheel [STATIC]
└── 🔗 Link: test_orphan [STATIC] [STATIC]
⚙️  Joint: to_test_orphan [TF: robot_state_publisher | JointState: ❌ none]
❌ /joint_states topic

— COMPLIANCE DIAGNOSTIC —
❌ Recommendation: map
❌ Recommendation: odom
✅ Recommendation: base_link

This should lead the user to publish joint_states :

and get this output, running tf-tree -p mobile

░▀█▀░█▀▀░░░░░▀█▀░█▀▄░█▀▀░█▀▀
░░█░░█▀▀░▄▄▄░░█░░█▀▄░█▀▀░█▀▀
░░▀░░▀░░░░░░░░▀░░▀░▀░▀▀▀░▀▀▀
TF-TREE CLI DEBUGGER

[INFO] [1767558488.085976588] [tf_tree_cli_helper]: Mode: REP 105 (Mobile)
[INFO] [1767558488.086777159] [tf_tree_cli_helper]: Single-shot mode: Buffering (3s)…

— TF SNAPSHOT: 2026-01-04 20:28:11 —
🔗 Link: base_link [STATIC]
├── 🔗 Link: base_footprint [STATIC] [STATIC]
│   ⚙️  Joint: to_base_footprint [TF: robot_state_publisher | JointState: joint_state_publisher]
├── 🔗 Link: camera_link [STATIC] [STATIC]
│   ⚙️  Joint: to_camera_link [TF: robot_state_publisher | JointState: joint_state_publisher]
│   └── 🔗 Link: camera_link_optical [STATIC] [STATIC]
│       ⚙️  Joint: to_camera_link_optical [TF: robot_state_publisher | JointState: joint_state_publisher]
├── 🔗 Link: left_wheel [13.8 Hz] [LIVE: 72.6ms]
│   ⚙️  Joint: to_left_wheel [TF: robot_state_publisher | JointState: joint_state_publisher]
└── 🔗 Link: right_wheel [13.7 Hz] [LIVE: 72.9ms]
⚙️  Joint: to_right_wheel [TF: robot_state_publisher | JointState: joint_state_publisher]
└── 🔗 Link: test_orphan [STATIC] [STATIC]
⚙️  Joint: to_test_orphan [TF: robot_state_publisher | JointState: joint_state_publisher]
✅ /joint_states topic

— COMPLIANCE DIAGNOSTIC —
❌ Recommendation: map
❌ Recommendation: odom
✅ Recommendation: base_link

or use the light version of tree, running tf-tree -l -p mobile

░▀█▀░█▀▀░░░░░▀█▀░█▀▄░█▀▀░█▀▀
░░█░░█▀▀░▄▄▄░░█░░█▀▄░█▀▀░█▀▀
░░▀░░▀░░░░░░░░▀░░▀░▀░▀▀▀░▀▀▀
TF-TREE CLI DEBUGGER

[INFO] [1767558700.138535938] [tf_tree_cli_helper]: Mode: REP 105 (Mobile)
[INFO] [1767558700.139257144] [tf_tree_cli_helper]: Single-shot mode: Buffering (3s)…

— TF SNAPSHOT: 2026-01-04 20:31:43 —
base_link
├── base_footprint
├── camera_link
│   └── camera_link_optical
├── left_wheel
└── right_wheel
└── test_orphan

Also, I added command flags :

| Flag | Short | Description |

| --- | --- | --- |

| `--profile` | `-p` | Diagnostic mode: `mobile`, `arm`, or `auto` (default). |

| `--save` | `-s` | Export the analysis to a file (e.g., `-s robot_report.txt`). |

| `--alive` | `-a` | Keep node alive; refreshes terminal every 5 seconds. |

| `--light` | `-l` | Get a light TF-Tree as output |

| `--help` | `-h` | Show help message and exit. |

Hope this goes the right way! :crossed_fingers:

4 Likes

Ok, I have finally completed the package as I imagined, and I integrated CI with github actions workflows :

I have made PRs to rosdistro (humble, jazzy, kilted) :

https://github.com/ros/rosdistro/pulls

These PRs to be able to resolve dependencies, that is actually problematic for the binding I created for ros2cli with the new package ros2tftree as you suggested. It works fine using several ros-distro on docker’s containers.

The PR:

I confess that Copilot was helpful for this binding… :melting_face:

Thank you for your insights, I am of course open to suggestions or comments!

2 Likes

Thank you for great tool for ROS2. I have tried it and published a post with the overview of your tool on Medium. Here is it: https://medium.com/@sigmoid90/overview-of-tool-tf-tree-for-ros-2-18bff3960565?postPublishedType=initial

3 Likes

Thank you for this complete review :slight_smile:

Happy to share with the community!