我大膽把玩了"Stanford Doggo"四足機器人!

  • 2019 年 12 月 27 日
  • 筆記

好久沒玩機械了,斯坦福大學的開源四足機器人項目「Stanford Doggo」成功地引起了我的興趣。它的腿部結構有別於傳統的開鏈結構,這激發了小瓦進一步探索的慾望。 ——聰明的瓦肯人


說大膽把玩 當然是要強迫她 按照我的意思 擺出我喜歡的體位 走出我側目的步調 扭出我滿意的身姿

so LET『S GOOOO! 1 我們先來看看這隻四足是怎麼回事 作為一款開源作品 自然是被我找到了模型 總體樣子就如封面所示

再來看看複雜的動力源部分

用了兩條同步齒形帶 兩個原動機 還有一個三角支架 用來抵抗皮帶張緊力帶來的彎矩 在張緊力的方向 類似於將懸臂樑轉為簡支梁 保持了皮帶張力

分解之後零件倒是不少 不過很多都是螺釘螺母

總體來說 機械結構並不複雜 腿部機構是 雙曲柄五連桿機構 鉸鏈連接 單腿自由度為2 這個五連桿很特殊 團隊使用了同軸結構 這也是整個機器最複雜部分

巧妙的同軸結構使結構更緊湊 更一般的五連桿應該是下面這樣

實際上 斯坦福團隊在doggo的介紹影片中 有一個天大的笑話

enmmmmm 這位小姐姐一定是斯坦福的宣傳委員 這字幕可不是亂加的哦! 難道美國人對連桿機構的定義 與咱中國人不一樣?

2 逼叨完了 是時候開幹了 團隊小哥Nathan Kau 最引以為豪的事 就是該項目沒有做任何模擬 一切都在現實環境下調試 不得不說

把開源模型以STP格式導入UG 我的陳年老電腦花了 6、7分鐘才載入完畢 之後又是一頓猛刪 去除不必要零件後 又以STL格式導出 再將STL模型導入V-rep 在V-rep中又是一頓實體化 添加關節 設置動力學參數 最後還搭建了單腿結構場景 用於單腿實驗

就這麼慢慢地的實驗 夾雜著之前對切比雪夫四足的理解 最後有所成功 切比雪夫機構就是曲柄搖桿機構 基於切比雪夫機構的四足之前我就做過

話不多說 最後就來獻醜了 先來看看doggo的表現

原地跳躍

聽說能達到60cm nice! 我也稍微做了一個模擬

最開始想通過python腳本遠程控制 奈何V-rep與python總是通訊失敗 只好使用自帶的Lua腳本語言了 這又花了一段時間自學了Lua 不過好歹最後成功了 你或許發現了機身的旋轉 若是細看 這是左右腿結構不對稱導致的 Doggo團隊建裝配圖的小哥怕是睡著了 送上程式碼

require "math"    if (sim_call_type==sim_childscriptcall_initialization) then      motorHandle=simGetObjectHandle("Revolute_joint")           --取得關節句柄      motorHandle0=simGetObjectHandle("Revolute_joint0")      motorHandle1=simGetObjectHandle("Revolute_joint1")      motorHandle2=simGetObjectHandle("Revolute_joint2")      motorHandle3=simGetObjectHandle("Revolute_joint3")      motorHandle4=simGetObjectHandle("Revolute_joint4")      motorHandle5=simGetObjectHandle("Revolute_joint5")      motorHandle6=simGetObjectHandle("Revolute_joint6")        targetPosition1=simGetJointPosition(motorHandle1)          --獲取關節位置      targetPosition2=simGetJointPosition(motorHandle2)        if (math.abs(targetPosition2)<0.01 and math.abs(targetPosition1)<0.01) then          simSetJointTargetPosition(motorHandle1,-1.5)           --設置關節位置          simSetJointTargetPosition(motorHandle2,1.1)          simSetJointTargetPosition(motorHandle5,1.1)          simSetJointTargetPosition(motorHandle6,-1.5)            simSetJointTargetPosition(motorHandle0,0.9)          simSetJointTargetPosition(motorHandle3,0.9)          simSetJointTargetPosition(motorHandle,-1.7)          simSetJointTargetPosition(motorHandle4,-1.7)      end  end    if (sim_call_type==sim_childscriptcall_actuation) then      targetPosition=simGetJointPosition(motorHandle)      targetPosition0=simGetJointPosition(motorHandle0)      targetPosition1=simGetJointPosition(motorHandle1)      targetPosition2=simGetJointPosition(motorHandle2)      targetPosition3=simGetJointPosition(motorHandle3)      targetPosition4=simGetJointPosition(motorHandle4)      targetPosition5=simGetJointPosition(motorHandle5)      targetPosition6=simGetJointPosition(motorHandle6)          if (math.abs(targetPosition1+1.5)<0.01 and math.abs(targetPosition4+1.7)<0.01) then          simSetJointTargetPosition(motorHandle1,0.5)          simSetJointTargetPosition(motorHandle2,-0.9)          simSetJointTargetPosition(motorHandle5,-0.9)          simSetJointTargetPosition(motorHandle6,0.5)            simSetJointTargetPosition(motorHandle0,-1.1)          simSetJointTargetPosition(motorHandle3,-1.1)          simSetJointTargetPosition(motorHandle,0.3)          simSetJointTargetPosition(motorHandle4,0.3)      end        if (math.abs(targetPosition1-0.5)<0.01 and math.abs(targetPosition4-0.3)<0.01) then          simSetJointTargetPosition(motorHandle1,-0.5)          simSetJointTargetPosition(motorHandle2,0.1)          simSetJointTargetPosition(motorHandle5,0.1)          simSetJointTargetPosition(motorHandle6,-0.5)            simSetJointTargetPosition(motorHandle0,-0.1)          simSetJointTargetPosition(motorHandle3,-0.1)          simSetJointTargetPosition(motorHandle,-0.7)          simSetJointTargetPosition(motorHandle4,-0.7)      end  end    if (sim_call_type==sim_childscriptcall_sensing) then      -- Put your main SENSING code here    end    if (sim_call_type==sim_childscriptcall_cleanup) then      -- Put some restoration code here    end

這就是子程式程式碼

非常的easy 都是在調用函數 實際上程式內聯PID程式 這裡不顯示 如果想更改速度 就去改比例控制參數P即可 OK!下一個!

對角線步態行走 沒問題

顯而易見 對角線步態 有一個靜不安定過程 在不添加IMU測姿的情況下 要想儘可能保持平穩 就得合理分配品質 說白了機身的重心要與 觸地四點對角線中心重合 除此之外 處理好追求速度與步態幅度的關係 過長的對角線雙腿滯空時間 給機身傾斜帶來了機會 其結果就是小碎步 當腿在「工進」的時候 也就是推動機身往前時 2個觸地點距離機身的位置盡量變化小 這樣機身擺動幅度較小 這個準則幾乎所有足類機器通用 就如下面的Jansen Walker連桿機構的軌跡

當然 如果加裝了IMU測姿回饋 自然就可以控制電機帶動機身平衡 程式碼如下

require "math"    if (sim_call_type==sim_childscriptcall_initialization) then      motorHandle=simGetObjectHandle("Revolute_joint")      motorHandle0=simGetObjectHandle("Revolute_joint0")      motorHandle1=simGetObjectHandle("Revolute_joint1")      motorHandle2=simGetObjectHandle("Revolute_joint2")      motorHandle3=simGetObjectHandle("Revolute_joint3")      motorHandle4=simGetObjectHandle("Revolute_joint4")      motorHandle5=simGetObjectHandle("Revolute_joint5")      motorHandle6=simGetObjectHandle("Revolute_joint6")        targetPosition1=simGetJointPosition(motorHandle1)      targetPosition2=simGetJointPosition(motorHandle2)        if (math.abs(targetPosition2)<0.01 and math.abs(targetPosition1)<0.01) then          simSetJointTargetPosition(motorHandle1,-0.5)          simSetJointTargetPosition(motorHandle2,0.1)          simSetJointTargetPosition(motorHandle5,0.1)          simSetJointTargetPosition(motorHandle6,-0.5)            simSetJointTargetPosition(motorHandle0,-0.1)          simSetJointTargetPosition(motorHandle3,-0.1)          simSetJointTargetPosition(motorHandle,-0.7)          simSetJointTargetPosition(motorHandle4,-0.7)      end  end    if (sim_call_type==sim_childscriptcall_actuation) then      targetPosition=simGetJointPosition(motorHandle)      targetPosition0=simGetJointPosition(motorHandle0)      targetPosition1=simGetJointPosition(motorHandle1)      targetPosition2=simGetJointPosition(motorHandle2)      targetPosition3=simGetJointPosition(motorHandle3)      targetPosition4=simGetJointPosition(motorHandle4)      targetPosition5=simGetJointPosition(motorHandle5)      targetPosition6=simGetJointPosition(motorHandle6)              if (math.abs(targetPosition1+0.5)<0.01 and math.abs(targetPosition4+0.7)<0.01) then              simSetJointTargetPosition(motorHandle0,0.8)              simSetJointTargetPosition(motorHandle5,0.6)              simSetJointTargetPosition(motorHandle,-0.5)              simSetJointTargetPosition(motorHandle6,-0.4)              simSetJointTargetPosition(motorHandle1,-0.8)              simSetJointTargetPosition(motorHandle2,-0.3)              simSetJointTargetPosition(motorHandle3,-0.4)              simSetJointTargetPosition(motorHandle4,-0.8)          end            if (math.abs(0.4+targetPosition6)<0.01 and math.abs(0.8-targetPosition0)<0.01) then              simSetJointTargetPosition(motorHandle0,-0.1)              simSetJointTargetPosition(motorHandle5,0.1)              simSetJointTargetPosition(motorHandle,-0.7)              simSetJointTargetPosition(motorHandle6,-0.5)          end            if (math.abs(targetPosition0+0.1)<0.01 and math.abs(targetPosition+0.7)<0.01) then              simSetJointTargetPosition(motorHandle0,-0.4)              simSetJointTargetPosition(motorHandle,-0.8)              simSetJointTargetPosition(motorHandle5,-0.3)              simSetJointTargetPosition(motorHandle6,-0.8)                simSetJointTargetPosition(motorHandle2,0.4)              simSetJointTargetPosition(motorHandle3,1.1)              simSetJointTargetPosition(motorHandle1,-0.5)              simSetJointTargetPosition(motorHandle4,-0.3)          end            if (math.abs(targetPosition4+0.3)<0.01 and math.abs(0.4-targetPosition2)<0.01) then              simSetJointTargetPosition(motorHandle2,0.1)              simSetJointTargetPosition(motorHandle3,-0.1)              simSetJointTargetPosition(motorHandle1,-0.5)              simSetJointTargetPosition(motorHandle4,-0.7)          end  end    if (sim_call_type==sim_childscriptcall_sensing) then      -- Put your main SENSING code here    end    if (sim_call_type==sim_childscriptcall_cleanup) then      -- Put some restoration code here    end

Next!

大家或許發現 這似乎不是Doggo 是的,這不是 這是Ghost Robotics的Minitaur 其腿部結構與Doggo類似 所以我也做了立定跳遠

跳遠向前的力 主要由蹬腿帶來 同時 蹬腿之前腿向後摞 底面摩擦力提供了初始向前加速度 理論上應該是靜摩擦 實際上由於摩擦力不足 最終變成了動摩擦 而且觸地時間,距離極短

立定跳遠高度為12.75cm 跳遠距離為51.64cm 差不多一個身位

一樣的,也將放上程式碼

require "math"    if (sim_call_type==sim_childscriptcall_initialization) then      motorHandle=simGetObjectHandle("Revolute_joint")      motorHandle0=simGetObjectHandle("Revolute_joint0")      motorHandle1=simGetObjectHandle("Revolute_joint1")      motorHandle2=simGetObjectHandle("Revolute_joint2")      motorHandle3=simGetObjectHandle("Revolute_joint3")      motorHandle4=simGetObjectHandle("Revolute_joint4")      motorHandle5=simGetObjectHandle("Revolute_joint5")      motorHandle6=simGetObjectHandle("Revolute_joint6")        targetPosition1=simGetJointPosition(motorHandle1)      targetPosition2=simGetJointPosition(motorHandle2)        if (math.abs(targetPosition2)<0.01 and math.abs(targetPosition1)<0.01) then          simSetJointTargetPosition(motorHandle1,-1.9)          simSetJointTargetPosition(motorHandle2,0.7)          simSetJointTargetPosition(motorHandle5,0.7)          simSetJointTargetPosition(motorHandle6,-1.9)            simSetJointTargetPosition(motorHandle0,0.6)          simSetJointTargetPosition(motorHandle3,0.6)          simSetJointTargetPosition(motorHandle,-1.9)          simSetJointTargetPosition(motorHandle4,-1.9)      end  end    if (sim_call_type==sim_childscriptcall_actuation) then      targetPosition=simGetJointPosition(motorHandle)      targetPosition0=simGetJointPosition(motorHandle0)      targetPosition1=simGetJointPosition(motorHandle1)      targetPosition2=simGetJointPosition(motorHandle2)      targetPosition3=simGetJointPosition(motorHandle3)      targetPosition4=simGetJointPosition(motorHandle4)      targetPosition5=simGetJointPosition(motorHandle5)      targetPosition6=simGetJointPosition(motorHandle6)        if (math.abs(targetPosition2-0.7)<0.01 and math.abs(targetPosition1+1.9)<0.01) then          simSetJointTargetPosition(motorHandle1,-0.9)          simSetJointTargetPosition(motorHandle2,1.7)          simSetJointTargetPosition(motorHandle5,1.7)          simSetJointTargetPosition(motorHandle6,-0.9)            simSetJointTargetPosition(motorHandle0,1.6)          simSetJointTargetPosition(motorHandle3,1.6)          simSetJointTargetPosition(motorHandle,-0.9)          simSetJointTargetPosition(motorHandle4,-0.9)        end            if (math.abs(targetPosition1+0.9)<0.01 and math.abs(targetPosition4+0.9)<0.01) then              simSetJointTargetPosition(motorHandle1,1.2)              simSetJointTargetPosition(motorHandle2,-0.4)              simSetJointTargetPosition(motorHandle5,-0.4)              simSetJointTargetPosition(motorHandle6,1.2)                simSetJointTargetPosition(motorHandle0,-0.7)              simSetJointTargetPosition(motorHandle3,-0.7)              simSetJointTargetPosition(motorHandle,1.1)              simSetJointTargetPosition(motorHandle4,1.1)          end        if (math.abs(targetPosition1-1.2)<0.01 and math.abs(targetPosition4-1.1)<0.01) then              simSetJointTargetPosition(motorHandle1,-0.4)              simSetJointTargetPosition(motorHandle2,0)              simSetJointTargetPosition(motorHandle5,0)              simSetJointTargetPosition(motorHandle6,-0.4)                simSetJointTargetPosition(motorHandle0,0)              simSetJointTargetPosition(motorHandle3,0)              simSetJointTargetPosition(motorHandle,0)              simSetJointTargetPosition(motorHandle4,0)          end  end    if (sim_call_type==sim_childscriptcall_sensing) then      -- Put your main SENSING code here    end    if (sim_call_type==sim_childscriptcall_cleanup) then      -- Put some restoration code here    end

實際上 Doggo還會後空翻

最開始我認為這不是事兒 後來我模擬過才知道 根本不是那麼回事兒 當然 也有可能是我V-rep用的不好導致失敗 下一次可能會帶給大家 奔跑、後空翻、側空翻 Bye~Bye~ 咦~~ 不對呀 開源項目 我怎麼蠢到不去看看源程式碼的演算法 我被我的愚蠢所震驚


Doggo團隊說,自己DIY一隻大概要3000美元,我就呵呵了,在我們大中國,有某寶坐鎮,我信你個鬼! ——聰明的瓦肯人