side.cpp - OpenExo

side.cpp

  • 構造函數
    • 源代碼
  • run_side - 核心
  • read_data()
    • 源代碼
    • FSR壓力傳感器讀取與賦值
    • 步態事件檢測:落地(ground_strike)
    • 步態周期自適應:期望步長更新
    • Toe-Off/Toe-On事件檢測與站立/擺動窗口更新
    • 步態百分比進度估算
    • FSR閾值動態讀取(可能受標定/漂移影響)
    • 坡度/傾角檢測
    • 關節級傳感器/數據讀取
  • check_calibration()
    • 源代碼
    • 整體結構:只對啟用側做標定
    • FSR(壓力傳感器)腳趾標定與精細化標定
    • FSR腳跟標定與精細化標定
    • 關節(Hip/Knee/Ankle/Elbow)逐個檢查并執行標定
  • _check_ground_strike()
    • 源代碼
    • 保存上一次heel/toe接觸狀態
    • 讀取當前heel/toe的觸地狀態
    • 同步當前heel/toe狀態到side全局數據
    • 步態事件標志初始化
    • 實時記錄toe strike事件
    • 只在擺動相(懸空)檢測ground strike
    • 檢測heel或toe的上升沿(rising edge)
  • 更新歷史狀態
    • 返回ground strike事件標志
  • _check_toe_on()
    • 源代碼
    • 變量初始化
    • 檢測 toe on 上升沿
    • 保存本次 toe 狀態
    • 返回結果
  • _calc_percent_gait()
    • 源碼
    • 獲取當前時間戳
    • 默認值初始化
    • 只有有期望步態周期時才進行計算
    • 返回結果
  • _update_expected_duration()
    • 源碼
    • 步態周期測量
    • 初始化與邊界檢查
    • 檢查滑動窗口內未初始化的數量
    • 獲取滑動窗口的最大最小值(為“異常剔除窗口”做準備)
    • 初始化期:填滿滑動窗口
    • 正常采樣期:窗口剔除+滑動平均
    • 返回

構造函數

源代碼

Side::Side(bool is_left, ExoData* exo_data)
: _hip((config_defs::joint_id)((uint8_t)(is_left ? config_defs::joint_id::left : config_defs::joint_id::right) | (uint8_t)config_defs::joint_id::hip), exo_data)        //We need to cast to uint8_t to do bitwise or, then we have to cast it back to joint_id
, _knee((config_defs::joint_id)((uint8_t)(is_left ? config_defs::joint_id::left : config_defs::joint_id::right) | (uint8_t)config_defs::joint_id::knee), exo_data)
, _ankle((config_defs::joint_id)((uint8_t)(is_left ? config_defs::joint_id::left : config_defs::joint_id::right) | (uint8_t)config_defs::joint_id::ankle), exo_data)
, _elbow((config_defs::joint_id)((uint8_t)(is_left ? config_defs::joint_id::left : config_defs::joint_id::right) | (uint8_t)config_defs::joint_id::elbow), exo_data)
, _heel_fsr(is_left ? logic_micro_pins::fsr_sense_left_heel_pin : logic_micro_pins::fsr_sense_right_heel_pin) //Check if it is the left and use the appropriate pin for the side.
, _toe_fsr(is_left ? logic_micro_pins::fsr_sense_left_toe_pin : logic_micro_pins::fsr_sense_right_toe_pin)
{_data = exo_data;_is_left = is_left;//This data object is set for the specific side so we don't have to keep checking._side_data = _is_left ? &(_data->left_side) : &(_data->right_side);#ifdef SIDE_DEBUGlogger::print(_is_left ? "Left " : "Right ");logger::println("Side :: Constructor : _data set");#endif_prev_heel_contact_state = true;        //Initialized to true so we don't get a strike the first time we read_prev_toe_contact_state = true;_prev_toe_contact_state_toe_off = true;_prev_toe_contact_state_toe_on = true;for (int i = 0; i<_num_steps_avg; i++){_step_times[i] = 0;}_ground_strike_timestamp = 0;_prev_ground_strike_timestamp = 0;//_expected_step_duration = 0;#ifdef SIDE_DEBUGlogger::print(_is_left ? "Left " : "Right ");logger::println("Side :: Constructor : Exit");#endif_heel_fsr.get_contact_thresholds(_side_data->heel_fsr_lower_threshold, _side_data->heel_fsr_upper_threshold);_toe_fsr.get_contact_thresholds(_side_data->toe_fsr_lower_threshold, _side_data->toe_fsr_upper_threshold);inclination_detector = new InclinationDetector();
};

功能總結:

  • 分配關節對象,自動綁定側別(左/右),不需要在后續邏輯反復判斷左/右。

  • FSR傳感器(heel/toe)自動選pin。

  • 初始化狀態機相關變量、步態窗口數組,防止“誤觸發”。

  • 關聯傾角檢測器,用于動態地形或坡度環境下的自適應。

run_side - 核心

void Side::run_side()
{#ifdef SIDE_DEBUGlogger::print("\nmicros : ");logger::println(micros());logger::print(_is_left ? "Left " : "Right ");logger::println("Side :: run_side : checking calibration");#endifcheck_calibration();#ifdef SIDE_DEBUGlogger::print(_is_left ? "Left " : "Right ");logger::println("Side :: run_side : reading data");#endif_check_thresholds();//Read all the data before we calculate and send the new motor commandsread_data();#ifdef SIDE_DEBUGlogger::print(_is_left ? "Left " : "Right ");logger::println("Side :: run_side : updating motor commands");#endif//Calculates the new motor commands and sends them.update_motor_cmds();}; 
  • 檢查&觸發FSR或關節標定流程
  • FSR的閾值設定(防止標定變化)
  • 讀取傳感器/關節/步態狀態
  • 計算并下發新的電機指令

read_data()

源代碼

void Side::read_data()
{//Check the FSRs_side_data->heel_fsr = _heel_fsr.read();_side_data->toe_fsr = _toe_fsr.read();//Check if a ground strike is detected_side_data->ground_strike = _check_ground_strike();//If a strike is detected, update the expected duration of the step if (_side_data->ground_strike){_side_data->expected_step_duration = _update_expected_duration();}//Check if the toe on or toe off is occuring_side_data->toe_off = _check_toe_off();_side_data->toe_on = _check_toe_on();//If toe off is detected, updated expected stance durationif (_side_data->toe_off == true){_side_data->expected_stance_duration = _update_expected_stance_duration();}//If toe on is detected, update expected swing durationif (_side_data->toe_on == true){_side_data->expected_swing_duration = _update_expected_swing_duration();}//Calculate Percent Gait, Percent Stance, and Percent Swing_side_data->percent_gait = _calc_percent_gait();_side_data->percent_stance = _calc_percent_stance();_side_data->percent_swing = _calc_percent_swing();//Get the contact thesholds for the Heel and Toe FSRs_heel_fsr.get_contact_thresholds(_side_data->heel_fsr_lower_threshold, _side_data->heel_fsr_upper_threshold);_toe_fsr.get_contact_thresholds(_side_data->toe_fsr_lower_threshold, _side_data->toe_fsr_upper_threshold);//Check the inclination_side_data->inclination = inclination_detector->check(_side_data->toe_stance, _side_data->do_calibration_refinement_toe_fsr, _side_data->ankle.joint_position);//Check the joint sensors if the joint is used.if (_side_data->hip.is_used){_hip.read_data();}if (_side_data->knee.is_used){_knee.read_data();}if (_side_data->ankle.is_used){_ankle.read_data();}if (_side_data->elbow.is_used){_elbow.read_data();}};

FSR壓力傳感器讀取與賦值

_side_data->heel_fsr = _heel_fsr.read();
_side_data->toe_fsr  = _toe_fsr.read();
  • 這里調用了heel/toe FSR(腳跟/腳趾壓力傳感器)的read()方法,獲得當前壓力或傳感器值,并存入對應的SideData成員。

  • 這是所有步態事件檢測(落地、抬腳、踢地等)的基礎感知信號。

步態事件檢測:落地(ground_strike)

_side_data->ground_strike = _check_ground_strike();
  • 用FSR的“接觸/離地”狀態,判斷本步是否發生了“落地”。

  • 這種事件通常只在腳由“空中”轉為“地面”時才判定為真,有效避免重復誤判。

  • 檢測到ground_strike之后(即完成一次步態周期),會進入下個步驟。

步態周期自適應:期望步長更新

if (_side_data->ground_strike)
{_side_data->expected_step_duration = _update_expected_duration();
}
  • 如果檢測到新的一次落地事件,則用當前步長(兩次落地時間差),更新一個滑動窗口(見前文分析)。

  • 用窗口平均自適應地估計本側當前的步長(步態周期),用于百分比進度估算和節奏自調。

  • 這樣能讓外骨骼“跟隨使用者節奏”,而不是死板設定。

Toe-Off/Toe-On事件檢測與站立/擺動窗口更新

_side_data->toe_off = _check_toe_off();
_side_data->toe_on  = _check_toe_on();if (_side_data->toe_off)_side_data->expected_stance_duration = _update_expected_stance_duration();
if (_side_data->toe_on)_side_data->expected_swing_duration  = _update_expected_swing_duration();
  • 分別檢測“腳趾離地”(擺動開始)和“腳趾著地”(擺動結束/站立開始)。

  • 每檢測到一次事件,就用最新時長更新“期望站立/擺動時長”,同樣采用滑動窗口方式。

  • 這樣做的好處:

    • 能區分不同步態階段(stance/swing)。

    • 自動適應不同用戶、速度、模式下的站立/擺動比例(如老人慢步和跑步的占比差別)。

步態百分比進度估算

_side_data->percent_gait   = _calc_percent_gait();
_side_data->percent_stance = _calc_percent_stance();
_side_data->percent_swing  = _calc_percent_swing();
  • 利用已知的周期時長、事件時間戳等,實時計算本側“已走過的步態百分比”、“已站立的比例”、“已擺動的比例”。

  • 這些百分比是時序同步、控制器觸發、力矩規劃、數據記錄的核心指標,也是下游“高級控制算法/自適應助力”必備輸入。

FSR閾值動態讀取(可能受標定/漂移影響)

_heel_fsr.get_contact_thresholds(_side_data->heel_fsr_lower_threshold, _side_data->heel_fsr_upper_threshold);
_toe_fsr.get_contact_thresholds(_side_data->toe_fsr_lower_threshold, _side_data->toe_fsr_upper_threshold);
  • 獲取heel/toe當前的“接觸判定閾值”——可能在動態標定或補償漂移后被修改。

  • 這樣后續所有事件檢測判據都能“自適應傳感器特性/環境變化”。

坡度/傾角檢測

_side_data->inclination = inclination_detector->check(_side_data->toe_stance, _side_data->do_calibration_refinement_toe_fsr, _side_data->ankle.joint_position);
  • 這里利用關節角、FSR、標定標志等信息,調用InclinationDetector檢測當前步態的“地面坡度/使用者踝關節姿態”。

  • 這對于動態坡度補償、上下坡時的力矩修正、平地與障礙判別等場景非常有用。

關節級傳感器/數據讀取

if (_side_data->hip.is_used)    { _hip.read_data(); }
if (_side_data->knee.is_used)   { _knee.read_data(); }
if (_side_data->ankle.is_used)  { _ankle.read_data(); }
if (_side_data->elbow.is_used)  { _elbow.read_data(); }
  • 調用各關節對象的 read_data() 方法,采集角度/力矩/速度/電流等狀態。

  • 是否“參與”由 is_used 決定,可靈活屏蔽部分關節,適應不同外骨骼拓撲。

  • 關節本身負責數據融合和內部狀態更新,不影響Side的主流程。

check_calibration()

源代碼

void Side::check_calibration()
{if (_side_data->is_used){//Make sure FSR calibration is done before refinement.if (_side_data->do_calibration_toe_fsr){_side_data->do_calibration_toe_fsr = _toe_fsr.calibrate(_side_data->do_calibration_toe_fsr);_data->set_status(status_defs::messages::fsr_calibration);}else if (_side_data->do_calibration_refinement_toe_fsr) {_side_data->do_calibration_refinement_toe_fsr = _toe_fsr.refine_calibration(_side_data->do_calibration_refinement_toe_fsr);_data->set_status(status_defs::messages::fsr_refinement);}if (_side_data->do_calibration_heel_fsr){_side_data->do_calibration_heel_fsr = _heel_fsr.calibrate(_side_data->do_calibration_heel_fsr);_data->set_status(status_defs::messages::fsr_calibration);}else if (_side_data->do_calibration_refinement_heel_fsr) {_side_data->do_calibration_refinement_heel_fsr = _heel_fsr.refine_calibration(_side_data->do_calibration_refinement_heel_fsr);_data->set_status(status_defs::messages::fsr_refinement);}//Check the joint sensors if the joint is used.if (_side_data->hip.is_used){_hip.check_calibration();}if (_side_data->knee.is_used){_knee.check_calibration();}if (_side_data->ankle.is_used){_ankle.check_calibration();}if (_side_data->elbow.is_used){_elbow.check_calibration();}}        
};
  • 此函數是整個“單側傳感與控制鏈”的標定調度樞紐,上層能清晰了解當前哪個部分正在標定。

  • 典型用法場景:UI/藍牙/上位機發出一次標定指令→ 相關flag置位 → 控制循環自動推進流程 → 標定完成自動退回。

  • 便于加裝其他類型傳感器或關節——只要新增對應的do_calibration_xxx和check_calibration()接口就可以。

  • 整個流程不阻塞主控循環,適合穿戴場景的高魯棒、快速上電自檢。

整體結構:只對啟用側做標定

if (_side_data->is_used)
{// ...
}
  • 只對被啟用的side(即實際接入的左/右腿)執行標定流程,未啟用的側完全跳過,提高效率且防止誤操作。

FSR(壓力傳感器)腳趾標定與精細化標定

if (_side_data->do_calibration_toe_fsr)
{_side_data->do_calibration_toe_fsr = _toe_fsr.calibrate(_side_data->do_calibration_toe_fsr);_data->set_status(status_defs::messages::fsr_calibration);
}
else if (_side_data->do_calibration_refinement_toe_fsr) 
{_side_data->do_calibration_refinement_toe_fsr = _toe_fsr.refine_calibration(_side_data->do_calibration_refinement_toe_fsr);_data->set_status(status_defs::messages::fsr_refinement);
}
  • 先判斷do_calibration_toe_fsr(腳趾FSR是否需要標定)。若為真,執行FSR初始標定流程(如采集多幀信號平均、設閾值等)。

  • 標定完成后,calibrate返回false,flag被置0,防止重復執行。

  • 標定時同步全局狀態,讓上位機/LED/UI等實時反映“當前正處于FSR標定中”。

  • 只有初始標定完成后,才允許進入精細化(refine_calibration)。這個流程多用于二次補償、更高精度調節。

  • 這種分階段、自動推進的設計,保障了標定流程不會混亂,也方便用戶交互(比如按鈕點一次就是一輪完整流程)。

FSR腳跟標定與精細化標定

if (_side_data->do_calibration_heel_fsr)
{_side_data->do_calibration_heel_fsr = _heel_fsr.calibrate(_side_data->do_calibration_heel_fsr);_data->set_status(status_defs::messages::fsr_calibration);
}
else if (_side_data->do_calibration_refinement_heel_fsr) 
{_side_data->do_calibration_refinement_heel_fsr = _heel_fsr.refine_calibration(_side_data->do_calibration_refinement_heel_fsr);_data->set_status(status_defs::messages::fsr_refinement);
}
  • 腳跟FSR與腳趾FSR標定流程完全一樣,各自獨立,互不干擾。

  • 支持先后/多輪分階段標定,適合應對傳感器偏移、環境變化等。

關節(Hip/Knee/Ankle/Elbow)逐個檢查并執行標定

if (_side_data->hip.is_used)   { _hip.check_calibration(); }
if (_side_data->knee.is_used)  { _knee.check_calibration(); }
if (_side_data->ankle.is_used) { _ankle.check_calibration(); }
if (_side_data->elbow.is_used) { _elbow.check_calibration(); }
  • 對每個關節(只要在本側被啟用)依次下發check_calibration(),這會遞歸調用到關節/電機/角度傳感器等子系統的標定。

  • 這種“遞歸式”設計保證了上層Side類只需關注高層流程,不用管每種關節的細節,強解耦。

  • 新增關節只需實現自己的check_calibration即可,對系統無侵入。

_check_ground_strike()

源代碼

bool Side::_check_ground_strike()
{_side_data->prev_heel_stance = _prev_heel_contact_state;  _side_data->prev_toe_stance = _prev_toe_contact_state;bool heel_contact_state = _heel_fsr.get_ground_contact();bool toe_contact_state = _toe_fsr.get_ground_contact();_side_data->heel_stance = heel_contact_state;_side_data->toe_stance = toe_contact_state;bool ground_strike = false;// logger::print("Side::_check_ground_strike : _prev_heel_contact_state - ");// logger::print(_prev_heel_contact_state);// logger::print("\n");// logger::print("\t_prev_toe_contact_state - ");// logger::print(_prev_toe_contact_state);// logger::print("\n");//Only check if in swing_side_data->toe_strike = toe_contact_state > _prev_toe_contact_state;if(!_prev_heel_contact_state & !_prev_toe_contact_state) //If we were previously in swing{//Check for rising edge on heel and toe, toe is to account for flat foot landingsif ((heel_contact_state > _prev_heel_contact_state) | (toe_contact_state > _prev_toe_contact_state))    //If either the heel or toe FSR is on the ground and it previously wasn't on the ground{ground_strike = true;_prev_ground_strike_timestamp = _ground_strike_timestamp;_ground_strike_timestamp = millis();}}_prev_heel_contact_state = heel_contact_state;_prev_toe_contact_state = toe_contact_state;return ground_strike;
};

_check_ground_strike() 是步態周期檢測的核心,用于識別足底由“空中(擺動)”到“著地”這一關鍵事件(即“ground strike”/落地瞬間),它是外骨骼助力/數據記錄的基本觸發點。

保存上一次heel/toe接觸狀態

_side_data->prev_heel_stance = _prev_heel_contact_state;  
_side_data->prev_toe_stance = _prev_toe_contact_state;

把上一次采樣時的“腳跟/腳趾是否觸地”狀態,存到side數據結構中,方便全局分析和后續計算(比如UI、數據同步、事件溯源)。

讀取當前heel/toe的觸地狀態

bool heel_contact_state = _heel_fsr.get_ground_contact();
bool toe_contact_state = _toe_fsr.get_ground_contact();
  • 實時讀取腳跟、腳趾FSR(力敏電阻)當前是否“有地面壓力”。

  • 這通常是一個bool(0/1),傳感器閾值判決由FSR類內部實現。

同步當前heel/toe狀態到side全局數據

_side_data->heel_stance = heel_contact_state;
_side_data->toe_stance = toe_contact_state;
  • 方便整個系統(UI、控制、記錄)直接用side數據就能拿到“當前這一幀”的足底狀態。

步態事件標志初始化

bool ground_strike = false;
  • 初始化一個flag,用于記錄“這一幀是否檢測到地面沖擊事件(足部落地)”。

實時記錄toe strike事件

_side_data->toe_strike = toe_contact_state > _prev_toe_contact_state;
  • 如果當前腳趾由“未觸地”變成“觸地”(即從0變1),則toe_strike為真,代表檢測到腳趾首次著地。

  • 用于某些步態模型(如平足或腳尖先著地的人群)。

只在擺動相(懸空)檢測ground strike

if(!_prev_heel_contact_state & !_prev_toe_contact_state) //If we were previously in swing
{//...
}
  • 只有上一次采樣時,腳跟和腳趾都沒觸地(即這只腳在擺動),才判定接下來有無落地事件。

  • 避免反復觸發、只在真實“從空中到落地”那一瞬間識別。

檢測heel或toe的上升沿(rising edge)

if ((heel_contact_state > _prev_heel_contact_state) | (toe_contact_state > _prev_toe_contact_state))
{ground_strike = true;_prev_ground_strike_timestamp = _ground_strike_timestamp;_ground_strike_timestamp = millis();
}
  • 如果“腳跟”或“腳趾”任何一個傳感器狀態從0變1(剛好踩到地面),判定為ground strike。

  • 為什么要“或”而不是“與”?——有人落地先腳跟,有人先腳趾(甚至平足),都能檢測到,更健壯。

  • 一旦檢測到,更新落地時間戳,為步態周期估算等后續分析做準備。

更新歷史狀態

_prev_heel_contact_state = heel_contact_state;
_prev_toe_contact_state = toe_contact_state;
  • 把本幀的觸地狀態存下來,供下次采樣時做“上升沿”判斷。

返回ground strike事件標志

return ground_strike;
  • 外部主循環會用它來決定是否觸發步態事件、調整助力、記錄數據等。

_check_toe_on()

源代碼

bool Side::_check_toe_on()
{bool toe_on = false;if (_side_data->toe_stance > _prev_toe_contact_state_toe_on){toe_on = true;_prev_toe_strike_timestamp = _toe_strike_timestamp;_toe_strike_timestamp = millis();}_prev_toe_contact_state_toe_on = _side_data->toe_stance;return toe_on;
};
  • 步態檢測:toe on常用于區分“擺動相結束、站立相開始”,也就是腳尖第一次踩到地面。

  • 實時性強:通過對比上/下采樣周期,只會在真正狀態變化瞬間觸發,避免長時間觸地反復觸發。

  • 事件驅動:便于同步下游模塊(比如:助力控制、數據采集、相位切換、計時等)。

  • 時間戳為分析和自適應奠定基礎:累計周期時間可用來估算步頻、步幅、異常檢測等。

變量初始化

bool toe_on = false;
  • 初始設置返回值為false,即“默認本周期沒有檢測到toe on事件”。

檢測 toe on 上升沿

if (_side_data->toe_stance > _prev_toe_contact_state_toe_on)
{toe_on = true;_prev_toe_strike_timestamp = _toe_strike_timestamp;_toe_strike_timestamp = millis();
}
  • _side_data->toe_stance 是當前采樣幀腳趾的觸地狀態(一般是bool,0/1)。

  • _prev_toe_contact_state_toe_on 是上一幀的腳趾觸地狀態。

  • 如果 當前 > 上一幀,說明剛剛由“懸空”變為“落地”,也就是toe on事件發生了。

  • 一旦檢測到:

    • toe_on = true,主循環可根據它做響應(如記錄步態相位切換、輔助控制等)。

    • 時間戳管理:

      • _prev_toe_strike_timestamp = _toe_strike_timestamp; 把“上次toe on”的時間存為“前一次”,方便統計周期、濾除異常等。
    • _toe_strike_timestamp = millis(); 記錄本次toe on發生的時刻。

保存本次 toe 狀態

_prev_toe_contact_state_toe_on = _side_data->toe_stance;
  • 更新 toe 狀態,為下次采樣時做“上升沿檢測”準備。

返回結果

return toe_on;
  • 返回是否檢測到 toe on 事件。

_calc_percent_gait()

實時計算當前步態周期內已經走了多少百分比,也就是gait phase progress,通常用于步態相位同步、控制器參數插值、實時分析等場景。

源碼

float Side::_calc_percent_gait()
{int timestamp = millis();int percent_gait = -1;//Only calulate if the expected step duration has been established.if (_side_data->expected_step_duration > 0){percent_gait = 100 * ((float)timestamp - _ground_strike_timestamp) / _side_data->expected_step_duration;percent_gait = min(percent_gait, 100); //Set saturation.// logger::print("Side::_calc_percent_gait : percent_gait_x10 = ");// logger::print(percent_gait_x10);// logger::print("\n");}return percent_gait;
};

獲取當前時間戳

int timestamp = millis();
  • millis() 返回系統運行到現在的毫秒數。

  • 這個時間戳用來計算距離本周期步態開始(上一次ground strike)已經過去多久。

默認值初始化

int percent_gait = -1;
  • 默認值為-1,表明當前還無法判斷百分比(比如未初始化或步態未檢測到)。

只有有期望步態周期時才進行計算

if (_side_data->expected_step_duration > 0)
{percent_gait = 100 * ((float)timestamp - _ground_strike_timestamp) / _side_data->expected_step_duration;percent_gait = min(percent_gait, 100); //Set saturation.
}
  • 只有expected_step_duration(期望步態周期,單位ms)大于0才進行計算,防止未初始化導致錯誤。

  • 計算公式為:

    • ((float)timestamp - _ground_strike_timestamp):距離上次落地(ground strike)已經過去的時間。

    • 除以期望步態周期,得到本周期已完成的比例。

    • 乘100,得到百分數。

    • 最后min(percent_gait, 100):保證最大不會超過100%(即0-100之間),防止走慢或滯后造成溢出。

返回結果

return percent_gait;
  • 返回步態周期百分比,供后續模塊(如相位插值、控制曲線索引、步態進度顯示)使用。

_update_expected_duration()

  • Side::_update_expected_duration() 的作用是自適應地估算當前步態周期(一步所需的平均時長),用來動態調整后續的步態判別、步態百分比計算、控制器相位插值等參數。
  • 它的核心是“滑動平均+窗口判斷”,有點像低通濾波器+異常剔除,適應用戶步頻的變化。

源碼

float Side::_update_expected_duration()
{unsigned int step_time = _ground_strike_timestamp - _prev_ground_strike_timestamp;float expected_step_duration = _side_data->expected_step_duration;if (0 == _prev_ground_strike_timestamp) //If the prev time isn't set just return.{return expected_step_duration;}uint8_t num_uninitialized = 0;//Check that everything is set.for (int i = 0; i < _num_steps_avg; i++){num_uninitialized += (_step_times[i] == 0);}//Get the max and min values of the array for determining the window for expected values.unsigned int* max_val = std::max_element(_step_times, _step_times + _num_steps_avg);unsigned int* min_val = std::min_element(_step_times, _step_times + _num_steps_avg);if  (num_uninitialized > 0)  //If all the values haven't been replaced{//Shift all the values and insert the new onefor (int i = (_num_steps_avg - 1); i>0; i--){_step_times[i] = _step_times[i-1];}_step_times[0] = step_time;// logger::print("Side::_update_expected_duration : _step_times not fully initialized- [\t");// for (int i = 0; i < _num_steps_avg; i++)// {// logger::print(_step_times[i]);// logger::print("\t");// }// logger::print("\t]\n");    }//Consider it a good step if the ground strike falls within a window around the expected duration. Then shift the step times and put in the new value.else if ((step_time <= (_side_data->expected_duration_window_upper_coeff * *max_val)) & (step_time >= (_side_data->expected_duration_window_lower_coeff * *min_val))) // and (armed_time > ARMED_DURATION_PERCENT * self.expected_duration)): # a better check can be used.  If the person hasn't stopped or the step is good update the vector.  {int sum_step_times = step_time;for (int i = (_num_steps_avg - 1); i>0; i--){sum_step_times += _step_times[i-1];_step_times[i] = _step_times[i-1];}_step_times[0] = step_time;expected_step_duration = sum_step_times / _num_steps_avg;  //Average to the nearest ms// logger::print("Side::_update_expected_duration : _expected_step_duration - ");// logger::print(_expected_step_duration);// logger::print("\n");}return expected_step_duration;
};

步態周期測量

unsigned int step_time = _ground_strike_timestamp - _prev_ground_strike_timestamp;

用當前一次落地(ground strike)和上一次的時間戳差,得到本次步態周期的實際時長。

初始化與邊界檢查

if (0 == _prev_ground_strike_timestamp) //If the prev time isn't set just return.
{return expected_step_duration;
}
  • 如果是第一次運行,_prev_ground_strike_timestamp還沒有設置,直接返回之前的expected_step_duration,避免出錯。

檢查滑動窗口內未初始化的數量

uint8_t num_uninitialized = 0;
for (int i = 0; i < _num_steps_avg; i++)
{num_uninitialized += (_step_times[i] == 0);
}
  • _step_times[]保存最近N步的周期(比如N=5或N=10)。

  • 只要有一個沒初始化(==0),num_uninitialized就大于0。

獲取滑動窗口的最大最小值(為“異常剔除窗口”做準備)

unsigned int* max_val = std::max_element(_step_times, _step_times + _num_steps_avg);
unsigned int* min_val = std::min_element(_step_times, _step_times + _num_steps_avg);
  • 記錄最近N步最大和最小的步態周期。

初始化期:填滿滑動窗口

if  (num_uninitialized > 0)
{//Shift all the values and insert the new onefor (int i = (_num_steps_avg - 1); i>0; i--){_step_times[i] = _step_times[i-1];}_step_times[0] = step_time;
}
  • 只要有沒填滿的數據,采用“先進先出”方式:把新的step_time插到第0個,其他依次后移。

  • 填滿滑動窗口后才進行下一步的“正常采樣和異常剔除”。

正常采樣期:窗口剔除+滑動平均

else if ((step_time <= (_side_data->expected_duration_window_upper_coeff * *max_val)) & (step_time >= (_side_data->expected_duration_window_lower_coeff * *min_val)))
{int sum_step_times = step_time;for (int i = (_num_steps_avg - 1); i>0; i--){sum_step_times += _step_times[i-1];_step_times[i] = _step_times[i-1];}_step_times[0] = step_time;expected_step_duration = sum_step_times / _num_steps_avg;  //Average to the nearest ms
}
  • 如果新采樣的step_time在合理的窗口范圍內(上下限由expected_duration_window_upper_coeff和expected_duration_window_lower_coeff修正的最大/最小步長):

    • 將其加入滑動窗口(同上)。

    • 用全部N步求均值,得到新的expected_step_duration。

    • 這樣可以“剔除異常值”(如偶發停頓、抖動導致的極大極小步長),只吸收正常步態變化。

    • 如果不在窗口內,則忽略本次采樣(不更新均值,防止異常值污染估算)。

返回

return expected_step_duration;
  • 得到最新的滑動平均步態周期,用于后續所有需要步態相位的模塊。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/916152.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/916152.shtml
英文地址,請注明出處:http://en.pswp.cn/news/916152.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

基于Java+MySQL實現(Web)文件共享管理系統(仿照百度文庫)

文件共享管理系統的設計與實現摘要&#xff1a;本文件共享管理系統解決了用戶在搜索文件不需要下載文件到本地硬盤后才能查看文件的詳細內容的弊端&#xff1b;解決用戶在搜索關鍵字不明確條件下無法搜索到自己需要的文件弊端&#xff1b;解決了系統用戶并發量增加后服務器宕機…

go語言基礎教程:1. Go 下載安裝和設置

1. Go 下載安裝和設置1. 安裝Go 官網下載安裝即可&#xff0c;注意要記住安裝的位置&#xff0c;例如D:\Go cmd輸入go 或者go env 會輸出各種信息&#xff0c;代表安裝成功 2. hello go &#xff08;1&#xff09;編寫 hello.go go是以文件夾為最小單位管理程序的&#xff0c…

使用相機不同曝光時間測試燈光閃爍頻率及Ai解釋

1.背景坐地鐵上&#xff0c;撥弄著手機拍照中的專業模式&#xff0c;偶然發現拍出了條紋&#xff0c;懷疑是燈光的緣故&#xff0c;但是隨后在家里的LED等下就拍不出類似的效果了。好奇心?讓我又嘗試多了解了一點和不斷嘗試&#xff0c;發現不同的曝光時間可以拍出不同明顯程度…

力扣-416.分割等和子集

題目鏈接 416.分割等和子集 class Solution {public boolean canPartition(int[] nums) {int sum 0;for (int i 0; i < nums.length; i) {sum nums[i];}if (sum % 2 1)return false;int target sum / 2;// dp[i]表示&#xff1a;背包容量為i時&#xff0c;能裝的最大…

http協議學習-body各種類型

1、概述使用postman工具和nc命令分析http協議中body各種類型的格式。2、分析環境準備虛擬機中用nc命令模仿服務器&#xff0c;啟動監聽狀態。 windows機器安裝postmannc -k -l 192.168.202.223 80821、params參數postman中params添加倆個key為m、n&#xff1b;value為1、2&…

C++中的塔尖算法(Tarjan算法)詳解

C中的塔尖算法&#xff08;Tarjan算法&#xff09;詳解——目錄C中的塔尖算法&#xff08;Tarjan算法&#xff09;詳解一、什么是Tarjan算法&#xff1f;二、算法原理與實現步驟1. 核心概念2. 主要邏輯3. C代碼示例三、應用場景與擴展1. 典型應用2. 注意事項四、為什么選擇Tarj…

Qt 數據庫事務處理與數據安全

在 Qt 應用程序中&#xff0c;數據庫事務處理是確保數據完整性和一致性的關鍵技術。通過事務&#xff0c;可以將多個數據庫操作作為一個不可分割的單元執行&#xff0c;保證數據在并發訪問和異常情況下的安全性。本文將詳細介紹 Qt 中數據庫事務的處理方法和數據安全策略。 一、…

Redis的事務和Lua之間的區別

Redis的事務和Lua之間的區別 Redis 提供了事務和 Lua 腳本兩種實現原子性操作的方式。當需要以原子方式執行多個命令時,我們可以選擇其中一種方案。 原子性保證 兩者都確保操作的不可分割性 需要注意:不管是事務還是 Lua 腳本都不支持回滾機制 區別: 事務:某個命令失敗不會…

騰訊云SDK

SDK的用途&#xff0c;現在顯然是想更系統地了解它的產品定位和核心能力。 用戶可能是開發者或者技術決策者&#xff0c;正在評估騰訊云的開發工具鏈。從ta連續追問云服務相關技術細節的習慣看&#xff0c;應該具備相當的技術背景&#xff0c;但需要避免過度使用術語。 需要突出…

大數據集分頁優化:LIMIT OFFSET的替代方案

針對大數據集分頁場景中 LIMIT OFFSET 的性能瓶頸&#xff0c;以下是已驗證的高效替代方案及實施要點&#xff1a;?? 一、LIMIT OFFSET 的核心問題當偏移量&#xff08;OFFSET&#xff09;增大時&#xff0c;數據庫需?物理掃描并丟棄前 N 條記錄?&#xff0c;導致資源浪費和…

Linux網絡框架分析

在 Linux 內核架構中,/net 和 /drivers/net 是網絡子系統的兩個核心組成部分,它們之間的關系體現了 Linux 經典的 “抽象層分離” 設計哲學。以下是深入分析: 一、核心關系圖解 #mermaid-svg-esFw9i3LN65SYumi {font-family:"trebuchet ms",verdana,arial,sans-se…

ISIS高級特性GR

一、概述IS-IS GR是一種支持GR能力的高可靠性技術&#xff0c;可以實現數據的不間斷轉發。與我們之前介紹的OSPF的GR功能幾乎一致,但實現方法并不相同。1、GR支持GR的ISIS的設備,IIH報文中一定會攜帶TLV211(GR),TLV211包含的字段(1)RR:restart request 請求重啟,默認是3秒發送1…

電廠液壓執行器自動化升級:Modbus TCP與DeviceNet的協議貫通實踐

一、項目背景在我們電廠的汽輪機控制區&#xff0c;液壓執行器是實打實的“關鍵選手”——從調節蒸汽閥門開度到控制閘板起落&#xff0c;全靠它在高壓環境下精準動作。但這套系統一直有個“溝通障礙”&#xff1a;負責統籌控制的施耐德PLC走Modbus TCP協議&#xff0c;而液壓執…

ucharts 搭配uniapp 自定義x軸文字 實現截取顯示

formatter格式化問題因為組件不能傳遞 function&#xff0c;所有的 formatter 均需要變成別名 format 來定義&#xff0c;并在 config-ucharts.js 或 config-echarts.js 配置對應的 formatter 方法&#xff0c;組件會根據 format 的值自動替換配置文件中的 formatter 方法。uCh…

Logstash 多表增量同步 MySQL 到 Elasticsearch:支持邏輯刪除與熱加載,Docker 快速部署實戰

? 1. 項目結構 install-elk/ ├── start-elastic.sh ├── es-data/ # Elasticsearch 持久化目錄&#xff08;自動創建&#xff09; ├── logstash/├── logstash.yml├── pipeline/│ ├── user.conf│ ├── articles.conf│ …

服務器托管:網站經常被攻擊該怎么辦?

“木馬”對于孩子來說是個玩具&#xff0c;但是對于網絡行業來說是一個病毒威脅&#xff0c;站長在進行建站的過程中&#xff0c;通常都會面臨一個問題網站被掛馬&#xff0c;有些網站服務器托管在進行多次處理木馬之后得不到根治&#xff0c;后續會受到頻繁的攻擊該怎么辦&…

判斷子序列-leetcode

給定字符串 s 和 t &#xff0c;判斷 s 是否為 t 的子序列。 字符串的一個子序列是原始字符串刪除一些&#xff08;也可以不刪除&#xff09;字符而不改變剩余字符相對位置形成的新字符串。&#xff08;例如&#xff0c;"ace"是"abcde"的一個子序列&#x…

如何提高微信小程序的應用速度

1、如何提高微信小程序的應用速度&#xff1f;加載時1、上傳代碼時&#xff0c;對代碼進行壓縮。2、清理點代碼中無效的代碼和資源文件。3、減少本地代碼中圖片等資源的數量和大小。如將多個圖片合成一張圖片。還有將圖片資源放在靜態資源庫存儲。渲染1、在加載頁面時&#xff…

華為高頻算法題:最長連續遞增子序列(Longest Continuous Increasing Subsequence)

文章目錄前言題目描述&#xff08;華為校招真題&#xff09;解題思路分析Java 實現代碼單元測試代碼結語前言 在各大互聯網公司的算法面試中&#xff0c;數組類題目一直是考察的重點&#xff0c;尤其是對于應屆生和初級工程師的面試來說更是常見題型。華為作為國內頂尖的科技企…

JavaSE-圖書信息管理系統

目錄 前置知識點 項目部署說明 項目運行截圖 項目結構展示 項目編寫構思 book包 Book類 Booklist類 ioperations包 IOPeration接口 AddOperation類 BorrowOperation類 DelOperation類 FindOperation類 ReturnOperation類 ShowOperation類 ExitOperation類 use…