Odrive源碼分析(七) 逆park變換

Odrive源碼分析(七) Park逆變換

Odrive中FOC部分代碼分散在各個對象中,并不是集中在某一塊,所以試圖在某一段代碼就能得到FOC全貌是不現實的。

先看下FOC的整個流程:
FOC控制流程圖

控制變量到三相電流輸出的關鍵部分分為Park逆變換和SVPWM。本文主要討論逆park變換在odrive中的代碼實現。、

代碼主要在FieldOrientedController::get_alpha_beta_output函數中。

ODriveIntf::MotorIntf::Error FieldOrientedController::get_alpha_beta_output(uint32_t output_timestamp, std::optional<float2D>* mod_alpha_beta,std::optional<float>* ibus) {if (!vbus_voltage_measured_.has_value() || !Ialpha_beta_measured_.has_value()) {// FOC didn't receive a current measurement yet.return Motor::ERROR_CONTROLLER_INITIALIZING;} else if (abs((int32_t)(i_timestamp_ - ctrl_timestamp_)) > MAX_CONTROL_LOOP_UPDATE_TO_CURRENT_UPDATE_DELTA) {// Data from control loop and current measurement are too far apart.return Motor::ERROR_BAD_TIMING;}// TODO: improve efficiency in case PWM updates are requested at a higher// rate than current sensor updates. In this case we can reuse mod_d and// mod_q from a previous iteration.if (!Vdq_setpoint_.has_value()) {return Motor::ERROR_UNKNOWN_VOLTAGE_COMMAND;} else if (!phase_.has_value() || !phase_vel_.has_value()) {return Motor::ERROR_UNKNOWN_PHASE_ESTIMATE;} else if (!vbus_voltage_measured_.has_value()) {return Motor::ERROR_UNKNOWN_VBUS_VOLTAGE;}auto [Vd, Vq] = *Vdq_setpoint_;float phase = *phase_;float phase_vel = *phase_vel_;float vbus_voltage = *vbus_voltage_measured_;std::optional<float2D> Idq;// Park transformif (Ialpha_beta_measured_.has_value()) {auto [Ialpha, Ibeta] = *Ialpha_beta_measured_;float I_phase = phase + phase_vel * ((float)(int32_t)(i_timestamp_ - ctrl_timestamp_) / (float)TIM_1_8_CLOCK_HZ);float c_I = our_arm_cos_f32(I_phase);float s_I = our_arm_sin_f32(I_phase);Idq = {c_I * Ialpha + s_I * Ibeta,c_I * Ibeta - s_I * Ialpha};Id_measured_ += I_measured_report_filter_k_ * (Idq->first - Id_measured_);Iq_measured_ += I_measured_report_filter_k_ * (Idq->second - Iq_measured_);} else {Id_measured_ = 0.0f;Iq_measured_ = 0.0f;}float mod_to_V = (2.0f / 3.0f) * vbus_voltage;float V_to_mod = 1.0f / mod_to_V;float mod_d;float mod_q;if (enable_current_control_) {// Current control modeif (!pi_gains_.has_value()) {return Motor::ERROR_UNKNOWN_GAINS;} else if (!Idq.has_value()) {return Motor::ERROR_UNKNOWN_CURRENT_MEASUREMENT;} else if (!Idq_setpoint_.has_value()) {return Motor::ERROR_UNKNOWN_CURRENT_COMMAND;}auto [p_gain, i_gain] = *pi_gains_;auto [Id, Iq] = *Idq;auto [Id_setpoint, Iq_setpoint] = *Idq_setpoint_;float Ierr_d = Id_setpoint - Id;float Ierr_q = Iq_setpoint - Iq;// Apply PI control (V{d,q}_setpoint act as feed-forward terms in this mode)mod_d = V_to_mod * (Vd + v_current_control_integral_d_ + Ierr_d * p_gain);mod_q = V_to_mod * (Vq + v_current_control_integral_q_ + Ierr_q * p_gain);// Vector modulation saturation, lock integrator if saturated// TODO make maximum modulation configurablefloat mod_scalefactor = 0.80f * sqrt3_by_2 * 1.0f / std::sqrt(mod_d * mod_d + mod_q * mod_q);if (mod_scalefactor < 1.0f) {mod_d *= mod_scalefactor;mod_q *= mod_scalefactor;// TODO make decayfactor configurablev_current_control_integral_d_ *= 0.99f;v_current_control_integral_q_ *= 0.99f;} else {v_current_control_integral_d_ += Ierr_d * (i_gain * current_meas_period);v_current_control_integral_q_ += Ierr_q * (i_gain * current_meas_period);}} else {// Voltage control modemod_d = V_to_mod * Vd;mod_q = V_to_mod * Vq;}// Inverse park transformfloat pwm_phase = phase + phase_vel * ((float)(int32_t)(output_timestamp - ctrl_timestamp_) / (float)TIM_1_8_CLOCK_HZ);float c_p = our_arm_cos_f32(pwm_phase);float s_p = our_arm_sin_f32(pwm_phase);float mod_alpha = c_p * mod_d - s_p * mod_q;float mod_beta = c_p * mod_q + s_p * mod_d;// Report final applied voltage in stationary frame (for sensorless estimator)final_v_alpha_ = mod_to_V * mod_alpha;final_v_beta_ = mod_to_V * mod_beta;*mod_alpha_beta = {mod_alpha, mod_beta};if (Idq.has_value()) {auto [Id, Iq] = *Idq;*ibus = mod_d * Id + mod_q * Iq;power_ = vbus_voltage * (*ibus).value();}return Motor::ERROR_NONE;
}

下面主要分析上述函數的具體實現:

step1 在進入本函數計算之前,必須已經完成線電壓的測量和相電流的測量,同時要保證時效性,如下所示:

//本函數調用前,在ControlLoop_IRQHandler函數中已經完成了測量。所以本條件必然滿足。
if (!vbus_voltage_measured_.has_value() || !Ialpha_beta_measured_.has_value()) {// FOC didn't receive a current measurement yet.return Motor::ERROR_CONTROLLER_INITIALIZING;
} 
//i_timestamp_和ctrl_timestamp_實際上是同一個值,因為都是外部傳進來的
else if (abs((int32_t)(i_timestamp_ - ctrl_timestamp_)) > MAX_CONTROL_LOOP_UPDATE_TO_CURRENT_UPDATE_DELTA) {// Data from control loop and current measurement are too far apart.return Motor::ERROR_BAD_TIMING;
}

step2 相關控制數據檢查,如下所示:

//判斷是否已經計算出Vdq_setpoint(motor.cpp已通過PID計算得出)
if (!Vdq_setpoint_.has_value()) {return Motor::ERROR_UNKNOWN_VOLTAGE_COMMAND;
} 
//判斷是否已經得到電角度和電角速度(已經通過encode.cpp計算得出)
else if (!phase_.has_value() || !phase_vel_.has_value()) {return Motor::ERROR_UNKNOWN_PHASE_ESTIMATE;
} 
//這里其實重復判斷了...
else if (!vbus_voltage_measured_.has_value()) {return Motor::ERROR_UNKNOWN_VBUS_VOLTAGE;
}//變量提取
auto [Vd, Vq] = *Vdq_setpoint_;
float phase = *phase_;
float phase_vel = *phase_vel_;
float vbus_voltage = *vbus_voltage_measured_;

step3 通過測量到的電流進行Park變換轉換到觀察到的DQ,如下所示:

// 檢查AB空間數據有效性,Ialpha_beta_measured_是通過FieldOrientedController父類 AlphaBetaFrameController::on_measurement計算得出。
if (Ialpha_beta_measured_.has_value()) {auto [Ialpha, Ibeta] = *Ialpha_beta_measured_;//這里得到電流測量時刻的電角度,這里很嚴謹,雖然i_timestamp_和 ctrl_timestamp_其實是相等的值,但是軟件保留了拓展能力。float I_phase = phase + phase_vel * ((float)(int32_t)(i_timestamp_ - ctrl_timestamp_) / (float)TIM_1_8_CLOCK_HZ);//計算Park變換得到測量的DQ值用于后面的電流環閉環float c_I = our_arm_cos_f32(I_phase);float s_I = our_arm_sin_f32(I_phase);Idq = {c_I * Ialpha + s_I * Ibeta,c_I * Ibeta - s_I * Ialpha};Id_measured_ += I_measured_report_filter_k_ * (Idq->first - Id_measured_);Iq_measured_ += I_measured_report_filter_k_ * (Idq->second - Iq_measured_);
} else {Id_measured_ = 0.0f;Iq_measured_ = 0.0f;
}//相關中間變量計算
//這里mod_to_V是線電壓的2/3.實際上這是相電壓能達到的最大值,這里可以理解為SVPWM扇區的內接圓半徑。
float mod_to_V = (2.0f / 3.0f) * vbus_voltage;
float V_to_mod = 1.0f / mod_to_V;
float mod_d;
float mod_q;

step4 電流環控制,如下所示:

//只有在開啟了電流閉環控制時以下代碼才會生效
//當motor_.config_.motor_type 為 Motor::MOTOR_TYPE_GIMBA 云臺電機時,不使用電流控制模式而采用電壓控制模式。
if (enable_current_control_) {//檢查是否配置了PI參數if (!pi_gains_.has_value()) {return Motor::ERROR_UNKNOWN_GAINS;} //電流環控制必須已經得到了Idq(上面計算已經得出)else if (!Idq.has_value()) {return Motor::ERROR_UNKNOWN_CURRENT_MEASUREMENT;} //是否存在控制量Idq_setpoint_(該變量已經由motor.cpp中計算得出)else if (!Idq_setpoint_.has_value()) {return Motor::ERROR_UNKNOWN_CURRENT_COMMAND;}//語法糖auto [p_gain, i_gain] = *pi_gains_;auto [Id, Iq] = *Idq;auto [Id_setpoint, Iq_setpoint] = *Idq_setpoint_;//使用PI控制計算得出mod_q,mod_d,這里mod_q和mod_d可以理解為mod_to_V的倍數。float Ierr_d = Id_setpoint - Id;float Ierr_q = Iq_setpoint - Iq;//Vd/Vq作為前饋量加入到電流環PI控制中mod_d = V_to_mod * (Vd + v_current_control_integral_d_ + Ierr_d * p_gain);mod_q = V_to_mod * (Vq + v_current_control_integral_q_ + Ierr_q * p_gain);// Vector modulation saturation, lock integrator if saturated// TODO make maximum modulation configurablefloat mod_scalefactor = 0.80f * sqrt3_by_2 * 1.0f / std::sqrt(mod_d * mod_d + mod_q * mod_q);if (mod_scalefactor < 1.0f) {mod_d *= mod_scalefactor;mod_q *= mod_scalefactor;// TODO make decayfactor configurablev_current_control_integral_d_ *= 0.99f;v_current_control_integral_q_ *= 0.99f;} else {v_current_control_integral_d_ += Ierr_d * (i_gain * current_meas_period);v_current_control_integral_q_ += Ierr_q * (i_gain * current_meas_period);}} else {//直接使用Vd/Vq 電壓控制,得到控制量為mod_to_V的倍數。mod_d = V_to_mod * Vd;mod_q = V_to_mod * Vq;
}

step5 正式完成park逆變換,如下所示:

//這里得到電角度,這里output_timestamp的時間會大于ctrl_timestamp_(理論上真好會大于一個控制周期),此時的電角度比測量電流時的電角度是不同的。
float pwm_phase = phase + phase_vel * ((float)(int32_t)(output_timestamp - ctrl_timestamp_) / (float)TIM_1_8_CLOCK_HZ);
float c_p = our_arm_cos_f32(pwm_phase);
float s_p = our_arm_sin_f32(pwm_phase);
float mod_alpha = c_p * mod_d - s_p * mod_q;
float mod_beta = c_p * mod_q + s_p * mod_d;// Report final applied voltage in stationary frame (for sensorless estimator)
final_v_alpha_ = mod_to_V * mod_alpha;
final_v_beta_ = mod_to_V * mod_beta;//得到最終計算出的AB控制量(后面會輸入到SVPWM模塊逆變出三相電流)
*mod_alpha_beta = {mod_alpha, mod_beta};//計算出相關數據
if (Idq.has_value()) {auto [Id, Iq] = *Idq;//計算出線電流*ibus = mod_d * Id + mod_q * Iq;//計算功率(電壓和電流乘積)power_ = vbus_voltage * (*ibus).value();
}

完成Park逆變換后,下一步就直接送入了SVPWM模塊了。

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

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

相關文章

Flink Hive Catalog最佳實踐

Flink Hive Catalog 最佳實踐 一、配置與初始化 依賴管理 Hive Connector 版本對齊&#xff1a;需確保 flink-sql-connector-hive 版本與 Hive 版本嚴格匹配&#xff08;如 Hive 3.1.3 對應 flink-sql-connector-hive-3.1.3_2.12&#xff09;&#xff0c;同時添加 Hadoop 遮蔽…

通過人類和機器人演示進行聯合逆向和正向動力學的機器人訓練

25年3月來自哥倫比亞大學的論文“Train Robots in a JIF: Joint Inverse and Forward Dynamics with Human and Robot Demonstrations”。 在大型機器人演示數據集上進行預訓練是學習各種操作技能的強大技術&#xff0c;但通常受到收集以機器人為中心數據的高成本和復雜性限制…

金融簡單介紹及金融詐騙防范

在當今社會&#xff0c;金融學如同一股無形卻強大的力量&#xff0c;深刻影響著我們生活的方方面面。無論是個人的日常收支、投資理財&#xff0c;還是國家的宏觀經濟調控&#xff0c;都與金融學緊密相連。? 一、金融學的概念? 金融學&#xff0c;簡單來說&#xff0c;是研…

JavaScript `new Date()` 方法移動端 `兼容 ios`,ios環境new Date()返回NaN

在 iOS 環境下&#xff0c;new Date() 方法會返回 NaN&#xff0c;這通常是由于時間字符串的格式問題。iOS 的 Date 構造函數對時間字符串的格式要求比其他平臺更嚴格。 原因&#xff1a;ios端不兼容“-”為連接符的時間。 解決辦法&#xff1a; 替換時間格式 IOS 不支持某…

【網絡編程】網絡編程基礎和Socket套接字

目錄 一. 網絡編程的概念 二. 網絡編程基礎知識 1&#xff09;網卡 2&#xff09;接收端和發送端 3&#xff09;客戶端和服務器 4&#xff09;請求和響應 5&#xff09;客戶端和服務器的交互模式 三. Socket 套接字模型 一. 網絡編程的概念 網絡編程 是通過編程實現不同…

盛水最多的容器問題詳解:雙指針法與暴力法的對比與實現

文章目錄 問題描述方法探討方法一&#xff1a;暴力法&#xff08;Brute Force&#xff09;思路代碼實現復雜度分析 方法二&#xff1a;雙指針法&#xff08;Two Pointers&#xff09;思路正確性證明代碼實現復雜度分析 方法對比總結 摘要 盛水最多的容器&#xff08;Container …

圖論-BFS搜索圖/樹-最短路徑問題的解決

續上篇~圖論--DFS搜索圖/樹-CSDN博客 先看第一次學習的博客&#xff01;&#xff01;&#x1f447;&#x1f447;&#x1f447;&#x1f447; &#x1f449; 有一些問題是廣搜 和 深搜都可以解決的&#xff0c;例如島嶼問題&#xff0c;這里我們記dfs的寫法就好啦&#xff0c;…

C++進階——C++11_智能指針

目錄 1、問題引入 2、RAII和智能指針 3、C標準庫的智能指針 3.1 auto_ptr (不好) 3.2 unique_ptr 3.3 shared_ptr (重點) 3.4 weak_ptr (重點) 4、shared_ptr的循環引用問題(重點) 5、shared_ptr的線程安全問題 6、C11智能指針和boost的關系 7、內存泄漏 7.1 什么是…

數據庫的基本原則

數據庫的核心原則 原子性與持久性&#xff1a;原子性&#xff08;Atomicity&#xff09;確保一個事務中的所有操作要么全部完成&#xff0c;要么完全不執行&#xff0c;不會出現部分完成的情況。持久性&#xff08;Durability&#xff09;則保證一旦事務提交成功&#xff0c;即…

Java設計模式實戰:裝飾模式在星巴克咖啡系統中的應用

一、裝飾模式簡介 裝飾模式&#xff08;Decorator Pattern&#xff09;是一種結構型設計模式&#xff0c;它允許向一個現有的對象添加新的功能&#xff0c;同時又不改變其結構。這種模式創建了一個裝飾類&#xff0c;用來包裝原有的類&#xff0c;并在保持類方法簽名完整性的前…

使用MPI-IO并行讀寫HDF5文件

使用MPI-IO并行讀寫HDF5文件 HDF5支持通過MPI-IO進行并行讀寫&#xff0c;這對于大規模科學計算應用非常重要。下面我將提供C和Fortran的示例程序&#xff0c;展示如何使用MPI-IO并行讀寫HDF5文件。 準備工作 在使用MPI-IO的HDF5之前&#xff0c;需要確保: HDF5庫編譯時啟用…

七、自動化概念篇

自動化測試概念 自動化測試是把以人為驅動的測試行為轉化為機器執行的一種過程。通常&#xff0c;在設計了測試用例并通過評審之后&#xff0c;由測試人員根據測試用例中描述的過程一步步執行測試&#xff0c;得到實際結果與期望結果的比較。在此過程中&#xff0c;為了節省人…

redis cluster 的通信機制

Redis Cluster 的通信機制是其分布式架構的核心&#xff0c;基于 Gossip 協議 和 Cluster Bus 實現節點間狀態同步與數據協調。以下是其通信機制的核心要點&#xff1a; 二進制協議&#xff1a;數據以字節流形式編碼&#xff08;如Protobuf、Thrift、MQTT、Gossip&#xff09;。…

CTF web入門之文件上傳

知識點 產生文件上傳漏洞的原因 原因: 對于上傳文件的后綴名(擴展名)沒有做較為嚴格的限制 對于上傳文件的MIMETYPE(用于描述文件的類型的一種表述方法) 沒有做檢查 權限上沒有對于上傳的文件目錄設置不可執行權限,(尤其是對于shebang類型的文件) 對于web server對于上傳…

PhotoShop學習09

1.彎曲鋼筆工具 PhotoShop提供了彎曲鋼筆工具可以直觀地創建路徑&#xff0c;只需要對分段推拉就能夠進行修改。彎曲港幣工具位于工具面板中的鋼筆工具里&#xff0c;它的快捷鍵為P。 在使用前&#xff0c;可以把填充和描邊選為空顏色&#xff0c;并打開路徑選項&#xff0c;勾…

tsconfig.json配置不生效

說明一下我遇到的問題&#xff0c;這是我的配置文件代碼的 {"compilerOptions": {"module": "none","target": "ES5","outFile": "./dist/bundle.js"} } 和我想象不同的是&#xff0c;我編譯成 js 沒…

源代碼加密之零日攻擊

# SDC沙盒&#xff1a;有效防御零日攻擊的多層防護體系 在當今復雜多變的網絡安全環境中&#xff0c;零日攻擊已成為企業面臨的重大威脅之一。零日攻擊利用尚未被公眾發現或尚未被軟件供應商修復的漏洞進行攻擊&#xff0c;具有極高的隱蔽性和破壞性。SDC沙盒作為一種先進的數…

記錄一次TDSQL網關夯住故障

環境信息&#xff1a; TDSQL-MySQL同城雙中心集群&#xff0c;集中式實例&#xff0c;一主三副本&#xff0c;每個中心兩個db副本&#xff0c;每個中心一個VIP&#xff0c;V每個IP通過硬件做負載均衡指向該中心兩個proxy&#xff0c;操作系統為麒麟v10 arm。 故障描述&#xf…

代碼隨想錄八股訓練營完結總結

&#xff01; 40天的訓練營&#xff0c;我總結了自己完整的八股文&#xff0c;后續在面試過程中可以補充 很感謝這次訓練營&#xff0c;真的高頻&#xff0c;在面試中能擊中60%以上&#xff0c;剩下的就靠平時的積累了。 感謝訓練營的小伙伴&#xff0c;很多次想偷懶&#x…

VS Code 的 .S 匯編文件里面的注釋不顯示綠色

1. 確認文件語言模式 打開 .S 文件后&#xff0c;查看 VS Code 右下角的狀態欄&#xff0c;確認當前文件的識別模式&#xff08;如 Assembly、Plain Text 等&#xff09;。如果顯示為 Plain Text 或其他非匯編模式&#xff1a; 點擊狀態欄中的語言模式&#xff08;如 Plain Te…