CARLA
Loading...
Searching...
No Matches
CarlaLidarPublisher.cpp
Go to the documentation of this file.
1// Copyright (c) 2026 Computer Vision Center (CVC) at the Universitat Autonoma
2// de Barcelona (UAB).
3//
4// This work is licensed under the terms of the MIT license.
5// For a copy, see <https://opensource.org/licenses/MIT>.
6
8
9#include <cmath>
10#include <cstring>
11
12namespace carla {
13namespace ros2 {
14
15namespace {
16
17// Wire-compatible POD mirror of sensor::data::LidarDetection: {x, y, z,
18// intensity} as four contiguous float32s. The real struct lives behind
19// carla/sensor/data/LidarData.h, which transitively includes
20// carla/MsgPack.h + carla/Buffer.h and therefore pulls Boost — not
21// available on the carla-ros2-native ExternalProject include path. The
22// POD shape and stride (16 B per point) are pinned by kLidarFields in
23// PointCloudFieldsLayout.h and by test_point_cloud_fields.cpp.
24struct LidarPoint {
25 float x;
26 float y;
27 float z;
28 float intensity;
29};
30
31static_assert(
32 sizeof(LidarPoint) == 16u,
33 "LidarPoint must be tightly packed (4 float32s) for the wire layout");
34
35// Wire POD for the extended Autoware PointXYZIRCAEDT layout (tier4
36// SetDataEx). Natural alignment yields no padding: the uint16 channel lands
37// on offset 14 and the trailing uint32 closes the struct at 32 B, matching
38// kLidarFieldsEx in PointCloudFieldsLayout.h byte for byte.
39struct LidarPointEx {
40 float x{0.0f};
41 float y{0.0f};
42 float z{0.0f};
43 std::uint8_t intensity{0u};
44 std::uint8_t return_type{0u};
45 std::uint16_t channel{0u};
46 float azimuth{0.0f};
47 float elevation{0.0f};
48 float distance{0.0f};
49 std::uint32_t time_stamp{0u};
50};
51
52static_assert(
53 sizeof(LidarPointEx) == 32u,
54 "LidarPointEx must match the kLidarFieldsEx wire layout (32 B stride)");
55
56} // namespace
57
59 return _extended ? sizeof(LidarPointEx) : sizeof(LidarPoint);
60}
61
65
67 return _extended ? kLidarFieldsEx.size() : kLidarFields.size();
68}
69
71 std::int32_t seconds,
72 std::uint32_t nanoseconds,
73 std::uint32_t height,
74 std::uint32_t width,
75 const std::uint8_t *data,
76 const std::uint32_t *channel_point_counts,
77 std::size_t channel_count,
78 const std::vector<float> &vertical_angles) {
79 _extended = true;
81 channel_point_counts, channel_point_counts + channel_count);
82 _vertical_angles = vertical_angles;
84 seconds, nanoseconds, height, width, data);
85}
86
88 std::uint32_t height, std::uint32_t width, const std::uint8_t *data) const {
89 const std::size_t total_points =
90 static_cast<std::size_t>(height) * static_cast<std::size_t>(width);
91
92 if (!_extended) {
93 const std::size_t total_bytes = total_points * sizeof(LidarPoint);
94
95 std::vector<std::uint8_t> bytes(total_bytes);
96 std::memcpy(bytes.data(), data, total_bytes);
97
98 // Mirror the Y axis to land in the ROS right-handed frame. The buffer is
99 // a contiguous array of LidarPoint PODs by contract; aliasing into the
100 // copy is safe.
101 auto *points = reinterpret_cast<LidarPoint *>(bytes.data());
102 for (std::size_t i = 0; i < total_points; ++i) {
103 points[i].y *= -1.0f;
104 }
105 return bytes;
106 }
107
108 // Extended path (tier4 SetDataEx semantics): expand each XYZI detection
109 // into a LidarPointEx.
110 const auto *input = reinterpret_cast<const LidarPoint *>(data);
111
112 std::vector<std::uint8_t> bytes(total_points * sizeof(LidarPointEx));
113 auto *points = reinterpret_cast<LidarPointEx *>(bytes.data());
114
115 for (std::size_t i = 0; i < total_points; ++i) {
116 auto &point = points[i];
117 point = LidarPointEx{};
118 point.x = input[i].x;
119 point.y = -input[i].y; // ROS right-handed frame, same flip as XYZI path.
120 // tier4 truncates CARLA's [0, 1] float intensity straight into the uint8
121 // slot (so it is effectively 0/1); replicated verbatim for wire parity.
122 point.intensity = static_cast<std::uint8_t>(input[i].intensity);
123 point.z = input[i].z;
124 // return_type stays 0 (tier4 leaves it as dummy data).
125 // Azimuth/distance are derived from the (already mirrored) Cartesian
126 // coordinates, matching tier4's post-conversion computation.
127 point.distance = std::hypot(point.x, point.y, point.z);
128 point.azimuth = std::atan2(point.y, point.x);
129 // Autoware's distortion corrector reads time_stamp as a nanosecond offset
130 // from header.stamp. CARLA casts every ray of a scan at the same sim
131 // instant, so the offset is zero for all points.
132 point.time_stamp = 0u;
133 }
134
135 // Channel index and per-channel elevation from the LidarData header's
136 // per-channel point counts. Points are serialized channel-major, so the
137 // running offset recovers each point's channel.
138 std::size_t accumulated = 0u;
139 for (std::size_t channel = 0; channel < _channel_point_counts.size(); ++channel) {
140 const std::size_t count = _channel_point_counts[channel];
141 const float elevation =
143 for (std::size_t i = 0; i < count && accumulated + i < total_points; ++i) {
144 points[accumulated + i].channel = static_cast<std::uint16_t>(channel);
145 points[accumulated + i].elevation = elevation;
146 }
147 accumulated += count;
148 }
149
150 return bytes;
151}
152
153} // namespace ros2
154} // namespace carla
float azimuth
float distance
float intensity
float elevation
std::uint8_t return_type
std::uint32_t time_stamp
std::uint16_t channel
float y
float x
float z
geom::Point point
Definition LaneGraph.cpp:41
std::vector< std::uint8_t > ComputePointCloud(std::uint32_t height, std::uint32_t width, const std::uint8_t *data) const override
std::vector< std::uint32_t > _channel_point_counts
std::size_t GetFieldDescriptorCount() const override
const PointFieldDescriptor * GetFieldDescriptors() const override
bool WriteExtendedPointCloud(std::int32_t seconds, std::uint32_t nanoseconds, std::uint32_t height, std::uint32_t width, const std::uint8_t *data, const std::uint32_t *channel_point_counts, std::size_t channel_count, const std::vector< float > &vertical_angles)
std::size_t GetPointSize() const override
bool WritePointCloud(std::int32_t seconds, std::uint32_t nanoseconds, std::uint32_t height, std::uint32_t width, const std::uint8_t *data)
constexpr std::array< PointFieldDescriptor, 4 > kLidarFields
constexpr std::array< PointFieldDescriptor, 10 > kLidarFieldsEx
This file contains definitions of common data structures used in traffic manager.