ExoStack / ROS 2 interfaces

External inputs

Your own nodes: estimators and sensors that feed ExoStack, and drivers that take its set-points.

ExoStack accepts signals from your own nodes and hands its set-points to any ROS 2 driver. Publish on the topic named in the YAML file, with the type and layout shown, and use best-effort QoS to match ExoStack.

TopicDescriptionMessage type
/<speed_topic>walking speed for the lookup table → profile nodestd_msgs/Float32estimator input layout
/<intent_topic>locomotion intent → profile nodestd_msgs/Float32estimator input layout
/<gait_phase_topic>gait phase → plannerstd_msgs/Float32estimator input layout
/<external_sensors_topic>metabolic, EMG or other signals → cost evaluatorstd_msgs/Float32MultiArrayexternal sensors layout
/<additional_gait_marker_topic>in-cycle marker → cost evaluatorstd_msgs/Int32gait marker layout

Driving another actuator

A planner's output_topic is an ordinary std_msgs/Float32 set-point, so a ROS 2 driver of any actuator can subscribe to it instead of an actuation hub.

A minimal estimator

speed_estimator.py
import rclpy
from rclpy.node import Node
from rclpy.qos import QoSProfile, QoSReliabilityPolicy
from std_msgs.msg import Float32


class SpeedEstimator(Node):
    def __init__(self):
        super().__init__("speed_estimator")
        qos = QoSProfile(depth=10, reliability=QoSReliabilityPolicy.BEST_EFFORT)
        self.pub = self.create_publisher(Float32, "/walking_speed", qos)   # speed_topic in profile.yaml
        self.create_timer(0.1, self.tick)                                  # 10 Hz

    def tick(self):
        self.pub.publish(Float32(data=3.5))                                # your estimate here


rclpy.init()
rclpy.spin(SpeedEstimator())