|
@@ -8,6 +8,7 @@
|
|
|
|
|
|
Navigation::~Navigation()
|
|
|
{
|
|
|
+ exit_=true;
|
|
|
if(terminator_)
|
|
|
delete terminator_;
|
|
|
if(monitor_)
|
|
@@ -18,6 +19,22 @@ Navigation::~Navigation()
|
|
|
thread_->join();
|
|
|
delete thread_;
|
|
|
}
|
|
|
+ if(pubthread_!= nullptr)
|
|
|
+ {
|
|
|
+ if(pubthread_->joinable())
|
|
|
+ pubthread_->join();
|
|
|
+ delete pubthread_;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * 高斯函数压缩,x=3
|
|
|
+ */
|
|
|
+double limit(double x,double min,double max){
|
|
|
+ double r=x>=0?1.0:-1.0;
|
|
|
+ double delta=max/3.0;
|
|
|
+ return (max-(max-min)*exp(-x*x/(delta*delta)))*r;
|
|
|
+
|
|
|
}
|
|
|
|
|
|
bool Navigation::Init(const Navigation_parameter& parameter)
|
|
@@ -44,58 +61,85 @@ bool Navigation::Init(const Navigation_parameter& parameter)
|
|
|
}
|
|
|
}
|
|
|
inited_=true;
|
|
|
+
|
|
|
+ if(pubthread_!= nullptr)
|
|
|
+ {
|
|
|
+ exit_=true;
|
|
|
+ if(pubthread_->joinable())
|
|
|
+ pubthread_->join();
|
|
|
+ delete pubthread_;
|
|
|
+ }
|
|
|
+ exit_=false;
|
|
|
+ pubthread_=new std::thread(&Navigation::publish_statu,this);
|
|
|
+
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
-void Navigation::publish_statu(ActionType type,float velocity,float angular)
|
|
|
+void Navigation::publish_statu()
|
|
|
{
|
|
|
- NavStatu statu;
|
|
|
- if(running_)
|
|
|
- {
|
|
|
- statu.set_action_type(type);
|
|
|
- statu.set_isrunning(running_);
|
|
|
- std::queue<Trajectory> trajectorys_=global_trajectorys_;
|
|
|
- while(trajectorys_.empty()==false)
|
|
|
- {
|
|
|
- Trajectory traj=trajectorys_.front();
|
|
|
- TrajectoryMsg* trajMsg=statu.add_trajs();
|
|
|
- for(int i=0;i<traj.size();++i)
|
|
|
- {
|
|
|
- Pose2dMsg* pose=trajMsg->add_points();
|
|
|
- pose->set_x(traj[i].x());
|
|
|
- pose->set_y(traj[i].y());
|
|
|
- pose->set_theta(traj[i].theta());
|
|
|
+ while(exit_==false) {
|
|
|
+ std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
|
|
+ NavMessage::NavStatu statu;
|
|
|
+ statu.set_statu(eReady); //默认ready
|
|
|
+ NavMessage::Action *current_action = nullptr;
|
|
|
+ if (running_) {
|
|
|
+ statu.set_statu(eRunning);
|
|
|
+ statu.set_key(global_navCmd_.key());
|
|
|
+ std::queue<NavMessage::Action> actios = unfinished_cations_;
|
|
|
+ while (actios.empty() == false) {
|
|
|
+ //保存当前动作
|
|
|
+ NavMessage::Action *act = statu.add_unfinished_actions();
|
|
|
+ current_action = new NavMessage::Action(*act);
|
|
|
+
|
|
|
+ act->CopyFrom(actios.front());
|
|
|
+ actios.pop();
|
|
|
+
|
|
|
}
|
|
|
- trajectorys_.pop();
|
|
|
- }
|
|
|
- statu.set_velocity(velocity);
|
|
|
- statu.set_angular(angular);
|
|
|
- if(type==eMPC)
|
|
|
- {
|
|
|
- //发布mpc 预选点
|
|
|
- if(selected_traj_.timeout()==false) {
|
|
|
- for (int i = 0; i < selected_traj_.Get().size(); ++i){
|
|
|
- Pose2d pt=selected_traj_.Get()[i];
|
|
|
- Pose2dMsg* pose=statu.mutable_select_traj()->add_points();
|
|
|
- pose->set_x(pt.x());
|
|
|
- pose->set_y(pt.y());
|
|
|
- pose->set_theta(pt.theta());
|
|
|
- }
|
|
|
+
|
|
|
+ statu.set_pose_timeout(true); //默认设置位姿超时
|
|
|
+ statu.set_twist_timeout(true);
|
|
|
+ NavMessage::AGVStatu agvStatu;
|
|
|
+ if (timedPose_.timeout() == false) {
|
|
|
+ statu.set_pose_timeout(false);
|
|
|
+ Pose2d pose = timedPose_.Get();
|
|
|
+ agvStatu.set_x(pose.x());
|
|
|
+ agvStatu.set_y(pose.y());
|
|
|
+ agvStatu.set_theta(pose.theta());
|
|
|
+ }
|
|
|
+ if (timedV_.timeout() == false && timedA_.timeout() == false) {
|
|
|
+ statu.set_twist_timeout(false);
|
|
|
+ agvStatu.set_v(timedV_.Get());
|
|
|
+ agvStatu.set_vth(timedA_.Get());
|
|
|
}
|
|
|
- //发布 mpc 预测点
|
|
|
- if(predict_traj_.timeout()==false) {
|
|
|
- for (int i = 0; i < predict_traj_.Get().size(); ++i){
|
|
|
- Pose2d pt=predict_traj_.Get()[i];
|
|
|
- Pose2dMsg* pose=statu.mutable_predict_traj()->add_points();
|
|
|
- pose->set_x(pt.x());
|
|
|
- pose->set_y(pt.y());
|
|
|
- pose->set_theta(pt.theta());
|
|
|
+ statu.mutable_current_pose()->CopyFrom(agvStatu);
|
|
|
+
|
|
|
+ if (current_action != nullptr) {
|
|
|
+ delete current_action;
|
|
|
+ //发布mpc 预选点
|
|
|
+ if (selected_traj_.timeout() == false) {
|
|
|
+ for (int i = 0; i < selected_traj_.Get().size(); ++i) {
|
|
|
+ Pose2d pt = selected_traj_.Get()[i];
|
|
|
+ NavMessage::Pose2d *pose = statu.mutable_selected_traj()->add_poses();
|
|
|
+ pose->set_x(pt.x());
|
|
|
+ pose->set_y(pt.y());
|
|
|
+ pose->set_theta(pt.theta());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ //发布 mpc 预测点
|
|
|
+ if (predict_traj_.timeout() == false) {
|
|
|
+ for (int i = 0; i < predict_traj_.Get().size(); ++i) {
|
|
|
+ Pose2d pt = predict_traj_.Get()[i];
|
|
|
+ NavMessage::Pose2d *pose = statu.mutable_predict_traj()->add_poses();
|
|
|
+ pose->set_x(pt.x());
|
|
|
+ pose->set_y(pt.y());
|
|
|
+ pose->set_theta(pt.theta());
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+ terminator_->publish_nav_statu(statu);
|
|
|
}
|
|
|
- terminator_->publish_nav_statu(statu);
|
|
|
}
|
|
|
|
|
|
void Navigation::ResetStatu(double v,double a)
|
|
@@ -109,9 +153,9 @@ void Navigation::ResetPose(const Pose2d& pose)
|
|
|
timedPose_.reset(pose,0.5);
|
|
|
}
|
|
|
|
|
|
-bool Navigation::Start(std::queue<Trajectory> global_trajectorys)
|
|
|
+bool Navigation::Start(const NavMessage::NavCmd& cmd)
|
|
|
{
|
|
|
- if(running_==true)
|
|
|
+ if(unfinished_cations_.empty()==false) //正在运行中,上一次指令未完成
|
|
|
{
|
|
|
if(pause_)
|
|
|
{
|
|
@@ -129,7 +173,9 @@ bool Navigation::Start(std::queue<Trajectory> global_trajectorys)
|
|
|
}
|
|
|
|
|
|
//add 先检查当前点与起点的距离
|
|
|
- global_trajectorys_=global_trajectorys;
|
|
|
+ global_navCmd_=cmd;
|
|
|
+ for(int i=0;i<cmd.actions_size();++i)
|
|
|
+ unfinished_cations_.push(cmd.actions(i));
|
|
|
thread_=new std::thread(&Navigation::navigatting,this);
|
|
|
return true;
|
|
|
|
|
@@ -153,7 +199,7 @@ Navigation::Navigation()
|
|
|
}
|
|
|
|
|
|
|
|
|
-void Navigation::NavCmdCallback(const NavCmd& cmd,void* context)
|
|
|
+void Navigation::NavCmdCallback(const NavMessage::NavCmd& cmd,void* context)
|
|
|
{
|
|
|
Navigation* navigator=(Navigation*)context;
|
|
|
|
|
@@ -171,19 +217,7 @@ void Navigation::NavCmdCallback(const NavCmd& cmd,void* context)
|
|
|
return ;
|
|
|
}
|
|
|
|
|
|
- std::queue<Trajectory> trajs;
|
|
|
- for(int i=0;i<cmd.trajs_size();++i)
|
|
|
- {
|
|
|
- Trajectory traj;
|
|
|
- for(int j=0;j<cmd.trajs(i).points_size();++j)
|
|
|
- {
|
|
|
- Pose2dMsg pt_msg=cmd.trajs(i).points(j);
|
|
|
- Pose2d pt2d(pt_msg.x(),pt_msg.y(),pt_msg.theta());
|
|
|
- traj.push_point(pt2d);
|
|
|
- }
|
|
|
- trajs.push(traj);
|
|
|
- }
|
|
|
- navigator->Start(trajs);
|
|
|
+ navigator->Start(cmd);
|
|
|
}
|
|
|
void Navigation::RobotStatuCallback(double x,double y,double theta,double v,double vth,void* context)
|
|
|
{
|
|
@@ -192,114 +226,76 @@ void Navigation::RobotStatuCallback(double x,double y,double theta,double v,doub
|
|
|
navigator->ResetStatu(v,vth);
|
|
|
}
|
|
|
|
|
|
-bool Navigation::moveToPoint(const Pose2d& pose,const Pose2d& distance)
|
|
|
+bool Navigation::exec_adjust_action(const Pose2d& target,const Pose2d& target_diff)
|
|
|
{
|
|
|
if(inited_==false) {
|
|
|
printf(" navigation has not inited\n");
|
|
|
return false;
|
|
|
}
|
|
|
- Pose2d thresh=Pose2d::abs(distance);
|
|
|
- bool yCompleted=false;
|
|
|
- bool xCompleted=false;
|
|
|
- bool aCompleted=false;
|
|
|
- while(cancel_==false)
|
|
|
- {
|
|
|
+
|
|
|
+ Pose2d thresh=Pose2d::abs(target_diff);
|
|
|
+
|
|
|
+ int action=0; //1 原地旋转,2横移,3前进
|
|
|
+ while(cancel_==false) {
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
|
|
- if (pause_ == true)
|
|
|
- {
|
|
|
+ if (pause_ == true) {
|
|
|
//发送暂停消息
|
|
|
continue;
|
|
|
}
|
|
|
- if (timedPose_.timeout() == true)
|
|
|
- {
|
|
|
- printf(" navigation Error:Pose is timeout \n");
|
|
|
+ if (timedPose_.timeout() || timedV_.timeout() || timedA_.timeout()) {
|
|
|
+ printf(" navigation Error:Pose/v/a is timeout \n");
|
|
|
break;
|
|
|
}
|
|
|
-
|
|
|
- //计算目标pose在当前pose下的pose
|
|
|
- Pose2d diff = Pose2d::PoseByPose(pose,timedPose_.Get());
|
|
|
- if (Pose2d::abs(diff).y() < thresh.y())
|
|
|
- yCompleted=true;
|
|
|
- else if(Pose2d::abs(diff).y()>2.0*thresh.y())
|
|
|
- yCompleted=false;
|
|
|
-
|
|
|
- if (Pose2d::abs(diff).x() < thresh.x())
|
|
|
- xCompleted=true;
|
|
|
- else if(Pose2d::abs(diff).x() >2.0* thresh.x())
|
|
|
- xCompleted=false;
|
|
|
-
|
|
|
- if (Pose2d::abs(diff).theta() < thresh.theta())
|
|
|
- aCompleted=true;
|
|
|
- else
|
|
|
- aCompleted=false;
|
|
|
-
|
|
|
- if(yCompleted&&xCompleted&&aCompleted)
|
|
|
- {
|
|
|
+ //判断是否到达目标点
|
|
|
+ if (IsArrived(timedPose_.Get(), timedV_.Get(), timedA_.Get(), target, target_diff)) {
|
|
|
monitor_->stop();
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
- //先旋转使得Y=0摆直,再移动x,再X最后旋转
|
|
|
- if(yCompleted==false)
|
|
|
- {
|
|
|
- double theta=atan(diff.y()/diff.x());
|
|
|
-
|
|
|
- double maxtheta=20.*M_PI/180.0;
|
|
|
- double mintheta=5.*M_PI/180.0;
|
|
|
- if(abs(theta)<mintheta)
|
|
|
- {
|
|
|
- theta=(theta>0)?mintheta:-mintheta;
|
|
|
- }
|
|
|
- if(abs(theta)>maxtheta)
|
|
|
- {
|
|
|
- theta=(theta>0)?maxtheta:-maxtheta;
|
|
|
+ //计算目标点在当前pose下的pose
|
|
|
+ Pose2d diff = Pose2d::PoseByPose(target, timedPose_.Get());
|
|
|
+
|
|
|
+ //停止状态
|
|
|
+ if (action == 0) {
|
|
|
+ //优先进入旋转
|
|
|
+ if (Pose2d::abs(diff).theta() > thresh.theta()) {
|
|
|
+ action = 1; //原地旋转
|
|
|
+ } else if (Pose2d::abs(diff).y() > thresh.y()) {
|
|
|
+ //横移
|
|
|
+ action = 2;
|
|
|
+ } else if (Pose2d::abs(diff).x() > thresh.x()) {
|
|
|
+ //前进
|
|
|
+ action = 3;
|
|
|
}
|
|
|
- publish_statu(eRotate,0,theta);
|
|
|
- monitor_->rotate(theta);
|
|
|
- printf("旋转 diff.y:%.5f\n",diff.y());
|
|
|
- continue;
|
|
|
-
|
|
|
}
|
|
|
- if(xCompleted==false){
|
|
|
- double v=diff.x()/10.0;
|
|
|
- double maxv=1.9;
|
|
|
- double minv=0.1;
|
|
|
- if(abs(v)<minv)
|
|
|
- {
|
|
|
- v=(v>0)?minv:-minv;
|
|
|
- }
|
|
|
- if(abs(v)>maxv)
|
|
|
- {
|
|
|
- v=(v>0)?maxv:-maxv;
|
|
|
+ else
|
|
|
+ {
|
|
|
+ if (action == 1) {
|
|
|
+ if (Pose2d::abs(diff).theta() > thresh.theta()) {
|
|
|
+ double theta = limit(diff.theta(),5*M_PI/180.0,40*M_PI/180.0);
|
|
|
+ monitor_->set_speed(Monitor_emqx::eRotate, 0, theta);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
}
|
|
|
- publish_statu(eLine,v,0);
|
|
|
- monitor_->set_speed(v,0);
|
|
|
- printf("x移动 diff.x():%.5f\n",diff.x());
|
|
|
- continue;
|
|
|
- }
|
|
|
- if(aCompleted==false){
|
|
|
- double theta=diff.theta()/5.0;
|
|
|
- if(std::abs(diff.theta())>M_PI)
|
|
|
- theta=-theta;
|
|
|
- double maxtheta=20.*M_PI/180.0;
|
|
|
- double mintheta=5.*M_PI/180.0;
|
|
|
- if(abs(theta)<mintheta)
|
|
|
- {
|
|
|
- theta=(theta>0)?mintheta:-mintheta;
|
|
|
+ if (action == 2) {
|
|
|
+ if (Pose2d::abs(diff).y() > thresh.y()) {
|
|
|
+ monitor_->set_speed(Monitor_emqx::eHorizontal, limit(diff.y(),0.05,0.2), 0);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
}
|
|
|
- if(abs(theta)>maxtheta)
|
|
|
- {
|
|
|
- theta=(theta>0)?maxtheta:-maxtheta;
|
|
|
+ if (action == 3) {
|
|
|
+ if (Pose2d::abs(diff).x() > thresh.x()) {
|
|
|
+ monitor_->set_speed(Monitor_emqx::eHorizontal, limit(diff.x(),0.05,0.5), 0);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
}
|
|
|
- publish_statu(eRotate,0,theta);
|
|
|
- monitor_->rotate(theta);
|
|
|
- printf("旋转angular:%.5f diff.theta():%.5f\n",theta,diff.theta());
|
|
|
+ monitor_->stop();
|
|
|
+ printf(" action :%d completed!!! reset action type!!!\n", action);
|
|
|
+ action = 0;
|
|
|
}
|
|
|
-
|
|
|
}
|
|
|
- if(cancel_==true)
|
|
|
- return false;
|
|
|
- return true;
|
|
|
+ return false;
|
|
|
+
|
|
|
}
|
|
|
|
|
|
bool Navigation::mpc_once(const Trajectory& traj,std::vector<double>& out)
|
|
@@ -336,30 +332,31 @@ bool Navigation::mpc_once(const Trajectory& traj,std::vector<double>& out)
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
-bool Navigation::IsArrived(const Pose2d& cur,double speed,const Pose2d& target,const Pose2d& diff)
|
|
|
+bool Navigation::IsArrived(const Pose2d& cur,double velocity,double angular,
|
|
|
+ const Pose2d& target,const Pose2d& diff)
|
|
|
{
|
|
|
if(Pose2d::abs(cur-target)<diff)
|
|
|
{
|
|
|
- if(fabs(speed)<0.1)
|
|
|
+ if(fabs(velocity)<0.1 && angular< 10*M_PI/180.0)
|
|
|
return true;
|
|
|
}
|
|
|
return false;
|
|
|
}
|
|
|
-
|
|
|
-bool Navigation::execute_edge(const Trajectory& child,const Pose2d& head_diff,const Pose2d& target_diff)
|
|
|
+bool Navigation::execute_along_action(const Pose2d& target,const Pose2d& target_diff)
|
|
|
{
|
|
|
if(inited_==false) {
|
|
|
printf(" navigation has not inited\n");
|
|
|
return false;
|
|
|
}
|
|
|
-
|
|
|
- if(child.size()==0)
|
|
|
+ if (timedPose_.timeout() ) {
|
|
|
+ printf(" navigation Error:Pose is timeout \n");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ //生成轨迹
|
|
|
+ Trajectory traj=Trajectory::create_line_trajectory(timedPose_.Get(),target);
|
|
|
+ if(traj.size()==0)
|
|
|
return true;
|
|
|
- Pose2d head=child[0];
|
|
|
- Pose2d tail=child[child.size()-1];
|
|
|
|
|
|
- Trajectory traj=child;
|
|
|
- bool init_start_pose=false;
|
|
|
while(cancel_==false)
|
|
|
{
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
|
@@ -368,31 +365,17 @@ bool Navigation::execute_edge(const Trajectory& child,const Pose2d& head_diff,co
|
|
|
//发送暂停消息
|
|
|
continue;
|
|
|
}
|
|
|
- if(timedPose_.timeout()==true)
|
|
|
- {
|
|
|
- printf(" navigation Error:Pose is timeout \n");
|
|
|
- break;
|
|
|
+ if (timedPose_.timeout() || timedV_.timeout() || timedA_.timeout()) {
|
|
|
+ printf(" navigation Error:Pose/v/a is timeout \n");
|
|
|
+ return false;
|
|
|
}
|
|
|
//判断是否到达终点
|
|
|
- Pose2d diff=Pose2d::abs(timedPose_.Get()-tail);
|
|
|
- if(IsArrived(timedPose_.Get(),timedV_.Get(),tail,target_diff))
|
|
|
+ if(IsArrived(timedPose_.Get(),timedV_.Get(),timedA_.Get(),target,target_diff))
|
|
|
{
|
|
|
monitor_->stop();
|
|
|
printf(" edge navigation completed !!!\n");
|
|
|
return true;
|
|
|
- }else if(diff.x()<target_diff.x() && diff.y()<target_diff.y()){
|
|
|
- monitor_->stop();
|
|
|
- moveToPoint(tail,target_diff);
|
|
|
- }
|
|
|
- //std::cout<<"diff:"<<Pose2d::abs(timedPose_.Get()-tail)<<std::endl;
|
|
|
-
|
|
|
- //如果与起点差距过大先运动到起点
|
|
|
- if(false==init_start_pose)
|
|
|
- {
|
|
|
- init_start_pose= moveToPoint(head, head_diff);
|
|
|
- continue;
|
|
|
}
|
|
|
- else{
|
|
|
//一次变速
|
|
|
std::vector<double> out;
|
|
|
bool ret;
|
|
@@ -404,11 +387,10 @@ bool Navigation::execute_edge(const Trajectory& child,const Pose2d& head_diff,co
|
|
|
monitor_->stop();
|
|
|
return false;
|
|
|
}
|
|
|
- publish_statu(eMPC,out[0],out[1]);
|
|
|
- monitor_->set_speed(out[0],out[1]);
|
|
|
+ monitor_->set_speed(Monitor_emqx::eMPC,out[0],out[1]);
|
|
|
}
|
|
|
|
|
|
- }
|
|
|
+
|
|
|
return false;
|
|
|
}
|
|
|
|
|
@@ -423,36 +405,37 @@ void Navigation::navigatting()
|
|
|
running_=true;
|
|
|
printf(" navigation beg...\n");
|
|
|
|
|
|
-#if 0
|
|
|
- Trajectory gtraj;
|
|
|
- while(global_trajectorys_.empty()==false)
|
|
|
- {
|
|
|
- gtraj=gtraj+global_trajectorys_.front();
|
|
|
- global_trajectorys_.pop();
|
|
|
- }
|
|
|
- execute_edge(gtraj);
|
|
|
-#else
|
|
|
- double v=0,a=0;
|
|
|
- Pose2d head_diff(0.1,0.1,3/180.0*M_PI);
|
|
|
- Pose2d target_diff(0.03,0.05,0.5/180.0*M_PI);
|
|
|
- while(global_trajectorys_.empty()==false && cancel_==false)
|
|
|
+ while(unfinished_cations_.empty()==false && cancel_==false)
|
|
|
{
|
|
|
- Trajectory traj=global_trajectorys_.front();
|
|
|
- if(execute_edge(traj,head_diff,target_diff))
|
|
|
+ NavMessage::Action act=unfinished_cations_.front();
|
|
|
+ Pose2d target(act.target().x(),act.target().y(),act.target().theta());
|
|
|
+ Pose2d target_diff(act.target_diff().x(),act.target_diff().y(),act.target_diff().theta());
|
|
|
+ if(act.type()==1)
|
|
|
+ {
|
|
|
+ if(exec_adjust_action(target,target_diff)==false)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if(act.type()==2)
|
|
|
{
|
|
|
- global_trajectorys_.pop();
|
|
|
+ if(execute_along_action(target,target_diff)==false)
|
|
|
+ break;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
+ printf(" unknow action type:%d\n",act.type());
|
|
|
break;
|
|
|
}
|
|
|
+ unfinished_cations_.pop();
|
|
|
}
|
|
|
-#endif
|
|
|
+
|
|
|
monitor_->stop();
|
|
|
- if(cancel_==true)
|
|
|
+ if(cancel_==true) {
|
|
|
printf(" navigation canceled\n");
|
|
|
- printf(" navigation end...\n");
|
|
|
- if(global_trajectorys_.size()==0)
|
|
|
- printf("navigation completed!!!\n");
|
|
|
+ while(unfinished_cations_.empty()==false)
|
|
|
+ unfinished_cations_.pop();
|
|
|
+ }else {
|
|
|
+ if (unfinished_cations_.empty())
|
|
|
+ printf("navigation completed!!!\n");
|
|
|
+ }
|
|
|
running_=false;
|
|
|
}
|