Motor
The /qtrobot/motor/... services and topics list motors, control torque/homing/velocity, and stream joint state, errors, and commands. These examples assume the gateway from Connection is already running.
List motors
ros2 service call /qtrobot/motor/list qtrobot_interfaces/srv/MotorList "{}"
Torque on/off and homing
ros2 service call /qtrobot/motor/on/all qtrobot_interfaces/srv/MotorOnAll "{}"
ros2 service call /qtrobot/motor/move/home/all qtrobot_interfaces/srv/MotorMoveHomeAll "{}"
ros2 service call /qtrobot/motor/on qtrobot_interfaces/srv/MotorOn "{motor: 'HeadYaw'}"
ros2 service call /qtrobot/motor/off qtrobot_interfaces/srv/MotorOff "{motor: 'HeadYaw'}"
ros2 service call /qtrobot/motor/move/home qtrobot_interfaces/srv/MotorMoveHome "{motor: 'HeadYaw'}"
Set velocity
ros2 service call /qtrobot/motor/velocity/set qtrobot_interfaces/srv/MotorVelocitySet \
"{motor: 'HeadYaw', velocity: 50}"
Subscribing to joint state
Before writing a node, it's often useful to peek at a topic's live data and exact type from the command line:
ros2 topic echo /qtrobot/motor/joints/state/stream
ros2 interface show qtrobot_interfaces/msg/MotorJointsStateFrame
#!/usr/bin/env python3
import rclpy
from rclpy.node import Node
from qtrobot_interfaces.msg import MotorJointsStateFrame
class JointStateListener(Node):
def __init__(self):
super().__init__('joint_state_listener')
self.subscription = self.create_subscription(
MotorJointsStateFrame,
'/qtrobot/motor/joints/state/stream',
self.on_joint_state,
10,
)
def on_joint_state(self, msg: MotorJointsStateFrame):
for joint in msg.joints:
self.get_logger().info(
f'{joint.name}: position={joint.position:.1f} deg, '
f'velocity={joint.velocity:.1f} deg/s, temperature={joint.temperature:.1f} C'
)
def main():
rclpy.init()
node = JointStateListener()
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
MotorJointsStateFrame carries a joints array of MotorJointState entries (name, position, velocity, effort, voltage, temperature), published at 10 Hz.
Subscribing to joint errors
ros2 topic echo /qtrobot/motor/joints/error/stream
MotorJointsErrorFrame follows the same shape as joint state, but only publishes on change, carrying per-joint error flags (overload, voltage, temperature, sensor).
Sending joint commands
Instead of motor/velocity/set, you can drive joints continuously by publishing MotorJointsCommandFrame messages to the command stream:
#!/usr/bin/env python3
import rclpy
from rclpy.node import Node
from qtrobot_interfaces.msg import MotorJointsCommandFrame, MotorJointCommand
class JointCommander(Node):
def __init__(self):
super().__init__('joint_commander')
self.pub = self.create_publisher(MotorJointsCommandFrame, '/qtrobot/motor/joints/command/stream', 10)
def send(self, joint: str, position: float, velocity: float = 40.0):
cmd = MotorJointCommand(name=joint, position=position, velocity=velocity)
self.pub.publish(MotorJointsCommandFrame(joints=[cmd]))
def main():
rclpy.init()
node = JointCommander()
node.send('HeadYaw', position=15.0, velocity=40.0)
node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
MotorJointsCommandFrame carries a joints array of MotorJointCommand entries (name, position, velocity) — publish as many joints per message as you want to move together.
Next steps
See the full motor namespace in the ROS2 API Reference, or head back to the Tutorials overview to explore Python or TypeScript/Node.js.