Sensor Messages (C++)

All sensor types live in horus::msg:: namespace. Include via <horus/msg/sensor.hpp>.

Quick Reference

TypeSizeKey FieldsUse Case
LaserScan1472 Branges[360], angle_min/max2D LiDAR
Imu304 Borientation[4], angular_velocity[3], linear_acceleration[3]IMU
Odometry~700 Bpose (Pose2D), twist (Twist), covariance matricesWheel encoders
JointState~900 Bnames[16][32], positions[16], velocities[16], efforts[16]Robot arms
RangeSensor32 Brange, min/max_range, field_of_viewUltrasonic/IR
BatteryState40 Bvoltage, current, percentage, health, statusBattery monitor
NavSatFix112 Blatitude, longitude, altitude, satellitesGPS
MagneticField136 Bmagnetic_field[3], covarianceMagnetometer
Temperature56 Btemperature (Celsius), varianceThermometer
FluidPressure56 Bfluid_pressure (Pascals)Barometer
Illuminance56 Billuminance (Lux)Light sensor

LaserScan

The most common sensor message. 360 range readings for a full-circle 2D LiDAR.

#include <horus/horus.hpp>

class LidarNode : public horus::Node {
public:
    LidarNode() : Node("lidar") {
        pub_ = advertise<horus::msg::LaserScan>("lidar.scan");
    }

    void tick() override {
        auto scan = pub_->loan();

        // Fill range data (meters, 0 = invalid)
        for (int i = 0; i < 360; i++) {
            scan->ranges[i] = read_range(i);
        }
        scan->angle_min = 0.0f;          // start angle (radians)
        scan->angle_max = 6.28318f;       // end angle (2*pi)
        scan->min_range = 0.12f;          // sensor minimum (meters)
        scan->max_range = 12.0f;          // sensor maximum
        scan->time_increment = 0.0f;      // time between measurements
        scan->scan_time = 0.1f;           // full scan duration (seconds)
        scan->timestamp_ns = 0;

        pub_->publish(std::move(scan));
    }

private:
    horus::Publisher<horus::msg::LaserScan>* pub_;
    float read_range(int angle) { return 2.0f; }  // placeholder
};

Finding Minimum Range

auto scan = sub_->recv();
if (!scan) return;

float min_range = 999.0f;
int min_angle = 0;
for (int i = 0; i < 360; i++) {
    float r = scan->get()->ranges[i];
    if (r > 0.01f && r < min_range) {
        min_range = r;
        min_angle = i;
    }
}
// min_angle is in degrees (0-359)

Imu

9-axis IMU: orientation quaternion + angular velocity + linear acceleration, each with 3x3 covariance.

horus::msg::Imu imu{};
imu.orientation[0] = 0.0;    // qx
imu.orientation[1] = 0.0;    // qy
imu.orientation[2] = 0.0;    // qz
imu.orientation[3] = 1.0;    // qw (identity)
imu.angular_velocity[2] = 0.01;    // yaw rate (rad/s)
imu.linear_acceleration[2] = 9.81; // gravity (m/s^2)
imu.timestamp_ns = 0;
// Covariance: -1 in first element = "no data"
imu.orientation_covariance[0] = -1.0;

JointState

Up to 16 joints with names, positions, velocities, and efforts:

horus::msg::JointState js{};
js.joint_count = 6;  // 6-DOF arm

// Set joint names (null-terminated, max 31 chars)
std::strncpy(reinterpret_cast<char*>(js.names[0]), "shoulder_pan", 31);
std::strncpy(reinterpret_cast<char*>(js.names[1]), "shoulder_lift", 31);
std::strncpy(reinterpret_cast<char*>(js.names[2]), "elbow", 31);
std::strncpy(reinterpret_cast<char*>(js.names[3]), "wrist_1", 31);
std::strncpy(reinterpret_cast<char*>(js.names[4]), "wrist_2", 31);
std::strncpy(reinterpret_cast<char*>(js.names[5]), "wrist_3", 31);

// Set positions (radians for revolute joints)
js.positions[0] = 0.0;
js.positions[1] = -1.57;  // shoulder down
js.positions[2] = 1.57;   // elbow bent
// velocities and efforts default to 0

See Also