第1章:避障狀態機
想象我們擁有一架超級智能的無人機,它可以完全自主飛行
現在,假設這架無人機需要穿過一個擺滿傢俱的雜亂房間,或者在有許多樹木的森林中導航,而且不能發生碰撞。
它是如何知道每個時刻該做什麼的?當出現障礙物時,它如何決定何時起飛、何時前進或何時減速?
這就是**避障狀態機(AvoidanceStateMachine)**的用武之地
可以將其視為無人機避障的大腦,是它在複雜環境中導航的自動駕駛系統。它的主要任務是引導無人機完成飛行的不同階段,確保即使在缺乏區域詳細地圖的情況下也能避開障礙物。
避障狀態機解決了什麼問題?
讓我們以無人機在雜亂環境中飛行為具體例子。無人機不能隨機飛行,它需要一種結構化的方式來管理其行為。它需要:
- 啓動:做好準備,檢查系統。
- 等待指令:保持空閒狀態,直到收到指令。
- 起飛:安全上升到一定高度。
- 執行任務:飛向目標,但關鍵是要避開沿途的障礙物。
- 降落:安全下降並停止。
AvoidanceStateMachine負責管理整個生命週期,確保無人機知道每個步驟該做什麼,並對周圍環境做出適當反應。它就像是無人機飛行操作的高度組織化的管理者,專注於避障功能。
理解“狀態機”概念
“狀態機”聽起來可能很複雜,但其實是一個非常常見的概念。以交通信號燈為例:
- 它有狀態:
紅燈、黃燈、綠燈。 - 它根據規則在狀態之間轉換(例如,
綠燈幾秒後變為黃燈,黃燈幾秒後變為紅燈)。
AvoidanceStateMachine的工作原理類似。無人機始終處於一個特定的“狀態”,並根據傳感器信息和指令在這些狀態之間切換。
在Avoid-MPC中,AvoidanceStateMachine有五個主要狀態:
|
狀態
|
描述
|
類比(無人機的生命週期)
|
|
|
無人機系統正在啓動,準備就緒。它正在檢查傳感器和內部組件。
|
剛拆箱的無人機,正在啓動。
|
|
|
無人機已初始化並準備就緒,但正在等待開始任務的指令(例如“起飛”)。
|
無人機停在地面上,電機關閉,等待起飛。
|
|
|
無人機正在起飛並上升到目標飛行高度。
|
無人機緩慢升空。
|
|
|
無人機正在執行其主要任務,例如飛向特定目標或探索區域,同時主動避開障礙物。
|
無人機在房間中飛行,避開傢俱。
|
|
|
無人機正在安全下降並降落。
|
無人機緩緩返回地面。
|
避障狀態機如何引導無人機
讓我們看看AvoidanceStateMachine如何協調一次典型的飛行任務:
- 初始化(
INIT):當Avoid-MPC系統啓動時,AvoidanceStateMachine初始化。它建立與ROS(機器人操作系統)的連接,以便與傳感器和控制系統通信。 - 等待(
WAIT):初始化完成後,它耐心等待重要數據,例如當前位置(稱為“里程計”數據)。一旦收到這些關鍵信息,它就知道可以接收指令了。 - 起飛(
TAKEOFF):收到“起飛”指令(可能來自操作員或其他系統)後,AvoidanceStateMachine向無人機發送命令,使其上升到安全高度。 - 任務執行(
TASK):達到目標高度後,無人機進入TASK狀態。這裏是避障功能真正發揮作用的地方
- 它持續監聽傳感器數據(例如深度攝像頭“看到”障礙物,IMU/里程計感知自身運動)。
- 利用這些數據確定障礙物的位置。
- 然後使用高級技術(如高級MPC(模型預測控制))規劃安全路徑,在避開檢測到的障礙物的同時到達目標。
- 最後,向無人機發送精確的控制指令(例如“前進”、“左轉”、“減速”)。
- 降落(
LAND):任務完成或收到“降落”指令後,AvoidanceStateMachine切換到LAND狀態,引導無人機返回地面。
以下是AvoidanceStateMachine在飛行任務中與無人機系統不同部分交互的簡化流程:
代碼
讓我們看看這個“狀態機”在Avoid-MPC代碼中是如何實現的。
1. ProcessState枚舉
不同的狀態(INIT、WAIT、TAKEOFF、TASK、LAND)在C++中使用enum定義。這有助於組織代碼並明確無人機當前所處的狀態。
// 來源:roswrapper/ros/src/avoid_mpc/include/AvoidanceStateMachine.h
enum ProcessState {
INIT = 0u,
WAIT = 1u,
TAKEOFF = 2u,
TASK = 3u,
LAND = 4u
};
這段代碼展示了ProcessState枚舉,它是一組標籤,AvoidanceStateMachine用它們來跟蹤無人機當前的行為
INIT賦值為0,WAIT為1,依此類推。
2. 主循環(Step函數)
AvoidanceStateMachine有一個名為Step的特殊函數,它會像心跳一樣反覆運行
這個函數負責狀態轉換。它檢查當前狀態並決定執行什麼操作或切換到哪個狀態。
// 來源:roswrapper/ros/src/avoid_mpc/src/AvoidanceStateMachine.cpp(簡化版)
void AvoidanceStateMachine::Step(const ros::TimerEvent &event) {
double curTime = ros::Time::now().toSec(); // 獲取當前時間
switch (mStateProcess) { // 檢查當前狀態
case INIT: {
// INIT狀態代碼:檢查是否收到里程計數據
if (mbIsReceiveOdom) {
ROS_INFO("收到里程計數據,等待觸發。");
mStateProcess = WAIT; // 切換到WAIT狀態
}
break;
}
case WAIT: {
// WAIT狀態代碼:檢查起飛指令
if (mStatueQuad == quadrotor_msgs::BfctrlStatue::BFCTRL_STATUS_WAITINGCMD) {
ROS_INFO("觸發,起飛。");
mStateProcess = TAKEOFF; // 切換到TAKEOFF狀態
}
break;
}
case TAKEOFF: {
// TAKEOFF狀態代碼:發送起飛指令,檢查高度
if (mPos.z() < 0.6 * mHeight) {
// 仍在起飛,發佈起飛消息
quadrotor_msgs::TakeoffLand takeoffMsg;
takeoffMsg.takeoff_land_cmd = quadrotor_msgs::TakeoffLand::TAKEOFF;
mPubTakeoffLand.publish(takeoffMsg);
} else {
ROS_INFO("達到目標高度,開始任務。");
mStateProcess = TASK; // 切換到TASK狀態
}
break;
}
case TASK: {
// TASK狀態代碼:處理傳感器數據,規劃路徑,發送指令
// ...(MPC規劃和避障的複雜邏輯)...
PubCmd(u); // 發佈控制指令
break;
}
case LAND: {
// LAND狀態代碼:發送降落指令
quadrotor_msgs::TakeoffLand msg;
msg.takeoff_land_cmd = quadrotor_msgs::TakeoffLand::LAND;
mPubTakeoffLand.publish(msg);
break;
}
}
// 可視化代碼(如果啓用,在所有狀態下運行)
PtCloudVisualization();
ObstacleVisualization(mVecObstacles);
}
Step函數由定時器(這裏是ros::Timer)定期調用
- 內部的
switch語句檢查mStateProcess變量,該變量保存了狀態機的當前狀態。 - 根據狀態,它執行不同的代碼塊。例如,在
INIT狀態下,它等待里程計數據。一旦收到數據,就將mStateProcess更新為WAIT,導致下一次Step調用執行WAIT狀態的邏輯。
3. 接收數據(輸入)
AvoidanceStateMachine需要了解現實世界的情況。
通過傳感器數據和其他無人機系統獲取這些信息。它使用稱為“回調”的特殊函數,這些函數在新數據到達時觸發。
// 來源:roswrapper/ros/src/avoid_mpc/src/AvoidanceStateMachine.cpp(簡化版)
void AvoidanceStateMachine::OdomCallback(const nav_msgs::OdometryPtr &msg) {
mbIsReceiveOdom = true; // 收到里程計數據後設置標誌為true
// 存儲無人機的位置(mPos)、方向(mQuat)和速度(mVel)
mPos = Eigen::Vector3d(msg->pose.pose.position.x, /* ... */);
mQuat = Eigen::Quaterniond(msg->pose.pose.orientation.w, /* ... */);
mVel = Eigen::Vector3d(msg->twist.twist.linear.x, /* ... */);
}
void AvoidanceStateMachine::IMUCallback(const sensor_msgs::ImuConstPtr &msg) {
// 存儲無人機的加速度(mAcc)並可能優化方向
// ... 濾波和計算 ...
mAcc = mQuat.toRotationMatrix() * accbFiltered - Eigen::Vector3d(0, 0, 9.81);
}
void AvoidanceStateMachine::DepthCallback(const sensor_msgs::ImageConstPtr &msg) {
// 處理深度圖像以檢測障礙物並將其添加到地圖中
// ... 轉換並使用mKeyFrameMap更新地圖 ...
mKeyFrameMap.AddVertex(Twb, msg); // 將深度數據添加到障礙物地圖
}
這些回調函數就像是專用郵箱。(調用存儲)
OdomCallback接收里程計消息(告訴無人機其位置和速度)IMUCallback獲取IMU(慣性測量單元)數據以確定方向和加速度DepthCallback處理深度攝像頭的圖像以“看到”障礙物。
AvoidanceStateMachine利用所有這些傳入數據更新其對世界和無人機狀態的內部理解。這裏提到的mKeyFrameMap是框架KD地圖(雙KD樹)的一部分,我們稍後會探討
4. 發送指令(輸出)
一旦AvoidanceStateMachine處理完信息並決定下一步行動,它需要告訴無人機該做什麼。
通過發佈控制指令來實現這一點。
// 來源:roswrapper/ros/src/avoid_mpc/src/AvoidanceStateMachine.cpp(簡化版)
void AvoidanceStateMachine::PubCmd(std::vector<double> &u) {
quadrotor_msgs::Command cmdMsg;
cmdMsg.header.stamp = ros::Time::now();
cmdMsg.mode = quadrotor_msgs::Command::ACCELERATION_MODE;
cmdMsg.acceleration.x = u[0]; // 發送X方向的期望加速度
cmdMsg.acceleration.y = u[1]; // 發送Y方向的期望加速度
cmdMsg.acceleration.z = u[2]; // 發送Z方向的期望加速度
cmdMsg.yaw = 0; // 暫時保持偏航(繞垂直軸旋轉)固定
mPubCmd.publish(cmdMsg); // 發佈指令
}
void AvoidanceStateMachine::PubSlowDownCmd() {
// 根據當前速度和加速度計算減速加速度
Eigen::Vector3d accSlow = -mVel * mParamSlowDownKp - mAcc * mParamSlowDownKd;
// ... 對加速度施加限制 ...
quadrotor_msgs::Command cmdMsg;
// ... 填充減速加速度的cmdMsg ...
mPubCmd.publish(cmdMsg); // 發佈減速指令
}
PubCmd函數創建一個Command消息,並用計算出的期望加速度(來自高級MPC(模型預測控制))填充它。
PubSlowDownCmd是一個特殊指令,用於在檢測到緊急危險時讓無人機快速減速。這些=消息被髮送到無人機的底層飛行控制器,後者執行實際的物理運動。
總結
AvoidanceStateMachine確實是Avoid-MPC項目的“大腦”
作為中央協調器,管理無人機從啓動到降落的整個任務,重點是安全避開障礙物。通過將複雜問題分解為可管理的狀態並根據實時數據在這些狀態之間轉換,它使無人機能夠在動態環境中執行復雜的避障行為。
現在我們已經瞭解了整體管理器,接下來讓我們看看它用於決策的關鍵信息:關於無人機位置和方向的輸入數據。