[ロボ実験記録] ロボットアームと電動ピペットで水を移動するまで

概要

以下のシステムを作るまでのまとめです。

ロボットアーム

Dobot magicianという教育用のロボットアームを使いました。15万円くらいです。

Pythonで制御するプログラムにしました。
ロボット制御で有名なROSは使っていません(使いこなせていません)。
制御プログラムは以下の記事で公開しています。

アームの先端には、3Dプリンタで作成したピペットアームのホルダを装着しました。
突貫工事で作ったので、クオリティが低いです。そのうち、改造予定です。
fusion360のデータはこちら。

ピペット

Pipetty proの特注品を使いました。
USBまたはBluetooth通信で制御が可能です。

https://www.icomes.co.jp/product/pipetty/

制御プログラムについては、以下の記事でまとめています。

制御プログラム

初期化関連

 #dobot magicianの初期化
from DobotDriver.DobotWrapper import DobotWrapper
dobot=DobotWrapper("COM3")
dobot.initiate()

#pipettyの初期化
from pipetty.Pipetty import Pipetty
pipetty = Pipetty()

#通信確認と、タイムアウト時間の設定
pipetty.check_connect()
pipetty.set_timeout()

マウス+キーボードでシステムを制御してみます。
はじめは、AIやアルゴリズムではなく人間がロボットアームを制御することで、ロボットの気持ちを知ることができます。


import tkinter as tk


start_x, start_y = 0, 0
dobot_x, dobot_y, dobot_z = 0, 0, 0
def on_drag_start(event):
    global start_x, start_y, dobot_x, dobot_y, dobot_z
    start_x, start_y = event.x, event.y
    dobot_x, dobot_y, dobot_z ,_= dobot.get_position()

def on_drag(event):
    global start_x, start_y, dobot_x, dobot_y, dobot_z
    dx, dy = event.x - start_x, event.y - start_y
    dx=dx/5
    dy=dy/5
    dobot.move_arm(dobot_x-dx, dobot_y+dy, dobot_z, 0)
    #start_x, start_y = event.x, event.y

def on_mousewheel(event):
    global dobot_x, dobot_y, dobot_z

    dobot_x, dobot_y, dobot_z ,_= dobot.get_position()
    delta = event.delta/10  # normalize delta value to -1 or 1
    dobot_z -= delta
    dobot.move_arm(dobot_x, dobot_y, dobot_z, 0)

def on_keypress(event):
    global dobot_x, dobot_y, dobot_z

    dobot_x, dobot_y, dobot_z ,_= dobot.get_position()
    delta = 5

    if event.keysym == 'Up':
        dobot_z += delta
        dobot.move_arm(dobot_x, dobot_y, dobot_z, 0)
        #pipetty.vacuum(10)
    elif event.keysym == 'Down':
        dobot_z -= delta
        dobot.move_arm(dobot_x, dobot_y, dobot_z, 0)
    elif event.keysym=="Left":
        pipetty.vacuum(10)
    elif event.keysym=="Right":
        pipetty.home()


    root.unbind('<KeyPress>')  # Unbind key press event after the first detection

def on_keyrelease(event):
    root.bind('<KeyPress>', on_keypress)  # Re-bind key press event after key release

root = tk.Tk()

frame = tk.Frame(root, width=500, height=500)
frame.bind('<Button-1>', on_drag_start)  # Button-1 is the event when left mouse button is pressed
frame.bind('<B1-Motion>', on_drag)  # B1-Motion is the event when left mouse button is held down and mouse is moved
frame.bind('<MouseWheel>', on_mousewheel) 
frame.pack()

root.bind('<Key>', on_keypress)
root.bind('<KeyRelease>', on_keyrelease)
root.mainloop()

このコードを実行すると、ウィンドウが生成されます。
その上でマウスをドラッグすると、呼応してアームのXY座標が変化します。
カーソルキーの上、下で、アームの高さを上下できます
カーソルキーの左を押すと100 uLの吸引、右を押すと全て吐き出します

自動で液体を移動する

ガラス瓶1から瓶2へ液体を移動するプログラムを作ります。

初めに、以下のコマンドを実行し、ガラス瓶のx,y座標を把握しておきます。

dobot_x, dobot_y, dobot_z ,_= dobot.get_position()
dobot_x, dobot_y, dobot_z

その結果、以下のように容器1,2の座標を指定することになりました。

container1_pos=(250, -60)
container2_pos=(229,28)

同様に、ピペットを瓶の底まで下げたとき、瓶よりも十分に上に上げたときの座標も確認して、記録します。

up_z=50
down_z=-22

液体を移動する関数を定義します。


def lift_arm(dobot,up_z):
    dobot_x, dobot_y, dobot_z ,_= dobot.get_position()
    dobot.move_arm(dobot_x, dobot_y, up_z,0)


def transfer_liquid(container1_pos,container2_pos,
                    volume,
                    up_z,down_z,inter_z):
    lift_arm(dobot,up_z)
    dobot.move_arm(container1_pos[0], container1_pos[1], up_z,0)
    lift_arm(dobot,down_z)

    pipetty.vacuum(volume)

    lift_arm(dobot,up_z)
    dobot.move_arm(container2_pos[0], container2_pos[1], up_z,0)
    lift_arm(dobot,inter_z)
    pipetty.home()

人間はピペット操作を無意識に行いますが、ロボットに対しては全てを明示的に説明する必要があります。
上記のプログラムを日本語に直すと、以下の通りになります。

  • とりあえずアームを上に上げる (lift_arm(dobot,up_z))

    • 瓶類にぶつかるのを防ぐため

  • 容器1の上にアームを移動する( dobot.move_arm(container1_pos[0], container1_pos[1], up_z,0))

  • アームを下ろす(lift_arm(dobot,down_z))

  • 液を吸う (pipetty.vacuum(volume))

  • アームを上げる

  • 容器2の上にアームを移動する(dobot.move_arm(container2_pos[0], container2_pos[1], up_z,0))

  • アームを少し下げる(lift_arm(dobot,inter_z))

  • 液を排出する(pipetty.home())

今回のプログラムでは、せっかくなので、移動操作を3回繰り返しています。
動作の様子は、改めてこちら。

センシングについて考慮すべき点

今回のプログラムでは、システムに視覚を持たせない、「目隠しUFOキャッチャー」状態で実験を行いました。
言い換えると、アームの移動先は全て、人間側で教えてあげました。

簡単な系においては、目隠しUFOキャッチャーのアプローチでOKですが、以下のような欠点もあります。

  • 物体の座標を厳密に計測・指定する必要がある(面倒)

  • 物体が(意図せず)移動した際の対応が難しい

そのため、各種センシング技術を使ってアーム座標を全自動で決定するアプローチが、潜在的には重要です。

考えられるアプローチと想定課題について、考察します

X,Y座標の決定

画像認識が重要になります。三脚にUSBカメラを固定して、見下ろしながら作業するイメージです。
以下の通り、手法はいくつかありますが、実装難度や精度に課題があったりするので、注意が必要です。

Arucoマーカー
QRコードのような人工物を容器に貼り付けておき、カメラ上での座標を簡便に認識できます。
Arucoマーカーの便利な点は、画面上の座標に加え、カメラからの距離(3次元データ)も取得できることです。

ただし、この方法で取得できるのはあくまでマーカーの位置になります。
そのため、マーカーと容器・ピペットチップの先端etcの相対位置は、予め計測しておく必要があります。

物体認識
OpenCVによる輪郭抽出やディープラーニングによって、物体そのもののを認識し、座標を取得します。

この手法は基本的には2次元画面上の座標推定となるため、三次元データ(距離)を得るには、深度カメラや、ステレオカメラが必要になります。

深度カメラは5万円くらいで販売されています。

基本的にはレーザー方式で距離を計測するのですが、ガラスのような透明材料に弱かったりします。化学実験ではガラス器具をたくさん使うので、やや困ります。
まだ使いこなせてませんが、距離精度も、完璧ではない印象です。

ステレオカメラは、人間の目のように、複数のレンズで座標を認識する方式です。原理的には、USBカメラ2台で実装できますが、あまりコードが公開されていないので、色々と難しい点があるのではないかと思っています。

Z座標

地味に難しいのは、アームの高さ方向(z座標)の制御です。

ピペットの先端が上にありすぎると、液体を吸うことができません。
一方、ピペットを降下しすぎると、地面に衝突してしまいます。

人間はどのようにしてこの問題を防いでいるかというと、、

  • 容器を横から見て、高さを合わせる(視覚)

  • ピペットの先端をそっとガラス容器の底にあてて高さを合わせる(触覚)

  • (液体が少ない場合、容器を傾ける (2つ目の手))

これをフルにロボットで代替しようとすると、

  • 自在に動く(または横視点で固定された)カメラ

  • 触覚センサ

  • 二本目のロボットアームでの協調操作

が必要になります。カメラは比較、低コストですが、後者2つは研究レベルの案件になってきます。

人間側で、軽く高さ座標を教えてあげる、クッションやバネを容器の下 or アームに装着して、高さ方向に遊びをもたせる、などの工夫をする方が、多くの人にとっては低コストなアプローチかもしれません。

まとめ

化学実験の最低限のプロセスである、試薬の移動操作を自動化する手段について紹介しました。
今後は、実際の試薬で試しつつ、以下の操作も自動化を検討したいと思います(無限にありますね..)。

  • 撹拌、過熱

  • ピペットチップの交換

  • 瓶の移動

  • 蓋の装着

  • 蓋の取り外し

  • 不活性条件での合成

  • 各種装置への接続

  • 精製

  • などなど

20年以上前の、こちらの総説も興味深いです。



この記事が気に入ったらサポートをしてみませんか?