Skip to content
Open
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
8a3147b
gz-sim: CpuLidar system plugin with sensor discovery, RaycastData pop…
apojomovsky Feb 12, 2026
a87591a
docs: CpuLidar example world and documentation
apojomovsky Feb 12, 2026
f9b5ae5
copyright: update to 2026
apojomovsky Feb 13, 2026
242a7e0
copyright: update to 2026
apojomovsky Feb 13, 2026
cfc5d9f
test: refine CPU Lidar integration tests for consistency and robustness
apojomovsky Feb 13, 2026
91094e8
feat: prefer batch raycasting API in Physics system
apojomovsky Feb 17, 2026
0ed6f46
perf: gate raycasting to sensor update frequency, not physics rate
apojomovsky Feb 17, 2026
9013478
test: add PointCloudPublished end-to-end integration test
apojomovsky Feb 18, 2026
a6a44c7
bazel: add cpu_lidar system plugin target
apojomovsky Feb 19, 2026
e4441c3
revert: remove unrelated copyright year changes
apojomovsky Feb 19, 2026
9b34a31
style: tighten comments to match codebase conventions
apojomovsky Feb 19, 2026
f49df80
style: fix minor issues in Python binding docstrings
apojomovsky Feb 19, 2026
9d2eb66
Revert "style: fix minor issues in Python binding docstrings"
apojomovsky Feb 19, 2026
e3520e1
fix: revert comments accidentally removed
apojomovsky Feb 19, 2026
26a2b6a
fix: resolve cpplint violations in cpu_lidar
apojomovsky Feb 19, 2026
01235ee
fix: default needsRaycast to true, add subscriber in RaycastResultsPr…
apojomovsky Feb 20, 2026
c868be9
Merge branch 'main' into feature/cpu-lidar
apojomovsky Mar 23, 2026
895ca91
style: inline test subscription callbacks
apojomovsky Mar 24, 2026
ac80b71
Merge branch 'main' into feature/cpu-lidar
apojomovsky Mar 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,27 @@ gz_sim_system_libraries(
],
)

gz_sim_system_libraries(
srcs = glob(
[
"src/systems/cpu_lidar/**/*.cc",
"src/systems/cpu_lidar/**/*.hh",
],
),
so_lib_name = "gz-sim-cpu-lidar-system.so",
static_lib_name = "gz-sim-cpu-lidar-system-static",
visibility = ["//visibility:public"],
deps = [
":gz-sim",
"@gz-common//profiler",
"@gz-physics",
"@gz-plugin//:register",
"@gz-sensors",
"@gz-sensors//:cpu_lidar",
"@sdformat",
],
)

gz_sim_system_libraries(
srcs = glob(
[
Expand Down
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ gz_find_package(gz-sensors REQUIRED
air_pressure
air_speed
altimeter
cpu_lidar
imu
force_torque
logical_camera
Expand Down
233 changes: 233 additions & 0 deletions examples/worlds/cpu_lidar_sensor.sdf
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
<?xml version="1.0" ?>
<!--
CPU Lidar Sensor Demo

This world demonstrates a physics-based lidar sensor that uses the collision
engine (DART + Bullet) for raycasting instead of the GPU rendering pipeline.
It does not require a GPU or rendering engine.

Requirements:
- Physics engine must be DART with the Bullet collision detector.
Other collision detectors (ODE, FCL) do not support raycasting.
- The cpu-lidar system plugin must be loaded (not the sensors system).
- The sensor detects collision geometry, not visual meshes.

Echo lidar messages:
gz topic -e -t /cpu_lidar
Echo point cloud messages:
gz topic -e -t /cpu_lidar/points
-->
<sdf version="1.6">
<world name="cpu_lidar_demo">

<!-- DART physics with Bullet collision detector (required for raycasting) -->
<physics name="1ms" type="dart">
<max_step_size>0.001</max_step_size>
<real_time_factor>1.0</real_time_factor>
<dart>
<collision_detector>bullet</collision_detector>
</dart>
</physics>

<plugin
filename="gz-sim-physics-system"
name="gz::sim::systems::Physics">
</plugin>
<!-- CPU lidar system — processes lidar sensors via physics raycasting -->
<plugin
filename="gz-sim-cpu-lidar-system"
name="gz::sim::systems::CpuLidar">
</plugin>
<plugin
filename="gz-sim-scene-broadcaster-system"
name="gz::sim::systems::SceneBroadcaster">
</plugin>
<plugin
filename="gz-sim-user-commands-system"
name="gz::sim::systems::UserCommands">
</plugin>

<light type="directional" name="sun">
<cast_shadows>true</cast_shadows>
<pose>0 0 10 0 0 0</pose>
<diffuse>0.8 0.8 0.8 1</diffuse>
<specular>0.2 0.2 0.2 1</specular>
<attenuation>
<range>1000</range>
<constant>0.9</constant>
<linear>0.01</linear>
<quadratic>0.001</quadratic>
</attenuation>
<direction>-0.5 0.1 -0.9</direction>
</light>

<!-- Ground plane -->
<model name="ground_plane">
<static>true</static>
<link name="link">
<collision name="collision">
<geometry>
<box>
<size>20 20 0.1</size>
</box>
</geometry>
</collision>
<visual name="visual">
<geometry>
<box>
<size>20 20 0.1</size>
</box>
</geometry>
<material>
<ambient>0.8 0.8 0.8 1</ambient>
<diffuse>0.8 0.8 0.8 1</diffuse>
<specular>0.8 0.8 0.8 1</specular>
</material>
</visual>
</link>
</model>

<!-- Obstacle: box in front -->
<model name="box_front">
<static>true</static>
<pose>3 0 0.5 0 0 0</pose>
<link name="link">
<collision name="collision">
<geometry>
<box>
<size>1 1 1</size>
</box>
</geometry>
</collision>
<visual name="visual">
<geometry>
<box>
<size>1 1 1</size>
</box>
</geometry>
<material>
<ambient>1 0 0 1</ambient>
<diffuse>1 0 0 1</diffuse>
<specular>1 0 0 1</specular>
</material>
</visual>
</link>
</model>

<!-- Obstacle: cylinder to the left -->
<model name="cylinder_left">
<static>true</static>
<pose>0 3 0.5 0 0 0</pose>
<link name="link">
<collision name="collision">
<geometry>
<cylinder>
<radius>0.5</radius>
<length>1.0</length>
</cylinder>
</geometry>
</collision>
<visual name="visual">
<geometry>
<cylinder>
<radius>0.5</radius>
<length>1.0</length>
</cylinder>
</geometry>
<material>
<ambient>0 1 0 1</ambient>
<diffuse>0 1 0 1</diffuse>
<specular>0 1 0 1</specular>
</material>
</visual>
</link>
</model>

<!-- Obstacle: box behind and to the right -->
<model name="box_behind_right">
<static>true</static>
<pose>-2 -2 0.75 0 0 0.785</pose>
<link name="link">
<collision name="collision">
<geometry>
<box>
<size>1.5 0.5 1.5</size>
</box>
</geometry>
</collision>
<visual name="visual">
<geometry>
<box>
<size>1.5 0.5 1.5</size>
</box>
</geometry>
<material>
<ambient>0 0 1 1</ambient>
<diffuse>0 0 1 1</diffuse>
<specular>0 0 1 1</specular>
</material>
</visual>
</link>
</model>

<!-- Model with CPU lidar sensor -->
<model name="lidar_platform">
<static>true</static>
<pose>0 0 0.5 0 0 0</pose>
<link name="link">
<pose>0.05 0.05 0.05 0 0 0</pose>
<inertial>
<mass>0.1</mass>
<inertia>
<ixx>0.000166667</ixx>
<iyy>0.000166667</iyy>
<izz>0.000166667</izz>
</inertia>
</inertial>
<collision name="collision">
<geometry>
<box>
<size>0.1 0.1 0.1</size>
</box>
</geometry>
</collision>
<visual name="visual">
<geometry>
<box>
<size>0.1 0.1 0.1</size>
</box>
</geometry>
</visual>

<sensor name="cpu_lidar" type="lidar">
<topic>/cpu_lidar</topic>
<update_rate>10</update_rate>
<always_on>1</always_on>
<visualize>false</visualize>
<ray>
<scan>
<horizontal>
<samples>640</samples>
<resolution>1</resolution>
<min_angle>-3.14159</min_angle>
<max_angle>3.14159</max_angle>
</horizontal>
<vertical>
<samples>1</samples>
<resolution>1</resolution>
<min_angle>0</min_angle>
<max_angle>0</max_angle>
</vertical>
</scan>
<range>
<min>0.08</min>
<max>10.0</max>
<resolution>0.01</resolution>
</range>
</ray>
</sensor>
</link>
</model>

</world>
</sdf>
43 changes: 43 additions & 0 deletions include/gz/sim/components/CpuLidar.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright (C) 2026 Open Source Robotics Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#ifndef GZ_SIM_COMPONENTS_CPU_LIDAR_HH_
#define GZ_SIM_COMPONENTS_CPU_LIDAR_HH_

#include <sdf/Sensor.hh>
#include <gz/sim/components/Factory.hh>
#include <gz/sim/components/Component.hh>
#include <gz/sim/components/Serialization.hh>
#include <gz/sim/config.hh>

namespace gz
{
namespace sim
{
// Inline bracket to help doxygen filtering.
inline namespace GZ_SIM_VERSION_NAMESPACE {
namespace components
{
/// \brief A component type that contains a CPU lidar sensor,
/// sdf::Sensor, information.
using CpuLidar = Component<sdf::Sensor, class CpuLidarTag,
serializers::SensorSerializer>;
GZ_SIM_REGISTER_COMPONENT("gz_sim_components.CpuLidar", CpuLidar)
}
}
}
}
#endif
3 changes: 3 additions & 0 deletions include/gz/sim/components/RaycastData.hh
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ struct RaycastDataInfo

/// @brief The results of the raycasting.
std::vector<RaycastResultInfo> results;

/// @brief Whether the sensor needs fresh raycast results this step.
bool needsRaycast = true;
};
}

Expand Down
11 changes: 6 additions & 5 deletions src/SdfEntityCreator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
#include "gz/sim/components/DepthCamera.hh"
#include "gz/sim/components/ForceTorque.hh"
#include "gz/sim/components/Geometry.hh"
#include "gz/sim/components/CpuLidar.hh"
#include "gz/sim/components/GpuLidar.hh"
#include "gz/sim/components/Gravity.hh"
#include "gz/sim/components/Imu.hh"
Expand Down Expand Up @@ -1135,11 +1136,11 @@ Entity SdfEntityCreator::CreateEntities(const sdf::Sensor *_sensor)
}
else if (_sensor->Type() == sdf::SensorType::LIDAR)
{
// \todo(anyone) Implement CPU-based lidar
// this->dataPtr->ecm->CreateComponent(sensorEntity,
// components::Lidar(*_sensor));
gzwarn << "Sensor type LIDAR not supported yet. Try using"
<< "a GPU LIDAR instead." << std::endl;
this->dataPtr->ecm->CreateComponent(sensorEntity,
components::CpuLidar(*_sensor));

this->dataPtr->ecm->CreateComponent(sensorEntity,
components::WorldPose(math::Pose3d::Zero));
}
else if (_sensor->Type() == sdf::SensorType::DEPTH_CAMERA)
{
Expand Down
1 change: 1 addition & 0 deletions src/systems/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ add_subdirectory(buoyancy_engine)
add_subdirectory(collada_world_exporter)
add_subdirectory(comms_endpoint)
add_subdirectory(contact)
add_subdirectory(cpu_lidar)
add_subdirectory(camera_video_recorder)
add_subdirectory(detachable_joint)
add_subdirectory(diff_drive)
Expand Down
8 changes: 8 additions & 0 deletions src/systems/cpu_lidar/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
gz_add_system(cpu-lidar
SOURCES
CpuLidar.cc
PUBLIC_LINK_LIBS
gz-common::gz-common
PRIVATE_LINK_LIBS
gz-sensors::cpu_lidar
)
Loading