Skip to main content
Version: QTrobot V3

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.