{"id":134815,"date":"2026-10-23T09:17:21","date_gmt":"2026-10-23T09:17:21","guid":{"rendered":"https:\/\/www.seeedstudio.com\/blog\/?p=134815"},"modified":"2026-10-23T09:17:24","modified_gmt":"2026-10-23T09:17:24","slug":"mobile-robot-full-stack-development-in-practice-%e2%85%b2-how-does-a-robot-know-it-is-moving-lidar-imu-and-sensor-fusion","status":"publish","type":"post","link":"https:\/\/www.seeedstudio.com\/blog\/2026\/10\/23\/mobile-robot-full-stack-development-in-practice-%e2%85%b2-how-does-a-robot-know-it-is-moving-lidar-imu-and-sensor-fusion\/","title":{"rendered":"Mobile Robot Full-Stack Development in Practice \u2162: How Does a Robot Know It Is Moving? LiDAR, IMU and Sensor Fusion"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><strong>From point clouds and chassis feedback to time alignment and EKF state estimation.<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In <a href=\"https:\/\/www.seeedstudio.com\/blog\/2026\/10\/16\/mobile-robot-full-stack-development-in-practice-module-2-gmsl-calibration-bev\/\">the previous article<\/a>, GMSL2 cameras, calibration and bird&#8217;s-eye-view projection helped the robot observe its surroundings. Once the chassis starts moving, another set of questions appears. Why does the same wall leave a double outline in a point cloud? Why does an estimated trajectory curve while the robot is driving straight? Why can adding another sensor make the result jump?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Often, the robot has several valid-looking measurements without a consistent relationship between their times, coordinate frames and motion models.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Cameras record images, LiDAR measures surrounding geometry, encoders record wheel rotation, an IMU measures angular velocity and specific force, and GNSS provides an outdoor position reference. Each source contributes something useful, and each has limitations. Wheels slip, inertial sensors carry bias, and satellite measurements suffer from obstruction and multipath.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Robot sensor fusion combines these observations to estimate motion and state, with uncertainty attached to the result.<\/strong> Before combining them, we need to establish what each measurement means and whether it can be compared with the others.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Module 3 takes that process from ROS 2 data validation through point-cloud preprocessing, a common time reference and TF relationships, then a two-dimensional EKF using <code>robot_localization<\/code>. The first local experiment fuses wheel velocity and IMU yaw rate; GNSS introduces a further global-positioning layer. Raw LiDAR points do not enter this minimum EKF directly. These exercises establish the data foundation for the SLAM and mapping work in M05.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img fetchpriority=\"high\" decoding=\"async\" width=\"1030\" height=\"580\" src=\"https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/imu_gnss_canfd_integration-1-1030x580.png\" alt=\"Diagram showing IMU, GNSS and wheel feedback connected to a mobile robot computer.\" class=\"wp-image-134832\" srcset=\"https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/imu_gnss_canfd_integration-1-1030x580.png 1030w, https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/imu_gnss_canfd_integration-1-300x169.png 300w, https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/imu_gnss_canfd_integration-1-768x432.png 768w, https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/imu_gnss_canfd_integration-1-32x18.png 32w, https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/imu_gnss_canfd_integration-1-1536x864.png 1536w, https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/imu_gnss_canfd_integration-1-1023x576.png 1023w, https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/imu_gnss_canfd_integration-1.png 1672w\" sizes=\"(max-width: 1030px) 100vw, 1030px\" \/><\/figure>\n\n\n\n<p class=\"has-text-align-center wp-block-paragraph\"><em>Different sensors observe different aspects of the same robot. Their units, timestamps, frames and uncertainty must be understood before fusion.<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">New to the series? The <a href=\"https:\/\/www.seeedstudio.com\/blog\/2026\/09\/18\/mobile-robot-full-stack-development-course-introduction\/\">course introduction<\/a> explains the overall learning path, while <a href=\"https:\/\/www.seeedstudio.com\/blog\/2026\/09\/30\/jetson-powered-mobile-robot-full-stack-development-in-practice-module-1-jetson-ros2\/\">Module 1<\/a> establishes the Jetson and ROS 2 development environment. The M03 reference environment uses reComputer Robotics J501 hardware, Ubuntu 22.04 and ROS 2 Humble, with J5011 examples for sensor and chassis integration.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\ud83d\udc49 <a href=\"https:\/\/github.com\/Seeed-Projects\/mobile-robot-full-stack-course\"><strong>Course GitHub \u2014 Mobile Robot Full-Stack Development in Practice<\/strong><\/a><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The LiDAR Sees a Wall. Why Is That Not Yet Localization?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Starting a driver and seeing a room in RViz is a satisfying first result. That display does not yet tell us whether the measurements are complete, when they were acquired, or whether their geometry remains consistent as the robot moves.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The <a href=\"https:\/\/github.com\/Seeed-Projects\/mobile-robot-full-stack-course\/blob\/d315015688e43a2f70082e13a98199b6de21e7dc\/docs\/M03-LiDAR-and-Multi-Sensor-Fusion\/3.1_LiDAR_Integration_and_Point_Cloud_Preprocessing\/README_en_US.md\">LiDAR integration experiments<\/a> begin with the RPLIDAR A1 over USB, then move to the Ethernet-connected Livox MID-360. The two paths introduce different connections and ROS message structures.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">A 2D Scan Is a Sequence of Measurements<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The A1 driver publishes <code>\/scan<\/code> as <code>sensor_msgs\/msg\/LaserScan<\/code>. It sends ranges arranged by angle rather than a finished map. For sample i, its direction follows from the starting angle and angular increment:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>theta_i = angle_min + i * angle_increment\nx_i = range_i * cos(theta_i)\ny_i = range_i * sin(theta_i)\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">These coordinates belong to the scanner&#8217;s frame. Moving them into the robot or map frame requires a corresponding transform.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Range validity matters too. Infinite, invalid and out-of-range returns must be handled according to the message contract. Even a finite value below the declared <code>range_min<\/code> is not a usable obstacle measurement.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">After starting the configured driver and sourcing the ROS environment, inspect the interface, observed rate and message contents:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ros2 topic info \/scan --verbose\nros2 topic hz \/scan\nros2 topic echo \/scan --once\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The course&#8217;s A1 reference test records approximately 7.55 Hz while the configured target is 10 Hz. That result belongs to the documented setup: configuration and subscriber-observed publication rate are separate things to check on your own robot.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" width=\"2089\" height=\"1254\" src=\"https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/rviz_rplidar-1.jpg\" alt=\"RPLIDAR A1 laser scan displayed in RViz using the laser coordinate frame.\" class=\"wp-image-134835\" srcset=\"https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/rviz_rplidar-1.jpg 2089w, https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/rviz_rplidar-1-300x180.jpg 300w, https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/rviz_rplidar-1-768x461.jpg 768w, https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/rviz_rplidar-1-32x19.jpg 32w, https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/rviz_rplidar-1-1023x614.jpg 1023w\" sizes=\"(max-width: 2089px) 100vw, 2089px\" \/><\/figure>\n\n\n\n<p class=\"has-text-align-center wp-block-paragraph\"><em>A1 scan output in <code>laser<\/code>. The visible TF warning also shows that viewing a sensor stream and completing the robot&#8217;s frame tree are separate milestones.<\/em><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Ethernet Adds a Network Dependency and an Output Choice<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The MID-360 sends point-cloud and IMU data over Ethernet. The sensor, the host&#8217;s wired interface and the driver&#8217;s addresses must agree, while the development machine may still need another interface for internet access. Use the address of the interface connected to the LiDAR; copying a Wi-Fi address or an example IP into the configuration can break the intended path.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The launch choice also changes the data contract. In the course, <code>msg_MID360_launch.py<\/code> provides Livox custom point-cloud messages and IMU output, while <code>rviz_MID360_launch.py<\/code> provides the standard <code>PointCloud2<\/code> visualization path.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>A topic name does not guarantee a message type.<\/strong> Check <code>\/livox\/lidar<\/code> before attaching a subscriber. If later processing needs per-point timing, verify that conversion preserves it. A representation convenient for display may omit information needed for motion correction.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"1030\" height=\"379\" src=\"https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/pointcloud2_view-1-1030x379.jpg\" alt=\"Livox MID-360 point cloud displayed in RViz beside the ROS 2 driver terminal.\" class=\"wp-image-134839\" srcset=\"https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/pointcloud2_view-1-1030x379.jpg 1030w, https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/pointcloud2_view-1-300x110.jpg 300w, https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/pointcloud2_view-1-766x282.jpg 766w, https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/pointcloud2_view-1-2048x754.jpg 2048w, https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/pointcloud2_view-1-32x12.jpg 32w, https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/pointcloud2_view-1-1021x376.jpg 1021w\" sizes=\"(max-width: 1030px) 100vw, 1030px\" \/><\/figure>\n\n\n\n<p class=\"has-text-align-center wp-block-paragraph\"><em>MID-360 <code>PointCloud2<\/code> output using <code>livox_frame<\/code>. The Fixed Frame warning remains part of this sensor-preview example; TF alignment must still be checked.<\/em><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Preprocessing Changes What the Next Algorithm Can See<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A raw cloud can contain the robot&#8217;s own body, distant irrelevant regions and isolated noise. The course introduces range and body cropping, voxel downsampling, outlier removal and coordinate transformation.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A voxel grid divides space into cells and keeps a representative point for each occupied cell. Larger cells reduce density and processing work, but can also weaken thin poles, edges and small obstacles. Choose the resolution around the geometry the task must retain; a setting useful for large-area mapping may be unsuitable for detecting small nearby objects.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1030\" height=\"580\" src=\"https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/lidar_point_cloud_preprocessing-1030x580.png\" alt=\"Diagram illustrating LiDAR point cloud cropping, downsampling and outlier removal.\" class=\"wp-image-134840\" srcset=\"https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/lidar_point_cloud_preprocessing-1030x580.png 1030w, https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/lidar_point_cloud_preprocessing-300x169.png 300w, https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/lidar_point_cloud_preprocessing-768x432.png 768w, https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/lidar_point_cloud_preprocessing-32x18.png 32w, https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/lidar_point_cloud_preprocessing-1536x864.png 1536w, https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/lidar_point_cloud_preprocessing-1023x576.png 1023w, https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/lidar_point_cloud_preprocessing.png 1672w\" sizes=\"(max-width: 1030px) 100vw, 1030px\" \/><\/figure>\n\n\n\n<p class=\"has-text-align-center wp-block-paragraph\"><em>Preprocessing reduces unwanted input while preserving geometry needed downstream. This is a conceptual diagram, not a measured filtering comparison.<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Motion adds another issue: the points in a scan may be acquired at different times. During a turn, the LiDAR&#8217;s pose changes between the first and last samples. Treating them as simultaneous can distort a flat wall. Deskewing uses per-point acquisition times and motion from IMU or odometry to express those points at a common reference time.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Preserving timing prepares that later processing. A working driver or a visible cloud does not establish that deskewing, LiDAR odometry or SLAM has been completed.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Why Do Wheels and an IMU Need Each Other?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Encoders provide a direct way to observe wheel rotation. Converting rotation into chassis motion depends on wheel dimensions, track width, ground contact and slip. The IMU observes a different physical process: the gyroscope measures angular velocity and the accelerometer measures specific force. It responds quickly, but bias and noise accumulate when measurements are integrated.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Understanding those measurement mechanisms makes the benefits and limits of fusion easier to explain. The <a href=\"https:\/\/github.com\/Seeed-Projects\/mobile-robot-full-stack-course\/blob\/d315015688e43a2f70082e13a98199b6de21e7dc\/docs\/M03-LiDAR-and-Multi-Sensor-Fusion\/3.2_IMU_GNSS_and_CAN-FD_Integration\/README_en_US.md\">IMU, GNSS and chassis integration material<\/a> develops these inputs before asking a filter to use them.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">CAN Frames Still Need to Become Odometry<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The course introduces CAN-FD and demonstrates a J5011 connected to a dual DM-H65 motor chassis. Distinguish the interface capability from the connected device&#8217;s protocol: <strong>this DM-H65 example uses Classical CAN at 1 Mbit\/s<\/strong>. CAN-FD settings are not interchangeable with that configuration.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">CAN-FD can carry up to 64 data bytes per frame and can use a faster data phase, but it requires compatible devices and matching bus settings. Wiring, termination and the physical connection still need checking.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">With the course chassis service running, inspect the interface and passively capture existing traffic:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ip -details -statistics link show can0\ntimeout 4s candump -L can0\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">A healthy interface does not prove that decoding is correct. In the course example, motor identity must also be read from payload fields rather than inferred from the CAN arbitration ID alone. Check byte order, signed values, scale factors and the left\/right direction convention against the device protocol.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">After converting wheel feedback to linear velocities, a differential-drive chassis follows:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>v = (v_right + v_left) \/ 2\nomega = (v_right - v_left) \/ track_width\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Equal wheel velocities describe ideal straight motion; their difference produces rotation. A wheel-radius error changes distance scale, a track-width error changes the estimated turn, and slip breaks the relationship between rotation and displacement.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The complete path is therefore:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Protocol decoding \u2192 units and direction \u2192 chassis kinematics \u2192 timestamps and covariance \u2192 Odometry publication.<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The <a href=\"https:\/\/github.com\/Seeed-Projects\/mobile-robot-full-stack-course\/tree\/main\/hardware\/robot_drivers\">chassis SDK and driver resources<\/a> support motor-side integration. The ROS adapter must still provide the validated <code>nav_msgs\/msg\/Odometry<\/code> measurement contract required by the filter.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Why Can an IMU Drift While the Robot Is Stationary?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The course uses the MID-360&#8217;s built-in IMU on <code>\/livox\/imu<\/code>, with a reference publication rate of approximately 200 Hz. A stable rate is useful, but does not establish that the values describe the right motion.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If a stationary gyroscope retains a small bias b, its integrated angle error grows approximately as b multiplied by elapsed time. A value that looks close to zero can therefore produce a visible heading drift after several minutes.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Start with a stationary recording, then deliberately rotate the robot in a known direction. Check rad\/s for angular velocity, m\/s\u00b2 for acceleration, the expected signs and how the sensor axes map onto the body frame. The usual ROS body convention is x forward, y left and z up. A different mounting or native convention requires the appropriate transformation.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1030\" height=\"441\" src=\"https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/imu_topic-1030x441.jpg\" alt=\"ROS 2 terminal showing Livox IMU topic details, publisher information and QoS settings.\" class=\"wp-image-134841\" srcset=\"https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/imu_topic-1030x441.jpg 1030w, https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/imu_topic-300x128.jpg 300w, https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/imu_topic-766x328.jpg 766w, https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/imu_topic-1536x658.jpg 1536w, https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/imu_topic-32x14.jpg 32w, https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/imu_topic-1023x438.jpg 1023w, https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/imu_topic.jpg 1553w\" sizes=\"(max-width: 1030px) 100vw, 1030px\" \/><\/figure>\n\n\n\n<p class=\"has-text-align-center wp-block-paragraph\"><em>Establish the IMU interface before evaluating bias, units, axis conventions and timestamps. This launch also shows the LiDAR&#8217;s custom message type.<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Check whether acceleration includes gravity and whether valid orientation is available. Gravity compensation depends on orientation; subtracting a fixed value from one axis is not a general solution as the robot tilts.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The <a href=\"https:\/\/github.com\/ros2\/common_interfaces\/blob\/humble\/sensor_msgs\/msg\/Imu.msg\">ROS 2 Imu definition<\/a> also assigns meaning to covariance fields: an all-zero matrix means uncertainty is unknown, while a first element of \u22121 marks the corresponding estimate unavailable. Zero-filled fields do not establish a perfect measurement or a valid absolute heading.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">GNSS Quality Must Be Observed Over Time<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">GNSS adds an outdoor geographic position reference, but successive fixes need not have equal quality. Record position scatter, status and covariance while stationary in an open area. For RTK, distinguish Fixed, Float and standalone positioning. A momentary Fixed indication does not demonstrate sustained fixed-solution performance.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Wheel feedback supports short-term motion, the IMU captures rapid rotation, and GNSS can anchor outdoor position. They complement one another only when their current limitations are reflected in the system.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The computing platform must accommodate those drivers alongside chassis communication, ROS 2 processing, visualization and recording. The course builds around reComputer Robotics J501 hardware. For a matching development path, review the <a href=\"https:\/\/wiki.seeedstudio.com\/ai_robotics_recomputer_j501_robotics_getting_started\/\">J501 Robotics interface and getting-started guide<\/a> and the relevant product configuration:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\ud83d\udc49 <a href=\"https:\/\/www.seeedstudio.com\/reComputer-Robotics-J5011-with-GMSL-extension-board-p-6681.html\"><strong>reComputer Robotics J5011 with GMSL<\/strong><\/a> \u2014 Jetson AGX Orin 32GB configuration used in the course examples.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\ud83d\udc49 <a href=\"https:\/\/www.seeedstudio.com\/reComputer-Robotics-J5012-with-GMSL-extension-board-p-6682.html\"><strong>reComputer Robotics J5012 with GMSL<\/strong><\/a> \u2014 Jetson AGX Orin 64GB configuration to evaluate against your workload and peripheral requirements.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Why Do Correct-Looking Streams Misalign in Motion?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Individually working sensors still need to describe comparable moments in a common spatial reference. The <a href=\"https:\/\/github.com\/Seeed-Projects\/mobile-robot-full-stack-course\/blob\/d315015688e43a2f70082e13a98199b6de21e7dc\/docs\/M03-LiDAR-and-Multi-Sensor-Fusion\/3.3_Multi-Sensor_Time_Synchronization_and_Spatial_Alignment\/README_en_US.md\">time and spatial alignment experiments<\/a> address both relationships.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1030\" height=\"580\" src=\"https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/time_and_spatial_alignment-1030x580.png\" alt=\"Diagram illustrating sensor time synchronization and coordinate transformations between sensors and the robot body.\" class=\"wp-image-134842\" srcset=\"https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/time_and_spatial_alignment-1030x580.png 1030w, https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/time_and_spatial_alignment-300x169.png 300w, https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/time_and_spatial_alignment-32x18.png 32w, https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/time_and_spatial_alignment-1536x864.png 1536w, https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/time_and_spatial_alignment-768x432.png 768w, https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/time_and_spatial_alignment-1023x576.png 1023w, https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/time_and_spatial_alignment.png 1672w\" sizes=\"(max-width: 1030px) 100vw, 1030px\" \/><\/figure>\n\n\n\n<p class=\"has-text-align-center wp-block-paragraph\"><em>A common time reference on the left, and sensor-to-body relationships on the right. Each relationship needs its own verification.<\/em><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">First Establish What a Timestamp Represents<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Sampling, host reception and processing happen at different times. At 1 m\/s, a 100 ms difference corresponds to 10 cm of travel. During rotation, the same delay also changes the direction associated with an observation. This is a geometric illustration, not a course benchmark.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Determine whether a header contains device sampling time or host reception time, how clocks are related, and how transport and buffering affect arrival. The course introduces <code>chrony<\/code>, PTP, hardware synchronization and GNSS PPS, but their responsibilities differ.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Host clock alignment does not automatically synchronize every sensor&#8217;s internal clock. PTP depends on actual device and network-interface support. A shared hardware trigger requires compatible sampling inputs. GNSS PPS provides a second boundary, while accompanying time information identifies the absolute time; receiving position fixes alone does not synchronize all sensors.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">ROS <code>ApproximateTimeSynchronizer<\/code> instead pairs messages using existing timestamps. Too small a window loses pairs; too large a window can combine observations from noticeably different instants. Increasing the tolerance cannot repair a wrong clock.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Extrinsics Need a Direction, Not Just Six Numbers<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A point starts in its sensor&#8217;s coordinate frame. Expressing it in the robot body frame requires rotation R and translation t:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>p_base = R_base_from_sensor * p_sensor + t_base_from_sensor\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This is the same class of geometric problem as camera extrinsics in Module 2. State both the source and target frames, then use the transform in that direction.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Translation errors shift geometry; angular errors become more visible at longer range. As an illustrative calculation, a 1\u00b0 orientation error creates approximately 8.7 cm of lateral displacement at 5 m. Visual inspection of a bracket cannot replace measurement.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Measure mounting position and orientation, publish the corresponding static transform, then check a floor, wall or known target. Changing only <code>frame_id<\/code> to <code>base_link<\/code> does not transform the points themselves.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">A TF Tree Also Assigns Publishing Responsibility<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">For a local estimate, begin with:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>odom \u2192 base_link \u2192 sensor frames\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Rigid sensor mounts normally give fixed sensor-to-body relationships. The <code>odom \u2192 base_link<\/code> relationship changes with motion and needs one designated publisher. If the chassis node and EKF both publish that edge, adding another transform publisher will not resolve the conflict.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">After publishing the measured mounting transform, inspect it:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ros2 run tf2_ros tf2_echo base_link livox_frame\nros2 run tf2_tools view_frames\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Check stationary geometry first, then low-speed translation and rotation. A persistent offset at rest suggests checking extrinsics; misalignment that grows with motion calls for timing and scan-distortion checks. These patterns narrow the investigation rather than proving a single cause. For bag replay, also keep simulated time and <code>\/clock<\/code> usage consistent across the relevant nodes.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">How Does an EKF Help, and Why Can Fusion Make Things Worse?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Once time and coordinates are meaningful, the <a href=\"https:\/\/github.com\/Seeed-Projects\/mobile-robot-full-stack-course\/blob\/d315015688e43a2f70082e13a98199b6de21e7dc\/docs\/M03-LiDAR-and-Multi-Sensor-Fusion\/3.4_EKF_and_State_Estimation\/README_en_US.md\">state-estimation experiments<\/a> use <code>robot_localization<\/code> to establish a minimum wheel-plus-IMU EKF.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">An Extended Kalman Filter repeatedly predicts the next state from a motion model, then corrects that prediction with observations.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Understand Prediction Before Editing the Configuration<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Consider a simplified planar model for explanation: position x and y, heading theta, forward speed v and yaw rate omega. Over a small interval dt:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>x_next = x + v * cos(theta) * dt\ny_next = y + v * sin(theta) * dt\ntheta_next = theta + omega * dt\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The trigonometric terms make this relationship nonlinear. An EKF linearizes around the current estimate to propagate state and uncertainty, then incorporates new observations. This illustration explains the mechanism; it is not a replacement for the package&#8217;s full state definition.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Slip, unmodeled motion and measurement error keep prediction from being exact. The filter therefore compares the actual observation with the observation expected from its predicted state:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>innovation = actual_observation - predicted_observation\nupdated_state = predicted_state + Kalman_gain * innovation\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The gain depends on uncertainty in the prediction and observation. A smaller measurement covariance generally gives that observation more influence. Process noise Q describes uncertainty left by the motion model; measurement covariance R describes uncertainty in the observation. These are different responsibilities.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">An unrealistically small covariance can let a biased sensor dominate the estimate. A very large one can make its contribution negligible. Parameter selection therefore needs an explanation of why an input deserves that weight, beyond how smooth the path looks.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Start with Inputs Whose Roles You Can Explain<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The course&#8217;s minimum local configuration uses wheel forward velocity and IMU yaw rate, with <code>two_d_mode<\/code> constraining planar motion. Wheel feedback supplies translation information, while the IMU supplies turning information.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>odom0_config<\/code> and <code>imu0_config<\/code> arrays select the fused variables. Read their variable order rather than copying a Boolean list without understanding it. The example <code>\/imu\/data<\/code> input also needs to match the actual <code>\/livox\/imu<\/code> output through configuration or remapping.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Avoid treating encoder-derived position, heading and velocity as independent evidence when they come from the same source. Their correlation can make a filter overconfident if the same information is repeatedly counted. Also designate whether the chassis or filter owns <code>odom \u2192 base_link<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Raw point clouds are outside this minimum EKF input path. LiDAR integration and local motion estimation are foundations that later odometry and SLAM components can use together.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Use Measurements to Justify the Parameters<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The course recommends recording 5\u201310 minutes of stationary IMU data to examine mean, variance and drift, then repeating straight and turning tests to investigate wheel measurements. Use those observations to justify covariance rather than tuning entirely by feel.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Persistent wheel-radius, track-width or mounting errors require model or calibration corrections. Weight changes cannot reliably turn a systematic error into a correct measurement.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">After starting the configured filter, check its output:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ros2 topic hz \/odometry\/filtered\nros2 topic echo \/odometry\/filtered --once\nros2 run tf2_ros tf2_echo odom base_link\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Compare raw and fused trajectories in RViz. Inspect endpoint and heading differences, latency and covariance behavior. A smooth path can still be wrong or delayed; improvement needs a repeatable comparison with physical motion.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Add GNSS Without Confusing Local Continuity with Global Correction<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Latitude and longitude cannot be used directly as local Cartesian coordinates. The global extension introduces <code>navsat_transform_node<\/code>, alongside requirements for valid fixes, heading, local odometry and antenna extrinsics. IMU yaw rate alone does not supply an absolute heading.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The <a href=\"https:\/\/github.com\/ros-infrastructure\/rep\/blob\/master\/rep-0105.rst\">ROS coordinate-frame conventions<\/a> distinguish the responsibilities in:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>map \u2192 odom \u2192 base_link\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><code>odom<\/code> provides a locally continuous frame that may drift. <code>map<\/code> allows global correction. Keeping those responsibilities distinct lets the system express a global position adjustment without assigning the same discontinuity to its locally continuous odometry.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1030\" height=\"580\" src=\"https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/ekf_sensor_fusion-1030x580.png\" alt=\"Diagram illustrating EKF fusion of wheel odometry, IMU and GNSS measurements to estimate a mobile robot\u2019s trajectory and uncertainty.\" class=\"wp-image-134843\" srcset=\"https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/ekf_sensor_fusion-1030x580.png 1030w, https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/ekf_sensor_fusion-300x169.png 300w, https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/ekf_sensor_fusion-32x18.png 32w, https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/ekf_sensor_fusion-768x432.png 768w, https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/ekf_sensor_fusion-1536x864.png 1536w, https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/ekf_sensor_fusion-1023x576.png 1023w, https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/ekf_sensor_fusion.png 1672w\" sizes=\"(max-width: 1030px) 100vw, 1030px\" \/><\/figure>\n\n\n\n<p class=\"has-text-align-center wp-block-paragraph\"><em>Broader fusion architecture, including GNSS. The minimum local experiment uses wheel velocity and IMU yaw rate; the global extension has additional fix-quality, heading and transform requirements.<\/em><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What Makes the Result Worth Keeping?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Move from a running node to a result someone else can reproduce. Start with a small sequence of experiments.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Stationary:<\/strong> inspect IMU bias, zero wheel velocity, estimate drift and advancing timestamps.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Straight motion:<\/strong> use a known distance to check scale and sideways deviation, comparing wheel-based motion with the physical displacement.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>In-place turns:<\/strong> examine wheel signs, angular-velocity direction, track width and heading estimates. Watch whether point-cloud distortion grows with turning speed.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>A rectangular route:<\/strong> compare endpoint, heading and delay for raw and fused estimates. Record surface and operating conditions so comparisons use similar experiments.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Under controlled conditions, inspect how the system behaves when an input stops and returns. Look for stalled estimates, jumps and diagnostic changes.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Troubleshoot in the same order:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Connection and driver \u2192 messages and units \u2192 timestamps \u2192 frames and extrinsics \u2192 motion model \u2192 fusion configuration \u2192 experimental result.<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">No data calls for a connection check. Wrong magnitudes call for unit and decoding checks. Static offsets suggest geometry checks; motion-dependent errors require timing checks. Only then use residuals and repeatable tests to guide filter tuning.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A useful M03 result is a configuration, a recording, a raw-versus-fused comparison and an explanation of the observed behavior. Together they provide a stronger foundation for mapping and navigation than a screenshot of active topics alone.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Build Along and Bring Your Experiment to the Community<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">From the Jetson and ROS 2 foundation through camera perception to LiDAR, inertial and chassis feedback, this series connects the parts of a mobile robot into a system.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">M03 develops a practical habit: for every measurement, ask what it observes, when it was acquired, which frame describes it and how much uncertainty it carries.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Star or fork the <a href=\"https:\/\/github.com\/Seeed-Projects\/mobile-robot-full-stack-course\">course repository<\/a>, run the <a href=\"https:\/\/github.com\/Seeed-Projects\/mobile-robot-full-stack-course\/tree\/main\/docs\/M03-LiDAR-and-Multi-Sensor-Fusion\">complete M03 experiments<\/a>, and contribute reproducible issues or device-integration notes. A corrected CAN decoder, a measured mounting transform or an explanation of a drifting trajectory can help the next developer.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Share your hardware, software version, topic details, TF tree, configuration and a short recording alongside the result. A question such as \u201cWhy does this wall align at rest but split during a turn?\u201d gives other builders something concrete to investigate.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Continue the conversation with the Seeed Studio community:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/discord.gg\/9PQnDAHmkK\">Seeed Studio Discord<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/chat.whatsapp.com\/La3VQc8Znw8B4oTYGg2rdO\">WhatsApp Robotics Discussion Group<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/www.facebook.com\/groups\/rebotarm\">Facebook AI Robotics Community Group<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/forum.seeedstudio.com\/\">Seeed Studio Forum<\/a><\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Get the robot moving, then make the measurements behind that motion understandable.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Resources Used in This Article<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Hardware:<\/strong> <a href=\"https:\/\/www.seeedstudio.com\/reComputer-Robotics-J5011-with-GMSL-extension-board-p-6681.html\">reComputer Robotics J5011 with GMSL<\/a>, with the <a href=\"https:\/\/www.seeedstudio.com\/reComputer-Robotics-J5012-with-GMSL-extension-board-p-6682.html\">J5012 configuration<\/a> as an alternative to evaluate; RPLIDAR A1; Livox MID-360 with built-in IMU; the DM-H65 dual-motor chassis example. GNSS is an additional input for the global-positioning extension.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Setup and drivers:<\/strong> <a href=\"https:\/\/wiki.seeedstudio.com\/ai_robotics_recomputer_j501_robotics_getting_started\/\">J501 Robotics getting-started guide<\/a> \u00b7 <a href=\"https:\/\/github.com\/Seeed-Projects\/mobile-robot-full-stack-course\/tree\/main\/hardware\/robot_drivers\">Chassis SDK, WebUI and driver resources<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Software:<\/strong> Ubuntu 22.04 \u00b7 ROS 2 Humble \u00b7 RViz2 \u00b7 Livox-SDK2 and ROS Driver2 \u00b7 SocketCAN \u00b7 chrony and PTP where supported \u00b7 <code>robot_localization<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Hands-on course:<\/strong> <a href=\"https:\/\/github.com\/Seeed-Projects\/mobile-robot-full-stack-course\/tree\/main\/docs\/M03-LiDAR-and-Multi-Sensor-Fusion\">M03 \u2014 LiDAR and Multi-Sensor Fusion<\/a>. All seven inline images are original course assets reused from the corresponding experiments.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>From point clouds and chassis feedback to time alignment and EKF state estimation. In the<\/p>\n","protected":false},"author":3703,"featured_media":134817,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_lmt_disableupdate":"","_lmt_disable":"","_price":"","_stock":"","_tribe_ticket_header":"","_tribe_default_ticket_provider":"","_tribe_ticket_capacity":"0","_ticket_start_date":"","_ticket_end_date":"","_tribe_ticket_show_description":"","_tribe_ticket_show_not_going":false,"_tribe_ticket_use_global_stock":"","_tribe_ticket_global_stock_level":"","_global_stock_mode":"","_global_stock_cap":"","_tribe_rsvp_for_event":"","_tribe_ticket_going_count":"","_tribe_ticket_not_going_count":"","_tribe_tickets_list":"[]","_tribe_ticket_has_attendee_info_fields":false,"iawp_total_views":0,"footnotes":""},"categories":[5007,1],"tags":[267,5721,330,1824,5720,5633,5719],"class_list":["post-134815","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-feature","category-news","tag-imu","tag-jetson-course","tag-lidar","tag-nvidia-jetson","tag-recomputer-robotics","tag-ros-2","tag-sensor-fusion"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v24.0 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Robot Sensor Fusion with ROS 2: LiDAR, IMU &amp; EKF | Module 03<\/title>\n<meta name=\"description\" content=\"Build robot sensor fusion with ROS 2: validate LiDAR, IMU and wheel data, align time and TF, then configure an EKF on reComputer Robotics J501.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.seeedstudio.com\/blog\/2026\/10\/23\/mobile-robot-full-stack-development-in-practice-\u2172-how-does-a-robot-know-it-is-moving-lidar-imu-and-sensor-fusion\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Robot Sensor Fusion with ROS 2: LiDAR, IMU &amp; EKF | Module 03\" \/>\n<meta property=\"og:description\" content=\"Build robot sensor fusion with ROS 2: validate LiDAR, IMU and wheel data, align time and TF, then configure an EKF on reComputer Robotics J501.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.seeedstudio.com\/blog\/2026\/10\/23\/mobile-robot-full-stack-development-in-practice-\u2172-how-does-a-robot-know-it-is-moving-lidar-imu-and-sensor-fusion\/\" \/>\n<meta property=\"og:site_name\" content=\"Latest News from Seeed Studio\" \/>\n<meta property=\"article:published_time\" content=\"2026-10-23T09:17:21+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-10-23T09:17:24+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/mobile-robot-dev-m3_Featured.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1734\" \/>\n\t<meta property=\"og:image:height\" content=\"907\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Frank\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Frank\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"16 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.seeedstudio.com\/blog\/2026\/10\/23\/mobile-robot-full-stack-development-in-practice-%e2%85%b2-how-does-a-robot-know-it-is-moving-lidar-imu-and-sensor-fusion\/\",\"url\":\"https:\/\/www.seeedstudio.com\/blog\/2026\/10\/23\/mobile-robot-full-stack-development-in-practice-%e2%85%b2-how-does-a-robot-know-it-is-moving-lidar-imu-and-sensor-fusion\/\",\"name\":\"Robot Sensor Fusion with ROS 2: LiDAR, IMU & EKF | Module 03\",\"isPartOf\":{\"@id\":\"https:\/\/www.seeedstudio.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.seeedstudio.com\/blog\/2026\/10\/23\/mobile-robot-full-stack-development-in-practice-%e2%85%b2-how-does-a-robot-know-it-is-moving-lidar-imu-and-sensor-fusion\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.seeedstudio.com\/blog\/2026\/10\/23\/mobile-robot-full-stack-development-in-practice-%e2%85%b2-how-does-a-robot-know-it-is-moving-lidar-imu-and-sensor-fusion\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/mobile-robot-dev-m3_Featured.png\",\"datePublished\":\"2026-10-23T09:17:21+00:00\",\"dateModified\":\"2026-10-23T09:17:24+00:00\",\"author\":{\"@id\":\"https:\/\/www.seeedstudio.com\/blog\/#\/schema\/person\/1dd116f442e8ed65ddbd35b6d3f28d8a\"},\"description\":\"Build robot sensor fusion with ROS 2: validate LiDAR, IMU and wheel data, align time and TF, then configure an EKF on reComputer Robotics J501.\",\"breadcrumb\":{\"@id\":\"https:\/\/www.seeedstudio.com\/blog\/2026\/10\/23\/mobile-robot-full-stack-development-in-practice-%e2%85%b2-how-does-a-robot-know-it-is-moving-lidar-imu-and-sensor-fusion\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.seeedstudio.com\/blog\/2026\/10\/23\/mobile-robot-full-stack-development-in-practice-%e2%85%b2-how-does-a-robot-know-it-is-moving-lidar-imu-and-sensor-fusion\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.seeedstudio.com\/blog\/2026\/10\/23\/mobile-robot-full-stack-development-in-practice-%e2%85%b2-how-does-a-robot-know-it-is-moving-lidar-imu-and-sensor-fusion\/#primaryimage\",\"url\":\"https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/mobile-robot-dev-m3_Featured.png\",\"contentUrl\":\"https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/mobile-robot-dev-m3_Featured.png\",\"width\":1734,\"height\":907,\"caption\":\"Mobile Robot Full-Stack Development in Practice \u2014 Module 3: LiDAR, IMU, GNSS and EKF Sensor Fusion\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.seeedstudio.com\/blog\/2026\/10\/23\/mobile-robot-full-stack-development-in-practice-%e2%85%b2-how-does-a-robot-know-it-is-moving-lidar-imu-and-sensor-fusion\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.seeedstudio.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Mobile Robot Full-Stack Development in Practice \u2162: How Does a Robot Know It Is Moving? LiDAR, IMU and Sensor Fusion\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.seeedstudio.com\/blog\/#website\",\"url\":\"https:\/\/www.seeedstudio.com\/blog\/\",\"name\":\"Latest News from Seeed Studio\",\"description\":\"Emerging IoT, AI and Autonomous Applications on the Edge\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.seeedstudio.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.seeedstudio.com\/blog\/#\/schema\/person\/1dd116f442e8ed65ddbd35b6d3f28d8a\",\"name\":\"Frank\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.seeedstudio.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/1e890dec78807f51d88e5a819bc5b8ed232418f466c308274995ea7e7dbe0183?s=96&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/1e890dec78807f51d88e5a819bc5b8ed232418f466c308274995ea7e7dbe0183?s=96&r=g\",\"caption\":\"Frank\"},\"url\":\"https:\/\/www.seeedstudio.com\/blog\/author\/frank\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Robot Sensor Fusion with ROS 2: LiDAR, IMU & EKF | Module 03","description":"Build robot sensor fusion with ROS 2: validate LiDAR, IMU and wheel data, align time and TF, then configure an EKF on reComputer Robotics J501.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.seeedstudio.com\/blog\/2026\/10\/23\/mobile-robot-full-stack-development-in-practice-\u2172-how-does-a-robot-know-it-is-moving-lidar-imu-and-sensor-fusion\/","og_locale":"en_US","og_type":"article","og_title":"Robot Sensor Fusion with ROS 2: LiDAR, IMU & EKF | Module 03","og_description":"Build robot sensor fusion with ROS 2: validate LiDAR, IMU and wheel data, align time and TF, then configure an EKF on reComputer Robotics J501.","og_url":"https:\/\/www.seeedstudio.com\/blog\/2026\/10\/23\/mobile-robot-full-stack-development-in-practice-\u2172-how-does-a-robot-know-it-is-moving-lidar-imu-and-sensor-fusion\/","og_site_name":"Latest News from Seeed Studio","article_published_time":"2026-10-23T09:17:21+00:00","article_modified_time":"2026-10-23T09:17:24+00:00","og_image":[{"width":1734,"height":907,"url":"https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/mobile-robot-dev-m3_Featured.png","type":"image\/png"}],"author":"Frank","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Frank","Est. reading time":"16 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.seeedstudio.com\/blog\/2026\/10\/23\/mobile-robot-full-stack-development-in-practice-%e2%85%b2-how-does-a-robot-know-it-is-moving-lidar-imu-and-sensor-fusion\/","url":"https:\/\/www.seeedstudio.com\/blog\/2026\/10\/23\/mobile-robot-full-stack-development-in-practice-%e2%85%b2-how-does-a-robot-know-it-is-moving-lidar-imu-and-sensor-fusion\/","name":"Robot Sensor Fusion with ROS 2: LiDAR, IMU & EKF | Module 03","isPartOf":{"@id":"https:\/\/www.seeedstudio.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.seeedstudio.com\/blog\/2026\/10\/23\/mobile-robot-full-stack-development-in-practice-%e2%85%b2-how-does-a-robot-know-it-is-moving-lidar-imu-and-sensor-fusion\/#primaryimage"},"image":{"@id":"https:\/\/www.seeedstudio.com\/blog\/2026\/10\/23\/mobile-robot-full-stack-development-in-practice-%e2%85%b2-how-does-a-robot-know-it-is-moving-lidar-imu-and-sensor-fusion\/#primaryimage"},"thumbnailUrl":"https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/mobile-robot-dev-m3_Featured.png","datePublished":"2026-10-23T09:17:21+00:00","dateModified":"2026-10-23T09:17:24+00:00","author":{"@id":"https:\/\/www.seeedstudio.com\/blog\/#\/schema\/person\/1dd116f442e8ed65ddbd35b6d3f28d8a"},"description":"Build robot sensor fusion with ROS 2: validate LiDAR, IMU and wheel data, align time and TF, then configure an EKF on reComputer Robotics J501.","breadcrumb":{"@id":"https:\/\/www.seeedstudio.com\/blog\/2026\/10\/23\/mobile-robot-full-stack-development-in-practice-%e2%85%b2-how-does-a-robot-know-it-is-moving-lidar-imu-and-sensor-fusion\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.seeedstudio.com\/blog\/2026\/10\/23\/mobile-robot-full-stack-development-in-practice-%e2%85%b2-how-does-a-robot-know-it-is-moving-lidar-imu-and-sensor-fusion\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.seeedstudio.com\/blog\/2026\/10\/23\/mobile-robot-full-stack-development-in-practice-%e2%85%b2-how-does-a-robot-know-it-is-moving-lidar-imu-and-sensor-fusion\/#primaryimage","url":"https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/mobile-robot-dev-m3_Featured.png","contentUrl":"https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/mobile-robot-dev-m3_Featured.png","width":1734,"height":907,"caption":"Mobile Robot Full-Stack Development in Practice \u2014 Module 3: LiDAR, IMU, GNSS and EKF Sensor Fusion"},{"@type":"BreadcrumbList","@id":"https:\/\/www.seeedstudio.com\/blog\/2026\/10\/23\/mobile-robot-full-stack-development-in-practice-%e2%85%b2-how-does-a-robot-know-it-is-moving-lidar-imu-and-sensor-fusion\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.seeedstudio.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Mobile Robot Full-Stack Development in Practice \u2162: How Does a Robot Know It Is Moving? LiDAR, IMU and Sensor Fusion"}]},{"@type":"WebSite","@id":"https:\/\/www.seeedstudio.com\/blog\/#website","url":"https:\/\/www.seeedstudio.com\/blog\/","name":"Latest News from Seeed Studio","description":"Emerging IoT, AI and Autonomous Applications on the Edge","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.seeedstudio.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/www.seeedstudio.com\/blog\/#\/schema\/person\/1dd116f442e8ed65ddbd35b6d3f28d8a","name":"Frank","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.seeedstudio.com\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/1e890dec78807f51d88e5a819bc5b8ed232418f466c308274995ea7e7dbe0183?s=96&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/1e890dec78807f51d88e5a819bc5b8ed232418f466c308274995ea7e7dbe0183?s=96&r=g","caption":"Frank"},"url":"https:\/\/www.seeedstudio.com\/blog\/author\/frank\/"}]}},"modified_by":"Frank","views":12,"featured_image_urls":{"full":["https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/mobile-robot-dev-m3_Featured.png",1734,907,false],"thumbnail":["https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/mobile-robot-dev-m3_Featured-80x80.png",80,80,true],"medium":["https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/mobile-robot-dev-m3_Featured-300x157.png",300,157,true],"medium_large":["https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/mobile-robot-dev-m3_Featured-768x402.png",640,335,true],"large":["https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/mobile-robot-dev-m3_Featured-1030x539.png",640,335,true],"1536x1536":["https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/mobile-robot-dev-m3_Featured-1536x803.png",1536,803,true],"2048x2048":["https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/mobile-robot-dev-m3_Featured.png",1734,907,false],"visody_icon":["https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/mobile-robot-dev-m3_Featured-32x17.png",32,17,true],"magazine-7-slider-full":["https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/mobile-robot-dev-m3_Featured-1536x907.png",1536,907,true],"magazine-7-slider-center":["https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/mobile-robot-dev-m3_Featured-936x897.png",936,897,true],"magazine-7-featured":["https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/mobile-robot-dev-m3_Featured-1024x536.png",1024,536,true],"magazine-7-medium":["https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/mobile-robot-dev-m3_Featured-720x380.png",720,380,true],"magazine-7-medium-square":["https:\/\/www.seeedstudio.com\/blog\/wp-content\/uploads\/2026\/10\/mobile-robot-dev-m3_Featured-675x450.png",675,450,true]},"author_info":{"display_name":"Frank","author_link":"https:\/\/www.seeedstudio.com\/blog\/author\/frank\/"},"category_info":"<a href=\"https:\/\/www.seeedstudio.com\/blog\/category\/feature\/\" rel=\"category tag\">Feature<\/a> <a href=\"https:\/\/www.seeedstudio.com\/blog\/category\/news\/\" rel=\"category tag\">News<\/a>","tag_info":"News","comment_count":"0","_links":{"self":[{"href":"https:\/\/www.seeedstudio.com\/blog\/wp-json\/wp\/v2\/posts\/134815","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.seeedstudio.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.seeedstudio.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.seeedstudio.com\/blog\/wp-json\/wp\/v2\/users\/3703"}],"replies":[{"embeddable":true,"href":"https:\/\/www.seeedstudio.com\/blog\/wp-json\/wp\/v2\/comments?post=134815"}],"version-history":[{"count":4,"href":"https:\/\/www.seeedstudio.com\/blog\/wp-json\/wp\/v2\/posts\/134815\/revisions"}],"predecessor-version":[{"id":134844,"href":"https:\/\/www.seeedstudio.com\/blog\/wp-json\/wp\/v2\/posts\/134815\/revisions\/134844"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.seeedstudio.com\/blog\/wp-json\/wp\/v2\/media\/134817"}],"wp:attachment":[{"href":"https:\/\/www.seeedstudio.com\/blog\/wp-json\/wp\/v2\/media?parent=134815"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.seeedstudio.com\/blog\/wp-json\/wp\/v2\/categories?post=134815"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.seeedstudio.com\/blog\/wp-json\/wp\/v2\/tags?post=134815"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}