Planning的输入和输出

前言

通常我们讲,自动驾驶根据自车状态和周边环境的信息进行规划,然后将规划结果交给控制模块执行。很容易理解规划模块的输入应该是自车状态和周边环境,输出应该是规划出的可执行轨迹,但是,往更深的层面挖掘,自车状态和周边环境具体包括哪些物理信息,这些物理信息又该如何分类,如何用程序语言描述,另外可执行轨迹又该如何设计,这貌似是个非常细节、严谨的环节,那么我们来看看 Apollo 是如何处理的,随笔记录一下,分享给大家。

参考资料:

正文

我们先看下Apollo的数据流向:​​​

可以看到规划(planning)模块的上游是Localization, Prediction, Routing模块,而下游是Control模块。Routing模块先规划出一条导航线路,然后Planning模块根据这条线路做局部优化,如果Planning模块发现短期规划的线路行不通(比如前面修路,或者错过了路口),会触发Routing模块重新规划线路,因此这两个模块的数据流是双向的。 Planning模块的输入在"planning_component.h"中,接口如下:

  bool Proc(const std::shared_ptr<prediction::PredictionObstacles>&
                prediction_obstacles,
            const std::shared_ptr<canbus::Chassis>& chassis,
            const std::shared_ptr<localization::LocalizationEstimate>&
                localization_estimate) override;
输入参数为: 
  1. 预测的障碍物信息(prediction_obstacles)

  2. 车辆底盘(chassis)信息(车辆的速度,加速度,航向角等信息)

  3. 车辆当前位置(localization_estimate)

实际上还有高精度地图信息,不在参数中传入,而是在函数中直接读取的。

另外,在"planning_component.h"中还定义了一系列 reader,用于读取目标 topic 中的信息,如下所示:

 private:
  std::shared_ptr<cyber::Reader<perception::TrafficLightDetection>>
      traffic_light_reader_;
  std::shared_ptr<cyber::Reader<routing::RoutingResponse>> routing_reader_;
  std::shared_ptr<cyber::Reader<planning::PadMessage>> pad_msg_reader_;
  std::shared_ptr<cyber::Reader<relative_map::MapMsg>> relative_map_reader_;
  std::shared_ptr<cyber::Reader<storytelling::Stories>> story_telling_reader_;

这些信息被读取后最终会整合至 LocalView 中,其为一个结构体,包含了规划所需要的全部输入信息,如下所示: 

/**
 * @struct local_view
 * @brief LocalView contains all necessary data as planning input
 */
​
struct LocalView {
  std::shared_ptr<prediction::PredictionObstacles> prediction_obstacles;
  std::shared_ptr<canbus::Chassis> chassis;
  std::shared_ptr<localization::LocalizationEstimate> localization_estimate;
  std::shared_ptr<perception::TrafficLightDetection> traffic_light;
  std::shared_ptr<routing::RoutingResponse> routing;
  std::shared_ptr<relative_map::MapMsg> relative_map;
  std::shared_ptr<PadMessage> pad_msg;
  std::shared_ptr<storytelling::Stories> stories;
};

Planning模块的输出结果主要包含3个,如下所示:

  std::shared_ptr<cyber::Writer<ADCTrajectory>> planning_writer_;
  std::shared_ptr<cyber::Writer<routing::RoutingRequest>> rerouting_writer_;
  std::shared_ptr<cyber::Writer<PlanningLearningData>>
      planning_learning_data_writer_;

下面针对以上数据结构分别展开分析,Apollo 中采用了 Protobuf ,其文件格式是 .proto,在编译时会生成对应的 .h 和 .cpp 文件,包含一些默认的函数接口,主要是用来存取数据的,没有其他的逻辑运算。

下面的部分会略显无聊,因为我搬运了大量源码。最后有我自己的一些想法,有兴趣者可来赐教或探讨。

PredictionObstacles

这部分为障碍物信息,障碍物信息源于 perception 模块,其输出的 PerceptionObstacles 作为 prediction 模块的输入,经过加工处理,丰富了障碍物的预测信息,然后输出至 planning 模块。

PerceptionObstacle

注意区分 PerceptionObstacle 和 PerceptionObstacles,Apollo 中未加 s 的消息格式通常是基本的结构,表征单体,加了 s 的消息格式通常是单体的集合,可以由任意个单体构成,另外会有额外的补充信息。

这里暂且只关注 PerceptionObstacle,是由 perception 模块生成的消息类型,其包含了单体障碍物的全部信息,其中可能包含一些预留信息,如下所示:

message PerceptionObstacle {
  optional int32 id = 1;  // obstacle ID.
​
  // obstacle position in the world coordinate system.
  optional apollo.common.Point3D position = 2;
​
  optional double theta = 3;  // heading in the world coordinate system.
  optional apollo.common.Point3D velocity = 4;  // obstacle velocity.
​
  // Size of obstacle bounding box.
  optional double length = 5;  // obstacle length.
  optional double width = 6;   // obstacle width.
  optional double height = 7;  // obstacle height.
​
  repeated apollo.common.Point3D polygon_point = 8;  // obstacle corner points.
​
  // duration of an obstacle since detection in s.
  optional double tracking_time = 9;
​
  enum Type {
    UNKNOWN = 0;
    UNKNOWN_MOVABLE = 1;
    UNKNOWN_UNMOVABLE = 2;
    PEDESTRIAN = 3;  // Pedestrian, usually determined by moving behavior.
    BICYCLE = 4;     // bike, motor bike
    VEHICLE = 5;     // Passenger car or truck.
  };
  optional Type type = 10;         // obstacle type
  optional double timestamp = 11;  // GPS time in seconds.
​
  // Just for offline debugging, will not fill this field on board.
  // Format: [x0, y0, z0, x1, y1, z1...]
  repeated double point_cloud = 12 [packed = true];
​
  optional double confidence = 13 [deprecated = true];
  enum ConfidenceType {
    CONFIDENCE_UNKNOWN = 0;
    CONFIDENCE_CNN = 1;
    CONFIDENCE_RADAR = 2;
  };
  optional ConfidenceType confidence_type = 14 [deprecated = true];
  // trajectory of object.
  repeated apollo.common.Point3D drops = 15 [deprecated = true];
​
  // The following fields are new added in Apollo 4.0
  optional apollo.common.Point3D acceleration = 16;  // obstacle acceleration
​
  // a stable obstacle point in the world coordinate system
  // position defined above is the obstacle bounding box ground center
  optional apollo.common.Point3D anchor_point = 17;  // 这个点的物理意义?   
  optional BBox2D bbox2d = 18;
​
  enum SubType {
    ST_UNKNOWN = 0;
    ST_UNKNOWN_MOVABLE = 1;
    ST_UNKNOWN_UNMOVABLE = 2;
    ST_CAR = 3;
    ST_VAN = 4;
    ST_TRUCK = 5;
    ST_BUS = 6;
    ST_CYCLIST = 7;
    ST_MOTORCYCLIST = 8;
    ST_TRICYCLIST = 9;
    ST_PEDESTRIAN = 10;
    ST_TRAFFICCONE = 11;
  };
  optional SubType sub_type = 19;  // obstacle sub_type
​
  repeated SensorMeasurement measurements = 20;  // sensor measurements
​
  // orthogonal distance between obstacle lowest point and ground plane
  optional double height_above_ground = 21 [default = nan];
​
  // position covariance which is a row-majored 3x3 matrix
  repeated double position_covariance = 22 [packed = true];  // 协方差矩阵,以行为主,3*3
  // velocity covariance which is a row-majored 3x3 matrix
  repeated double velocity_covariance = 23 [packed = true];
  // acceleration covariance which is a row-majored 3x3 matrix
  repeated double acceleration_covariance = 24 [packed = true];
​
  // lights of vehicles
  optional LightStatus light_status = 25;
​
  // Debug Message
  optional DebugMessage msg = 26;
​
  enum Source {
    HOST_VEHICLE = 0;
    V2X = 1;
  };
​
  optional Source source = 27 [default = HOST_VEHICLE];
  optional V2XInformation v2x_info = 28;
}

其中,SensorMeasurement 表示观测到该障碍物的所有传感器的测量信息,其消息格式如下:

message SensorMeasurement {
  optional string sensor_id = 1;
  optional int32 id = 2;
​
  optional apollo.common.Point3D position = 3;
  optional double theta = 4;
  optional double length = 5;
  optional double width = 6;
  optional double height = 7;
​
  optional apollo.common.Point3D velocity = 8;
​
  optional PerceptionObstacle.Type type = 9;
  optional PerceptionObstacle.SubType sub_type = 10;
  optional double timestamp = 11;
  optional BBox2D box = 12;  // only for camera measurements
}

BBox2D 表示在相机视图上包络障碍物的矩形框,其消息格式如下:

message BBox2D {
  optional double xmin = 1;  // in pixels.
  optional double ymin = 2;  // in pixels.
  optional double xmax = 3;  // in pixels.
  optional double ymax = 4;  // in pixels.
}

LightStatus 表示障碍物的刹车灯和转向灯信息,其消息格式如下:

message LightStatus {
  optional double brake_visible = 1;
  optional double brake_switch_on = 2;
  optional double left_turn_visible = 3;
  optional double left_turn_switch_on = 4;
  optional double right_turn_visible = 5;
  optional double right_turn_switch_on = 6;
}

DebugMessage 表示障碍物的轨迹,此处应该是观测轨迹,不包含预测信息,其消息格式如下:

message DebugMessage {
  // can have multiple trajectories per obstacle
  repeated Trajectory trajectory = 1;
}

V2XInformation 的消息格式如下:

message V2XInformation {
  enum V2XType {
    NONE = 0;
    ZOMBIES_CAR = 1;
    BLIND_ZONE = 2;
  };
  repeated V2XType v2x_type = 1;
}

PredictionObstacle

注意此处未加 s,为经过 prediction 模块加工后的障碍物信息,增加了预测周期、障碍物轨迹、障碍物意图、历史特征序列等信息,具体如下所示:

message PredictionObstacle {
  optional apollo.perception.PerceptionObstacle perception_obstacle = 1;
  optional double timestamp = 2;  // GPS time in seconds
  // the length of the time for this prediction (e.g. 10s)
  optional double predicted_period = 3;
  // can have multiple trajectories per obstacle
  repeated Trajectory trajectory = 4;
​
  // estimated obstacle intent
  optional ObstacleIntent intent = 5;
​
  optional ObstaclePriority priority = 6;
​
  optional bool is_static = 7 [default = false];
​
  // Feature history latest -> earliest sequence
  repeated Feature feature = 8;
}

其中 ObstacleIntent 表示估计的障碍物的意图,具体如下所示:

// estimated obstacle intent
message ObstacleIntent {
  enum Type {
    UNKNOWN = 0;
    STOP = 1;
    STATIONARY = 2;
    MOVING = 3;
    CHANGE_LANE = 4;
    LOW_ACCELERATION = 5;
    HIGH_ACCELERATION = 6;
    LOW_DECELERATION = 7;
    HIGH_DECELERATION = 8;
  }
  optional Type type = 1 [default = UNKNOWN];
}

Feature 表示障碍物的特征,其修饰符为 repeated,表明对于同一障碍物,存储了历史特征,个人感觉这个 Feature 类型的消息涵盖了 PerceptionObstacle 的信息,但是这两者在 PredictionObstacle 中是共存的,不知道是否冗余,Feature 消息的具体格式如下:

message Feature {
  // Obstacle ID
  optional int32 id = 1;
​
  // Obstacle features
  repeated apollo.common.Point3D polygon_point = 30;
  optional apollo.common.Point3D position = 2;
  optional apollo.common.Point3D front_position = 27;
  optional apollo.common.Point3D velocity = 3;
  optional apollo.common.Point3D raw_velocity = 28;  // from perception
  optional apollo.common.Point3D acceleration = 4;
  optional double velocity_heading = 5;
  optional double speed = 6;
  optional double acc = 7;
  optional double theta = 8;
  optional double length = 9;
  optional double width = 10;
  optional double height = 11;
  optional double tracking_time = 12;
  optional double timestamp = 13;
​
  // Obstacle type-specific features
  optional Lane lane = 14;
  optional JunctionFeature junction_feature = 26;
​
  // Obstacle tracked features
  optional apollo.common.Point3D t_position = 16;
  optional apollo.common.Point3D t_velocity = 17 [deprecated = true];
  optional double t_velocity_heading = 18 [deprecated = true];
  optional double t_speed = 19 [deprecated = true];
  optional apollo.common.Point3D t_acceleration = 20 [deprecated = true];
  optional double t_acc = 21 [deprecated = true];
​
  optional bool is_still = 22 [default = false];
  optional apollo.perception.PerceptionObstacle.Type type = 23;
  optional double label_update_time_delta = 24;
​
  optional ObstaclePriority priority = 25;
​
  optional bool is_near_junction = 29 [default = false];
​
  // Obstacle ground-truth labels:
  repeated PredictionTrajectoryPoint future_trajectory_points = 31;
​
  // Obstacle short-term predicted trajectory points
  repeated apollo.common.TrajectoryPoint
      short_term_predicted_trajectory_points = 32;
​
  // Obstacle predicted trajectories
  repeated Trajectory predicted_trajectory = 33;
​
  // ADC trajectory at the same frame
  repeated apollo.common.TrajectoryPoint adc_trajectory_point = 34;
​
  // Surrounding lanes
  repeated string surrounding_lane_id = 35;
  repeated string within_lane_id = 36;
}

PredictionObstacles

前面做的两种消息类型的铺垫,是为了引出最后这种 PredictionObstacles 消息类型,根据其命名规则就很容易想到它是 PredictionObstacle 的复数形式,也就是说包含了当前帧中所有的障碍物信息,当然也可能是0个(没有障碍物),先看其消息格式:

message PredictionObstacles {
  // timestamp is included in header
  optional apollo.common.Header header = 1;
​
  // make prediction for multiple obstacles
  repeated PredictionObstacle prediction_obstacle = 2;
​
  // perception error code
  optional apollo.common.ErrorCode perception_error_code = 3;
​
  // start timestamp
  optional double start_timestamp = 4;
​
  // end timestamp
  optional double end_timestamp = 5;
​
  // self driving car intent
  optional Intent intent = 6;
​
  // Scenario
  optional Scenario scenario = 7;
}

其中,ErrorCode 的信息如下:

// Error codes enum for API's categorized by modules.
enum ErrorCode {
  // No error, returns on success.
  OK = 0;
​
  // Control module error codes start from here.
  CONTROL_ERROR = 1000;
  CONTROL_INIT_ERROR = 1001;
  CONTROL_COMPUTE_ERROR = 1002;
  CONTROL_ESTOP_ERROR = 1003;
  PERFECT_CONTROL_ERROR = 1004;
​
  // Canbus module error codes start from here.
  CANBUS_ERROR = 2000;
  CAN_CLIENT_ERROR_BASE = 2100;
  CAN_CLIENT_ERROR_OPEN_DEVICE_FAILED = 2101;
  CAN_CLIENT_ERROR_FRAME_NUM = 2102;
  CAN_CLIENT_ERROR_SEND_FAILED = 2103;
  CAN_CLIENT_ERROR_RECV_FAILED = 2104;
​
  // Localization module error codes start from here.
  LOCALIZATION_ERROR = 3000;
  LOCALIZATION_ERROR_MSG = 3100;
  LOCALIZATION_ERROR_LIDAR = 3200;
  LOCALIZATION_ERROR_INTEG = 3300;
  LOCALIZATION_ERROR_GNSS = 3400;
​
  // Perception module error codes start from here.
  PERCEPTION_ERROR = 4000;
  PERCEPTION_ERROR_TF = 4001;
  PERCEPTION_ERROR_PROCESS = 4002;
  PERCEPTION_FATAL = 4003;
  PERCEPTION_ERROR_NONE = 4004;
  PERCEPTION_ERROR_UNKNOWN = 4005;
​
  // Prediction module error codes start from here.
  PREDICTION_ERROR = 5000;
​
  // Planning module error codes start from here
  PLANNING_ERROR = 6000;
  PLANNING_ERROR_NOT_READY = 6001;
​
  // HDMap module error codes start from here
  HDMAP_DATA_ERROR = 7000;
​
  // Routing module error codes
  ROUTING_ERROR = 8000;
  ROUTING_ERROR_REQUEST = 8001;
  ROUTING_ERROR_RESPONSE = 8002;
  ROUTING_ERROR_NOT_READY = 8003;
​
  // Indicates an input has been exhausted.
  END_OF_INPUT = 9000;
​
  // HTTP request error codes.
  HTTP_LOGIC_ERROR = 10000;
  HTTP_RUNTIME_ERROR = 10001;
​
  // Relative Map error codes.
  RELATIVE_MAP_ERROR = 11000;  // general relative map error code
  RELATIVE_MAP_NOT_READY = 11001;
​
  // Driver error codes.
  DRIVER_ERROR_GNSS = 12000;
  DRIVER_ERROR_VELODYNE = 13000;
​
  // Storytelling error codes.
  STORYTELLING_ERROR = 14000;
}

Intent 表示自车的意图,如下:

// self driving car intent
message Intent {
  enum Type {
    UNKNOWN = 0;
    STOP = 1;
    CRUISE = 2;
    CHANGE_LANE = 3;
  }
  optional Type type = 1 [default = UNKNOWN];
}

Scenario 表示场景,如下:

message Scenario {
  enum Type {
    UNKNOWN = 0;
    CRUISE = 1000;
    CRUISE_URBAN = 1001;
    CRUISE_HIGHWAY = 1002;
    JUNCTION = 2000;
    JUNCTION_TRAFFIC_LIGHT = 2001;
    JUNCTION_STOP_SIGN = 2002;
  }
  optional Type type = 1 [default = UNKNOWN];
  optional string junction_id = 2;
}

至此,planning 模块的输入之一 PredictionObstacles 的消息类型分析完毕,下面分析同样来自 perception 模块的 TraficLightDetection。

TraficLightDetection

这个消息类型比 PredictionObstacles 要简单的多,主要为交通灯的信息,如下所示:

message TrafficLightDetection {
  optional apollo.common.Header header = 2;
  repeated TrafficLight traffic_light = 1;
  optional TrafficLightDebug traffic_light_debug = 3;
  optional bool contain_lights = 4;
  enum CameraID {
    CAMERA_FRONT_LONG = 0;
    CAMERA_FRONT_NARROW = 1;
    CAMERA_FRONT_SHORT = 2;
    CAMERA_FRONT_WIDE = 3;
  };
  optional CameraID camera_id = 5;
}

其中 TrafficLight 表示交通灯的基本信息,其中包括交通灯的颜色、可信度、已经跟踪的时间、是否闪烁以及剩余时间(通过v2x才能获取的信息),但是个人感觉还应该补充交通灯的 type,以区分信号灯和信号指示灯,或许在其他地方有体现,暂时保留该意见,如下所示:

message TrafficLight {
  enum Color {
    UNKNOWN = 0;
    RED = 1;
    YELLOW = 2;
    GREEN = 3;
    BLACK = 4;
  };
  optional Color color = 1;
​
  // Traffic light string-ID in the map data.
  optional string id = 2;
​
  // How confidence about the detected results, between 0 and 1.
  optional double confidence = 3 [default = 1.0];
​
  // Duration of the traffic light since detected.
  optional double tracking_time = 4;
​
  // Is traffic blinking
  optional bool blink = 5;
​
  // v2x traffic light remaining time.
  optional double remaining_time = 6;
}

另外 TrafficLightDebug 看起来好像是图片处理的过程信息,这个地方暂时保留,其信息如下:

message TrafficLightDebug {
  optional TrafficLightBox cropbox = 1;
  repeated TrafficLightBox box = 2;
  optional int32 signal_num = 3;
  optional int32 valid_pos = 4;
  optional double ts_diff_pos = 5;
  optional double ts_diff_sys = 6;
  optional int32 project_error = 7;
  optional double distance_to_stop_line = 8;
  optional int32 camera_id = 9 [deprecated = true];
  repeated TrafficLightBox crop_roi = 10;  // roi: region of interest
  repeated TrafficLightBox projected_roi = 11;
  repeated TrafficLightBox rectified_roi = 12;
  repeated TrafficLightBox debug_roi = 13;
}

其中,TrafficLightBox 表示一个框框,这个框框的信息如下所示:

message TrafficLightBox {
  optional int32 x = 1;
  optional int32 y = 2;
  optional int32 width = 3;
  optional int32 height = 4;
  optional TrafficLight.Color color = 5;
  optional bool selected = 6;
  optional string camera_name = 7;
}

至此,TraficLightDetection 的分析完毕。

Stories

Stories 的作用是,描述自车相对于道路特征的位置信息,比如到路口的距离、到信号灯的距离、到交通标识牌的距离,在规划模块中有助于 scenario 判断或者某个 scenario 中 stage 之间的切换,其消息格式如下:

// Usage guide for action modules:
// 1. Call `stories.has_XXX()` to check if a story you are interested is in
//    charge.
// 2. Access the story details if necessary, and take action accordingly.
message Stories {
  optional apollo.common.Header header = 1;
​
  optional CloseToClearArea close_to_clear_area = 2;
  optional CloseToCrosswalk close_to_crosswalk = 3;
  optional CloseToJunction close_to_junction = 4;
  optional CloseToSignal close_to_signal = 5;
  optional CloseToStopSign close_to_stop_sign = 6;
  optional CloseToYieldSign close_to_yield_sign = 7;
}

其中 CloseToClearArea ,表示迷茫,

message CloseToClearArea {
  optional string id = 1;
  optional double distance = 2 [default = nan];
}

CloseToCrosswalk 表示在接近人行横道,

message CloseToCrosswalk {
  optional string id = 1;
  optional double distance = 2 [default = nan];
}

CloseToJunction 表示在接近路口,

message CloseToJunction {
  enum JunctionType {
    PNC_JUNCTION = 1;
    JUNCTION = 2;
  };
  optional string id = 1;
  optional JunctionType type = 2;
  optional double distance = 3 [default = nan];
}

CloseToSignal 表示在接近信号灯,

message CloseToSignal {
  optional string id = 1;
  optional double distance = 2 [default = nan];
}

CloseToStopSign 表示在接近停车标识牌,

message CloseToStopSign {
  optional string id = 1;
  optional double distance = 2 [default = nan];
}

CloseToYieldSign 表示在接近让行标识牌,

message CloseToYieldSign {
  optional string id = 1;
  optional double distance = 2 [default = nan];
}

RoutingResponse

RoutingResponse 为一次 routing 的输出结果,属于基于 topo graph 进行的车道线级别的全局规划,在 planning 模块中调用 RoutingResponse,然后进行局部规划,其中 topo graph 是基于 HD map 生成的,具备车道线级别的信息。RoutingResponse 的消息格式如下:

message RoutingResponse {
  optional apollo.common.Header header = 1;
  repeated RoadSegment road = 2;
  optional Measurement measurement = 3;
  optional RoutingRequest routing_request = 4;
​
  // the map version which is used to build road graph
  optional bytes map_version = 5;
  optional apollo.common.StatusPb status = 6;
}

其中主要信息是包含了多个 RoadSegment,这是一段路,起点至终点全局路径的纵向分割,这里要想清楚一个问题,根据什么规则分割更有效率?欢迎讨论,其消息格式如下:

message RoadSegment {
  optional string id = 1;
  repeated Passage passage = 2;
}

发现 RoadSegment 在形式上很简单,主要是包含多个 Passage,而 Passage 就是横向分割,即车道,每一段路都可能有多个车道,当然也可以只有1个,再来看 Passage 的消息格式:

message Passage {
  repeated LaneSegment segment = 1;
  optional bool can_exit = 2;
  optional ChangeLaneType change_lane_type = 3 [default = FORWARD];
}

可见每个 Passage 又包含多个 LaneSegment,两外还包含了是否可通行、接下来的变道方式,先说后两个简单的。

车道是否可通行很容易理解,比如,某段车道施工。

至于变道方式,这段车道行驶时,下一时刻的状态可能包含三种,保持原车道、向右变道或向左变道,因此 ChangeLaneType 如下:

enum ChangeLaneType {
  FORWARD = 0;
  LEFT = 1;
  RIGHT = 2;
};

你可能觉得先后经过纵向和横向的切割,Passage 已经是最小的道路单元了,然而并不是,举一个简单的例子,全局规划的起点和终点总是道路上的一个点,在地图上是个坐标,这个坐标不可能刚好落在 Passage 的端点,这个时候,就需要把 Passage 进行纵向切割,实际上同时需要将与其并列的 Passage 进行同比例分割,这就有了 LaneSegment,还有些时候,车道上的某一段明显的通行效率低,甚至无法通行,为提高全局规划效率,同样需要将 Passage 的进行纵向切割。如果能理解到这里,那么对于 LaneSegment 该如何定义想必就有思路了,老样子,id 必须有,然后就是起点和终点分别距离 Passage 起点的距离,如下所示:

message LaneSegment {
  optional string id = 1;
  optional double start_s = 2;
  optional double end_s = 3;
}

LocalizationEstimate

LocalizationEstimate 主要包含两个方面的信息,其一为自车实时的位姿及相应的标准差,其二为自车轨迹点的预测,其他的就是时间戳、各传感器的一致性及其状态,如下所示:

message LocalizationEstimate {
  optional apollo.common.Header header = 1;
  optional apollo.localization.Pose pose = 2;
  optional Uncertainty uncertainty = 3;
​
  // The time of pose measurement, seconds since 1970-1-1 (UNIX time).
  optional double measurement_time = 4;  // In seconds.
​
  // Future trajectory actually driven by the drivers
  repeated apollo.common.TrajectoryPoint trajectory_point = 5;
​
  // msf status
  optional MsfStatus msf_status = 6;
  // msf quality
  optional MsfSensorMsgStatus sensor_status = 7;
}

对于自车实时的信息,没必要过多解释,这是定位模块的基本功能,Pose 的消息格式如下:

message Pose {
  // Position of the vehicle reference point (VRP) in the map reference frame.
  // The VRP is the center of rear axle.
  optional apollo.common.PointENU position = 1;
​
  // A quaternion that represents the rotation from the IMU coordinate
  // (Right/Forward/Up) to the
  // world coordinate (East/North/Up).
  optional apollo.common.Quaternion orientation = 2;
​
  // Linear velocity of the VRP in the map reference frame.
  // East/north/up in meters per second.
  optional apollo.common.Point3D linear_velocity = 3;
​
  // Linear acceleration of the VRP in the map reference frame.
  // East/north/up in meters per square second.
  optional apollo.common.Point3D linear_acceleration = 4;
​
  // Angular velocity of the vehicle in the map reference frame.
  // Around east/north/up axes in radians per second.
  optional apollo.common.Point3D angular_velocity = 5;
​
  // Heading
  // The heading is zero when the car is facing East and positive when facing
  // North.
  optional double heading = 6;
​
  // Linear acceleration of the VRP in the vehicle reference frame.
  // Right/forward/up in meters per square second.
  optional apollo.common.Point3D linear_acceleration_vrf = 7;
​
  // Angular velocity of the VRP in the vehicle reference frame.
  // Around right/forward/up axes in radians per second.
  optional apollo.common.Point3D angular_velocity_vrf = 8;
​
  // Roll/pitch/yaw that represents a rotation with intrinsic sequence z-x-y.
  // in world coordinate (East/North/Up)
  // The roll, in (-pi/2, pi/2), corresponds to a rotation around the y-axis.
  // The pitch, in [-pi, pi), corresponds to a rotation around the x-axis.
  // The yaw, in [-pi, pi), corresponds to a rotation around the z-axis.
  // The direction of rotation follows the right-hand rule.
  optional apollo.common.Point3D euler_angles = 9;
}

Uncertainty 中主要是 pose 参数的标准差,如下所示:

message Uncertainty {
  // Standard deviation of position, east/north/up in meters.
  optional apollo.common.Point3D position_std_dev = 1;
​
  // Standard deviation of quaternion qx/qy/qz, unitless.
  optional apollo.common.Point3D orientation_std_dev = 2;
​
  // Standard deviation of linear velocity, east/north/up in meters per second.
  optional apollo.common.Point3D linear_velocity_std_dev = 3;
​
  // Standard deviation of linear acceleration, right/forward/up in meters per
  // square second.
  optional apollo.common.Point3D linear_acceleration_std_dev = 4;
​
  // Standard deviation of angular velocity, right/forward/up in radians per
  // second.
  optional apollo.common.Point3D angular_velocity_std_dev = 5;
​
  // TODO: Define covariance items when needed.
}

对于自车轨迹点的预测,我认为可以借助上个规划周期的规划结果来进行自车的轨迹预测,从而对动态障碍物做出合理的反应,因此是包含多个时刻的 TrajectoryPoint 的,其消息格式如下所示:

message TrajectoryPoint {
  // path point
  optional PathPoint path_point = 1;
  // linear velocity
  optional double v = 2;  // in [m/s]
  // linear acceleration
  optional double a = 3;
  // relative time from beginning of the trajectory
  optional double relative_time = 4;
  // longitudinal jerk
  optional double da = 5;
  // The angle between vehicle front wheel and vehicle longitudinal axis
  optional double steer = 6;
​
  // Gaussian probability information
  optional GaussianInfo gaussian_info = 7;
}

其中,PathPoint 为道路上的某一点,包含点的坐标、以及道路在此处的走向、曲率等信息,如下所示:

message PathPoint {
  // coordinates
  optional double x = 1;
  optional double y = 2;
  optional double z = 3;
​
  // direction on the x-y plane
  optional double theta = 4;
  // curvature on the x-y planning
  optional double kappa = 5;
  // accumulated distance from beginning of the path
  optional double s = 6;
​
  // derivative of kappa w.r.t s.
  optional double dkappa = 7;
  // derivative of derivative of kappa w.r.t s.
  optional double ddkappa = 8;
  // The lane ID where the path point is on
  optional string lane_id = 9;
​
  // derivative of x and y w.r.t parametric parameter t in CosThetareferenceline
  optional double x_derivative = 10;
  optional double y_derivative = 11;
}

Chassis

Chassis 为从 can 上获取的自车底盘信息,这些都比较容易理解,不做过多解释,如下所示:

message Chassis {
  enum DrivingMode {
    COMPLETE_MANUAL = 0;  // human drive
    COMPLETE_AUTO_DRIVE = 1;
    AUTO_STEER_ONLY = 2;  // only steer
    AUTO_SPEED_ONLY = 3;  // include throttle and brake
​
    // security mode when manual intervention happens, only response status
    EMERGENCY_MODE = 4;
  }
​
  enum ErrorCode {
    NO_ERROR = 0;
​
    CMD_NOT_IN_PERIOD = 1;  // control cmd not in period
​
    // car chassis report error, like steer, brake, throttle, gear fault
    CHASSIS_ERROR = 2;
​
    // classify the types of the car chassis errors
    CHASSIS_ERROR_ON_STEER = 6;
    CHASSIS_ERROR_ON_BRAKE = 7;
    CHASSIS_ERROR_ON_THROTTLE = 8;
    CHASSIS_ERROR_ON_GEAR = 9;
​
    MANUAL_INTERVENTION = 3;  // human manual intervention
​
    // receive car chassis can frame not in period
    CHASSIS_CAN_NOT_IN_PERIOD = 4;
​
    UNKNOWN_ERROR = 5;
  }
​
  enum GearPosition {
    GEAR_NEUTRAL = 0;
    GEAR_DRIVE = 1;
    GEAR_REVERSE = 2;
    GEAR_PARKING = 3;
    GEAR_LOW = 4;
    GEAR_INVALID = 5;
    GEAR_NONE = 6;
  }
​
  optional bool engine_started = 3;
​
  // Engine speed in RPM.
  optional float engine_rpm = 4 [default = nan];
​
  // Vehicle Speed in meters per second.
  optional float speed_mps = 5 [default = nan];
​
  // Vehicle odometer in meters.
  optional float odometer_m = 6 [default = nan];
​
  // Fuel range in meters.
  optional int32 fuel_range_m = 7;
​
  // Real throttle location in [%], ranging from 0 to 100.
  optional float throttle_percentage = 8 [default = nan];
​
  // Real brake location in [%], ranging from 0 to 100.
  optional float brake_percentage = 9 [default = nan];
​
  // Real steering location in [%], ranging from -100 to 100.
  // steering_angle / max_steering_angle
  // Clockwise: negative
  // CountClockwise: positive
  optional float steering_percentage = 11 [default = nan];
​
  // Applied steering torque in [Nm].
  optional float steering_torque_nm = 12 [default = nan];
​
  // Parking brake status.
  optional bool parking_brake = 13;
​
  // Light signals.
  optional bool high_beam_signal = 14 [deprecated = true];
  optional bool low_beam_signal = 15 [deprecated = true];
  optional bool left_turn_signal = 16 [deprecated = true];
  optional bool right_turn_signal = 17 [deprecated = true];
  optional bool horn = 18 [deprecated = true];
​
  optional bool wiper = 19;
  optional bool disengage_status = 20 [deprecated = true];
  optional DrivingMode driving_mode = 21 [default = COMPLETE_MANUAL];
  optional ErrorCode error_code = 22 [default = NO_ERROR];
  optional GearPosition gear_location = 23;
​
  // timestamp for steering module
  optional double steering_timestamp = 24;  // In seconds, with 1e-6 accuracy
​
  // chassis also needs it own sending timestamp
  optional apollo.common.Header header = 25;
​
  optional int32 chassis_error_mask = 26 [default = 0];
​
  optional apollo.common.VehicleSignal signal = 27;
​
  // Only available for Lincoln now
  optional ChassisGPS chassis_gps = 28;
​
  optional apollo.common.EngageAdvice engage_advice = 29;
​
  optional WheelSpeed wheel_speed = 30;
​
  optional Surround surround = 31;
​
  // Vehicle registration information
  optional License license = 32 [deprecated = true];
​
  // Real gear location.
  // optional int32 gear_location = 10 [deprecated = true]; deprecated use enum
  // replace this [id 23]
​
  optional apollo.common.VehicleID vehicle_id = 33;
​
  optional int32 battery_soc_percentage = 34 [default = -1];
}

其中,ChassisGPS 如下:

message ChassisGPS {
  optional double latitude = 1;
  optional double longitude = 2;
  optional bool gps_valid = 3;
​
  optional int32 year = 4;
  optional int32 month = 5;
  optional int32 day = 6;
  optional int32 hours = 7;
  optional int32 minutes = 8;
  optional int32 seconds = 9;
  optional double compass_direction = 10;
  optional double pdop = 11;
  optional bool is_gps_fault = 12;
  optional bool is_inferred = 13;
​
  optional double altitude = 14;
  optional double heading = 15;
  optional double hdop = 16;
  optional double vdop = 17;
  optional GpsQuality quality = 18;
  optional int32 num_satellites = 19;
  optional double gps_speed = 20;
}
​
enum GpsQuality {
  FIX_NO = 0;
  FIX_2D = 1;
  FIX_3D = 2;
  FIX_INVALID = 3;
}

WheelSpeed 如下:

message WheelSpeed {
  enum WheelSpeedType {
    FORWARD = 0;
    BACKWARD = 1;
    STANDSTILL = 2;
    INVALID = 3;
  }
  optional bool is_wheel_spd_rr_valid = 1 [default = false];
  optional WheelSpeedType wheel_direction_rr = 2 [default = INVALID];
  optional double wheel_spd_rr = 3 [default = 0.0];
  optional bool is_wheel_spd_rl_valid = 4 [default = false];
  optional WheelSpeedType wheel_direction_rl = 5 [default = INVALID];
  optional double wheel_spd_rl = 6 [default = 0.0];
  optional bool is_wheel_spd_fr_valid = 7 [default = false];
  optional WheelSpeedType wheel_direction_fr = 8 [default = INVALID];
  optional double wheel_spd_fr = 9 [default = 0.0];
  optional bool is_wheel_spd_fl_valid = 10 [default = false];
  optional WheelSpeedType wheel_direction_fl = 11 [default = INVALID];
  optional double wheel_spd_fl = 12 [default = 0.0];
}

Surround 如下:

message Surround {
  optional bool cross_traffic_alert_left = 1;
  optional bool cross_traffic_alert_left_enabled = 2;
  optional bool blind_spot_left_alert = 3;
  optional bool blind_spot_left_alert_enabled = 4;
  optional bool cross_traffic_alert_right = 5;
  optional bool cross_traffic_alert_right_enabled = 6;
  optional bool blind_spot_right_alert = 7;
  optional bool blind_spot_right_alert_enabled = 8;
  optional double sonar00 = 9;
  optional double sonar01 = 10;
  optional double sonar02 = 11;
  optional double sonar03 = 12;
  optional double sonar04 = 13;
  optional double sonar05 = 14;
  optional double sonar06 = 15;
  optional double sonar07 = 16;
  optional double sonar08 = 17;
  optional double sonar09 = 18;
  optional double sonar10 = 19;
  optional double sonar11 = 20;
  optional bool sonar_enabled = 21;
  optional bool sonar_fault = 22;
  repeated double sonar_range = 23;
  repeated Sonar sonar = 24;
}
​
message Sonar {
  optional double range = 1;                       // Meter
  optional apollo.common.Point3D translation = 2;  // Meter
  optional apollo.common.Quaternion rotation = 3;
}

EngageAdvice 如下:

// This is the engage advice that published by critical runtime modules.
message EngageAdvice {
  enum Advice {
    UNKNOWN = 0;
    DISALLOW_ENGAGE = 1;
    READY_TO_ENGAGE = 2;
    KEEP_ENGAGED = 3;
    PREPARE_DISENGAGE = 4;
  }
​
  optional Advice advice = 1 [default = DISALLOW_ENGAGE];
  optional string reason = 2;
}

以上就是 planning 模块主要的输入信息,接下来看一下输出信息,其实就是 ADCTrajectory 这个消息类型。

ADCTrajectory

ADCTrajectory 名为轨迹,要明确其不同于路径,轨迹不仅包含了行驶路线,还包含了每个时刻车辆的速度、加速度、方向转向等信息,车辆的行驶轨迹是由一系列的点来描述的,其中主要是 TrajectoryPoint 和 PathPoint,而这两个消息结构在上面 LocalizationEstimate 中已经有过说明。

message ADCTrajectory {
  optional apollo.common.Header header = 1;
​
  optional double total_path_length = 2;  // in meters
  optional double total_path_time = 3;    // in seconds
​
  // path data + speed data
  repeated apollo.common.TrajectoryPoint trajectory_point = 12;
​
  optional EStop estop = 6;
​
  // path point without speed info
  repeated apollo.common.PathPoint path_point = 13;
​
  // is_replan == true mean replan triggered
  optional bool is_replan = 9 [default = false];
  optional string replan_reason = 22;
​
  // Specify trajectory gear
  optional apollo.canbus.Chassis.GearPosition gear = 10;
​
  optional apollo.planning.DecisionResult decision = 14;
​
  optional LatencyStats latency_stats = 15;
​
  // the routing used for current planning result
  optional apollo.common.Header routing_header = 16;
  optional apollo.planning_internal.Debug debug = 8;
​
  enum RightOfWayStatus {
    UNPROTECTED = 0;
    PROTECTED = 1;
  }
  optional RightOfWayStatus right_of_way_status = 17;
​
  // lane id along current reference line
  repeated apollo.hdmap.Id lane_id = 18;
​
  // set the engage advice for based on current planning result.
  optional apollo.common.EngageAdvice engage_advice = 19;
​
  // the region where planning cares most
  message CriticalRegion {
    repeated apollo.common.Polygon region = 1;
  }
​
  // critical region will be empty when planning is NOT sure which region is
  // critical
  // critical regions may or may not overlap
  optional CriticalRegion critical_region = 20;
​
  enum TrajectoryType {
    UNKNOWN = 0;
    NORMAL = 1;
    PATH_FALLBACK = 2;
    SPEED_FALLBACK = 3;
    PATH_REUSED = 4;
  }
  optional TrajectoryType trajectory_type = 21 [default = UNKNOWN];
​
  // lane id along target reference line
  repeated apollo.hdmap.Id target_lane_id = 23;
​
  // output related to RSS
  optional RSSInfo rss_info = 100;
}

写在后面的话

这里分享一些我的拙见,不一定正确,所以就不放在前面献丑了,欢迎指正和讨论,hhh。

首先,为什么要总结 planning 的输入与输出呢?主要是樊神的那篇论文已经把 Apollo 的 EMplanner 的架构和算法思想讲解的非常透彻了,另外王方浩、保罗的酒吧、知行合一2018三位大佬的博文对于 Apollo 的解析,在整体架构、运行逻辑和源码分析三个方面也已经做的很透彻了,虽然已有的博文大多是针对 Apollo3.5 版本的源码,但还是有助于快速的熟悉 Apollo,在此基础上可以做一些有意思的事情,比如,如果你认为某个 task 中的二次优化算法比较粗暴,太依赖算力,你可以使用更巧妙的方法进行替换,但是要关注下成功率,毕竟实际情况很复杂,往往粗暴的方法成功率更高;甚至如果你觉得有些场景没有覆盖到,你可以仿照已有的形式设计scenario、stage、task,然后进行验证,验证的方法可以参考我的另外一篇博文“Apollo6.0 + lgSVL 联合仿真平台搭建”。相比之下,master 版本有功能的完善和补充,有代码的优化,解析输入输出,一方面可以帮助初学者对于 planning 模块的功能有个初步的认识,另一方面是我对新增的 learning based planning 更感兴趣。

那么输入和输出跟 learning based planning 有什么关系呢?显然,这里的输入和输出同样适用于 learning mode,但并不是直接用,而是经过处理,但是 planning 肯定不是一个简单的 regression 的问题,应该是通过 force learning 不断提升规划的质量,细心的同学应该已经发现了,开篇说了在 planning 模块中,定义了 3 个 writer,其中有个 planning_learning_data_writer_ ,这个应该就是将本周期的信息作为 training data 发布出去。

我没有车,怎么训练和验证模型的?还是一样的,lgSVL 能够满足你的需求,创造一个足够逼真的物理环境,按照你的想法选择或设计车辆模型,部署传感器,随心所欲的创建交通场景,去吧,快去让你的算法可以真正像人类驾驶员一样自我成长吧。

<think>好的,我现在需要帮助用户解决在Vivado中设置输入输出(I/O)的问题。首先,我需要回想一下自己对Vivado的I/O配置流程的了解,并结合用户提供的参考资料来组织回答。 用户的问题很明确,想了解如何在Vivado中设置输入输出,可能需要一个步骤式的教程。根据引用内容,特别是引用[2]提到了保存代码后的步骤,比如设置顶层模块功能仿真,这可能I/O配置相关。另外,引用[3]提到锁相环的使用教程,虽然不直接相关,但可能涉及模块接口,这也需要I/O定义。 首先,Vivado中设置输入输出通常是在设计顶层模块时,通过端口定义来完成的。用户需要在自己的HDL代码(如Verilog或VHDL)中声明输入输出端口,然后在综合后通过I/O Planning工具进行物理引脚分配。引用[2]中提到的设置顶层模块保存后的步骤可能涉及这一过程。 接下来,我需要分步骤说明: 1. **在HDL代码中定义端口**:用户需要在顶层模块中声明输入输出信号,比如使用inputoutput关键字。例如,一个LED控制的模块可能有时钟输入、复位输入LED输出。 2. **综合设计**:综合后,Vivado会生成逻辑网表,此时可以进入I/O Planning界面进行引脚分配。 3. **使用I/O Planning工具**:在这里,用户可以将逻辑端口分配到FPGA的物理引脚上,同时可以设置电压标准、驱动强度等参数。引用[2]提到的功能仿真可能是在这一步之后进行的。 4. **生成约束文件(XDC)**:Vivado会根据用户的引脚分配自动生成或更新XDC约束文件,这些文件包含了引脚位置电气特性等信息。 5. **验证实现**:完成约束后,进行实现步骤,包括布局布线,确保引脚分配正确无误。 可能需要注意的细节包括:保存代码时Vivado的自动检查(如引用[2]所述),以及如何手动设置顶层模块。此外,引用[1]提到使用第三方仿真工具,但用户可能更关注Vivado自带的流程,所以应强调内置工具的使用。 用户可能遇到的问题包括引脚分配冲突或约束文件错误,因此需要提醒仔细检查XDC文件。此外,初学者可能对I/O Planning界面不熟悉,需要简要说明界面布局常用操作。 最后,根据用户需求生成相关问题,比如如何验证引脚分配,如何处理多时钟域,以及约束文件的语法,这些都能帮助用户进一步学习。</think>### Vivado输入输出(I/O)配置教程 在FPGA开发中,输入输出端口定义与物理引脚分配是关键步骤,以下是详细操作流程: #### 一、HDL代码端口定义 在Verilog/VHDL顶层模块中声明输入输出: ```verilog module top( input clk_50MHz, // 时钟输入 input rst_n, // 复位输入(低有效) output [3:0] leds // LED输出 ); // 逻辑实现... endmodule ``` 保存代码后,Vivado会自动进行语法检查(如引用[2]所述),需确认无错误提示[^2] #### 二、设置顶层模块 1. 在Sources窗口右键模块文件 2. 选择"Set as Top"(如引用[2]手动指定顶层模块的方法)[^2] #### 三、I/O引脚分配 1. 完成综合(Synthesis)后,点击**Open Elaborated Design** 2. 在菜单栏选择**Window > I/O Ports**进入规划界面 | 逻辑端口 | 物理引脚 | I/O标准 | 驱动强度 | |----------|----------|-----------|----------| | clk_50MHz| E3 | LVCMOS33 | 8mA | | rst_n | C12 | LVCMOS18 | 4mA | | leds[0] | A14 | LVCMOS33 | 12mA | > 引脚编号需参考具体FPGA型号的数据手册 #### 四、生成约束文件(XDC) 1. 自动生成:通过I/O Planning界面保存生成.xdc文件 2. 手动编辑示例: ```tcl set_property PACKAGE_PIN E3 [get_ports clk_50MHz] set_property IOSTANDARD LVCMOS33 [get_ports clk_50MHz] ``` #### 五、验证配置 1. 运行实现(Implementation)检查布线结果 2. 通过Schematic视图验证物理连接
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

饭来1993

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值