On this page

Choosing a Communication Pattern

ROS 2 gives nodes four ways to exchange information, and the Qt ROS2 Bridge exposes all four directly in QML. Picking the right one is almost always a question about the shape of the interaction — how many participants there are, whether the caller expects an answer, and how long the work takes — not about the data itself. This guide describes each pattern, when it fits, and the QML types that implement it.

At a glance

PatternDirectionReturns a result?Best for
TopicMany-to-many, one-way streamNoContinuous or event-driven data with no reply: sensor readings, poses, velocity commands, status.
ServiceOne-to-one request/responseYes, once, quicklyA short call that must return an answer: a query, or a command that reports whether it succeeded.
ActionOne-to-one goal with feedbackYes, eventually, with progressA long-running task you want to monitor and be able to cancel: navigation, motion, a scripted routine.
ParameterNamed, typed configuration valuen/a (read/write state)Tuning knobs and configuration that change rarely and belong to a node, not to a message stream.

A useful rule of thumb: if the sender does not care who is listening and never waits for a reply, use a topic. If it needs an answer and the work is quick, use a service. If the work takes long enough that you want progress updates or a cancel button, use an action. If you are describing how a node should behave rather than sending it data, use a parameter.

Topics: one-way streams

A topic is an anonymous, many-to-many channel. Any number of publishers can write to it and any number of subscribers can read from it; neither side knows the other exists, and a publisher never blocks or waits for acknowledgement. This makes topics the right choice for data that flows continuously or fires on events — laser scans, camera frames, odometry, joint states, velocity commands — and for anything where "fire and forget" is acceptable.

In QML each message type has a generated <Msg>Publisher and <Msg>Subscriber, declared as children of a Node:

import QtRos2.GeometryMsgs

Node {
    nodeName: "example_node"

    PoseStampedPublisher {
        id: posePublisher
        topic: "/pose"
    }

    PoseStampedSubscriber {
        topic: "/other_pose"
        onMessageReceived: console.log(message.pose.position.x)
    }
}

Topics carry no delivery guarantee by default; you tune reliability, history, and durability through QualityOfService on the entity's qos property. Because there is no reply, a subscriber that misses a message simply never sees it — if you need to know that the other side acted, you want a service or an action instead.

See: Simple Publisher, Simple Subscriber.

Services: request and response

A service is a one-to-one call: the client sends a request and receives exactly one response. Use it when the caller genuinely needs the answer and the work is quick and bounded — a query ("what is the current map?"), or a command whose outcome matters ("switch the lamp on; did it work?"). Services have no separate error channel: failure is reported in-band, as ordinary fields of the response value.

Both ends are declared as children of a Node. A client can call by binding its request property or imperatively with callService(), which returns a JavaScript promise:

import QtRos2.StdSrvs

SetBoolServiceClient {
    id: lampClient
    topic: "/lamp"
}
// ...
lampClient.callService(true).then(response => {
    console.log(response.success, response.message)
})

A server answers each request, either from a declarative response binding or from a handler callback. It never blocks the GUI thread, and it can defer a reply and send it later.

A service is the wrong tool if the work can take a long time: the caller is left waiting with no progress and no way to cancel, and a slow server ties up the call. For that, reach for an action.

See: Simple Service, Simple Service Client, Qt ROS2 std_srvs QML Types.

Actions: long-running goals

An action is built on top of topics and services to manage a task that takes time. The client sends a goal; the server accepts or rejects it, streams feedback while it works, and finally reports a result. The goal can be canceled, and its status is observable throughout. Use an action whenever a request may take seconds or longer and you want a progress indicator, a cancel button, or both — navigation goals, arm motions, scripted sequences.

The generated <Action>ActionClient exposes sendGoal() (returning a promise), a feedback property, an ActionState, and cancelGoal():

navClient.sendGoal({ x: 2.0, y: 1.0 })   // goal message fields
// navClient.feedback updates as the server reports progress
// navClient.state reflects Accepted, Succeeded, Canceled, Aborted, ...

The generated <Action>ActionServer emits goalReceived with a goal handle whose methods — publishFeedback(), succeed(), abort(), canceled() — drive the goal to completion and whose cancelRequested property lets long work bail out cleanly.

Reserve actions for genuinely long or cancelable work. For a call that returns immediately, the extra goal/feedback/result machinery is overhead a service avoids.

See: TurtleSim Controller.

Parameters: node configuration

Parameters are named, typed values that configure a node — thresholds, frame names, gains, feature switches. They are not a data channel: they change rarely, they belong to a specific node, and they persist as that node's state rather than flowing as messages. Reach for a parameter when you are describing how a node should behave, and for a topic, service, or action when you are moving data or commands.

The bridge covers both sides of the parameter system:

  • Parameter declares and owns a parameter on the local node, with a type, description, optional read-only flag, and range constraints. It reports external changes so the UI can follow them.
  • RemoteParameter gets and sets a parameter on another node by name, tracking the remote value live — the natural way to build a settings panel for a driver or controller running elsewhere on the graph.
import QtRos2.Core

RemoteParameter {
    remoteNode: "/camera_driver"
    name: "exposure"
    // value is two-way: reading reflects the remote node,
    // writing pushes the new value to it
}

Combining patterns

Real applications use several patterns at once, and a single logical feature often spans more than one. A navigation panel might set a planner's tuning through parameters, dispatch a route as an action so it can show progress and offer a cancel button, publish manual overrides on a cmd_vel topic, and query the current map through a service. Choose each channel independently, by the shape of that specific interaction.

Two cross-cutting concepts apply regardless of the pattern you pick:

  • QualityOfService governs delivery semantics for topics (and the underlying transport of services and actions) — reliability, history depth, and durability.
  • transforms (TF) carry the spatial relationships between coordinate frames, which most of the data on your topics is expressed in.

Where to go next

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.