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
| Sensor | BOM line | Connected to | ROS 2 topic | Frame |
|---|---|---|---|---|
| Adafruit LSM6DSOX 6-DoF IMU (4438) | lsm6dsox | XIAO, I²C 0x6A | /imu/data | imu_link |
| Slamtec RPLIDAR C1 + USB adapter | rplidar_c1 | Pi USB, /dev/rplidar | /scan | lidar_link |
| Raspberry Pi Camera Module 3 Wide | camera_module_3_wide | Pi CSI, 22→15 pin FFC | /camera/image_raw | camera_optical_frame |
| Adafruit INA260 power monitor (4226) | ina260 | XIAO, 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.
| Sensor | Measures | Unit | Strido setting |
|---|---|---|---|
| Accelerometer | specific force: gravity plus any acceleration | m/s² (g) | ±8 g range, 416 Hz output data rate |
| Gyroscope | angular velocity about each axis | rad/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):
that is,
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
The first term integrates the gyro rate
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
The proportional gain
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 monitorshows roll and pitch on the bench.mcu_bridgenever publishes a fake upright orientation: if the IMU reports a fault (imu_okfalse),/imu/datais 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 |
|---|---|
| Range | 0.05–12 m (white target), 0.05–6 m (black target) |
| Sample rate | 5000 samples/s |
| Scan frequency | 10 Hz typical (8–12 Hz) |
| Angular resolution | 0.72° at 10 Hz |
| Accuracy | ±30 mm |
| Interface | 3.3 V TTL UART, 460 800 baud (here via a USB adapter) |
| Supply | 5 V, about 230 mA running, about 800 mA at start-up |
| Laser safety | IEC 60825 Class 1 |
The numbers fit together:
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 |
|---|---|
| Sensor | Sony IMX708, 11.9 MP (4608 × 2592), back-illuminated, stacked |
| Lens | 2.75 mm, F2.2 |
| Field of view | 120° diagonal, 102° horizontal, 67° vertical |
| Focus | phase-detection autofocus, 5 cm to infinity |
| Video modes | 1080p50, 720p100, 480p120 |
| Interface | MIPI 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
| Sensor | Read by | Rate | Software that uses it |
|---|---|---|---|
| LSM6DSOX | XIAO, I²C | 200 Hz, published at 100 Hz | Mahony filter → /imu/data → policy_node, monitoring |
| INA260 | XIAO, I²C | 10 Hz | low-battery warning and cut-off, /battery_state |
| RPLIDAR C1 | Pi, USB serial | 10 Hz scans | /scan → SLAM, Nav2, obstacle avoidance |
| Camera Module 3 Wide | Pi, CSI | video rate | /camera/image_raw → your perception nodes |
Check yourself
- Why can the accelerometer alone not give a reliable orientation while the robot trots?
- In the Mahony filter, what does the proportional gain decide, and what would a non-zero integral gain add?
- How many points does one C1 scan contain at 10 Hz, and what is their angular spacing?
- Why was the camera moved from the top shell to the front shell?
- Which robot heading information can the IMU not provide, and which sensor fills the gap?
Answers
- While moving, it measures footstep accelerations on top of gravity, so the "down" direction it reports is noisy.
- 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.
points, apart. - On the top shell the robot's nose filled 42 % of the image; on the front shell it fills 0 %.
- Yaw (heading) is not observable from gravity and drifts; the LiDAR (scan matching or SLAM) provides it.
Further reading
- LSM6DSOX product page (STMicroelectronics)
- Adafruit LSM6DSOX guide
- RPLIDAR C1 datasheet (Slamtec)
- Camera Module 3 product brief (Raspberry Pi)
- Open-source IMU and AHRS algorithms (x-io, includes Mahony)
- Related pages: Buses, Power, ROS 2, Kinematics and gaits
- Theory: Frames and rotations, State estimation