Skip to content

Sensors ​

Sensors are how the robot knows anything about itself and its surroundings. Hobby servos report nothing back, so Strido relies on four sensors: an IMU for body orientation, a 2D LiDAR for distances, a wide-angle camera for images and a power monitor for the battery. This page explains how each one works, where it sits and which software uses its data.

In Strido

SensorBOM lineConnected toROS 2 topicFrame
Adafruit LSM6DSOX 6-DoF IMU (4438)lsm6dsoxXIAO, I²C 0x6A/imu/dataimu_link
Slamtec RPLIDAR C1 + USB adapterrplidar_c1Pi USB, /dev/rplidar/scanlidar_link
Raspberry Pi Camera Module 3 Widecamera_module_3_widePi CSI, 22→15 pin FFC/camera/image_rawcamera_optical_frame
Adafruit INA260 power monitor (4226)ina260XIAO, I²C 0x41/battery_state—

Field-of-view checks: docs/verification/sensors.md. Firmware set-up: firmware/strido_esp32s3/src/main.cpp.

IMU: LSM6DSOX accelerometer and gyroscope ​

An IMU (inertial measurement unit) measures motion from the inside, with no external reference. The LSM6DSOX is a 6-DoF IMU: two 3-axis MEMS sensors in one chip.

SensorMeasuresUnitStrido setting
Accelerometerspecific force: gravity plus any accelerationm/s² (g)±8 g range, 416 Hz output data rate
Gyroscopeangular velocity about each axisrad/s (°/s)±1000 °/s range, 416 Hz output data rate

The firmware reads both every 5 ms (200 Hz). Running the chip at 416 Hz means every read gets a fresh sample.

Mounting and axes ​

The IMU sits in a pocket in the electronics_deck, chip side up with its STEMMA QT sockets towards the rear, held by two M2.5 × 6 screws. Mounted that way, its x axis points to the robot's left and its y axis to the rear. The firmware rotates every reading into the robot's base_link convention (x forward, y left, z up, REP-103):

(xyz)base=(0−10100001)⏟Rbase,imu(xyz)imu

that is, xbase=−yimu, ybase=ximu, zbase=zimu. The matrix Rbase,imu is a rotation of +90∘ about z; the same matrix is applied to both the accelerometer and the gyro vectors. Rotation matrices are explained in frames and rotations.

A wrong sign here would make the robot believe it is tilting the other way, the classic first bug in any balance controller.

From two sensors to one orientation ​

Neither sensor alone gives a good orientation:

  • The accelerometer at rest measures only gravity, so roll and pitch follow directly from which way "down" points. While walking, every footstep adds acceleration, so this estimate is noisy.
  • The gyroscope is smooth and fast. Integrating angular velocity over time gives the angle change, but any small bias integrates into a steadily growing drift.

Sensor fusion combines the two: trust the gyro over short times, and let the accelerometer slowly pull the estimate back towards true "down". The simplest version is the complementary filter for one angle θ (say pitch), updated every time step Δt:

θ^k=α(θ^k−1+ωkΔt)+(1−α)θacc,k,θacc=atan2(−ax, ay2+az2)

The first term integrates the gyro rate ω; the second is the tilt computed from the gravity direction in the accelerometer reading a. With α close to 1 the filter is a high-pass on the gyro and a low-pass on the accelerometer, which is where the name comes from: the two filters add up to one. The crossover time constant is τ=αΔt/(1−α); for example α=0.98 at Δt=5ms gives τ≈0.25s, so gyro drift is corrected within about a quarter of a second while footstep jolts shorter than that are smoothed out.

Strido runs a Mahony filter (Adafruit_Mahony) on the XIAO at 200 Hz, the 3D, quaternion form of the same idea. It integrates the gyro and feeds the error e=gmeas×g^ between the predicted and measured gravity direction back as a correction to the rate:

ωcorr=ωgyro+Kpe+Ki∫edt

The proportional gain Kp sets how hard the accelerometer pulls, and the optional integral gain Ki can learn a constant gyro bias. The firmware uses the library defaults: Kp=0.5 and Ki=0, so no bias learning. State estimation derives both filters and compares them with the Kalman filter.

The result is a quaternion, a four-number representation of 3D orientation that has no gimbal-lock problems. It travels in the STATE frame at 100 Hz and is published as /imu/data together with the raw gyro and acceleration.

Yaw is not observable

Gravity tells you roll and pitch but says nothing about heading (yaw). Without a magnetometer, the IMU's yaw estimate drifts. For heading over time, use the LiDAR (scan matching / SLAM).

Who uses it ​

  • policy_node (learned controller) turns the quaternion into the gravity direction in the body frame and feeds it, with the gyro rates, into the policy's observation every 50 Hz step. It does not start until it has seen IMU data.
  • strido-cli monitor shows roll and pitch on the bench.
  • mcu_bridge never publishes a fake upright orientation: if the IMU reports a fault (imu_ok false), /imu/data is simply not published.

LiDAR: RPLIDAR C1 ​

A 2D LiDAR spins a laser rangefinder and measures the distance to the nearest object at each angle in one horizontal plane. The C1 measures time of flight (DTOF): it times how long a 905 nm infrared laser pulse takes to come back.

Specification (Slamtec datasheet)Value
Range0.05–12 m (white target), 0.05–6 m (black target)
Sample rate5000 samples/s
Scan frequency10 Hz typical (8–12 Hz)
Angular resolution0.72° at 10 Hz
Accuracy±30 mm
Interface3.3 V TTL UART, 460 800 baud (here via a USB adapter)
Supply5 V, about 230 mA running, about 800 mA at start-up
Laser safetyIEC 60825 Class 1

The numbers fit together: 5000samples/s10scans/s=500 points per scan, and 360∘/500=0.72∘ between points. Time of flight turns into distance as d=cΔt/2 (the light travels out and back); at the 12 m maximum range the round trip lasts only Δt=2d/c≈80ns.

Placement ​

A LiDAR sees nothing below or above its scan plane, and anything of the robot's own that crosses the plane blinds part of the scan. Strido lifts the C1 on a 14 mm lidar_pedestal on top_shell_sensor. Its scan plane sits 71.6 mm above the hip axes, about 254 mm above ground when standing at 170 mm. The ray-cast check in docs/verification/sensors.md blocks 0 of 720 test rays in every pose: stand, crouch, trot swing apex, maximum stride and hips abducted.

Who uses it ​

sllidar_ros2 publishes /scan (sensor_msgs/LaserScan) at 10 Hz in lidar_link. The /scan topic follows the Go2 conventions, so standard ROS 2 tools run unchanged: SLAM to build a map, Nav2 for obstacle avoidance and path planning. This matters because gait_node's /odom integrates only the commanded velocity (dead reckoning, no slip model). For real navigation, a LiDAR-based odometry or SLAM source should replace it (gait_node.py docstring).

Camera: Camera Module 3 Wide ​

Specification (Raspberry Pi product brief)Value
SensorSony IMX708, 11.9 MP (4608 × 2592), back-illuminated, stacked
Lens2.75 mm, F2.2
Field of view120° diagonal, 102° horizontal, 67° vertical
Focusphase-detection autofocus, 5 cm to infinity
Video modes1080p50, 720p100, 480p120
InterfaceMIPI CSI-2, 15-pin 1 mm FPC

The Wide variant was chosen because a small robot close to the ground sees more of its surroundings with a 102° horizontal view than with the standard module's 66°.

Placement ​

The camera sits on a tilting mount (camera_mount_base and camera_tilt_plate) on the flat top of the front shell. An M3 × 40 pivot with a lock nut gives a friction tilt. The first design put the camera on the top shell, where the ray-cast check found the robot's own nose filling 42 % of the image. The new position shows 0 % in every checked pose (docs/design.md, docs/verification/sensors.md).

Who uses it ​

camera_ros publishes /camera/image_raw in camera_optical_frame. On the Pi 5 the camera needs the Raspberry Pi fork of libcamera, so the node is built on the host and enabled with camera:=true (default false; the Docker image has no camera node). The camera is there for your own perception nodes: teleoperation video, object detection, visual navigation.

Power monitor: INA260 ​

The INA260 measures voltage and current with a built-in 2 mΩ shunt resistor, 1.25 mA current resolution, up to 15 A and 36 V. In Strido it sits high-side in the logic branch (see Power):

  • Bus voltage = pack voltage (minus tiny losses). The firmware reads it every 100 ms. It warns at 6.8 V and relaxes the servos after 5 consecutive readings below 6.4 V.
  • Current = logic-branch current only. The servo current bypasses it.

mcu_bridge publishes both as /battery_state (sensor_msgs/BatteryState, technology LiPo, negative current = discharging). The firmware refuses to drive the servos without it, because a robot with no voltage sensor can drain its LiPo into damage.

Summary: sensor to software ​

SensorRead byRateSoftware that uses it
LSM6DSOXXIAO, I²C200 Hz, published at 100 HzMahony filter → /imu/data → policy_node, monitoring
INA260XIAO, I²C10 Hzlow-battery warning and cut-off, /battery_state
RPLIDAR C1Pi, USB serial10 Hz scans/scan → SLAM, Nav2, obstacle avoidance
Camera Module 3 WidePi, CSIvideo rate/camera/image_raw → your perception nodes

Check yourself ​

  1. Why can the accelerometer alone not give a reliable orientation while the robot trots?
  2. In the Mahony filter, what does the proportional gain decide, and what would a non-zero integral gain add?
  3. How many points does one C1 scan contain at 10 Hz, and what is their angular spacing?
  4. Why was the camera moved from the top shell to the front shell?
  5. Which robot heading information can the IMU not provide, and which sensor fills the gap?
Answers
  1. While moving, it measures footstep accelerations on top of gravity, so the "down" direction it reports is noisy.
  2. The proportional gain decides how strongly the accelerometer's gravity direction pulls the gyro-integrated estimate back. A non-zero integral gain would also learn a constant gyro bias; Strido leaves it at 0.
  3. 5000/10=500 points, 0.72∘ apart.
  4. On the top shell the robot's nose filled 42 % of the image; on the front shell it fills 0 %.
  5. Yaw (heading) is not observable from gravity and drifts; the LiDAR (scan matching or SLAM) provides it.

Further reading ​

MIT licence. Mechanical design derived from Yertle by Jerome Graves. CAD, simulation and native tests are not hardware evidence — see Verification.