TF States
The TF States are part of the yasmin_ros package and provide YASMIN states that integrate with the tf2 transform library. They enable state machines to set up and share transform listeners through the blackboard so that subsequent states can look up coordinate frame transforms without duplicating listener infrastructure.
TfBufferState
Creates a tf2_ros::Buffer (C++) or
tf2_ros.Buffer (Python) and an associated
TransformListener, then stores both objects in the
YASMIN blackboard under the keys tf_buffer and
tf_listener. Subsequent states retrieve these objects
from the blackboard and use them to perform transform lookups
without creating redundant listeners.
Outcomes
| Outcome | Description |
|---|---|
succeeded |
The tf2 buffer and transform listener were created successfully and written to the blackboard. |
aborted |
Failed to create the tf2 buffer or transform listener (e.g. an exception was thrown during construction). |
Blackboard Keys
| Direction | Key | C++ type | Python type | Description |
|---|---|---|---|---|
| Output | tf_buffer |
std::shared_ptr<tf2_ros::Buffer>
|
tf2_ros.Buffer |
The created tf2 buffer. Pass it to
tf2_ros::TransformBroadcaster or use
lookupTransform directly.
|
| Output | tf_listener |
std::shared_ptr<tf2_ros::TransformListener>
|
tf2_ros.TransformListener |
The transform listener that keeps tf_buffer
up to date. Must be kept alive for as long as transforms are
needed.
|
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
cache_time_sec |
float / double | 10.0 |
How many seconds of transform history the tf2 buffer should keep. Increase this value if you need to look up transforms that are older than 10 seconds. |
Plugin
TfBufferState is registered as a
pluginlib
plugin under the name yasmin_ros/TfBufferState. It can
be loaded from XML state machine files via the
YASMIN Factory.
Usage Examples
Python
Import TfBufferState from yasmin_ros and
add it as the first state in your state machine. Subsequent states
read tf_buffer from the blackboard to perform lookups.
import rclpy
from geometry_msgs.msg import TransformStamped
import yasmin
from yasmin import Blackboard, State, StateMachine
from yasmin_ros import TfBufferState, set_ros_loggers
from yasmin_ros.basic_outcomes import SUCCEED, ABORT
class LookupTransformState(State):
"""Looks up the transform from 'base_link' to 'map' using the shared buffer."""
def __init__(self):
super().__init__([SUCCEED, ABORT])
def execute(self, blackboard: Blackboard) -> str:
tf_buffer = blackboard["tf_buffer"]
try:
transform: TransformStamped = tf_buffer.lookup_transform(
"map", "base_link", rclpy.time.Time()
)
yasmin.YASMIN_LOG_INFO(
f"Transform: x={transform.transform.translation.x:.3f}"
f" y={transform.transform.translation.y:.3f}"
)
return SUCCEED
except Exception as exc:
yasmin.YASMIN_LOG_WARN(f"Transform lookup failed: {exc}")
return ABORT
def main():
rclpy.init()
set_ros_loggers()
tf_state = TfBufferState()
tf_state.set_parameter("cache_time_sec", 10.0)
tf_state.configure()
sm = StateMachine(outcomes=["done", "failed"])
sm.add_state("INIT_TF", tf_state,
{SUCCEED: "LOOKUP", ABORT: "failed"})
sm.add_state("LOOKUP", LookupTransformState(),
{SUCCEED: "done", ABORT: "failed"})
outcome = sm()
yasmin.YASMIN_LOG_INFO(outcome)
rclpy.shutdown()
if __name__ == "__main__":
main()
C++
Include yasmin_ros/tf_buffer_state.hpp and add
TfBufferState to your state machine. The blackboard
stores std::shared_ptr objects so they remain alive for
the lifetime of the state machine execution.
#include <memory>
#include <string>
#include <rclcpp/rclcpp.hpp>
#include <tf2_ros/buffer.hpp>
#include <tf2_ros/transform_listener.hpp>
#include "yasmin/blackboard.hpp"
#include "yasmin/logs.hpp"
#include "yasmin/state.hpp"
#include "yasmin/state_machine.hpp"
#include "yasmin_ros/basic_outcomes.hpp"
#include "yasmin_ros/tf_buffer_state.hpp"
#include "yasmin_ros/yasmin_node.hpp"
// A state that uses the shared tf buffer to look up a transform.
class LookupTransformState : public yasmin::State {
public:
LookupTransformState()
: yasmin::State({yasmin_ros::basic_outcomes::SUCCEED,
yasmin_ros::basic_outcomes::ABORT}) {}
std::string execute(yasmin::Blackboard::SharedPtr blackboard) override {
auto tf_buffer =
blackboard->get<std::shared_ptr<tf2_ros::Buffer>>("tf_buffer");
try {
auto transform = tf_buffer->lookupTransform(
"map", "base_link", tf2::TimePointZero);
YASMIN_LOG_INFO("x=%.3f y=%.3f",
transform.transform.translation.x,
transform.transform.translation.y);
return yasmin_ros::basic_outcomes::SUCCEED;
} catch (const tf2::TransformException &ex) {
YASMIN_LOG_WARN("Transform lookup failed: %s", ex.what());
return yasmin_ros::basic_outcomes::ABORT;
}
}
};
int main(int argc, char *argv[]) {
rclcpp::init(argc, argv);
yasmin_ros::set_ros_loggers();
auto sm = std::make_shared<yasmin::StateMachine>(
std::vector<std::string>{"done", "failed"});
sm->add_state("INIT_TF",
std::make_shared<yasmin_ros::TfBufferState>(),
{{yasmin_ros::basic_outcomes::SUCCEED, "LOOKUP"},
{yasmin_ros::basic_outcomes::ABORT, "failed"}});
sm->add_state("LOOKUP",
std::make_shared<LookupTransformState>(),
{{yasmin_ros::basic_outcomes::SUCCEED, "done"},
{yasmin_ros::basic_outcomes::ABORT, "failed"}});
try {
std::string outcome = (*sm)();
YASMIN_LOG_INFO("%s", outcome.c_str());
} catch (const std::exception &e) {
YASMIN_LOG_WARN("%s", e.what());
}
rclcpp::shutdown();
return 0;
}
XML (Factory)
TfBufferState is available as a plugin so it can be
referenced directly in XML state machine definitions loaded by the
YASMIN Factory.
<StateMachine outcomes="done failed">
<State name="InitTF"
type="cpp"
class="yasmin_ros/TfBufferState">
<Param name="cache_time_sec" value="10.0"/>
<Transition from="succeeded" to="Lookup"/>
<Transition from="aborted" to="failed"/>
</State>
<!-- Add your transform-using states here, reading tf_buffer
from the blackboard. -->
</StateMachine>
Design Notes
-
Single listener per state machine:
TfBufferStateis intended to run once at the start of a state machine. All subsequent states share the sametf_bufferandtf_listenerobjects stored on the blackboard, avoiding the overhead of starting multiple listeners. -
Lifetime management: The
tf_listenermust remain alive for transforms to continue being populated. Because YASMIN keeps the blackboard alive for the entire execution, the listener is automatically kept alive as long as its entry is not removed from the blackboard. -
Cache time: The default cache time is 10 seconds.
For applications that need to look up older transforms (e.g. when
processing historical sensor data), increase
cache_time_secaccordingly. -
Thread safety:
tf2_ros::Bufferis thread-safe. It can be accessed from states running in parallel inside a Concurrence container.
YASMIN