CARLA
 
载入中...
搜索中...
未找到
LibCarla/source/carla/client/LaneInvasionSensor.cpp
浏览该文件的文档.
1// Copyright (c) 2019 Computer Vision Center (CVC) at the Universitat Autonoma
2// de Barcelona (UAB).
3//
4// 压线传感器
5//
6// This work is licensed under the terms of the MIT license.
7// For a copy, see <https://opensource.org/licenses/MIT>.
8
10
11#include "carla/Logging.h"
12#include "carla/client/Map.h"
15#include "carla/geom/Location.h"
16#include "carla/geom/Math.h"
18
19#include <exception>
20
21namespace carla {
22namespace client {
23
24 // ===========================================================================
25 // -- 静态局部方法 ------------------------------------------------------------
26 // ===========================================================================
27 // 静态方法,根据给定的偏航角和位置进行旋转计算
28 // 此方法用于将一个位置按照给定的偏航角进行旋转,常用于坐标转换等相关操作
29 // 参数说明:
30 // - yaw:偏航角,以度为单位,代表绕Z轴旋转的角度。
31 // - location:要进行旋转的位置坐标(geom::Location类型)。
32 static geom::Location Rotate(float yaw, const geom::Location &location) {
33 // 将偏航角从度转换为弧度,因为后续的三角函数计算通常需要使用弧度制
34 yaw *= geom::Math::Pi<float>() / 180.0f;
35 const float c = std::cos(yaw);
36 const float s = std::sin(yaw);
37 // 按照二维平面旋转的数学公式进行坐标计算,返回旋转后的新位置坐标
38 // 这里实现的是绕原点的二维旋转,适用于平面坐标的转换情况
39 return {
40 c * location.x - s * location.y,
41 s * location.x + c * location.y,
42 location.z};
43 }
44
45 // ===========================================================================
46 // -- 压线回调类 LaneInvasionCallback -----------------------------------------
47 // ===========================================================================
48
49 class LaneInvasionCallback {
50 public:
51// 构造函数,接收车辆、地图智能指针和用户回调函数
52 // 用于初始化相关成员变量,这些变量将在后续判断车辆是否压线以及触发回调等操作中使用
53 // 参数说明:
54 // - vehicle:表示要检测压线情况的车辆对象,从中可以获取车辆的相关属性信息,如ID、边界框等。
55 // - map:指向地图对象的智能指针,地图包含了道路、车道等信息,用于判断车辆与车道的交叉情况。
56 // - user_callback:用户定义的回调函数,当检测到车辆压线时会调用该函数,将压线相关信息传递给用户。
57 LaneInvasionCallback(
58 const Vehicle &vehicle,
59 SharedPtr<Map> &&map,
60 Sensor::CallbackFunctionType &&user_callback)
61 : _parent(vehicle.GetId()),
62 _parent_bounding_box(vehicle.GetBoundingBox()),
63 _map(std::move(map)),
64 _callback(std::move(user_callback)) {
65 DEBUG_ASSERT(_map != nullptr);// 确保传入的地图指针不为空,保证后续操作能正常基于地图进行
66 }
67// 处理每一帧数据的函数,在每一帧都会被调用,用于检测当前帧车辆是否压线并相应地触发回调
68 void Tick(const WorldSnapshot &snapshot) const;
69
70 private:
71 // 内部结构体,用于存储一帧的边界信息
72 // 包含了帧编号以及车辆边界框四个角在世界坐标系下的位置信息
73 struct Bounds {
74 size_t frame;
75 std::array<geom::Location, 4u> corners;
76 };
77 // 创建边界信息的函数,根据给定的帧编号和车辆的变换信息生成当前帧车辆的边界信息
78 std::shared_ptr<const Bounds> MakeBounds(
79 size_t frame,
80 const geom::Transform &vehicle_transform) const;
81
82 ActorId _parent;// 存储父类车辆的唯一标识符(ID),用于在场景快照中查找对应的车辆对象
83 // 存储父类车辆的边界框信息,用于确定车辆在世界坐标系中的范围,以便后续判断是否压线
84 geom::BoundingBox _parent_bounding_box;
85// 地图的智能指针,指向整个场景的地图对象,通过地图来获取道路、车道等相关信息用于判断压线情况
86 SharedPtr<const Map> _map;
87// 用户定义的回调函数,类型为Sensor::CallbackFunctionType,当检测到车辆压线时将调用此函数通知用户
88 Sensor::CallbackFunctionType _callback;
89// 可变的原子共享指针,用于存储边界信息,原子操作保证了多线程环境下对该指针的安全访问
90 mutable AtomicSharedPtr<const Bounds> _bounds;
91 };
92// 处理每一帧数据,检查车辆是否压线并调用用户回调函数
93 void LaneInvasionCallback::Tick(const WorldSnapshot &snapshot) const {
94 // 确保父类(对应的车辆)还存活在当前场景中,如果在场景快照中找不到对应的车辆,则直接返回,不进行后续检测
95 auto parent = snapshot.Find(_parent);
96 if (!parent) {
97 return;
98 }
99
100 // 创建当前帧的边界信息,根据当前帧编号和车辆的变换信息生成车辆边界框四个角在世界坐标系下的位置
101 auto next = MakeBounds(snapshot.GetFrame(), parent->transform);
102 auto prev = _bounds.load();
103
104 // 第一帧它将为空,因为还没有上一帧的边界信息,此时将当前帧的边界信息设置为初始值,并直接返回,不进行后续比较等操作
105 if ((prev == nullptr) && _bounds.compare_exchange(&prev, next)) {
106 return;
107 }
108
109 // 确保距离足够长,设置一个距离阈值,用于判断车辆是否移动了足够的距离,避免在车辆几乎没移动的情况下误判压线情况
110 // 这里使用了一个极小的浮点数乘以一个倍数作为阈值,根据实际情况判断车辆位置变化是否足够显著
111 constexpr float distance_threshold = 10.0f * std::numeric_limits<float>::epsilon();
112 for (auto i = 0u; i < 4u; ++i) {
113 if ((next->corners[i] - prev->corners[i]).Length() < distance_threshold) {
114 return;
115 }
116 }
117
118 // 确保当前帧是最新的,通过比较当前帧和上一帧的编号来判断,防止出现顺序错乱等情况
119 // 如果当前帧编号小于等于上一帧编号,说明数据可能出现问题,直接返回,不进行后续压线判断操作
120 do {
121 if (prev->frame >= next->frame) {
122 return;
123 }
124 } while (!_bounds.compare_exchange(&prev, next));
125
126 // 最后,可以安全地计算交叉车道,即判断车辆在这两帧之间是否跨越了车道线
127 std::vector<road::element::LaneMarking> crossed_lanes;
128 for (auto i = 0u; i < 4u; ++i) {
129 // 通过地图对象的方法,根据车辆边界框角点的前后位置信息,计算出车辆跨越的车道线信息
130 const auto lanes = _map->CalculateCrossedLanes(prev->corners[i], next->corners[i]);
131 crossed_lanes.insert(crossed_lanes.end(), lanes.begin(), lanes.end());
132 }
133
134 // 如果有交叉车道,说明车辆压线了,此时调用用户回调函数,将压线相关的详细信息传递给用户定义的回调函数进行处理
135 if (!crossed_lanes.empty()) {
136 if (!crossed_lanes.empty()) {
137 _callback(MakeShared<sensor::data::LaneInvasionEvent>(
138 snapshot.GetTimestamp().frame,
139 snapshot.GetTimestamp().elapsed_seconds,
140 parent->transform,
141 _parent,
142 std::move(crossed_lanes)));
143 }
144 }
145// 创建边界信息的函数实现
146 std::shared_ptr<const LaneInvasionCallback::Bounds> LaneInvasionCallback::MakeBounds(
147 const size_t frame,
148 const geom::Transform &transform) const {
149 const auto &box = _parent_bounding_box;
150 const auto location = transform.location + box.location;
151 const auto yaw = transform.rotation.yaw;
152 // 根据车辆的边界框信息以及当前的位置和旋转角度,计算出车辆边界框四个角在世界坐标系下的坐标位置
153 // 先获取车辆整体的位置(考虑边界框的偏移),再通过旋转函数计算出四个角经过旋转后的准确位置
154 return std::make_shared<Bounds>(Bounds{frame, {
155 location + Rotate(yaw, geom::Location( box.extent.x, box.extent.y, 0.0f)),
156 location + Rotate(yaw, geom::Location(-box.extent.x, box.extent.y, 0.0f)),
157 location + Rotate(yaw, geom::Location( box.extent.x, -box.extent.y, 0.0f)),
158 location + Rotate(yaw, geom::Location(-box.extent.x, -box.extent.y, 0.0f))}});
159 }
160
161 // ===========================================================================
162 // -- 压线传感器 LaneInvasionSensor -------------------------------------------
163 // ===========================================================================
164 // 压线传感器的析构函数,在对象销毁时被调用,用于停止传感器的监听操作,释放相关资源
166 Stop();
167 }
168
169 // 开始监听,接收用户回调函数,用于设置当检测到车辆压线时要执行的回调操作
170 // 参数说明:
171 // - callback:用户定义的回调函数,符合Sensor::CallbackFunctionType类型要求,当车辆压线时会被调用。
172 void LaneInvasionSensor::Listen(CallbackFunctionType callback) {
173 // 获取传感器所附着的父对象,并尝试将其转换为车辆类型的智能指针,如果转换失败说明传感器没有正确附着到车辆上
174 auto vehicle = boost::dynamic_pointer_cast<Vehicle>(GetParent());
175 if (vehicle == nullptr) {
176 log_error(GetDisplayId(), ": not attached to a vehicle");
177 return;
178 }
179
180 auto episode = GetEpisode().Lock();
181 // 创建压线回调对象,传入车辆、地图以及用户回调函数等信息,用于后续在每帧数据处理时判断压线情况并触发回调
182 auto cb = std::make_shared<LaneInvasionCallback>(
183 *vehicle,
184 episode->GetCurrentMap(),
185 std::move(callback));
186
187 // 在当前的模拟场景(episode)中注册一个每帧触发的事件,将压线回调对象的Tick方法绑定到该事件上,
188 // 这样在每一帧数据更新时都会调用Tick方法来检测车辆是否压线,同时返回一个回调事件的唯一标识符
189 const size_t callback_id = episode->RegisterOnTickEvent([cb=std::move(cb)](const auto &snapshot) {
190 try {
191 cb->Tick(snapshot);
192 } catch (const std::exception &e) {
193 log_error("LaneInvasionSensor:", e.what());
194 }
195 });
196
197 const size_t previous = _callback_id.exchange(callback_id);
198 if (previous != 0u) {
199 // 如果之前已经存在注册的回调事件,先移除之前的事件,保证只有最新注册的事件生效
200 episode->RemoveOnTickEvent(previous);
201 }
202 }
203 // 停止监听,用于取消之前注册的车辆压线检测相关的回调事件,停止传感器的监听操作,释放相关资源
205 const size_t previous = _callback_id.exchange(0u);
206 auto episode = GetEpisode().TryLock();
207 if ((previous != 0u) && (episode != nullptr)) {
208 episode->RemoveOnTickEvent(previous);
209 }
210 }
211
212} // namespace client
213} // namespace carla
#define DEBUG_ASSERT(predicate)
Definition Debug.h:68
包含LaneInvasionSensor类的声明,这是一个继承自ClientSideSensor的类,用于检测车辆压线。
std::atomic_size_t _callback_id
原子性的回调ID,用于标识当前设置的回调。 当_callback_id不为0时,表示正在监听。
~LaneInvasionSensor()
析构函数,用于清理LaneInvasionSensor对象。
void Stop() override
停止监听新的车辆压线测量结果。
void Listen(CallbackFunctionType callback) override
注册一个回调,每次收到新的车辆压线测量值时执行。
SharedPtr< Actor > GetParent() const
const std::string & GetDisplayId() const
SharedPtrType TryLock() const noexcept
SharedPtrType Lock() const
与 TryLock 相同,但永远不会返回 nullptr。
uint32_t ActorId
Definition ActorId.h:20
CARLA模拟器的主命名空间。
Definition Carla.cpp:139
static void log_error(Args &&... args)
Definition Logging.h:115
包含CARLA客户端相关类和函数的命名空间。