【c++】Lanelet2 Examples笔记(二)

【c++】Lanelet2 Examples笔记(二)

02_regulatory_elements

#include <lanelet2_core/LaneletMap.h>
#include <lanelet2_core/primitives/BasicRegulatoryElements.h>
#include <lanelet2_core/primitives/Lanelet.h>

#include "lanelet2_examples/internal/ExampleHelpers.h"

// we want assert statements to work in release mode
#undef NDEBUG

void part1BasicRegulatoryElements();
void part2HandlingRegulatoryElements();
void part3AddingNewRegulatoryElements();

int main() {
  part1BasicRegulatoryElements();
  part2HandlingRegulatoryElements();
  part3AddingNewRegulatoryElements();
  return 0;
}

void part1BasicRegulatoryElements() {
  using namespace lanelet;
  // Lanelet2 定义了一些基本的监管元素,例如交通灯和限速。

  // 在lanelet2中使用LineString3d表示traffic lights,其type属性为TrafficLight
  LineString3d trafficLight = examples::getLineStringAtY(1);
  trafficLight.attributes()[AttributeName::Type] = AttributeValueString::TrafficLight;

  // this creates our traffic light. Regelems are passed around as shared pointers.
  RegulatoryElementPtr trafficLightRegelem = lanelet::TrafficLight::make(utils::getId(), {}, {trafficLight});

  // 通过.addRegulatoryElement()把交通信号灯加到一个Lanelet中
  Lanelet lanelet = examples::getALanelet();
  lanelet.addRegulatoryElement(trafficLightRegelem); 
  assert(lanelet.regulatoryElements().size() == 1);

  // 通过.regulatoryElements()查看lanelet上的交通监管信息,.regulatoryElements()返回一个vector
  RegulatoryElementPtr regelem = lanelet.regulatoryElements()[0];

  // 通过.regulatoryElementsAs<SpeedLimit>()查看一个Lanelet上的限速规则,<>中可以定义查看的具体规则类别
  assert(lanelet.regulatoryElementsAs<SpeedLimit>().empty()); 
  // .regulatoryElementsAs返回一个vector
  std::vector<TrafficLight::Ptr> trafficLightRegelems = lanelet.regulatoryElementsAs<TrafficLight>();
  assert(trafficLightRegelems.size() == 1);
  TrafficLight::Ptr tlRegelem = trafficLightRegelems.front();      
  assert(tlRegelem->constData() == trafficLightRegelem->constData());  

  // from traffic lights we can directly get the relevant lights and the stop line (we didnt set one, but we could).
  // since traffic lights can either be a polygon or a linestring, we get an object that represents both.
  LineStringOrPolygon3d theLight = tlRegelem->trafficLights().front();
  assert(theLight == trafficLight);
  
  // 可以修改
  // we can also modify it, and since regulatory element data is shared, this also affects the lanelet that holds it
  tlRegelem->setStopLine(examples::getLineStringAtY(2));
  assert(!!tlRegelem->stopLine());

  // there are much more regulatory elements, but they all work basically in the same way as shown here.
}

void part2HandlingRegulatoryElements() {
  using namespace lanelet;
  // GenericRegulatoryElementke可以是任何交通规则
  GenericRegulatoryElement regelem(utils::getId());
  
  // 任何primitive都可以被赋予交通规则,并通过.addParameter添加到GenericRegulatoryElement
  // to the generic regulatory elements we can add any primitive (point, linestring, lanelet, area) with any role:
  Lanelet lanelet = examples::getALanelet();
  regelem.addParameter(RoleName::Refers, lanelet);
  Point3d point(utils::getId(), 0, 0, 0);
  regelem.addParameter(RoleName::Refers, point);
   
  // 通过.getParameters<primitive类型>(规则类型),得到GenericRegulatoryElementke中的被赋予指定交通规则的primitive
  // now two different primitives have been added with the same role. Internally they are stored as boost::variants.
  // to read them from the regelem, we have to pass the type we are looking for:
  Points3d pts = regelem.getParameters<Point3d>(RoleName::Refers);
  assert(!pts.empty() && pts.front() == point);

  // this interface could be used add more things with more nonsense role names. But that would be hard to interpret.
  // For that reason, the implementations of regulatory elements provide an interface that gives less opportunity for
  // abuse.
}

// as an example, we create a new regulatory element and register it with lanelet2. It is a "LightsOn" regulatory
// element, that tells the vehicle to turn its lights on after passing a specific line
namespace example {
class LightsOn : public lanelet::RegulatoryElement {  // we have to inherit from the abstract regulatoryElement
 public:
  // lanelet2 looks for this string when matching the subtype of a regulatory element to the respective type
  static constexpr char RuleName[] = "lights_on";

  // returns the line where we are supposed to stop
  lanelet::ConstLineString3d fromWhere() const {
    return getParameters<lanelet::ConstLineString3d>(lanelet::RoleName::RefLine).front();
  }

 private:
  LightsOn(lanelet::Id id, lanelet::LineString3d fromWhere)
      : RegulatoryElement{std::make_shared<lanelet::RegulatoryElementData>(id)} {
    parameters().insert({lanelet::RoleNameString::RefLine, {fromWhere}});
  }

  // the following lines are required so that lanelet2 can create this object when loading a map with this regulatory
  // element
  friend class lanelet::RegisterRegulatoryElement<LightsOn>;
  explicit LightsOn(const lanelet::RegulatoryElementDataPtr& data) : RegulatoryElement(data) {}
};

#if __cplusplus < 201703L
constexpr char LightsOn::RuleName[];  // instanciate string in cpp file
#endif
}  // namespace example

namespace {
// this object actually does the registration work for us
lanelet::RegisterRegulatoryElement<example::LightsOn> reg;
}  // namespace

void part3AddingNewRegulatoryElements() {
  using namespace lanelet;
  // after creating our new class and registering it, we can test if it works. For that we use the
  // RegulatoryElementFactory that is used by Lanelet2_io when loading a map. If we did it right, it should now return
  // a regulatory element of the LightsOn class.

  // for that we create a valid regulatory element data object
  LineString3d fromWhere = examples::getLineStringAtX(1);
  RuleParameterMap rules{{RoleNameString::RefLine, {fromWhere}}};

  RegulatoryElementPtr regelem = RegulatoryElementFactory::create("lights_on", utils::getId(), rules);

  // now we can add it to a lanelet and query for it
  Lanelet lanelet = examples::getALanelet();
  lanelet.addRegulatoryElement(regelem);
  assert(!lanelet.regulatoryElementsAs<example::LightsOn>().empty());
}

03_lanelet_map

#include <lanelet2_core/geometry/BoundingBox.h>
#include <lanelet2_core/geometry/Lanelet.h>
#include <lanelet2_core/geometry/Point.h>
#include <lanelet2_core/primitives/Lanelet.h>

#include "lanelet2_examples/internal/ExampleHelpers.h"

#pragma GCC diagnostic ignored "-Wunused-variable"

// we want assert statements to work in release mode
#undef NDEBUG

void part1AboutLaneletMaps();
void part2CreatingLaneletMaps();
void part3QueryingInformation();
void part4LaneletSubmaps();

int main() {
  part1AboutLaneletMaps();
  part2CreatingLaneletMaps();
  part3QueryingInformation();
  part4LaneletSubmaps();
  return 0;
}

void part1AboutLaneletMaps() {
  using namespace lanelet;
  // lanelet maps是所有类型的lanelets primitives的集合体
  LaneletMap map = examples::getALaneletMap(); 

  // Lanelet的地图被由很多层组成,每种primitive是一层
  PointLayer& points = map.pointLayer;
  LineStringLayer& linestrings = map.lineStringLayer;

  // 层里的元素是无序的,可以通过迭代或id进行查找
  assert(points.size() > 1);
  Point3d aPoint = *points.begin();
  Point3d samePoint = *points.find(aPoint.id());
  assert(samePoint == aPoint);
  assert(points.exists(aPoint.id()));

  assert(!linestrings.empty());

  // LaneletMap类型的数据不能复制,但是可以进行move操作
  LaneletMap newMap = std::move(map);
  // map.exists(aPoint.id()); // move操作之后,move之前的地图不再有效
  assert(newMap.pointLayer.exists(aPoint.id()));

  // or be passed around as a pointer:
  LaneletMapUPtr mapPtr = std::make_unique<LaneletMap>(std::move(newMap));
  
  // 常量类型
  // there is also the concept of constness for lanelet maps: const maps return const primitives.
  const LaneletMap& constMap = *mapPtr;
  ConstPoint3d aConstPoint = *constMap.pointLayer.begin();
  assert(aConstPoint == aPoint);
}

void part2CreatingLaneletMaps() {
  using namespace lanelet;
  // 可以通过两种方式创建地图: 逐一向地图添加primitive,或直接从多个primitives创建地图
  LaneletMap laneletMap;

  Lanelet lanelet = examples::getALanelet();
  laneletMap.add(lanelet);
  assert(laneletMap.laneletLayer.size() == 1);

  // 添加Lanelet层元素的时候,points, linestrings, regulatory elements等primitives也会被添加
  assert(!laneletMap.pointLayer.empty());
  assert(laneletMap.pointLayer.exists(lanelet.leftBound().front().id()));

  // InvalId用于指向还不是lanelet map一部分的primitive。这类primitive被添加到lanelet map后,LaneletMap会给他们赋予id
  Point3d invalPoint1(InvalId, 0, 0, 0);
  Point3d invalPoint2(InvalId, 1, 0, 0);
  LineString3d invalLs(InvalId, {invalPoint1, invalPoint2});
  laneletMap.add(invalLs);
  assert(invalPoint1.id() != InvalId);

  // 用createMap()直接从多个primitives创建地图
  Lanelets lanelets{examples::getALanelet(), examples::getALanelet(), examples::getALanelet()};
  LaneletMapUPtr laneletsMap = utils::createMap(lanelets);
  assert(laneletsMap->laneletLayer.exists(lanelets.front().id()));
}

void part3QueryingInformation() {
  using namespace lanelet;
  
  // 除了通过id找LaneletMap中的primitives,还可以通过它们的关系和通过它们的位置找
  LaneletMap laneletMap = examples::getALaneletMap();
  Lanelet mapLanelet = *laneletMap.laneletLayer.begin();
  TrafficLight::Ptr trafficLight = mapLanelet.regulatoryElementsAs<TrafficLight>().front();

  // 可以通过points找Linestrings,通过linestrings或者regulatory element找lanelets和areas,通过任何相关的找regulatory elements

  // 通过linestrings找到lanelets:找到laneletMap中,所有包含mapLanelet.leftBound()的Lanelet
  auto laneletsOwningLinestring = laneletMap.laneletLayer.findUsages(mapLanelet.leftBound());
  assert(laneletsOwningLinestring.size() == 1 && laneletsOwningLinestring.front() == mapLanelet);
  // 通过linestrings找到regelems:找到laneletMap中代表trafficLight的lineString被赋予的所有regulatory elements
  auto regelemsOwningLs =
      laneletMap.regulatoryElementLayer.findUsages(*trafficLight->trafficLights().front().lineString());
  assert(regelemsOwningLs.size() == 1 && regelemsOwningLs.front() == trafficLight);
  // 通过regelems找到lanelets:找到laneletMap中引用了trafficLight的所有lanelet
  auto laneletsOwningRegelems = laneletMap.laneletLayer.findUsages(trafficLight);
  assert(!laneletsOwningRegelems.empty());

  // spacially means we can find primitives with geometrical queries. Because internally all primitives are stored as
  // bounding boxes, these queries only return the primitive with respect to their *bounding box*.
  // 通过几何关系查询primitives
  // 所有的primitives作为bounding boxes储存,通过几何关系查询可以得到bounding boxes产生交互的primitives
  Lanelets lanelets = laneletMap.laneletLayer.nearest(BasicPoint2d(0, 0), 1);  // 1 means the nearest "1" lanelets
  assert(!lanelets.empty());  // 查询得到持有和点(0, 0)的bound ing box最近bounding box的lanelet

  // to get the actually closest lanelets use this utility function:
  // 找到实际最近的lanelet,而不是bounding box最近
  std::vector<std::pair<double, Lanelet>> actuallyNearestLanelets =
      geometry::findNearest(laneletMap.laneletLayer, BasicPoint2d(0, 0), 1);
  assert(!actuallyNearestLanelets.empty());

  // finally we can get primitives using a search region (this also runs on the bounding boxes):
  // 通过指定左上和右下点,划出ROI box,.search找到其中的lanelet
  Lanelets inRegion = laneletMap.laneletLayer.search(BoundingBox2d(BasicPoint2d(0, 0), BasicPoint2d(10, 10)));
  assert(!inRegion.empty());  // IOU>0的所有lanelet

  // for advanced usage, there are the searchUntil and nearestUntil functions. You pass it a function that is called
  // with primitives with increasing bounding box distance until the function returns true. This is then the returned
  // primitive:
  
  // 得到持有和query点的bounding box的举例大于3m的bounding box的第一个lanelet
  // in this example we get the first lanelet whose bounding box distance is >3m distance to the query point
  BasicPoint2d searchPoint = BasicPoint2d(10, 10);
  // the search func is called with the bounding box of a primitive and the primitive itself
  auto searchFunc = [&searchPoint](const BoundingBox2d& lltBox, const Lanelet& /*llt*/) {
    return geometry::distance(searchPoint, lltBox) > 3;
  };
  Optional<Lanelet> lanelet = laneletMap.laneletLayer.nearestUntil(searchPoint, searchFunc);
  assert(!!lanelet && geometry::distance(geometry::boundingBox2d(*lanelet), searchPoint) > 3);
}

void part4LaneletSubmaps() {
  using namespace lanelet;
  // While LaneletMap has the property that when an element is added, all the things referenced by it are added as well,
  // LaneletSubmap does not have this property. This can be useful if you want to avoid that if you add a Lanelet, all
  // Lanelets referenced by its RegulatoryElements are added as well. Apart from that, basically everything you can do
  // with a LaneletMap can also be done with a LaneletSubmap:

  // LaneletSubmap不会添加相关的primitives,例如添加Lanelet层元素的时候,points, linestrings, regulatory elements等primitives不会被添加 
  LaneletSubmap submap{examples::getALaneletMap()};  // it can be constructed (moved) from an existing map

  // you can search its layers
  Lanelets inRegion = submap.laneletLayer.search(BoundingBox2d(BasicPoint2d(0, 0), BasicPoint2d(10, 10)));

  // you can create new submaps from some elements
  LaneletSubmapUPtr newSubmap = utils::createSubmap(inRegion);

  // but this submap will not contain any elements except for the ones you explicitly added
  assert(newSubmap->pointLayer.empty());
  assert(newSubmap->size() == inRegion.size());

  // ... unless you convert back into a laneletMap. This will again contain all primitives:
  LaneletMapUPtr newMap = newSubmap->laneletMap();
  assert(!newMap->pointLayer.empty());
}

结语

如果您有修改意见或问题,欢迎留言或者通过邮箱和我联系。
手打很辛苦,如果我的文章对您有帮助,转载请注明出处。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值