Skip to main content
Version: QTrobot V3

Connection

If you already have a ROS2-based system and would rather work with native ROS2 services/topics than the Python or TypeScript SDKs, the QTrobot ROS2 gateway bridges the two worlds: it connects to the robot's service hub like any other client, and re-exposes every capability as standard ROS2 interfaces under the /qtrobot/ namespace. Every other tutorial in this section assumes the gateway from this page is already running.

Install the gateway

git clone https://github.com/luxai-qtrobot/qtrobot-service-hub-gateway-ros.git
mkdir -p ~/ros2_ws/src
ln -s ~/qtrobot-service-hub-gateway-ros/qtrobot_interfaces ~/ros2_ws/src/qtrobot_interfaces

cd ~/ros2_ws
source /opt/ros/jazzy/setup.bash
colcon build --packages-select qtrobot_interfaces

Only the qtrobot_interfaces package (the message/service definitions) needs a colcon build — the gateway itself is a plain Python package, run directly:

cd ~/qtrobot-service-hub-gateway-ros
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

Launch the gateway

In every new terminal:

source ~/qtrobot-service-hub-gateway-ros/.venv/bin/activate
source /opt/ros/jazzy/setup.bash
source ~/ros2_ws/install/setup.bash
cd ~/qtrobot-service-hub-gateway-ros

Then start the gateway, pointing it at your robot's IP address:

python -m gateway.qtrobot_ros2 --ros-args -p robot_ip:=<ROBOT_IP> -r __ns:=/qtrobot

Confirm it's running by listing the new services:

ros2 service list | grep qtrobot

You should see entries such as /qtrobot/tts/engine/say/text, /qtrobot/face/emotion/show, /qtrobot/motor/list, and many more. The camera/IMU services come from a second, independent gateway process — see Camera.

Calling a service from the command line

Before writing any code, it's worth trying a service directly:

ros2 service call /qtrobot/tts/engine/say/text qtrobot_interfaces/srv/TtsEngineSayText \
"{text: 'Hello, I am QTrobot.'}"

Calling a service from a ROS2 node

Here's a minimal Python node that calls the same service:

#!/usr/bin/env python3
import rclpy
from rclpy.node import Node
from qtrobot_interfaces.srv import TtsEngineSayText


class TtsClient(Node):
def __init__(self):
super().__init__('tts_client')
self.client = self.create_client(TtsEngineSayText, '/qtrobot/tts/engine/say/text')
self.client.wait_for_service(timeout_sec=5.0)

def say(self, text: str):
request = TtsEngineSayText.Request()
request.text = text
future = self.client.call_async(request)
rclpy.spin_until_future_complete(self, future)
return future.result()


def main():
rclpy.init()
node = TtsClient()
response = node.say('Hello, I am QTrobot.')
node.get_logger().info(f'TTS call status: {response.status}')
node.destroy_node()
rclpy.shutdown()


if __name__ == '__main__':
main()

Every QTrobot service follows this same shape: a typed Request, a typed Response with at least a status field telling you whether the call succeeded, and the usual wait_for_service / call_async / spin_until_future_complete rclpy pattern. Subscribing to a topic follows the standard rclpy subscription pattern instead — see Motor or Microphone for examples.

Next steps

Continue with the TTS tutorial, or see the full service/topic/message reference in the ROS2 API Reference.