TopicsNEORACER DOCS
NEORACER DOCS
These docs are public and open source.Edit on GitHub
API REFERENCE / ROS 2

ROS 2 TOPICS.

The whole stack is sensors publishing and actuators subscribing. You read five sensor topics, you publish one drive topic, and the nodes in between do the arbitration and scaling. Below is the exact list the default launch brings up, with the message type on each.

Publish /driveRead /scan · /imu · /cameraackermann_msgs
THE FULL LIST

THE TOPIC LIST.

TopicMessage typeYour roleWhat it carries
/scansensor_msgs/LaserScanRead itOne planar sweep from the Lakibeam LiDAR, ~1440 samples, frame_id laser. The depth source for wall follow, gap follow, and mapping. Publisher is RELIABLE.
/imusensor_msgs/ImuRead itOrientation, linear acceleration (m/s²), and angular velocity (rad/s) at ~200 Hz, frame_id imu_link. Published by the controller node from the MCU state frame.
/odomnav_msgs/OdometryRead itWheel odometry at ~200 Hz, integrated on the MCU from the motor encoder. Useful for short-horizon dead reckoning between LiDAR scans.
/batterysensor_msgs/BatteryStateRead itPack voltage and a 3S charge fraction at ~0.5 Hz. The dashboard shows the same reading as a battery card.
/camerasensor_msgs/ImageRead itA JPEG-compressed colour frame at 60 fps from the camera node (USB webcam in MJPG). The bytes are raw JPEG with encoding="jpeg", decode with cv2.imdecode before display. Publisher is best-effort: subscribe with sensor-data QoS or you receive nothing.
/driveackermann_msgs/AckermannDriveStampedPublish itThe autonomy channel. This is the one topic your own node writes to. The mux only forwards it while autonomy mode is armed.
/joysensor_msgs/JoyTeleopFlysky RC stick and switch state, published by the controller node from the receiver on the ESP32. The gamepad and mux nodes read it; you rarely need to.
/gamepad_driveackermann_msgs/AckermannDriveStampedTeleopThe drive command gamepad_node builds from the sticks. Gated by the mux the same way /drive is.
/mux_outackermann_msgs/AckermannDriveStampedInternalWhichever command won arbitration, teleop or autonomy. You read this to see what the car was actually told.
/motorackermann_msgs/AckermannDriveStampedInternalThe throttle-scaled command the controller node turns into the ESP32 serial command. The last hop before hardware.
/led_matrix/commandstd_msgs/StringPublish itWrite text or simple commands to the 8x8 dot-matrix display on the back of the car. Handled by the led_matrix node over USB-UART.
THE DRIVE PIPELINE

THE DRIVE PIPELINE.

Your message does not go straight to the motor. It passes through an arbiter that decides whether or autonomy is in control, then a throttle stage that scales speed down to a safe range before the node ever touches the hardware. That is why a raw set_speed_angle(1.0, 0) does not launch the car at full speed.

// the drive pipeline
/drive(yours)mux_node/mux_outthrottle_node/motorcontrollerESP32 → ESC + steering servo
/gamepad_drive(teleop)mux_node joins the same arbiter. SWB picks the winner: up is teleop, down is /drive.
//Reading and writing from your own node
python
import rclpy from rclpy.node import Node from sensor_msgs.msg import LaserScan from ackermann_msgs.msg import AckermannDriveStamped class GapFollower(Node): def __init__(self): super().__init__("gap_follower") # Read the LiDAR. self.create_subscription(LaserScan, "/scan", self.on_scan, 10) # Publish to the autonomy channel. The mux forwards it when RB is held. self.drive = self.create_publisher(AckermannDriveStamped, "/drive", 10) def on_scan(self, scan): msg = AckermannDriveStamped() msg.drive.speed = 0.25 msg.drive.steering_angle = 0.0 self.drive.publish(msg) def main(): rclpy.init() rclpy.spin(GapFollower()) if __name__ == "__main__": main()