【ROS-Navigation】系列(X) ——Base Local Planner局部規(guī)劃-TrajectoryPlanner源碼解讀(3/3)

本篇記錄局部規(guī)劃器生成路徑時(shí)用到的幾個(gè)“輔助工具”富腊,內(nèi)容簡(jiǎn)單也比較少逆屡。MapCell類(lèi)與MapGrid類(lèi)用于獲取軌跡點(diǎn)與目標(biāo)點(diǎn)/全局路徑之間的距離高诺,為路徑打分提供參考,CostmapModel類(lèi)則能夠獲取點(diǎn)峭梳、連線舰绘、多邊形邊緣(機(jī)器人足跡)的cost,是局部規(guī)劃器與costmap間的一個(gè)橋梁葱椭。

【結(jié)構(gòu)示意圖】

【相關(guān)文件】

  • base_local_planner/src/trajectory_planner_ros.cpp
  • base_local_planner/src/trajectory_planner.cpp
  • base_local_planner/src/map_grid.cpp
  • base_local_planner/src/map_cell.cpp
  • base_local_planner/src/costmap_model.cpp
    本篇記錄對(duì)MapCell捂寿、MapGrid類(lèi)和CostmapModel類(lèi)閱讀和理解。

【代碼分析】

一孵运、 MapCell類(lèi)與MapGrid類(lèi)

這兩個(gè)類(lèi)是局部規(guī)劃器專(zhuān)門(mén)用來(lái)確定各cell與目標(biāo)點(diǎn)和全局規(guī)劃路徑的距離的秦陋,它們是TrajectoryPlanner的成員,在為路徑打分時(shí)被使用治笨。

  • MapGrid類(lèi)數(shù)據(jù)成員:
 public:
    double goal_x_, goal_y_; /**< @brief The goal distance was last computed from */
    unsigned int size_x_, size_y_; ///< @brief The dimensions of the grid
 private:
    std::vector<MapCell> map_; ///< @brief Storage for the MapCells

MapGrid是“地圖”驳概,它包含一個(gè)由MapCell類(lèi)對(duì)象數(shù)組的成員,即cell數(shù)組旷赖,地圖大小為size_x_×size_y_顺又。goal_map和path_map都是MapGrid類(lèi)實(shí)例,這里就分別稱(chēng)這兩張地圖為 “目標(biāo)地圖” 和 “路徑地圖”杠愧。

  • MapCell類(lèi)數(shù)據(jù)成員:
 public:
     unsigned int cx, cy; ///< @brief Cell index in the grid map
     double target_dist; ///< @brief Distance to planner's path
     bool target_mark; ///< @brief Marks for computing path/goal distances
     bool within_robot; ///< @brief Mark for cells within the robot footprint

MapCell就代表地圖上的一個(gè)cell待榔,它記錄x、y坐標(biāo)(索引)流济。target_dist表示目標(biāo)距離锐锣,它被初始化為無(wú)窮大,經(jīng)過(guò)path_map和goal_map的計(jì)算后绳瘟,它可以表示該cell距目標(biāo)點(diǎn)/全局路徑的距離雕憔。target_mark是該點(diǎn)已更新過(guò)的flag。within_robot表示該點(diǎn)在機(jī)器人足跡范圍內(nèi)糖声。

二斤彼、MapGrid::setTargetCells 處理路徑地圖

首先調(diào)用類(lèi)內(nèi)sizeCheck函數(shù)分瘦,檢查路徑地圖尺寸是否和costmap相同,若不同琉苇,則按照costmap尺寸對(duì)其重新設(shè)置嘲玫,并且檢查MapCell在MapGrid中的index是否和它本身的坐標(biāo)索引一致。

  void MapGrid::setTargetCells(const costmap_2d::Costmap2D& costmap,
      const std::vector<geometry_msgs::PoseStamped>& global_plan) {
    sizeCheck(costmap.getSizeInCellsX(), costmap.getSizeInCellsY());

    bool started_path = false;

    queue<MapCell*> path_dist_queue; //用于儲(chǔ)存全局路徑上的MapCell

    std::vector<geometry_msgs::PoseStamped> adjusted_global_plan;

傳入的全局規(guī)劃是global系下的并扇,調(diào)用adjustPlanResolution函數(shù)對(duì)其分辨率進(jìn)行一個(gè)簡(jiǎn)單的調(diào)整去团,使其達(dá)到costmap的分辨率,方法也很簡(jiǎn)單穷蛹,當(dāng)相鄰點(diǎn)之間距離>分辨率土陪,在其間插入點(diǎn),以達(dá)到分辨率要求肴熏。

    // 調(diào)整全局路徑的分辨率使其能夠達(dá)到cost_map的分辨率
    adjustPlanResolution(global_plan, adjusted_global_plan, costmap.getResolution());
    if (adjusted_global_plan.size() != global_plan.size()) {
      ROS_DEBUG("Adjusted global plan resolution, added %zu points", adjusted_global_plan.size() - global_plan.size());
    }
    unsigned int i;

接下來(lái)將全局路徑的點(diǎn)轉(zhuǎn)換到“路徑地圖”上鬼雀,并將對(duì)應(yīng)cell的target_dist設(shè)置為0,標(biāo)記已計(jì)算過(guò)蛙吏。由于分辨率已經(jīng)過(guò)調(diào)整源哩,故不會(huì)出現(xiàn)路徑不連續(xù)的情況。在“路徑地圖”上得到一條在地圖范圍內(nèi)出刷、且不經(jīng)過(guò)costmap上未知cell的全局路徑璧疗,每個(gè)點(diǎn)的target_dist都為0。

    for (i = 0; i < adjusted_global_plan.size(); ++i) {
      double g_x = adjusted_global_plan[i].pose.position.x;
      double g_y = adjusted_global_plan[i].pose.position.y;
      unsigned int map_x, map_y;
      // 如果成功把一個(gè)全局規(guī)劃上的點(diǎn)的坐標(biāo)轉(zhuǎn)換到地圖坐標(biāo)(map_x, map_y)上馁龟,且在代價(jià)地圖上這一點(diǎn)不是未知的
      if (costmap.worldToMap(g_x, g_y, map_x, map_y) && costmap.getCost(map_x, map_y) != costmap_2d::NO_INFORMATION) {
        // 用current來(lái)引用這個(gè)cell
        MapCell& current = getCell(map_x, map_y);
        // 將這個(gè)點(diǎn)的target_dist設(shè)置為0崩侠,即在全局路徑上
        current.target_dist = 0.0;
        // 標(biāo)記已經(jīng)計(jì)算了距離
        current.target_mark = true;
        // 把該點(diǎn)放進(jìn)path_dist_queue隊(duì)列中
        path_dist_queue.push(&current);
        // 標(biāo)記已經(jīng)開(kāi)始把點(diǎn)轉(zhuǎn)換到地圖坐標(biāo)
        started_path = true;
      } 
      // 當(dāng)代價(jià)地圖上這一點(diǎn)的代價(jià)不存在了(規(guī)劃路徑已經(jīng)到達(dá)了代價(jià)地圖的邊界)
      // 并且標(biāo)記了已經(jīng)開(kāi)始轉(zhuǎn)換,退出循環(huán)
      else if (started_path) {
          break;
      }
    }
    // 如果循環(huán)結(jié)束后并沒(méi)有確定任何在地圖坐標(biāo)上的點(diǎn)
    if (!started_path) {
      ROS_ERROR("None of the %d first of %zu (%zu) points of the global plan were in the local costmap and free",
          i, adjusted_global_plan.size(), global_plan.size());
      return;
    }

接下來(lái)調(diào)用類(lèi)內(nèi)computeTargetDistance函數(shù)坷檩,開(kāi)始“填充”却音,最終得到一張完整的路徑地圖。

   // 計(jì)算本地地圖上的每一個(gè)cell與規(guī)劃路徑之間的距離
   computeTargetDistance(path_dist_queue, costmap);
 }

三矢炼、MapGrid::setLocalGoal 處理目標(biāo)地圖

同樣系瓢,先調(diào)整全局規(guī)劃的分辨率。

  void MapGrid::setLocalGoal(const costmap_2d::Costmap2D& costmap,
      const std::vector<geometry_msgs::PoseStamped>& global_plan) {
    sizeCheck(costmap.getSizeInCellsX(), costmap.getSizeInCellsY());

    int local_goal_x = -1;
    int local_goal_y = -1;
    bool started_path = false;

    std::vector<geometry_msgs::PoseStamped> adjusted_global_plan;
    adjustPlanResolution(global_plan, adjusted_global_plan, costmap.getResolution());

然后句灌,通過(guò)迭代找到全局路徑的終點(diǎn)夷陋,即目標(biāo)點(diǎn),但如果迭代過(guò)程當(dāng)中到達(dá)了局部規(guī)劃costmap的邊際或經(jīng)過(guò)障礙物胰锌,立即退出迭代骗绕,將上一個(gè)有效點(diǎn)作為終點(diǎn)。

    // skip global path points until we reach the border of the local map
    for (unsigned int i = 0; i < adjusted_global_plan.size(); ++i) {
      double g_x = adjusted_global_plan[i].pose.position.x;
      double g_y = adjusted_global_plan[i].pose.position.y;
      unsigned int map_x, map_y;
      if (costmap.worldToMap(g_x, g_y, map_x, map_y) && costmap.getCost(map_x, map_y) != costmap_2d::NO_INFORMATION) {
        // 不斷迭代local_goal_x资昧、local_goal_y酬土,嘗試找到終點(diǎn)
        local_goal_x = map_x;
        local_goal_y = map_y;
        started_path = true;
      } else {
        if (started_path) {
          break;
        }// else we might have a non pruned path, so we just continue
      }
    }
    if (!started_path) {
      ROS_ERROR("None of the points of the global plan were in the local costmap, global plan points too far from robot");
      return;
    }

將迭代得到的目標(biāo)點(diǎn)對(duì)應(yīng)在“目標(biāo)地圖”上的cell的target_dist標(biāo)記為0。setTargetCells和setLocalGoal這兩個(gè)函數(shù)格带,前者在“路徑地圖”上將全局路徑點(diǎn)target_dist標(biāo)記為0撤缴,后者在“目標(biāo)地圖”上將目標(biāo)點(diǎn)target_dist標(biāo)記為0刹枉。最后同樣調(diào)用computeTargetDistance函數(shù)。

    queue<MapCell*> path_dist_queue;
    // 如果找到的目標(biāo)點(diǎn)橫縱坐標(biāo)都大于0
    if (local_goal_x >= 0 && local_goal_y >= 0) {
      MapCell& current = getCell(local_goal_x, local_goal_y);
      costmap.mapToWorld(local_goal_x, local_goal_y, goal_x_, goal_y_);
      current.target_dist = 0.0;
      current.target_mark = true;
      path_dist_queue.push(&current);
    }
    computeTargetDistance(path_dist_queue, costmap);
  }

依次檢查“父cell”四周的cell并標(biāo)記它已計(jì)算屈呕,對(duì)它調(diào)用updatePathCell函數(shù)微宝,得到該cell的值。若有效凉袱,加入循環(huán)隊(duì)列里芥吟,彈出“父cell”侦铜,四周的cell成為新的“父cell”专甩, 循環(huán),直到所有cell更新完畢钉稍。

check_cell其實(shí)是children_cell涤躲,是current_cell的子節(jié)點(diǎn),computeTargetDistance其實(shí)是個(gè)dijstra算法

      // 如果該點(diǎn)的橫坐標(biāo)大于0
      if(current_cell->cx > 0){
        // 檢查它左側(cè)相鄰的點(diǎn)check_cell
        check_cell = current_cell - 1;
        // 如果check_cell未被標(biāo)記
        if(!check_cell->target_mark){
          // 標(biāo)記它
          check_cell->target_mark = true;
          // updatePathCell功能:如果check_cell不是障礙物贡未,將target_dist設(shè)置為current_cell.target_distance+1种樱;否則,設(shè)置為無(wú)窮
          if(updatePathCell(current_cell, check_cell, costmap)) {
            // check_cell入隊(duì)
            dist_queue.push(check_cell);
          }
        }
      }
      // 如果該點(diǎn)的橫坐標(biāo)小于圖像寬度-1
      if(current_cell->cx < last_col){
        // 檢查它右側(cè)相鄰的點(diǎn)check_cell
        check_cell = current_cell + 1;
        if(!check_cell->target_mark){
          check_cell->target_mark = true;
          if(updatePathCell(current_cell, check_cell, costmap)) {
            dist_queue.push(check_cell);
          }
        }
      }
      // 如果該點(diǎn)的縱坐標(biāo)>0
      if(current_cell->cy > 0){
        // 檢查它上面的點(diǎn)
        check_cell = current_cell - size_x_;
        if(!check_cell->target_mark){
          check_cell->target_mark = true;
          if(updatePathCell(current_cell, check_cell, costmap)) {
            dist_queue.push(check_cell);
          }
        }
      }
      // 如果該點(diǎn)的縱坐標(biāo)<圖像長(zhǎng)度-1
      if(current_cell->cy < last_row){
        // 檢查它下面的點(diǎn)
        check_cell = current_cell + size_x_;
        if(!check_cell->target_mark){
          check_cell->target_mark = true;
          if(updatePathCell(current_cell, check_cell, costmap)) {
            dist_queue.push(check_cell);
          }
        }
      }
      // 所以已經(jīng)計(jì)算過(guò)的點(diǎn)不斷被加入俊卤,然后利用它找到周?chē)c(diǎn)后嫩挤,彈出它,直到地圖上所有點(diǎn)的距離都被計(jì)算出來(lái)
    }
  }
};

關(guān)于updatePathCell函數(shù)消恍,先獲取該cell在costmap上的值岂昭,若發(fā)現(xiàn)它是障礙物或未知cell或它在機(jī)器人足跡內(nèi),直接返回false狠怨,該cell將不會(huì)進(jìn)入循環(huán)隊(duì)列约啊,也就是不會(huì)由它繼續(xù)向下傳播;若非以上情況佣赖,讓它的值=鄰點(diǎn)值+1恰矩,若<原值,用它更新憎蛤。

  inline bool MapGrid::updatePathCell(MapCell* current_cell, MapCell* check_cell,
     const costmap_2d::Costmap2D& costmap){

   unsigned char cost = costmap.getCost(check_cell->cx, check_cell->cy);
  if(! getCell(check_cell->cx, check_cell->cy).within_robot &&
       (cost == costmap_2d::LETHAL_OBSTACLE ||
       cost == costmap_2d::INSCRIBED_INFLATED_OBSTACLE ||
        cost == costmap_2d::NO_INFORMATION)){
     check_cell->target_dist = obstacleCosts();
     return false;
   }

   // 不是障礙物的話外傅,比較它的target_dist(本來(lái)通過(guò)resetPathDist被設(shè)置為unreachableCellCosts)與>current_cell->target_dist + 1
   double new_target_dist = current_cell->target_dist + 1;
   if (new_target_dist < check_cell->target_dist) {
     check_cell->target_dist = new_target_dist;
   }
   return true;
 }

這和全局規(guī)劃器使用Dijkstra算法時(shí)用costarr生成potarr很類(lèi)似,過(guò)程中的“累加”能夠反映各cell與初始cell之間的距離俩檬,這樣萎胰,最終得到完整的path_map和goal_map。

四豆胸、CostmapModel類(lèi)

這個(gè)類(lèi)幫助局部規(guī)劃器在Costmap上進(jìn)行計(jì)算奥洼,footprintCost、lineCost晚胡、pointCost三個(gè)類(lèi)方法分別能通過(guò)Costmap計(jì)算出機(jī)器人足跡范圍灵奖、兩個(gè)cell連線嚼沿、單個(gè)cell的代價(jià),并將值返回給局部規(guī)劃器瓷患。這里簡(jiǎn)單看一下footprintCost函數(shù)骡尽。

  • 若預(yù)設(shè)的足跡點(diǎn)數(shù)<3,考慮足跡的形狀沒(méi)有意義擅编,這時(shí)只計(jì)算機(jī)器人位置點(diǎn)在costmap上的代價(jià)攀细;

  • 若預(yù)設(shè)的足跡點(diǎn)數(shù)≥3,把足跡視為多邊形爱态,循環(huán)調(diào)用lineCost計(jì)算多邊形各邊的cell谭贪,注意首尾閉合,* 最后返回代價(jià)锦担。
    返回值:

  • -1.0 :覆蓋至少一個(gè)障礙cell

  • -2.0 :覆蓋至少一個(gè)未知cell

  • -3.0 :不在地圖上

  • 其他正cost

為什么這里只要多邊形邊緣不經(jīng)過(guò)障礙就認(rèn)為足跡不經(jīng)過(guò)障礙呢俭识?而不是多邊形內(nèi)部所有的點(diǎn)都要檢測(cè)呢?

  • 1.因?yàn)檎J(rèn)為機(jī)器人是實(shí)心的,一定是最外面的邊緣線碰到障礙物洞渔,所以是檢測(cè)多邊形的邊緣

    1. 因?yàn)檫@個(gè)函數(shù)是在軌跡點(diǎn)生成過(guò)程中套媚,對(duì)新生成的軌跡點(diǎn)調(diào)用的,而既然能新生成軌跡點(diǎn)磁椒,說(shuō)明該軌跡點(diǎn)必不是障礙物堤瘤,當(dāng)多邊形邊緣不經(jīng)過(guò)障礙、中心也不是障礙的時(shí)候浆熔,如果機(jī)器人比較小本辐,可以認(rèn)為足跡不經(jīng)過(guò)障礙。
  double CostmapModel::footprintCost(const geometry_msgs::Point& position, const std::vector<geometry_msgs::Point>& footprint,
      double inscribed_radius, double circumscribed_radius){
    //聲明地圖坐標(biāo)系上的坐標(biāo)
    unsigned int cell_x, cell_y;

    /****************************************考慮機(jī)器人中心******************************************/
    //得到機(jī)器人中心點(diǎn)的cell坐標(biāo)蘸拔,存放在cell_x cell_y中
    //如果得不到坐標(biāo)师郑,說(shuō)明不在地圖上,直接返回-3
    if(!costmap_.worldToMap(position.x, position.y, cell_x, cell_y))
      return -3.0;

    //如果腳拥髑稀(預(yù)先輸入的)點(diǎn)數(shù)小于三宝冕,默認(rèn)機(jī)器人形狀為圓形,不考慮腳印邓萨,只考慮中心
    if(footprint.size() < 3){
      unsigned char cost = costmap_.getCost(cell_x, cell_y); 
      //如果中心位于未知代價(jià)的cell上地梨,返回-2
      if(cost == NO_INFORMATION)
        return -2.0;
      //如果中心位于致命障礙cell上,返回-1
      if(cost == LETHAL_OBSTACLE || cost == INSCRIBED_INFLATED_OBSTACLE) //這幾個(gè)宏是在costvalue中定義的缔恳,是灰度值
        return -1.0;
      //如果機(jī)器人位置既不是未知也不是致命宝剖,返回它的代價(jià)
      return cost;
    }

    /****************************************考慮機(jī)器人腳印******************************************/
    //接下來(lái)在costmap上考慮腳印
    unsigned int x0, x1, y0, y1;
    double line_cost = 0.0; //初始化
    double footprint_cost = 0.0; //初始化0

    //對(duì)footprint進(jìn)行光柵化算法處理得到柵格點(diǎn)
    //計(jì)算各個(gè)腳印點(diǎn)連成的多邊形的邊緣上是否碰撞到障礙物
    //i是索引,大小是腳印的size歉甚,第一次万细,i=1,下面得到footprint[1]和footprint[2]的連線的cost纸泄,后面以此類(lèi)推
    for(unsigned int i = 0; i < footprint.size() - 1; ++i){
      //得到腳印中第一個(gè)點(diǎn)的坐標(biāo)x0, y0
      if(!costmap_.worldToMap(footprint[i].x, footprint[i].y, x0, y0))
        //如果無(wú)法轉(zhuǎn)換赖钞,說(shuō)明第一個(gè)腳印點(diǎn)不在地圖上腰素,直接返回-3
        return -3.0;

      //得到腳印中下一個(gè)點(diǎn)的坐標(biāo)x1, y1
      if(!costmap_.worldToMap(footprint[i + 1].x, footprint[i + 1].y, x1, y1))
        //如果無(wú)法轉(zhuǎn)換,說(shuō)明下一個(gè)腳印點(diǎn)不在地圖上雪营,直接返回-3
        return -3.0;

      //得到連線的代價(jià)
      line_cost = lineCost(x0, x1, y0, y1);
      //不斷用最大的連線代價(jià)迭代footprint_cost
      footprint_cost = std::max(line_cost, footprint_cost);

      //如果某條邊緣線段代價(jià)<0弓千,直接停止生成代價(jià),返回這個(gè)負(fù)代價(jià)
      if(line_cost < 0) 
        return line_cost;
    }
    //再把footprint的最后一個(gè)點(diǎn)和第一個(gè)點(diǎn)連起來(lái)献起,形成閉合
    if(!costmap_.worldToMap(footprint.back().x, footprint.back().y, x0, y0))
      return -3.0;
    if(!costmap_.worldToMap(footprint.front().x, footprint.front().y, x1, y1))
      return -3.0;
    line_cost = lineCost(x0, x1, y0, y1);
    footprint_cost = std::max(line_cost, footprint_cost);
    if(line_cost < 0)
      return line_cost;

    //如果所有邊緣線的代價(jià)都是合法的洋访,那么返回足跡的代價(jià)
    return footprint_cost;
  }

轉(zhuǎn)載:https://blog.csdn.net/Neo11111/article/details/104720103#commentBox

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市谴餐,隨后出現(xiàn)的幾起案子姻政,更是在濱河造成了極大的恐慌,老刑警劉巖总寒,帶你破解...
    沈念sama閱讀 219,427評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件扶歪,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡摄闸,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)妹萨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)年枕,“玉大人,你說(shuō)我怎么就攤上這事乎完⊙郑” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,747評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵树姨,是天一觀的道長(zhǎng)摩桶。 經(jīng)常有香客問(wèn)我,道長(zhǎng)帽揪,這世上最難降的妖魔是什么硝清? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,939評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮转晰,結(jié)果婚禮上芦拿,老公的妹妹穿的比我還像新娘。我一直安慰自己查邢,他們只是感情好蔗崎,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著扰藕,像睡著了一般缓苛。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上邓深,一...
    開(kāi)封第一講書(shū)人閱讀 51,737評(píng)論 1 305
  • 那天未桥,我揣著相機(jī)與錄音番官,去河邊找鬼。 笑死钢属,一個(gè)胖子當(dāng)著我的面吹牛徘熔,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播淆党,決...
    沈念sama閱讀 40,448評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼酷师,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了染乌?” 一聲冷哼從身側(cè)響起山孔,我...
    開(kāi)封第一講書(shū)人閱讀 39,352評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎荷憋,沒(méi)想到半個(gè)月后台颠,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,834評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡勒庄,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評(píng)論 3 338
  • 正文 我和宋清朗相戀三年串前,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片实蔽。...
    茶點(diǎn)故事閱讀 40,133評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡荡碾,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出局装,到底是詐尸還是另有隱情狰晚,我是刑警寧澤弹灭,帶...
    沈念sama閱讀 35,815評(píng)論 5 346
  • 正文 年R本政府宣布驹暑,位于F島的核電站或衡,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏宣增。R本人自食惡果不足惜玫膀,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望统舀。 院中可真熱鬧匆骗,春花似錦、人聲如沸誉简。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,022評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)闷串。三九已至瓮钥,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背碉熄。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,147評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工桨武, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人锈津。 一個(gè)月前我還...
    沈念sama閱讀 48,398評(píng)論 3 373
  • 正文 我出身青樓呀酸,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親琼梆。 傳聞我的和親對(duì)象是個(gè)殘疾皇子性誉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容