ACCUEILCAS D'USAGESPLATEFORMEMATÉRIELACCOMPAGNEMENTACADÉMIENOUS
Hengbot Sirius

Hengbot Sirius: developer documentation

The robot's real protocol, its sensors, and the four traps that cost you days.

The Hengbot Sirius is sold as code-friendly, but the vendor documentation, as of July 2026, stops at motion: no sensor readout at all, and the official spec sheet does not even mention the camera. This page collects what we established while mapping the robot, and verified live on the machine. Everything marked as verified was observed on a real Sirius; anything still uncertain is flagged as such.

Last updated 26 July 2026

Official Hengbot visual of the Sirius quadruped robot, three-quarter view, standing on all four legs

1In short

If your Sirius accepts your commands and still does nothing useful, the answer is almost always in this list. All four traps below produce the same symptom: a robot that acknowledges the command, echoes the value back faithfully, and marches in place.

  1. The speed unit: commands are normalised to [-1, 1], they are not m/s. Sending 0.15 asks for 15 % of full travel, not 0.15 m/s.
  2. The robot mode: out of the box the robot is in desktop, a mode that deliberately limits gait so it will not walk off a table.
  3. Action priority: an ACTION_PLAY sent at priority 1 gets overridden by the running autonomous behaviour. You need priority 5.
  4. Sleep: after a period of inactivity the robot crouches and stops doing anything useful. Playing the stand-up action puts it back on its feet and working, remotely.

And a fifth trap on the protocol side: WebSocket command names are uppercase. The lowercase names found in the binaries are internal handlers, not what travels on the wire.

The most unpublished topic here is the ToF sensor: its actual position, its grid, its units and its self-occlusion appear nowhere else, and they are what makes the free cliff detection described below possible.

2What the robot actually is

Under the shell, the Sirius is a small Linux computer (D-Robotics RDK X3, around 5 TOPS) running ROS 2 Humble. Its entire behaviour is a set of ROS 2 nodes talking to each other. The application lives in /root/sirius_ros2 and starts at boot through ros2_launch.service.

There are two levels of control, and the choice matters for mechanical safety.

LevelWhatUse it for
High levelWebSocket :8765, ROS 2 services and topicsWalking, playing an action, reading gestures and vision. Goes through the robot's own protections (kinematic modes, per-action torque, balance).
Low levelUDP :8768 and up, Play_Keyframe packetsStreaming poses frame by frame, the route used by the Blender add-on. Bypasses those protections: only use it once you know the joint limits.

The nodes that actually run

ros2 node list returns 31 entries, two of them duplicates (core_api_node and camera_publisher_node appear twice): 29 distinct nodes. The ones that matter:

  • core_api_node: the WebSocket server on :8765, the one answering all 59 commands.
  • sirius_motion_control_node: motion. It consumes /gait_generation_trot/cmd_vel and publishes filtered_velocity, along with the /kinematics/* topics.
  • behavior_engine_node: autonomous behaviour, emotions, decision tree. It absorbed what the 2.3.6 package split between robot_behavior_controller and emotion_manager.
  • perception_bridge_node, face_detection_bridge, camera_publisher_node: visual perception.
  • state_sensor_tof_node: the distance sensor, subject of a whole section below.
  • imu_onbody, internal_sensor_battery_onbody: IMU and battery.
  • action_player_node: plays the named actions in /root/material/actions/.
  • lvgl_gui_node: the head screen, touch included.
  • sirius_gamepad and xbox_bluetooth_gamepad on topic /joy: Xbox controllers are supported natively, which the documentation never mentions.
  • And the rest of the machinery: lifecycle_manager_node, ota_update_node, network_config_node, sync_manager_node, motor_torque_controller_node, fan_controller_node, modbus_driver, wmix_audio_player_node, ai_interaction_node, character_state_node, user_data_manager, user_interface_udp_server_node, production_test_node, sirius_bt, ble_control_server, robot_led_controller_node.
Download the raw ROS inventoryTaken 25 July 2026 at 23:37 on the machine · plain text, 18 KB
Nodes, topics, message types: the source of everything this page claims about the architecture.

Official spec sheet

  • 250 x 130 x 250 mm, 960 g, 300 g payload.
  • Stated battery life 45 to 50 min in active use.
  • Stated top speed 0.4 m/s (see below: three contradictory figures are in circulation).
  • No sensor is listed in the Specifications section. No ToF, no IMU, not even the camera.

3Versions and firmware

Knowing which version you are on conditions everything else: between the source package the vendor ships with its tools and the firmware actually installed, the architecture changed.

ItemValueHow we know
Our robot's firmware2.5.0 beta, installed 23/07/2026Verified: the robot's Update Center, "System is up to date"
Latest stable release2.4.8, 17/07/2026Verified: RELEASE channel on the same page
On-board APISirius Core API 4.0.0Verified: announced by the robot in server_info at handshake
Source package shipped with the OTA tools2.3.6Verified: older than the installed firmware, and the gap is documented by the vendor itself
App build quoted in the official documentation240726Documented: likely a July 2024 build, which dates the documentation

What the changelog taught us

  • 2.5.0, 23 July 2026: "removed behavior and emotion nodes, merged into a behavior tree driver node". That is the exact explanation for the mismatch with package 2.3.6.
  • 2.4.7, 2 July 2026: switch to WebRTC for web video streaming, and addition of human skeleton detection. So the documentation calling the camera "unavailable" is not wrong: it predates this.
  • 2.4.7 and 2.4.9: swipe up unlocks the screen, swipe down puts it on standby, described as a "wake gesture". That settles a long open question, see trap 3.
  • 2.4.6, 10 June 2026: torque overload protection added to the low-level firmware. One more net, beneath the joint clamps and beneath any software cut-off.
  • 2.4.5, 3 June 2026: "API interface protocol updated". This is probably where the uppercase protocol described on this page comes from, and the divergence from the API the vendor documents.

Reading the version from the robot, if you prefer a shell to the console:

ssh root@<ROBOT_IP>
source /opt/ros/humble/setup.bash
source /root/sirius_ros2/install/setup.bash
export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp
[ -f /root/cyclonedds.xml ] && export CYCLONEDDS_URI=file:///root/cyclonedds.xml

ros2 topic echo --once /esp32/firmware_version   # microcontroller version
ros2 topic echo --once /ota/status               # update system state

The vendor console

Its navigation bar lists nine tools, several of them documented nowhere: Material Manager, Inner World, System Update, Node Management (ROS node management, with a log since 2.4.5), Gait Debug, Group Dance, Net Debug, Behavior Tree and Timeline Editor. A firmware rollback is available there too. We only explored two of them: the rest are open leads, not established facts.

4The seven communication channels

The robot exposes seven distinct entry points. The WebSocket is the main one: it pushes state continuously and answers requests. But the one that matters most to a developer is the last in the list.

ChannelAddressRoleStatus
WebSocketws://<IP>:8765?audience=webMain channel: pushed state and request/responseVerified
Video WebSocketws://<IP>:8766WebRTC camera signalling, no URL parameterVerified
RESThttp://<IP>:8088/api/v1/…AI configuration, credentials, skills, logsVerified
UDP<IP>:8768 (motion and LED), :8770 (eyes)Play_Keyframe pose streaming, the Blender routeDocumented
MJPEGhttp://<IP>:8080/video_streamPlain image stream, alternative to WebRTCTo confirm, credit dspeers
SSHroot@<IP>:22ROS 2 access and deploymentVerified
ROS 2on board, RMW CycloneDDS29 nodes, around 130 topics: the only access to the sensorsVerified

The ?audience=web parameter appears in the official web interface URL. Connecting without it works, but there is no reason to diverge from what the robot itself does.

WebSocket envelopes

// handshake received on connect
{"type":"event","event_type":"connection_info",
 "data":{"client_id":30,"status":"connected",
   "server_info":{"name":"Sirius Core API","version":"4.0.0",
     "architecture":"Service-based",
     "capabilities":["play_motion","status_monitoring","factory_test","ota_update"]}}}

// request
{"type":"request","request_type":"<NAME>","request_id":"<id>","data":{}}

// response
{"type":"response","request_id":"<id>","success":true,"code":"ok","data":{},"error":""}

// heartbeat, sent by the client
{"type":"ping","data":{"timestamp":0}}

Error codes seen in the wild: invalid_request, not_found (with an "Unknown request_type" message), invalid_argument, service_unavailable. Probing an unknown command name has no effect on the robot, so the API can be explored safely.

The pushed event stream

EventRatePayload
gait-trajectory10 Hzfiltered_velocity: what the gait generator actually applies
motor-load1 Hz14 motor loads, range ±1000, unit is PWM per mille
motor-temperature1 Hz4 legs, but returns 0 on this firmware (probes are silent)
battery-status1 Hzpercentage is a 0 to 1 ratio, plus voltage, current, temperature
emotion-update1 Hzemotion state, valence, arousal, satiety, fatigue
behavior-status1 Hzactive behaviour tree, intent, recent events (including head taps)
vision-detection~30 Hzdetections and skeletons, see the vision section
system_metrics1 HzCPU, load average, disk
lifecycle_update~0.1 Hzevery ROS node and its state

5Connecting to the robot

Finding its IP address

  • On the head screen, Network menu.
  • From your router's lease list.
  • Through the robot's hotspot mode: SSID sirius_xx, password hengbot123 (published by the vendor in its own manual), gateway 192.168.233.1.

Your machine and the robot must sit on the same network. The vendor's debug interface is a page hosted by Hengbot, not by the robot: http://8.163.38.44:8082/connect?robot=<ROBOT_IP>. It then opens a WebSocket straight to your robot on :8765. It gives you the virtual joystick, action playback, vision and autonomous mode. It is the fastest way to confirm everything responds before writing a single line of code, and it is that test which put us on the trail of trap 1.

SSH access

ssh root@<ROBOT_IP>

# load the ROS 2 environment, otherwise no node is visible
source /opt/ros/humble/setup.bash
source /root/sirius_ros2/install/setup.bash
export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp
[ -f /root/cyclonedds.xml ] && export CYCLONEDDS_URI=file:///root/cyclonedds.xml

ros2 node list      # 31 nodes
ros2 topic list     # around 130 topics

6Trap 1: speed is normalised, not in m/s

The linear_x, linear_y and angular_z fields expect a value in [-1, 1]: a fraction of full travel, not metres per second. The forward key in the official interface simply sends linear_x: 1.

{"type":"request","request_type":"gait_control",
 "data":{"linear_x":1,"linear_y":0,"angular_z":0}}

Sending 0.15 while thinking you are asking for 0.15 m/s actually asks for 15 % of top speed: the stride collapses and the robot walks on the spot. Measured orders of magnitude:

Command valueObserved behaviour
0.10marches in place
0.45 to 0.50walks properly

To convert a real speed into a command value, using the operating envelope read off the robot: linear_x = vx / 0.24 forward, vx / 0.16 backward, linear_y = vy / 0.20, angular_z = wz / 1.20.

The proof, on the ROS 2 side

input  : /gait_generation_trot/cmd_vel           geometry_msgs/msg/Twist
return : /gait_generation_trot/filtered_velocity geometry_msgs/msg/Twist

command vx=+0.50  ->  measured vx=+0.500   (ratio 1.00)

The command value comes back unchanged on filtered_velocity, yet 0.50 exceeds the robot's declared top speed. So it cannot be metres per second. filtered_velocity is the arbiter: it reports what the generator really applies, and lets you tell "the robot refuses" apart from "you are asking for too little".

Three contradictory top speeds

SourceStated forward speed
Official spec sheet0.4 m/s
WebSocket API documentation0.28 m/s
ros2 param dump on the robot0.24 m/s

The last figure governs actual behaviour, since it comes from the parameters loaded by the control node. Full envelope read on the machine: forward 0.24 m/s, backward 0.16 m/s, lateral ±0.20 m/s, rotation ±1.2 rad/s, pitch and roll ±0.524 rad. The joint_clamp_enabled and ws_clamp_enabled guards are active: an out of range command is clamped, not driven into the hard stop.

7Trap 2: command names are uppercase

This is the main trap of the WebSocket protocol. The wire uses uppercase keys, for instance BEHAVIOR_SET_PAUSE. The lowercase names you find while digging through the binaries, such as set_behavior_pause, are the internal handlers, not what travels.

Some commands accept both forms as legacy aliases: play_motion, gait_control, get_status and get_actions answer in lowercase. But mode commands exist in uppercase only. Probing set_behavior_pause on the wire returns "Unknown request_type" and wrongly suggests the feature is unreachable.

The full protocol holds 59 commands, extracted from the official bundle. The most useful ones:

Wire keyInternal handlerPurpose
ACTION_PLAYplay_motionplay a named action or a file
ACTION_STOP / ACTION_STOP_ALLcancel_motioninterrupt
ACTION_GET_LISTget_actionslist the robot's action library
BEHAVIOR_SET_PAUSEset_behavior_pausepause autonomous behaviour
BEHAVIOR_SET_RANDOM_ACTIONenable_random_actionspontaneous idle animations
USER_GET_ROBOT_MODE / USER_SET_ROBOT_MODEget/set_robot_modeground or desktop mode (trap 3)
MOTOR_SET_TORQUE / MOTOR_SET_THERMAL_PROTECTIONtorque and thermal protection
VISION_SET_DETECTIONenable_detectionenables perception and the video stream
VISION_SET_FACE_TRACKINGface_tracking_controlface tracking
BATTERY_GET_STATUSget_battery_statusbattery state

Full families are present too: EMOTION_*, USER_* (theme, MBTI, language, node parameters), LIFECYCLE_*, NETWORK_*, OTA_*, SYNC_*, MATERIAL_*.

8Trap 3: ground versus desktop mode

The second classic cause of "it marches without moving", after the speed unit. The robot has two operating modes, and ships in the one that limits walking.

USER_GET_ROBOT_MODE  {}                        -> {"robot_mode":"desktop"}
USER_SET_ROBOT_MODE  {"robot_mode":"ground"}   -> {"robot_mode":"ground"}
  • desktop: per the firmware, "limits large movements and gait to prevent the robot from falling off the table". The legs move on the spot.
  • ground: "enables all actions, including gait and large displacements".
The Sirius Studio mode selector set to DesktopThe same selector set to Ground
The Ground / Desktop selector in both states, as it appears in our control tool.

The head screen displays a state called "Ground", and the robot switches into it by itself after a period of inactivity. A vertical swipe on the screen releases that state, but it is not a robot mode change. The vendor release notes are explicit: swiping up unlocks the screen and acts as a wake gesture (versions 2.4.7 and 2.4.9), swiping down puts it on standby. Ground versus desktop mode only changes through USER_SET_ROBOT_MODE, or from an interface.

The official documentation also describes a Developer mode on the head screen, which "suspends the interface to allow Python programming through the APIs". We have not tested it. Navigating that screen is done with the ear buttons, confirmation by tapping the skull.

9Trap 4: the sensor is not where you think

For ten sessions, our robot in autonomous wandering mode stopped dead in front of an obstacle 191 mm away. Always 191 mm, with 5 mm of standard deviation. An obstacle that did not exist: it was running away from its own jaw.

The ToF sensor is not in the head, as the position of the robot's "mouth" suggests. It sits under the neck, high on the chest. Mounted that high and that far forward, it sees its own chin at the top of its field, and its own front legs at the bottom.

The measurement that settles it

With the head tilted ±0.30 rad (17°) on its pitch axis, ToF distances varied by only 1 to 2 mm. A sensor mounted in the head would see its floor distances shift by tens of millimetres for such a tilt.

Head axisBottom row shiftTop row shift
x (pitch, head visibly moves)1 mm2 mm
y (yaw)0 mm0 mm
z0 mm0 mm

The consequences are almost all favourable. ToF geometry depends only on body attitude, which the gait generator already regulates: there is no head angle to track in your own maths. And the head can look elsewhere, follow a face, without disturbing navigation. In exchange, the field is fixed relative to the body: you cannot steer the sensor to sweep the sides, you have to turn the whole body.

Self-occlusion by the legs

RowAt restHand in frontDrop
12-15 (top)866 mm168 mm79 %
8-11838 mm170 mm81 %
4-7334 mm211 mm37 %
0-3 (bottom)237 mm195 mm18 %, occluded

A hand waved in front of the sensor drops the measured distances by 80 % on the top rows, but by only 18 % on the bottom row: the front legs already fill that part of the field. And while walking they swing, entering and leaving the field with every stride. Hence two design rules: never base a detection on row 0-3 alone, and learn the background while walking, never at a standstill.

Full ToF characterisation

PropertyMeasured value
Topic/state_sensor/tof/distance_array
Typestate_sensor_tof/msg/ToFDistanceArray
Unitmillimetre
Range11 to 2047 mm, where 2047 means no target
Rateabout 38 Hz (384 frames in 10 s)
Zones16, on a 4 x 4 grid, all live

The same reading is also published as a 4 x 4 mono16 image on /state_sensor/tof/heatmap. The raw_registers field (11 values, often 65535) is vendor debug output, of no use.

        top    [12] [13] [14] [15]
               [ 8] [ 9] [10] [11]
               [ 4] [ 5] [ 6] [ 7]
        bottom [ 0] [ 1] [ 2] [ 3]
                left            right

Resting signature

robot standing, head neutral, clear field

   ..   765    ..    ..      <- horizon: out of range
  780   786    ..    ..      <- distant floor
  304   295   291   298      <- floor at ~30 cm
  198   219   218   201      <- floor at ~20 cm, right in front of the legs

The bottom rows see the floor at increasing distance. That is where the two readings that underpin any autonomous navigation come from, and the next section with them.

10Free cliff detection

This is the single most useful finding of the whole effort, and it needs no extra hardware. Since the bottom ToF rows permanently see the floor at 20 and 30 cm, all you have to do is watch for it disappearing.

  • Obstacle: the top rows stop saturating and the bottom rows shorten.
  • Cliff (table edge, step, stairs): the bottom rows, which were seeing the floor, jump abruptly to 2047, meaning "no target".

The implementation principle we settled on: the node learns its background over five seconds of walking in a straight line, as a per-zone minimum and maximum envelope. From then on it only reacts to deviations. What shortens is an obstacle, what lengthens is a void.

The guardrails that turned out to be necessary

  • A cliff overrides every other decision.
  • Only the bottom rows may declare a cliff, and only the zones that were seeing the floor at close range (under 50 cm) and nearly always (80 % of frames).
  • Any decision must persist for three cycles before being acted on: the trot makes the body pitch, and a single frame proves nothing.
  • Reversing is capped at half the forward speed and limited to 1.5 s, because the robot sees nothing behind it.
  • A dead-end rotation lasting more than 5 s triggers a reverse and a change of direction, otherwise the robot spins forever.
  • The node publishes ten zero commands on shutdown, including on SIGHUP. Validated for real during a Wi-Fi outage: the robot stops cleanly instead of coasting on its last command.

The version history says it all: endless reversing in v1 (only 8 zones learned, the rest screaming that something had appeared), phantom cliffs in v6 (saturated far zones taken for precipices), endless spinning in v7, and v8 which exposed the normalised speed trap. v9 slips through and dodges. v10 held 11 s of straight walking and survived an SSH drop.

v12 masks a narrow 170 to 225 mm band at the top of the field, the jaw signature, but only when the bottom rows do not corroborate, so a low shelf stays visible. It also replaces the fixed 120 mm margin with a margin proportional to the learned background (30 %, floored at 50 mm): with a 200 mm background, a fixed 120 mm margin made the bottom rows unable to trigger anything at all. Thirty-four offline tests replay real frames captured on the robot.

11Making the robot walk

Two routes, depending on whether you work from your machine (WebSocket) or from the robot itself (ROS 2). Either way, reread traps 1 and 3 first: they account for most cases where the robot refuses to move.

Over the WebSocket

CommandPayloadEffect
gait_control{linear_x, linear_y, angular_z} in [-1, 1]continuous walking
gait_step_move{linear_x, linear_y, angular_z, steps}walks a given number of strides, counted by the robot itself
set_motion_mode{mode, mode_name}DEFAULT, SLOW, WALK, PRECISION, CLIMB, FAST_RUN profiles
self_recover{}stand up after a fall

Over ROS 2, from the robot

import rclpy
from rclpy.node import Node
from geometry_msgs.msg import Twist

# WARNING: linear.x is NORMALISED in [-1, 1], not m/s.
# 0.10 marches in place. 0.45 actually walks.

rclpy.init()
node = Node('hello_move')
pub = node.create_publisher(Twist, '/gait_generation_trot/cmd_vel', 10)

cmd = Twist()
cmd.linear.x = 0.45
for _ in range(30):
    pub.publish(cmd)
    rclpy.spin_once(node, timeout_sec=0.1)

# always republish zeros when stopping
cmd.linear.x = 0.0
for _ in range(10):
    pub.publish(cmd)
    rclpy.spin_once(node, timeout_sec=0.05)

The /gait_generation_trot/step_move service (type gait_generation_trot/srv/GaitStepMove, fields velocity and duration) takes a clean step and returns. It is the simplest first test: a negative velocity walks backward.

Standing up, lying down

ACTION_PLAY {
  "file_path":"/root/material/actions/stand_default_returnPosition_brief.avi",
  "loop":false,
  "priority":5,
  "torque":2047
}

Three details matter, and each one cost time: the action is returnPosition, not stand_default_idle; priority 5 is mandatory, because at priority 1 (the default) the running autonomous behaviour overrides the action and the robot stays down; and torque must be at maximum.

Sleep

After inactivity the robot enters the sleeping emotion state, with arousal near zero, and crouches. In that state, walking commands produce nothing useful. To wake it remotely, play the stand-up action from the previous subsection, at priority 5. Verified on the robot: it stands back up and obeys again.

What cannot be driven remotely is the emotion state itself: EMOTION_SET_STATE answers service_unavailable on this firmware, and EMOTION_INTERACTION rejects every type we tried (touch_tap, touch, pet, dog_bone). The distinction matters: you cannot change its mood, you can absolutely put it back to work.

12Gestures, vision and camera

Hand gestures: check this on your own firmware

A gesture recognition model exists on the D-Robotics side, and the sirius_full_v2.3.6 package references the topic /hobot_hand_gesture_detection (ai_msgs/PerceptionTargets), with a gesture attribute carrying an integer value.

ValueGestureValueGesture
2ThumbUp11Okay
3Victory12Thumb Left
4Mute13Thumb Right
5Palm (open hand)14Awesome

The vision-detection event

Once VISION_SET_DETECTION is active, the robot pushes an event on the main channel at around 30 Hz. This is the perception model output, available without touching the video stream: you can track without ever displaying an image.

{"type":"event","event":"vision-detection","data":{
  "detections":[
    {"class_id":0,"class_name":"body","type":"body","confidence":1,
     "rect":{"x":239,"y":1,"width":126,"height":192}},
    {"class_name":"head","rect":{}},
    {"class_name":"face","rect":{}}
  ],
  "skeletons":[
    {"track_id":1,"type":"body",
     "points":[{"x":191,"y":3,"score":0.859}]}
  ],
  "image_width":640,"image_height":360}}
  • Coordinates are in the model's 640 x 360 frame, not that of the displayed video. Rescale them, and account for letterboxing if the video uses object-fit: contain.
  • track_id persists across frames: this is tracking, not per-frame detection.
  • Classes observed so far: body, head, face. The "bone, ball, plush toy" objects in the firmware are virtual items of the emotion system, not vision classes.
  • At 30 Hz, never route these frames through React state: keep them in a ref and draw inside a requestAnimationFrame.
The real-time tracking panel in Sirius Studio, with a counter per detected class
The tracking panel lists a fixed set of classes with a counter each, and automatically reveals any unknown class that shows up. It is the simplest way to find out what the on-board model can really detect: leave it running in front of a ball, a hand, an animal, and watch whether a new line appears.

The camera, over WebRTC

This is not an image to fetch: it is a WebRTC stream negotiated over a second WebSocket. The video then flows directly between browser and robot, peer to peer. A backend can only relay signalling, never serve the stream.

  1. On :8765, send VISION_SET_DETECTION {enabled: true}. The robot answers "Web streaming enabled".
  2. The browser opens ws://<IP>:8766, with no URL parameter. This is what cost us the most time: video signalling does not live on the main channel.
  3. The robot sends {"type":"welcome","client_id":"ws_N"} there. That identifier is a string, distinct from the numeric client_id of the handshake. Reconstructing ws_<number> does not work.
  4. The browser sends webrtc_offer with that client_id, the robot replies webrtc_answer.
  5. Exchange webrtc_ice. The robot's candidates arrive prefixed with a=, which must be stripped before addIceCandidate.

If peer to peer is blocked by the network, a simpler fallback remains: the ROS service /camera/capture_jpeg, displaying successive stills. Camera services on the ROS side: /camera/capture_jpeg, /camera_publisher_node/enable_web_streaming, /camera_publisher_node/enable_yolo_shm.

13Controlling the head

The channel confirmed on the robot is a ROS input topic of the inverse kinematics node. It receives a command and publishes nothing back.

topic : /kinematics/ik_subscriber/head_euler_follow
type  : geometry_msgs/msg/Point
unit  : RADIANS

x = pitch (up / down)
y = yaw (left / right)
z = no observable effect
  • x drives pitch, confirmed visually.
  • y drives yaw.
  • z has no observable effect: the head has only two degrees of freedom.
  • From 0.10 to 0.35 rad the motion is progressive. Stay under 0.35 rad: beyond roughly 1 rad you hit the joint stop.

Useful reminder: since the ToF sits in the chest and not the head, moving the head does not disturb navigation. You can have the head track a face while the body navigates on the same, unaffected ToF field.

14What does not exist, and what the docs omit

This section exists to stop you spending hours looking for something that is not in the machine.

What does not exist

  • No head angle feedback: the topics exist but stay silent.
  • No motor temperature: the motor-temperature event returns 0 on this firmware, the probes are mute.
  • No remote control of the emotion state: EMOTION_SET_STATE answers service_unavailable. Waking the robot, on the other hand, works fine through the stand-up action.
  • No native clap detection, and no pointing finger in the gesture model.
  • No vision classes beyond body, head and face so far. The enable_yolo_shm service suggests a YOLO model exists elsewhere, unconfirmed.

What does exist, and the documentation ignores too

The previous list has a positive counterpart: the IMU is readable, even though the spec sheet mentions it no more than any other sensor.

/state_sensor/imu_onbody/imu_publisher/imu_data    sensor_msgs/msg/Imu
/state_sensor/imu_onbody/imu_publisher/imu_angle   geometry_msgs/msg/Vector3
/state_sensor/imu_onbody/imu_publisher/diagnostic  "IMU sensor is operating normally"

imu_angle acts as a guardrail in our wandering node: if body attitude drifts more than 0.10 rad from the learned one, the ToF geometry is no longer valid and every cliff decision is suspended. A concrete case of one undocumented sensor being used to make another undocumented sensor safe.

What the official documentation omits

TopicDocumented by the vendor?
Any sensor readout at allNo
The ToF sensor, including in the specificationsNo
The IMU, the cameraNo
ROS 2, although an EDU version is sold on that argumentNo
Ground / desktop mode, action priority, sleepNo
The emotion system, the action library, AI interactionNo
The REST API on port 8088, SSH accessNo
The "Sirius Core API v4.0.0" protocol and its 59 commandsNo, it appears nowhere
The "keyframes" WebSocket API and the hengbot Python APIYes
The Emergency_Stop and Free_Mode modesYes, and we did not know about them

The official documentation does exist and is worth reading: it describes a different WebSocket API from the one we mapped, the "keyframes" route of UDP port 8768, with commands such as Mode_Switch, Control_Move, Start_Record, Play_Keyframe, Get_Status, Set_Parameter. It also documents a Freedom mode and a Developer mode on the head screen, plus a hardware self-test (IMU, buttons, battery, Wi-Fi, servos).

The independent ecosystem, as of July 2026

The ground is nearly untouched. Eight months after the first deliveries, the number of developer tutorials published on YouTube, in any language, is zero. Everything that exists fits into two repositories, from a single person:

  • dspeers/sirius-control-panel: finds the REST API on :8088 and an MJPEG stream on :8080. Its test_api.py is the only publicly available reconstructed API documentation, and the only public source mentioning the 4 x 4 grid.
  • dspeers/sirius-voice-bridge: swaps the stock speech recognition for a local one. It incidentally reveals that Sirius voice traffic goes through the ByteDance / Volcano Engine cloud, something no press coverage mentions.
  • mitchrk/sirius-osrd advertises an open source firmware and is completely empty.
  • The hengbot-api PyPI package is a false friend: it dates from November 2024 and drives the Sparky, the previous robot.

15Driving the robot from a browser

The kit we used throughout this work is available for download. It is not a product: it is a reverse-engineering tool that became usable, published because it saves hours to anyone getting started.

The Sirius Studio control station: a floor-level view on the left, two movement joysticks, a head joystick, motor safety and the API call log
The control station, English interface, connected to the simulator. The camera view is an illustration, not an image produced by the robot: the simulator replays the real protocol but streams no video. It is there to show the field of view of a thirty-centimetre machine. Everything else on screen is real, including the 14-motor telemetry and the API log.

What it does: two joysticks to walk and turn, a third for the head, the live camera view with detections drawn on top, the robot's action library with names translated from Chinese, telemetry for all 14 motors, a log of every API call, the Ground / Desktop switch, and a cut-off that stops the robot beyond 850 per mille of motor load sustained for 0.8 s.

How it is built: a Python bridge runs on your machine (FastAPI), speaks WebSocket to the robot and serves a React interface to your browser. The video goes straight from robot to browser over WebRTC: a backend can only relay signalling, never the stream.

The interface is bilingual French and English: an FR / EN button in the top bar, next to the theme switch, and the choice is remembered. The posture button is called Lying down rather than "on the ground", so it cannot be confused with Ground / Desktop mode, which describes the robot's environment and not its posture. Since v2.6 that selector reflects the robot's actual state, including when autonomous behaviour acts on its own: the posture is inferred from the action being played, flagged as such, and nothing lights up until it is known.

Download Sirius Studio v2.6 (129 KB)Windows · archive to unzip, no installer
SHA-256 eb6fcd75c78276e5bec7cd1ac2555eb7f2de4d30b2ca6403306d480cf4dfd2a4

What you need

  • Python 3 installed, with "Add Python to PATH" ticked during installation. This is the number one cause of startup failures.
  • A Sirius robot on the same Wi-Fi network as your PC.
  • A recent browser, since video goes through WebRTC.

No dependency to install by hand: the launcher fetches fastapi, uvicorn, websockets and httpx on first run, once and for all, in about thirty seconds.

Connecting, in three steps

  1. Turn the robot on and check it is on the same Wi-Fi network as your PC. Its IP address shows on the head screen, Network menu.
  2. Unzip the archive and double-click demarrer.bat. A console window opens, prints the version number, then your browser opens on the interface by itself. If it does not, go to http://127.0.0.1:8787.
  3. Enter the robot's IP address in the interface, then click Connect. It is remembered: next time, one click on the recent address is enough.
The Sirius Studio connection screen, with the field where the robot IP address is entered
Step 3: the address field. To stop everything, close the console window.

If it will not move

In order of frequency, and the first two causes are traps 1 and 3 of this page:

  • Legs move but it does not advance: it is in Desktop mode, switch to Ground in Modes and behaviour. Or the requested speed is too low: raise the slider.
  • It reacts to nothing: either it is asleep, play an action to wake it, or autonomous mode is on and overriding your commands, turn it off.
  • "Cannot reach the robot": check the IP address, and that PC and robot really are on the same network.
  • "Python not found": reinstall Python with "Add Python to PATH" ticked.
  • Seeing what happens: the bridge API is documented and testable on http://127.0.0.1:8787/docs.

The kit comes with no warranty: it drives hardware that can fall or jam. The launchers are .bat files, so Windows; the Python bridge is cross-platform but has been tested nowhere else. A bug, a remark, a test result: write to us, that is how this page improves.

16Safety

  • The robot sees nothing behind it. The ToF looks forward and there is no rear sensor. Any reverse motion is blind: low speed, bounded duration.
  • The motors get hot. Avoid very long continuous sessions, all the more since temperature reporting is mute on this firmware.
  • Do not modify firmware files without a backup. Work with your own scripts in /root/ or a folder of your own.
  • Keep an emergency stop at hand, in an already open terminal. Publishing zero commands in a loop is the most reliable way to stop everything.
  • The UDP Play_Keyframe route bypasses the robot's protections. Only use it once you have read the joint limits, and never for a first test.

Watching motor load

The motor-load event reports 14 loads at 1 Hz, in PWM per mille, over a ±1000 range. It is the only reliable instrument available to detect a stall, since the temperature probes are silent. Real measurements:

ConditionObserved peak
At rest120 to 162 per mille
Walking at 0.06 m/s540 per mille
Walking at top speed530 per mille
Rotating at top speed540 per mille
Stalled motor950 to 985 per mille, sustained

The firmware also applies its own guards, permanently active: joint angle clamping, workspace clamping, a 0.5 rad joint jump threshold, and motor speed capped at 200 rpm. An out of range command is clamped rather than driven into the stop. That excuses nothing, but it is a net.

Finally, the official documentation mentions an Emergency_Stop mode through Mode_Switch, which we have not tested yet and had rebuilt by hand. If you validate it, write to us.

Back to top