稚暉君又釋出了新的機器人,很是強大。
在編寫時看到了稚暉君的招聘資訊,好想去試試啊!
小時候都有一個科幻夢,如今的職業也算與夢想有些沾邊了。但看到稚暉君這種閃著光芒的作品,還是很是羨慕。
以前就想做一個機械臂,實現遠端象棋對戰等功能,看到稚暉君的作品,更加心動了。心動不如行動,下面就一步一步模擬一個簡單的機器人,最終移植控制現實的機械臂,實現真正的下象棋,甚至能遠端象棋對戰。
使用【FreeCAD軟體】繪製一個簡單的Scara機械臂,使用 【coppeliasim模擬軟體】模擬前面繪製的機械臂,使用python進行模擬控制,力求實現一個能夠移動棋子的模擬程式。後續目標是,將機械臂實物話,將模擬程式與實物實現通訊控制,最終實現一個移動象棋的機械臂。然後在它的基礎上新增視覺和象棋對戰演演算法,實現能夠自主象棋對戰(淘寶上已經有象棋對戰機械臂了)。當然,後面幾項需要花時間實現,現在先實現模擬控制。
學習機械臂的操作需要學習正逆運動學模型及解算、座標系等知識,本文由於涉及的較淺,暫不做說明,後續深入使用過程中涉及到時,再做說明。
FreeCAD是一個基於OpenCASCADE的開源CAD/CAE工具。 OpenCASCADE是一套開源的CAD/CAM/CAE幾何模型核心,來自法國Matra Datavision公司,是著名的CAD軟體EUCLID的開發平臺。
我個人的感覺是,FreeCAD介面操作、功能特性、實時畫面和渲染畫面等還是不如犀牛、3DMAX等軟體的,但是上手構建簡單的三維模型很快,軟體體積小,免費,學習成本也很低,對於非專業人士進行簡單的建模很方便,建模完成後如果需要可以去犀牛等軟體進行進一步精細處理。而且進階學習可以使用python進行操作,由於是開源的,你甚至可以整合在自己的python程式中。
推薦B站UP 灰大柱 的額視訊教學 freecad基礎教學全套-灰大柱 ,他的軟體版本較老,但是基本功能一致,跟著學習完成後簡單使用沒有問題。
其實FreeCAD也是可以進行機器模擬的,它有很多外掛,而且自帶 Robot 外掛,就可以進行模擬。比如可以直接開啟起始頁的機械臂的例子:
然後在【工作臺】中選擇 【Robot】工作臺:
用【shift】同時選中左側的軌跡
然後點選【任務】進入任務介面,就會出現機器人控制檯:
此時點選【模擬軌跡】,進入運動模擬介面,點選左側的執行右箭頭就可以進行軌跡運動了(如果有軌跡的話),機械臂就會自己動起來:
也可以自己進行示教運動軌跡錄入,具體方法可以參考官網說明,也可以檢視B站的視訊教學:【FreeCAD讓機器人動起來】,經過簡單的示教錄入,就能實現機械臂運動模擬。
對於FreeCAD的機械臂模擬,我認為簡單玩一玩還是可以的,但功能肯定是沒有【coppeliasim模擬軟體】這種專業軟體完善的。所以此處不做深入學習。
瑞士CoppeliaRobotics公司的CoppeliaSim軟體是一款基於分散式控制架構,具有整合式開發環境的基於物理引擎的動力學機器人模擬器。CoppeliaSim軟體曾叫Vrep(VirtualRobotExperimentationPlatform虛擬機器器器人實驗平臺),於2019年底正式更名為CoppeliaSim。CoppeliaSim與Vrep完全相容,並豐富的功能,特別是增加了對ROS2的支援。
CoppeliaSim 原生支援LUA指令碼語言控制和python語言控制,預設為LUA語言。且檔案主要以LUA語言為主。也支援C++等語言控制。由於使用python語言,可以很方便的呼叫一些庫,包括影象處理等方面的庫,所以此處選用python語言進行控制。感興趣的可以學習lua語言進行控制。
lua語言:Lua 是一個誕生於巴西的小巧的指令碼語言,純C語言編寫,體積小巧,速度高效,其執行速度較python更快,方便移植,可裸機執行於小資源的微控制器上。而python只有第三方的microPython和pikapython才能執行在小資源的微控制器上,相容性不如lua。但lua的庫沒有python豐富,因為它更多的被用作膠水語言,所以在程式界沒有python普及。
【菜鳥教學 lua】
【lua Git】
lua語法簡單,學過C和python的很快就能入手lua。但是後面不用它,所以不多做介紹,感興趣的可以自行了解。
coppeliasim 有幾個版本,推薦下載 EDU 教育版。player版本的功能限制太多,模型也不全。
關於教學,肯定是外網的教學較多,可自行查詢,此處不做推薦。內網的很多較亂,但跟著走也是可以的。
想快速入門的可以看 B站UP的視訊【聽塵listener】
詳細視訊教學可以看 B站搬運的【Vrep / CoppeliaSim 教學(4.3版本)】(沒看過,不知道質量如何)
文字教學可以看 【CoppeliaSim(原V-REP)新手上路】 ,跟著走一遍就能搭建基本的python控制程式。
安裝完後,可以拖拽即個範例模型進行測試。很多機械臂都是現實中存在的,比如和公司的那臺大機械臂,這裡面就有一款應該是同一家的很相似的產品【FrankaEmikaPanda】,拖拽後點選【start】就可以模擬自帶的程式:
當然,還提供了很多帶程式的模型,比如小車等,還有不帶程式的模型,比如牆體、椅子等物品,可用於搭建碰撞環境。
學習機械臂控制還需要了解和學習 正逆運動學求解 、座標系、程式設計甚至電子電路、建模等知識,此處不做講解,請根據實際需要進行學習。
SCARA是Selective Compliance Assembly Robot Arm的縮寫,意思是一種應用於裝配作業的機器人手臂。它有3個旋轉關節,最適用於平面定位。比如自帶的【MTB】機械臂:
選擇Scara機械臂,一方面是因為簡單好實現,另一方面是公司之前有設計過Scara機械臂,我層深度參與過底層程式的編寫。但是由於種種原因暫時擱置,後續會繼續開發。另外目前淘寶已經有了下象棋的機器人(可自行搜尋下象棋機器人),也是類似Scara的設計。由於結構簡單,對於電機的要求也不是很高,所以實現一個Scara機械臂是入門機械臂的優先選擇。
對於像稚暉君那種機械臂,我相信,在完成Scara機械臂的製作後,實現起來也會更加容易。雖然不推薦重複造輪子,但是很多事情只有從底層一步一步趟過去,才能有到達頂峰的實力。
一個用於支撐的支撐座:
一個上下移動的短臂:
第一長臂:
第二短臂:
合起來就是上面的簡化模型:
關節 | 名稱 |
---|---|
底座 | ShapeBase |
固定短臂 | ShapeArm1 |
第一長臂 | ShapeArm2 |
第二短臂 | ShapeArm3 |
移動關節 | BaseJoint |
轉動關節1 | ArmJoint1 |
轉動關節2 | ArmJoint2 |
移動幾個關節的位置到指定地方,使其符合移動邏輯:
在左側的樹狀圖中按照邏輯順序拖拽,使其符合主從關係:
簡化模型:
simRemoteApi.start(19999)
因為python控制coppeliasim模擬軟體實際上是用的遠端埠。
剩餘的程式碼可以自行理解其意思,然後對其程式設計操作。我們使用外部python程式設計,對程式不必關心,預設即可。
此時我們可以點選 【Start】 按鈕進行模擬,並沒有什麼現象,因為沒有控制程式碼。
如果想有現象,可以開啟動力學屬性,方法如下:對選中的機械臂,點選左側的【scene object properties】進入設定頁面,點選最下面的【Show dynamic properties dialog】,將【Body is respondable】和【Body is dynamic】勾選上。分別對三個臂進行同樣處理後,再點選執行,就可以看到,機械臂一起掉到了地上,且小臂會由於重力作用亂擺。
我們簡單實用程式測試的話,不需要動力學屬性模擬,所以都取消勾選就行了,後續深入學習時再根據需要開啟。
下面,開啟python環境,進行簡單的連線測試。此處我使用的是anaconda環境的Spyder IDE。
使用python控制機械臂,需要使用官方提供好的初始化指令碼和dll檔案。首先,開啟【coppeliasim】的安裝位置,在【CoppeliaRobotics\CoppeliaSimEdu\programming】資料夾下都是各種控制環境的支援包,在本資料夾下的【legacyRemoteApi\remoteApiBindings\python\python】資料夾下,找到 sim.py和simConst.py,以及 simpleTest.py,在【legacyRemoteApi\remoteApiBindings\lib\lib\Windows】下找到remoteApi.dll(其他環境請自行按要求設定),這四個檔案,將他們複製到自己的工作區資料夾,然後在Spyder中開啟simpleTest.py。
注意:在執行python程式前,需要先在【 coppeliasim】中點選【start】執行 ,在coppeliasim中開啟執行後,開始執行python程式,如果python的控制檯輸出滑鼠的X軸座標,說明一切正常,python控制成功。
機械臂基本的操作類如下:
# -*- coding: utf-8 -*-
"""
Created on Wed Apr 5 13:11:11 2023
@author: ZNZZ
"""
# Make sure to have the server side running in CoppeliaSim:
# in a child script of a CoppeliaSim scene, add following command
# to be executed just once, at simulation start:
#
# simRemoteApi.start(19999)
#
# then start simulation, and run this program.
#
# IMPORTANT: for each successful call to simxStart, there
# should be a corresponding call to simxFinish at the end!
try:
import sim
except:
print ('--------------------------------------------------------------')
print ('"sim.py" could not be imported. This means very probably that')
print ('either "sim.py" or the remoteApi library could not be found.')
print ('Make sure both are in the same folder as this file,')
print ('or appropriately adjust the file "sim.py"')
print ('--------------------------------------------------------------')
print ('')
import time
import math
class MyArmBasicClass():
def __init__(self):
print('MyArmTest Program started')
self.clientID = 0
self.Handle={ #字典,用於儲存各個模組的控制程式碼,方便拓展和查詢;key值要和CoppeliaSim中的模組命名一致
"ShapeBase":0,
"ShapArm1":0,
"ShapArm2":0,
"ShapArm3":0,
"BaseJoint":0,
"ArmJoint1":0,
"ArmJoint2":0,
}
self.HandleOrder=( #元組有固定得的順序,所以用元組作為順序記錄,要和上面的Handle的一致
"ShapeBase",
"ShapArm1",
"ShapArm2",
"ShapArm3",
"BaseJoint",
"ArmJoint1",
"ArmJoint2",
)
def ConnectedStart(self):
'''
連線初始化。
需要首先在 CoppeliaSimEdu 中點選執行,再執行本python程式,才能正確連線。
返回-1表示連線失敗
Returns
-------
TYPE
DESCRIPTION.
'''
sim.simxFinish(-1) # just in case, close all opened connections
self.clientID=sim.simxStart('127.0.0.1',19999,True,True,5000,5) # Connect to CoppeliaSim
if self.clientID!=-1:
print ('Connected to remote API server')
return 0
else:
print ('Connected faile,please check!')
return -1
def GetArmHandle(self):
'''
獲取各個模組的控制程式碼,用於操控
有些模組的控制程式碼獲取不到
'''
for i in self.HandleOrder:
self.Handle[i] = sim.simxGetObjectHandle(self.clientID, i, sim.simx_opmode_blocking) #獲取控制程式碼,返回兩個值,一個是ret,用於判斷是否獲取成功,一個是obj,表示控制程式碼號,兩個值以元組的方式存到Handle中
print(self.Handle[i][0])
if self.Handle[i][0] != sim.simx_return_ok:
print("Get "+i+" Handle Error!!")
else:
print("Get "+i+" Handle OK!!")
#實測可以看到,只獲取了 底座和三個關節的控制程式碼,所以能操控底座的位置,能操控三個關節的角度,但是不能操作三個臂
def GetShapeBasePosition(self):
'''
獲取底座的位置
'''
if self.clientID!=-1:
if self.Handle[self.HandleOrder[0]][0] != sim.simx_return_ok:
print("Get "+self.HandleOrder[0]+" Handle Error!!")
else:
ret, arr = sim.simxGetObjectPosition(self.clientID, self.Handle[self.HandleOrder[0]][1], -1, sim.simx_opmode_blocking)
print(ret,arr)
return ret, arr
else:
print("Something Error!!")
def SetShapeBasePosition(self,X,Y,Z):
'''
設定底座的位置
Parameters
----------
(X,Y,Z) : TYPE
DESCRIPTION:目標座標(世界座標系)
Returns
-------
None.
'''
if self.clientID!=-1:
if self.Handle[self.HandleOrder[0]][0] != sim.simx_return_ok:
print("Get "+self.HandleOrder[0]+" Handle Error!!")
else:
sim.simxSetObjectPosition(self.clientID, self.Handle[self.HandleOrder[0]][1],-1,(X,Y,Z), sim.simx_opmode_blocking)
print("Set ShapeBase Pos to X:"+str(X)+" Y:"+str(Y)+" Z:"+str(Z))
else:
print("Something Error in SetShapeBasePosition!!")
def GetJointAngle(self,num):
'''
獲取旋轉關節的角度
Parameters
----------
num : TYPE:控制哪個joint關節
DESCRIPTION:可以輸入 0 1 2 分分別表示 ShapeBase ArmJoint1 ArmJoint2
也可以直接輸入字串 ShapeBase ArmJoint1 ArmJoint2
Returns
-------
None.
'''
if self.clientID!=-1:
if self.Handle[self.HandleOrder[0]][0] != sim.simx_return_ok:
print("Get "+self.HandleOrder[0]+" Handle Error!!")
else:
if str(type(num)) == "<class 'int'>": #先判斷輸入的num型別
if num==1:
targetObj_Revolute_joint = "ArmJoint1"
elif num == 2:
targetObj_Revolute_joint = "ArmJoint2"
elif num == 0:
targetObj_Revolute_joint = "ShapeBase"
else:
print("Joint num Error !!")
return
elif str(type(num)) == "<class 'str'>":
targetObj_Revolute_joint = num
else:
print("Joint num type Erroe,Pleace give 1 or 2 or stringName")
position = sim.simxGetJointPosition(self.clientID, self.Handle[targetObj_Revolute_joint][1], sim.simx_opmode_blocking)
print("Joint "+targetObj_Revolute_joint + " Angle is "+str(position))
return position
else:
print("Something Error in GetJointAngle!!")
def SetJointAngle(self,num,angle):
'''
設定關節角度/位置
對於旋轉關節,是設定角度值,內部需要轉換為弧度;對於移動關節,我還沒搞明白其單位,推測是米,所以mm需要除以1000
Parameters
----------
num : TYPE:控制哪個joint關節
DESCRIPTION:可以輸入 0 1 2 分分別表示 ShapeBase ArmJoint1 ArmJoint2
也可以直接輸入字串 ShapeBase ArmJoint1 ArmJoint2
angle : TYPE 對於 ArmJoint1 ArmJoint2 ,為旋轉得到角度值,對於ShapeBase,就是拉伸,暫時沒注意具體拉伸多少
DESCRIPTION.
Returns
-------
None.
'''
if self.clientID!=-1:
if self.Handle[self.HandleOrder[0]][0] != sim.simx_return_ok:
print("Get "+self.HandleOrder[0]+" Handle Error!!")
else:
if str(type(num)) == "<class 'int'>": #先判斷輸入的num型別
if num==1:
targetObj_Revolute_joint = "ArmJoint1"
elif num == 2:
targetObj_Revolute_joint = "ArmJoint2"
elif num == 0:
targetObj_Revolute_joint = "ShapeBase"
else:
print("Joint num Error !!")
return -1
elif str(type(num)) == "<class 'str'>":
targetObj_Revolute_joint = num
else:
print("Joint num type Erroe,Pleace give 1 or 2 or stringName")
return -1
if targetObj_Revolute_joint == "ArmJoint1" or targetObj_Revolute_joint == "ArmJoint2":
setangle = angle*math.pi/90#角度要轉為弧度,但是弧度計算不是 A*π/180 嗎,此處90才是正常的?
else:
#ShapeBase 關節不是角度,是執行,目前還沒弄清數值與實際運動的關係
setangle = angle
sim.simxSetJointPosition(self.clientID, self.Handle[targetObj_Revolute_joint][1], setangle, sim.simx_opmode_blocking)
print("Set " + targetObj_Revolute_joint + "Angle to "+str(angle))
else:
print("Something Error in GetJointAngle!!")
def ConnectedStop(self):
'''
斷開操控連線
Returns
-------
None.
'''
if self.clientID != -1:
# Before closing the connection to CoppeliaSim, make sure that the last command sent out had time to arrive. You can guarantee this with (for example):
sim.simxGetPingTime(self.clientID)
sim.simxStopSimulation(self.clientID, sim.simx_opmode_oneshot)
# Now close the connection to CoppeliaSim:
sim.simxFinish(self.clientID)
print("Connecte Stoped!!")
else:
print("Pleace check clientID !")
下面,寫一個簡單的控制函數,實現三個關節的迴圈控制:
def main():
'''
簡易機械臂控制測試
'''
MyArmClass = MyArmBasicClass()
ret = MyArmClass.ConnectedStart()
if ret == 0: #連線成功
MyArmClass.GetArmHandle()
MyArmClass.GetShapeBasePosition()#獲取底座座標
position1 = MyArmClass.GetJointAngle("ArmJoint1") #獲取三個關節的角度/位置
position2 = MyArmClass.GetJointAngle("ArmJoint2")
position1 = MyArmClass.GetJointAngle("BaseJoint")
movedir = 0
movecount = 0
movestep = 5 #步進量,可用於調速
moveangle = 90 #目標角度
#讓機械臂迴圈流暢動起來
while(True):
if movedir == 0:
movecount = movecount-movestep
if movecount < -moveangle*movestep:
movedir = 1
elif movedir == 1:
movecount = movecount+movestep
if movecount > moveangle*movestep:
movedir = 0
MyArmClass.SetJointAngle("ArmJoint1",movecount/10)
MyArmClass.SetJointAngle("ArmJoint2",movecount/10)
MyArmClass.SetJointAngle("BaseJoint",movecount/5000) #可以看到機械臂在上下運動
MyArmClass.ConnectedStop() #若是需要斷掉,請主動呼叫
else:
print("Connected Error!! Pleace check and retry!!")
print("需要先在 coppeliasim 軟體中開啟模擬,再開始本python才能正確遠端連線!!")
if __name__ == '__main__':
#multiprocessing.freeze_support()
main()
後續會進一步實現運動學解算、象棋夾起放置等步驟,還會簡單實現一個對應的硬體,以實現實際控制。
以上完整的資料都放在【ChessRobot_PythonControlCoppeliasimModel】
本文水平有限,內容很多詞語由於知識水平問題不嚴謹或很離譜,但主要作為記錄作用,希望以後的自己和路過的大神對必要的錯誤提出批評與指點,對可笑的錯誤請指出來,我會改正的。
另外,轉載使用請註明作者和出處,不要刪除檔案中的關於作者的註釋。
隨夢,隨心,隨願,恆執念,為夢執戰,執戰蒼天! ------------------執念執戰