• Ethzasl MSF源码阅读(3):MSF_Core和PoseMeasurement


    1.MSF_Core的三个函数:ProcessIMU、ProcessExternallyPropagatedState和AddMeasurement

    MSF_Core维护了状态队列和观测值队列,这里需要结合论文思考这个状态队列的作用。

    ProcessIMU方法:

      1 template<typename EKFState_T>
      2 void MSF_Core<EKFState_T>::ProcessIMU(
      3     const msf_core::Vector3& linear_acceleration,
      4     const msf_core::Vector3& angular_velocity, const double& msg_stamp,
      5     size_t /*msg_seq*/) {
      6 
      7   if (!initialized_)
      8     return;
      9 
     10   msf_timing::DebugTimer timer_PropGetClosestState("PropGetClosestState");
     11   if (it_last_IMU == stateBuffer_.GetIteratorEnd()) {
     12     it_last_IMU = stateBuffer_.GetIteratorClosestBefore(msg_stamp);
     13   }
     14 
     15   shared_ptr<EKFState_T> lastState = it_last_IMU->second;
     16   timer_PropGetClosestState.Stop();
     17 
     18   msf_timing::DebugTimer timer_PropPrepare("PropPrepare");
     19   if (lastState->time == constants::INVALID_TIME) {
     20     MSF_WARN_STREAM_THROTTLE(
     21         2, __FUNCTION__<<"ImuCallback: closest state is invalid
    ");
     22     return;  // Early abort.
     23   }
     24 
     25   shared_ptr<EKFState_T> currentState(new EKFState_T);
     26   currentState->time = msg_stamp;
     27 
     28   // Check if this IMU message is really after the last one (caused by restarting
     29   // a bag file).
     30   if (currentState->time - lastState->time < -0.01 && predictionMade_) {
     31     initialized_ = false;
     32     predictionMade_ = false;
     33     MSF_ERROR_STREAM(
     34         __FUNCTION__<<"latest IMU message was out of order by a too large amount, "
     35         "resetting EKF: last-state-time: " << msf_core::timehuman(lastState->time)
     36         << " "<< "current-imu-time: "<< msf_core::timehuman(currentState->time));
     37     return;
     38   }
     39 
     40   static int seq = 0;
     41   // Get inputs.
     42   currentState->a_m = linear_acceleration;
     43   currentState->w_m = angular_velocity;
     44   currentState->noise_gyr = Vector3::Constant(usercalc_.GetParamNoiseGyr());
     45   currentState->noise_acc = Vector3::Constant(usercalc_.GetParamNoiseAcc());
     46 
     47 
     48   // Remove acc spikes (TODO (slynen): find a cleaner way to do this).
     49   static Eigen::Matrix<double, 3, 1> last_am =
     50       Eigen::Matrix<double, 3, 1>(0, 0, 0);
     51   if (currentState->a_m.norm() > 50)
     52     currentState->a_m = last_am;
     53   else {
     54     // Try to get the state before the current time.
     55     if (lastState->time == constants::INVALID_TIME) {
     56       MSF_WARN_STREAM(
     57           "Accelerometer readings had a spike, but no prior state was in the "
     58           "buffer to take cleaner measurements from");
     59       return;
     60     }
     61     last_am = lastState->a_m;
     62   }
     63   if (!predictionMade_) {
     64     if (lastState->time == constants::INVALID_TIME) {
     65       MSF_WARN_STREAM("Wanted to compare prediction time offset to last state, "
     66       "but no prior state was in the buffer to take cleaner measurements from");
     67       return;
     68     }
     69     if (fabs(currentState->time - lastState->time) > 0.1) {
     70       MSF_WARN_STREAM_THROTTLE(
     71           2, "large time-gap re-initializing to last state
    ");
     72       typename StateBuffer_T::Ptr_T tmp = stateBuffer_.UpdateTime(
     73           lastState->time, currentState->time);
     74       time_P_propagated = currentState->time;
     75       return;  // // early abort // // (if timegap too big)
     76     }
     77   }
     78 
     79   if (lastState->time == constants::INVALID_TIME) {
     80     MSF_WARN_STREAM(
     81         "Wanted to propagate state, but no valid prior state could be found in "
     82         "the buffer");
     83     return;
     84   }
     85   timer_PropPrepare.Stop();
     86 
     87   msf_timing::DebugTimer timer_PropState("PropState");
     88   //propagate state and covariance
     89   PropagateState(lastState, currentState);
     90   timer_PropState.Stop();
     91 
     92   msf_timing::DebugTimer timer_PropInsertState("PropInsertState");
     93   it_last_IMU = stateBuffer_.Insert(currentState);
     94   timer_PropInsertState.Stop();
     95 
     96   msf_timing::DebugTimer timer_PropCov("PropCov");
     97   PropagatePOneStep();
     98   timer_PropCov.Stop();
     99   usercalc_.PublishStateAfterPropagation(currentState);
    100 
    101   // Making sure we have sufficient states to apply measurements to.
    102   if (stateBuffer_.Size() > 3)
    103     predictionMade_ = true;
    104 
    105   if (predictionMade_) {
    106     // Check if we can apply some pending measurement.
    107     HandlePendingMeasurements();
    108   }
    109   seq++;
    110 
    111 }
    ProcessIMU

    ProcessExternallyPropagatedState方法:

      1 template<typename EKFState_T>
      2 void MSF_Core<EKFState_T>::ProcessExternallyPropagatedState(
      3     const msf_core::Vector3& linear_acceleration,
      4     const msf_core::Vector3& angular_velocity, const msf_core::Vector3& p,
      5     const msf_core::Vector3& v, const msf_core::Quaternion& q,
      6     bool is_already_propagated, const double& msg_stamp, size_t /*msg_seq*/) {
      7 
      8   if (!initialized_)
      9     return;
     10 
     11   // fast method to get last_IMU is broken
     12   // TODO(slynen): fix iterator setting for state callback
     13 
     14 //  // Get the closest state and check validity.
     15 //  if (it_last_IMU == stateBuffer_.getIteratorEnd()) {
     16 //    it_last_IMU = stateBuffer_.getIteratorClosestBefore(msg_stamp);
     17 //    assert(!(it_last_IMU == stateBuffer_.getIteratorEnd()));
     18 //  }
     19 
     20   // TODO(slynen): not broken, revert back when it_last_IMU->second from above is fixed
     21   shared_ptr<EKFState_T> lastState = stateBuffer_.GetLast();//it_last_IMU->second;
     22   if (lastState->time == constants::INVALID_TIME) {
     23     MSF_WARN_STREAM_THROTTLE(2, "StateCallback: closest state is invalid
    ");
     24     return;  // Early abort.
     25   }
     26 
     27   // Create a new state.
     28   shared_ptr<EKFState_T> currentState(new EKFState_T);
     29   currentState->time = msg_stamp;
     30 
     31   // Get inputs.
     32   currentState->a_m = linear_acceleration;
     33   currentState->w_m = angular_velocity;
     34   currentState->noise_gyr = Vector3::Constant(usercalc_.GetParamNoiseGyr());
     35   currentState->noise_acc = Vector3::Constant(usercalc_.GetParamNoiseAcc());
     36 
     37   // Remove acc spikes (TODO (slynen): Find a cleaner way to do this).
     38   static Eigen::Matrix<double, 3, 1> last_am =
     39       Eigen::Matrix<double, 3, 1>(0, 0, 0);
     40   if (currentState->a_m.norm() > 50)
     41     currentState->a_m = last_am;
     42   else
     43     last_am = currentState->a_m;
     44 
     45   if (!predictionMade_) {
     46     if (fabs(currentState->time - lastState->time) > 5) {
     47       typename StateBuffer_T::Ptr_T tmp = stateBuffer_.UpdateTime(
     48           lastState->time, currentState->time);
     49       MSF_WARN_STREAM_THROTTLE(
     50           2, "large time-gap re-initializing to last state: "
     51           << msf_core::timehuman(tmp->time));
     52       return;  // Early abort (if timegap too big).
     53     }
     54   }
     55 
     56   bool isnumeric = CheckForNumeric(
     57       currentState->template Get<StateDefinition_T::p>(),
     58       "before prediction p");
     59 
     60   // State propagation is made externally, so we read the actual state.
     61   if (is_already_propagated && isnumeric) {
     62     currentState->template Get<StateDefinition_T::p>() = p;
     63     currentState->template Get<StateDefinition_T::v>() = v;
     64     currentState->template Get<StateDefinition_T::q>() = q;
     65 
     66     // Zero props: copy non propagation states from last state.
     67     boost::fusion::for_each(
     68         currentState->statevars,
     69         msf_tmp::CopyNonPropagationStates<EKFState_T>(*lastState));
     70 
     71   } else if (!is_already_propagated || !isnumeric) {
     72     // Otherwise let's do the state prop. here.
     73     PropagateState(lastState, currentState);
     74   }
     75 
     76   // Clean reset of state and measurement buffer, before we start propagation.
     77   if (!predictionMade_) {
     78 
     79     // Make sure we keep the covariance for the first state.
     80     currentState->P = stateBuffer_.GetLast()->P;
     81     time_P_propagated = currentState->time;
     82 
     83     stateBuffer_.Clear();
     84     MeasurementBuffer_.Clear();
     85     while (!queueFutureMeasurements_.empty()) {
     86       queueFutureMeasurements_.pop();
     87     }
     88   }
     89 
     90   stateBuffer_.Insert(currentState);
     91   PropagatePOneStep();
     92   predictionMade_ = true;
     93   usercalc_.PublishStateAfterPropagation(currentState);
     94 
     95   isnumeric = CheckForNumeric(
     96       currentState->template Get<StateDefinition_T::p>(), "prediction p");
     97   isnumeric = CheckForNumeric(currentState->P, "prediction done P");
     98 
     99   // Check if we can apply some pending measurement.
    100   HandlePendingMeasurements();
    101 }
    ProcessExternallyPropagatedState

    AddMeasurement方法:

      1 template<typename EKFState_T>
      2 void MSF_Core<EKFState_T>::AddMeasurement(
      3     shared_ptr<MSF_MeasurementBase<EKFState_T> > measurement) {
      4 
      5   // Return if not initialized of no imu data available.
      6   if (!initialized_ || !predictionMade_)
      7     return;
      8 
      9   // Check if the measurement is in the future where we don't have imu
     10   // measurements yet.
     11   if (measurement->time > stateBuffer_.GetLast()->time) {
     12     queueFutureMeasurements_.push(measurement);
     13     return;
     14 
     15   }
     16   // Check if there is still a state in the buffer for this message (too old).
     17   if (measurement->time < stateBuffer_.GetFirst()->time) {
     18     MSF_WARN_STREAM(
     19         "You tried to give me a measurement which is too far in the past. Are "
     20         "you sure your clocks are synced and delays compensated correctly? "
     21         "[measurement: "<<timehuman(measurement->time)<<" (s) first state in "
     22             "buffer: "<<timehuman(stateBuffer_.GetFirst()->time)<<" (s)]");
     23     return;  // Reject measurements too far in the past.
     24   }
     25 
     26   // Add this measurement to the buffer and get an iterator to it.
     27   typename measurementBufferT::iterator_T it_meas =
     28       MeasurementBuffer_.Insert(measurement);
     29   // Get an iterator the the end of the measurement buffer.
     30   typename measurementBufferT::iterator_T it_meas_end = MeasurementBuffer_
     31       .GetIteratorEnd();
     32 
     33   // No propagation if no update is applied.
     34   typename StateBuffer_T::iterator_T it_curr = stateBuffer_.GetIteratorEnd();
     35 
     36   bool appliedOne = false;
     37 
     38   isfuzzyState_ = false;
     39 
     40   // Now go through all the measurements and apply them one by one.
     41   for (; it_meas != it_meas_end; ++it_meas) {
     42 
     43     if (it_meas->second->time <= 0)  // Valid?
     44       continue;
     45     msf_timing::DebugTimer timer_meas_get_state("Get state for measurement");
     46     // Propagates covariance to state.
     47     shared_ptr<EKFState_T> state = GetClosestState(it_meas->second->time);
     48     timer_meas_get_state.Stop();
     49     if (state->time <= 0) {
     50       MSF_ERROR_STREAM_THROTTLE(
     51           1, __FUNCTION__<< " GetClosestState returned an invalid state");
     52       continue;
     53     }
     54 
     55     msf_timing::DebugTimer timer_meas_apply("Apply measurement");
     56     // Calls back core::ApplyCorrection(), which sets time_P_propagated to meas
     57     // time.
     58     it_meas->second->Apply(state, *this);
     59     timer_meas_apply.Stop();
     60     // Make sure to propagate to next measurement or up to now if no more
     61     // measurements. Propagate from current state.
     62     it_curr = stateBuffer_.GetIteratorAtValue(state);
     63 
     64     typename StateBuffer_T::iterator_T it_end;
     65     typename measurementBufferT::iterator_T it_nextmeas = it_meas;
     66     ++it_nextmeas;  // The next measurement in the list.
     67     // That was the last measurement, so propagate state to now.
     68     if (it_nextmeas == it_meas_end) {
     69       it_end = stateBuffer_.GetIteratorEnd();
     70     } else {
     71       it_end = stateBuffer_.GetIteratorClosestAfter(it_nextmeas->second->time);
     72       if (it_end != stateBuffer_.GetIteratorEnd()) {
     73         // Propagate to state closest after the measurement so we can interpolate.
     74         ++it_end;
     75       }
     76     }
     77 
     78     typename StateBuffer_T::iterator_T it_next = it_curr;
     79     ++it_next;
     80 
     81     msf_timing::DebugTimer timer_prop_state_after_meas(
     82         "Repropagate state to now");
     83     // Propagate to selected state.
     84     for (; it_curr != it_end && it_next != it_end &&
     85            it_curr->second->time != constants::INVALID_TIME &&
     86            it_next->second->time != constants::INVALID_TIME; ++it_curr,
     87            ++it_next) {
     88       if (it_curr->second == it_next->second) {
     89         MSF_ERROR_STREAM(__FUNCTION__<< " propagation : it_curr points to same "
     90         "state as it_next. This must not happen.");
     91         continue;
     92       }
     93       // Break loop if EKF reset in the meantime.
     94       if (!initialized_ || !predictionMade_)
     95         return;
     96       PropagateState(it_curr->second, it_next->second);
     97     }
     98     timer_prop_state_after_meas.Stop();
     99     appliedOne = true;
    100   }
    101 
    102   if (!appliedOne) {
    103     MSF_ERROR_STREAM("No measurement was applied, this should not happen.");
    104     return;
    105   }
    106 
    107   // Now publish the best current estimate.
    108   shared_ptr<EKFState_T>& latestState = stateBuffer_.GetLast();
    109 
    110   PropPToState(latestState);  // Get the latest covariance.
    111 
    112   usercalc_.PublishStateAfterUpdate(latestState);
    113 }
    AddMeasurement

    注意AddMeasurement方法中的 it_meas->second->Apply(state, *this); 这句代码,调用了PoseMeasurement的Apply方法。注意Apply方法要求*this参数,将MSF_Core作为参数传入,这在Apply执行过程中有一个调用core.ApplyCorrection()的步骤。

    2.PoseMeasurement继承自PoseMeasurementBase,即msf_core::MSF_Measurement,这个又继承自MSF_MeasurementBase<EKFState_T>。

    1 template<typename T, typename RMAT_T, typename EKFState_T>
    2 class MSF_Measurement : public MSF_MeasurementBase<EKFState_T>
    1 typedef msf_core::MSF_Measurement<geometry_msgs::PoseWithCovarianceStamped,
    2     Eigen::Matrix<double, nMeasurements, nMeasurements>, msf_updates::EKFState> PoseMeasurementBase;
    struct PoseMeasurement : public PoseMeasurementBase 
    

     查看PoseMeasurement类Apply方法的代码:

      1 virtual void Apply(shared_ptr<EKFState_T> state_nonconst_new,
      2                      msf_core::MSF_Core<EKFState_T>& core) {
      3 
      4     if (isabsolute_) {  // Does this measurement refer to an absolute measurement,
      5       // or is is just relative to the last measurement.
      6       // Get a const ref, so we can read core states
      7       const EKFState_T& state = *state_nonconst_new;
      8       // init variables
      9       Eigen::Matrix<double, nMeasurements,
     10           msf_core::MSF_Core<EKFState_T>::nErrorStatesAtCompileTime> H_new;
     11       Eigen::Matrix<double, nMeasurements, 1> r_old;
     12 
     13       CalculateH(state_nonconst_new, H_new);
     14 
     15       // Get rotation matrices.
     16       Eigen::Matrix<double, 3, 3> C_wv = state.Get<StateQwvIdx>()
     17 
     18           .conjugate().toRotationMatrix();
     19       Eigen::Matrix<double, 3, 3> C_q = state.Get<StateDefinition_T::q>()
     20           .conjugate().toRotationMatrix();
     21 
     22       // Construct residuals.
     23       // Position.
     24       r_old.block<3, 1>(0, 0) = z_p_
     25           - (C_wv.transpose()
     26               * (-state.Get<StatePwvIdx>()
     27                   + state.Get<StateDefinition_T::p>()
     28                   + C_q.transpose() * state.Get<StatePicIdx>()))
     29               * state.Get<StateLIdx>();
     30 
     31       // Attitude.
     32       Eigen::Quaternion<double> q_err;
     33       q_err = (state.Get<StateQwvIdx>()
     34           * state.Get<StateDefinition_T::q>()
     35           * state.Get<StateQicIdx>()).conjugate() * z_q_;
     36       r_old.block<3, 1>(3, 0) = q_err.vec() / q_err.w() * 2;
     37       // Vision world yaw drift.
     38       q_err = state.Get<StateQwvIdx>();
     39 
     40       r_old(6, 0) = -2 * (q_err.w() * q_err.z() + q_err.x() * q_err.y())
     41           / (1 - 2 * (q_err.y() * q_err.y() + q_err.z() * q_err.z()));
     42 
     43       if (!CheckForNumeric(r_old, "r_old")) {
     44         MSF_ERROR_STREAM("r_old: "<<r_old);
     45         MSF_WARN_STREAM(
     46             "state: "<<const_cast<EKFState_T&>(state). ToEigenVector().transpose());
     47       }
     48       if (!CheckForNumeric(H_new, "H_old")) {
     49         MSF_ERROR_STREAM("H_old: "<<H_new);
     50         MSF_WARN_STREAM(
     51             "state: "<<const_cast<EKFState_T&>(state). ToEigenVector().transpose());
     52       }
     53       if (!CheckForNumeric(R_, "R_")) {
     54         MSF_ERROR_STREAM("R_: "<<R_);
     55         MSF_WARN_STREAM(
     56             "state: "<<const_cast<EKFState_T&>(state). ToEigenVector().transpose());
     57       }
     58 
     59       // Call update step in base class.
     60       this->CalculateAndApplyCorrection(state_nonconst_new, core, H_new, r_old,
     61                                         R_);
     62     } else {
     63       // Init variables: Get previous measurement.
     64       shared_ptr < msf_core::MSF_MeasurementBase<EKFState_T> > prevmeas_base =
     65           core.GetPreviousMeasurement(this->time, this->sensorID_);
     66 
     67       if (prevmeas_base->time == msf_core::constants::INVALID_TIME) {
     68         MSF_WARN_STREAM(
     69             "The previous measurement is invalid. Could not apply measurement! " "time:"<<this->time<<" sensorID: "<<this->sensorID_);
     70         return;
     71       }
     72 
     73       // Make this a pose measurement.
     74       shared_ptr<PoseMeasurement> prevmeas = dynamic_pointer_cast
     75           < PoseMeasurement > (prevmeas_base);
     76       if (!prevmeas) {
     77         MSF_WARN_STREAM(
     78             "The dynamic cast of the previous measurement has failed. "
     79             "Could not apply measurement");
     80         return;
     81       }
     82 
     83       // Get state at previous measurement.
     84       shared_ptr<EKFState_T> state_nonconst_old = core.GetClosestState(
     85           prevmeas->time);
     86 
     87       if (state_nonconst_old->time == msf_core::constants::INVALID_TIME) {
     88         MSF_WARN_STREAM(
     89             "The state at the previous measurement is invalid. Could "
     90             "not apply measurement");
     91         return;
     92       }
     93 
     94       // Get a const ref, so we can read core states.
     95       const EKFState_T& state_new = *state_nonconst_new;
     96       const EKFState_T& state_old = *state_nonconst_old;
     97 
     98       Eigen::Matrix<double, nMeasurements,
     99           msf_core::MSF_Core<EKFState_T>::nErrorStatesAtCompileTime> H_new,
    100           H_old;
    101       Eigen::Matrix<double, nMeasurements, 1> r_new, r_old;
    102 
    103       CalculateH(state_nonconst_old, H_old);
    104 
    105       H_old *= -1;
    106 
    107       CalculateH(state_nonconst_new, H_new);
    108 
    109       //TODO (slynen): check that both measurements have the same states fixed!
    110       Eigen::Matrix<double, 3, 3> C_wv_old, C_wv_new;
    111       Eigen::Matrix<double, 3, 3> C_q_old, C_q_new;
    112 
    113       C_wv_new = state_new.Get<StateQwvIdx>().conjugate().toRotationMatrix();
    114       C_q_new = state_new.Get<StateDefinition_T::q>().conjugate()
    115           .toRotationMatrix();
    116 
    117       C_wv_old = state_old.Get<StateQwvIdx>().conjugate().toRotationMatrix();
    118       C_q_old = state_old.Get<StateDefinition_T::q>().conjugate()
    119           .toRotationMatrix();
    120 
    121       // Construct residuals.
    122       // Position:
    123       Eigen::Matrix<double, 3, 1> diffprobpos = (C_wv_new.transpose()
    124           * (-state_new.Get<StatePwvIdx>() + state_new.Get<StateDefinition_T::p>()
    125               + C_q_new.transpose() * state_new.Get<StatePicIdx>()))
    126           * state_new.Get<StateLIdx>() - (C_wv_old.transpose()
    127           * (-state_old.Get<StatePwvIdx>() + state_old.Get<StateDefinition_T::p>()
    128               + C_q_old.transpose() * state_old.Get<StatePicIdx>()))
    129               * state_old.Get<StateLIdx>();
    130 
    131 
    132       Eigen::Matrix<double, 3, 1> diffmeaspos = z_p_ - prevmeas->z_p_;
    133 
    134       r_new.block<3, 1>(0, 0) = diffmeaspos - diffprobpos;
    135 
    136       // Attitude:
    137       Eigen::Quaternion<double> diffprobatt = (state_new.Get<StateQwvIdx>()
    138           * state_new.Get<StateDefinition_T::q>()
    139           * state_new.Get<StateQicIdx>()).conjugate()
    140           * (state_old.Get<StateQwvIdx>()
    141               * state_old.Get<StateDefinition_T::q>()
    142               * state_old.Get<StateQicIdx>());
    143 
    144       Eigen::Quaternion<double> diffmeasatt = z_q_.conjugate() * prevmeas->z_q_;
    145 
    146       Eigen::Quaternion<double> q_err;
    147       q_err = diffprobatt.conjugate() * diffmeasatt;
    148 
    149       r_new.block<3, 1>(3, 0) = q_err.vec() / q_err.w() * 2;
    150       // Vision world yaw drift.
    151       q_err = state_new.Get<StateQwvIdx>();
    152 
    153       r_new(6, 0) = -2 * (q_err.w() * q_err.z() + q_err.x() * q_err.y())
    154           / (1 - 2 * (q_err.y() * q_err.y() + q_err.z() * q_err.z()));
    155 
    156       if (!CheckForNumeric(r_old, "r_old")) {
    157         MSF_ERROR_STREAM("r_old: "<<r_old);
    158         MSF_WARN_STREAM(
    159             "state: "<<const_cast<EKFState_T&>(state_new). ToEigenVector().transpose());
    160       }
    161       if (!CheckForNumeric(H_new, "H_old")) {
    162         MSF_ERROR_STREAM("H_old: "<<H_new);
    163         MSF_WARN_STREAM(
    164             "state: "<<const_cast<EKFState_T&>(state_new). ToEigenVector().transpose());
    165       }
    166       if (!CheckForNumeric(R_, "R_")) {
    167         MSF_ERROR_STREAM("R_: "<<R_);
    168         MSF_WARN_STREAM(
    169             "state: "<<const_cast<EKFState_T&>(state_new). ToEigenVector().transpose());
    170       }
    171 
    172       // Call update step in base class.
    173       this->CalculateAndApplyCorrectionRelative(state_nonconst_old,
    174                                                 state_nonconst_new, core, H_old,
    175                                                 H_new, r_new, R_);
    176 
    177     }
    178   }
    179 };
    Apply

     里面有2个方法CalculateAndApplyCorrection和CalculateAndApplyCorrectionRelative,这2个方法在父类MSF_MeasurementBase<EKFState_T>中实现。

    这2个方法实现了EKF方法的更新步骤。

  • 相关阅读:
    Selenium(Python)等待元素出现
    java文件的I/O
    Map的四种遍历方式
    模板类实现链表
    字符串相关库函数使用
    动态规划之背包问题
    最长递增子序列
    深度优先搜索(DFS),逃离迷宫
    素数环问题(递归回溯)
    枚举(百鸡问题)
  • 原文地址:https://www.cnblogs.com/yhlx125/p/8522972.html
Copyright © 2020-2023  润新知