2016年12月15日木曜日

pyode vpython manipulator マニピュレータ アームがボール位置に自動で回転 羽鼠昆糞投記



2017/8/26再考

#アーム同士 アームと床の衝突回避に問題が残る

# 'sキーで「世界」の進行を止め' 'gキーで「世界」の進行を進めたり'
# 0, 1, 2, 3, 4, 9 でジョイント番号を選択して動かすジョイントを選択
# 9 全自動でボールを拾いにいく 予定.....
# ジョイントを動かしている状態でジョイント番号を変えると
# マシンが分解する。
# t でマシンを停止してからジョイント番号を変える 
# j で動き kで逆方向へ動く
# 指同士の接触を検知するのが難しいのでデータをファイルに書き込んで
# それを2回目の動作からは参照している。
# 詳しいことは忘れてしまった。 geomの接触検知でしても検知が遅いせいか
# マシンが分解してしまう。
# coding: UTF-8
# 2017/02/01




= pyode manipulator
[2016-12-15 04:55]

L:\goolgedrive\myprg_main\python_my_prg\wxpython\manipulator13_yubi.py
上ファイルにて、真上から見た平面上のランダム位置からおちてくる、一個のボール
のマニピュレータに対する角度を検知して、その方向にアームが向くようにす
る。
要するに ボールの落ちた方向にアームが向く。

if __name__=='__main__':
    .........
    .........
    while True:    
        kakudo = control()
        jointList[1].set_kaiten_kakudo2(kakudo)
        #jointList[1].set_kaiten_kakudo2(0.011)
        #jointList[index_m].set_kaiten_kakudo()

controlで一個のボールの位置をkakudoでかえしてやる。
vpythonでは上方向がy軸。それから右手方向へx軸。手前側へz軸。
で、ボールのposのx、z成分をかえすことになる。

それを    set_kaiten_kakudo2(self, theta2)に引数として送ると、それを目標
角度として回転する。

controlの中の以下のコードで角度を得ている。
    kakudo = math.atan2(p_b[2], p_b[0])

atan2(y,x)は、tan=x/yの逆関空?になっている。 座標のyとxをいれたら
角度が求まる。引数の順序がyから始まっているので注意。
アームは初期状態が角度0。
    j入力でマイナス方向で、うえからみて反時計まわり
    k入力でプラス方向で、 うえからみて 時計まわり
これはvpythonのhingejointの設定によるものか....
これにtanの通常の座標をどうあてはめたらいいか、わからなかった。
x軸はそのままとしたら、-zの方向が通常のy+方向になるから....
結局カット&トライで、上のcontrolの引数の順序、-を付け加えなければいけ
ないのか、-90度しなければいけないのか、いろいろして上のものに落ち着
く。

#jointList[index_m].set_kaiten_kakudo()
として、手動でアームを回転させるコマンドをころしている。
そうしないと「自動」でボールの位置にアームが回転しない。
以下 全コード

# coding: UTF-8
# 2012/9/8 09:13
#マニピュレータ   アームの作成から 2012/09/09 ニ完成
#アーム2個 第一アーム水平回転 第二アーム固定
# j k で回転(正転、逆転) 1 2 ...でジョイントを動かすジョイントを選択
#初期起動時 キーボードになかなか反応しない。ずーと押していると反応する
#台座 支柱のジョイントを回転運動+ヒンジの関節運動にしたいが
#hing2 UniversalJointはgetAngle2のコマンドがないため、台座2をつけて
#それを HingeJoint2つで実現してみる

# 2016/12/10
# 「指」の取り付け。これでものをはさむ
# これをどうやってつけるか?
# ランダムな位置にボールを一個落とす。その位置にアームを自動で動かす事
# ができた。

import math
import random
import ode
from visual import *

#ここでは、台座、支柱、アームすべてboxで表現している
#paraListからmetaSetJointでジョイントを作成(各種ジョイント作成関数に飛んで
#いる)
#paraListからmetaBoxでboxオブジェクトを作成している(Box関数を利用)

index = 1        # 動かすジョイントの選択
paraList = []    #ここで使われるbox型オブジェクトの全てのデータのリスト
#box_kosuu = 5
box_kosuu = 4
#box_kosuu = 3    #box型オブジェクトの個数
objList  = []   #box型オブジェクトのリスト

#ball_kosuu=500
ballList = []

jParaList = []    #全てのジョイントのパラメータのリスト
jointList = []    #jParaListから作成されたジョイントのリスト
joint_kosuu = 4
#joint_kosuu = 3

#上のように宣言することにより appendコマンドだけで関数内
#クラス内でグローバル宣言せずにリストに追加できる


#パラメータ このマニピュレータで使われるbox型オブジェクトのデータを、
#paraListで設定
##c言語のodeの座標からpyodeの座標へ(zとyを入れ替えるだけ)
paraList = [
        {"pos":(0,       0.50, 0 ), "m_density":2,  "v_x":2.0, "v_y":1.00, "v_z":2.0, "v_color":color.red},  #台座1
        {"pos":(0,       1.50, 0 ), "m_density":2,  "v_x":2.0, "v_y":1.00, "v_z":2.0, "v_color":color.orange},  #台座2
        {"pos":(0,    4.50, 0 ), "m_density":0.01,  "v_x":0.5, "v_y":5.00, "v_z":0.5, "v_color":color.white},#第一アーム
        {"pos":(2.75,    6.75, 0 ), "m_density":0.01,  "v_x":5.0, "v_y":0.50, "v_z":0.5, "v_color":color.blue}#第2アーム
        ]

        #{"pos":(6.0,  6.75, 0 ), "m_density":0.01,  "v_x":10.0, "v_y":0.50, "v_z":4.0, "v_color":color.blue},#第2アーム
        #{"pos":(2.75,   13.50, 0 ), "m_density":2,  "v_x":2.0, "v_y":2.00, "v_z":2.0, "v_color":color.yellow} 
        #{"pos":(7.75,    6.75, 0 ), "m_density":2,  "v_x":5.0, "v_y":0.50, "v_z":0.5, "v_color":color.cyan}        #実際には無効なコード


class Field:
    scene = display(autoscale=0 ,forward=norm((-2.0,-1.0,-1.0)))
   
    #物理世界を作成
    world=ode.World()
    world.setGravity((0, -9.81, 0))
    #自由落下させたいなら worldのコメントアウトのほうを生かし
    # 最後の方の ball.body.addForce((0,-1000,0)) を殺す
    #world.setGravity((0, 0, 0))
    #地平面を作成?
    space=ode.Space()

    #衝突関係
    jointgroup=ode.JointGroup()
    ode_floor=ode.GeomPlane(space,(0,1,0),0)
    #()なかの引数 上の例ではy軸方向に重力が発生
    #多分 spaceの最後の引数が y軸の座標を示す
    ode_floor.setCategoryBits(6)    #0111
    ode_floor.setCollideBits(6)        #0111
    ode_floor.viz=box(
        pos=(0,-0.5,0),
        width=20,length=20,height=1.0,color=(0.5,0.5,1.0))

    vball_x=sphere( pos=(5, 0, 0), radius=0.2, color=color.red)  #x軸
    vball_y=sphere( pos=(0, 5, 0), radius=0.2, color=color.white)#y軸
    vball_z=sphere( pos=(0, 0, 5), radius=0.2, color=color.yellow) #z軸

    target_fps=30
    #target_fps=30だから1秒間に30回の処理をする
    dt=1.0/target_fps

    def near_callback(self,args,geom1,geom2):
        #isBall = ((ground == o1) || (ground == o2));
        #if (isGround)//地面だけと衝突判定
        for c in ode.collide(geom1,geom2):
            c.setBounce(0.5)
            c.setMu(50)#摩擦
            c.setMu2(50)
            #random.seed(1)
            j=ode.ContactJoint(self.world,self.jointgroup,c)
            j.attach(geom1.getBody(),geom2.getBody())

    def tick(self):
        #print "tick"
        for i in  range(len(ballList)):
            ballList[i].update()
        for i in  range(box_kosuu):
            objList[i].update()
        time.sleep(0.1)
        self.space.collide((),self.near_callback)
        self.world.step(self.dt)
        self.jointgroup.empty()
        return True

#落ちるボール一個分のクラス MetaBallクラスでこれを利用する
class Ball:
    def __init__(
            self,field,
            m_density=1,
            m_radius=0.2,
            g_radius=0.2,
            v_radius=0.2,
            b_pos=vector(0,0,0),
            b_sp =vector(0,0,0),
            v_color=color.cyan
            ):
        self.pos = (0, 0, 0)

        self.body=ode.Body(field.world)
        M=ode.Mass()
        M.setSphere(m_density, m_radius)
        #(密度,半径)
        self.body.setMass(M)
        self.body.setPosition(b_pos)

        #self.body.addForce(b_sp)
        self.geom=ode.GeomSphere(
                space=field.space,
                radius=g_radius
                )
        #このradiusが実際のバウンドを決める
        #多分 sphereの座標が0で radiusが0.3だから
        #posが0.3ぐらいのバウンド点になる
        self.geom.setCategoryBits(1)  #0001
        self.geom.setCollideBits(3)   #0011
        #ボール同士の衝突を避ける
        #上は Boxのジオメトリを作成
        self.geom.setBody(self.body)
        # 剛体にジオメトリをセット
        #self.frame = frame()
        #self.vball=sphere(
        #    frame=self.frame, radius=v_radius,
        #    color=v_color
        #    )
        self.vball=sphere(
            radius=v_radius,
            color=v_color
            )

    def update(self):
        #pos_tup = self.body.getPosition()
        #boxのジオメトリの位置を得る
        #self.frame.pos = pos_tup
        #mat = self.body.getRotation()

        self.pos = self.geom.getPosition()
        mat = self.geom.getRotation()
        #self.frame.pos = pos
        #self.frame.axis = mat[0], mat[3], mat[6]
        #self.frame.up = mat[1], mat[4], mat[7]
        self.vball.pos = self.pos
        self.vball.axis = mat[0], mat[3], mat[6]
        self.vball.up = mat[1], mat[4], mat[7]

class MetaBall:
    #落ちるボールを4×4個作成、落下させてgeomの状態をみる
    def __init__(self, field):
        pos=vector(0,0,0)
        #for x in range(-6, 6):
        #    for z in range(-6, 6):
        #        pos.x = x
        #        pos.z = z
        #        pos.y = 10
        #        ballList.append(Ball(field, b_pos=pos))

        pos.x = random.uniform(-6, 6) #-6以上6未満の実数を発生
        pos.z = random.uniform(-6, 6)
        #pos.x = 0
        #pos.z = 4
        pos.y = 10
        ballList.append(Ball(field, b_pos=pos))


class Box:
    def __init__(
            self, field, flg=1,
            m_density=0.01,
            m_x=1, m_y=5, m_z=1,
            g_x=1, g_y=5, g_z=1,
            v_x=1, v_y=5, v_z=1,
            b_pos=vector(0,2.5,0),
            b_sp =vector(0,0,0),
            v_color=color.cyan
            ):
        if flg==1:
            #デフォルトで...(vpthonのパラメータだけですます)
            self.m_x = v_x
            self.m_y = v_y
            self.m_z = v_z
            self.g_x = v_x
            self.g_y = v_y
            self.g_z = v_z
        if flg==0:
            self.m_x = m_x
            self.m_y = m_y
            self.m_z = m_z
            self.g_x = g_x
            self.g_y = g_y
            self.g_z = g_z

        self.pos = (0, 0, 0)
       
        self.body=ode.Body(field.world)
        M=ode.Mass()
        M.setBox(m_density, self.m_z, self.m_y, self.m_x)#密度,z,y,x w,h,l
        self.body.setMass(M)
        self.body.setPosition(b_pos)
        self.geom = ode.GeomBox(
                space=field.space, lengths=(self.g_x, self.g_y, self.g_z)#w,h,l
                )
        self.geom.setCategoryBits(2)    #0010
        self.geom.setCollideBits(5)        #0101
        #アーム同士の衝突を避ける
        self.geom.setBody(self.body)
        self.f = frame()
        self.f.pos = b_pos
        self.vbox = box(frame=self.f,
                length=v_x, height=v_y, width=v_z, color=v_color)
        mat=self.geom.getRotation()
        self.f.axis=(mat[0],mat[3],mat[6])
        #self.f.axis=(1, 1, 1)
        #self.vbox.up=mat[1],mat[4],mat[7]
        #setRotationとmatの表示が微妙にずれるのでうえのコードで
        #vpytonのオブジェクトをmatで表示してからまたvpythonの
        #回転こまんどで微調整をしよう
   
        #self.geom.setRotation([0.866,0,0.5, 0,1,0, -0.5,0,0.866])
        # opacity :透明度
        #BoxのMassの大きさ,geomの大きさ,
        #   vboxの大きさを同じにする
        #print m_x,m_y,m_z

    def update(self):
        #print 'box_update'
        #boxのジオメトリの位置を得る
        self.pos = self.geom.getPosition()
        mat=self.geom.getRotation()

        #pos = self.body.getPosition()
        #mat=self.body.getRotation()
        self.f.pos = self.pos
        self.f.axis=(mat[0],mat[3],mat[6])

def metaBox(field):
    for i in range(box_kosuu):
        pos0     = paraList[i]["pos"]
        density0 = paraList[i]["m_density"]
        v_x0     = paraList[i]["v_x"]
        v_y0     = paraList[i]["v_y"]
        v_z0     = paraList[i]["v_z"]
        v_color0 = paraList[i]["v_color"]
        objList.append(Box(field, flg=1, b_pos=pos0,
            m_density=density0, v_x=v_x0, v_y=v_y0, v_z=v_z0, v_color=v_color0))


class SetFixedJoint:
    def __init__(self, field, name, obj1, obj2):
        #FixedJointで固定 
        self.name = "fixed"
        self.joint = ode.FixedJoint(field.world)
        if obj2=="env":
            self.joint.attach(obj1.body, ode.environment)
        else:
            self.joint.attach(obj1.body, obj2.body)
        self.joint.setFixed()

class SetHingeJoint:
    def __init__(self, field, name,obj1, obj2, anchor, j_axis, hani_kakudo):
        #theta  関節の目標とする角度 関節をどの方向へ曲げるか決める
        self.name = name
        self.joint = ode.HingeJoint(field.world)
        self.joint.attach(obj1.body, obj2.body)
        self.joint.setAnchor(anchor)
        self.joint.setAxis(j_axis)

        self.tmp1   = 0
        self.theta1 = 0
        self.z1     = 0
        self.tmp2   = 0
        self.theta2 = 0
        self.z2     = 0

        self.hani_kakudo = hani_kakudo
        self.flg1 = 1
        self.flg2 = 1
        self.k = 20.0
        #現在の角度
        #print "SetHingeJoint", self.theta1, self.tmp1

    def kaitenn_para(self):
        self.joint.setParam(ode.ParamLoStop, -self.hani_kakudo+0.1)
        self.joint.setParam(ode.ParamHiStop, self.hani_kakudo-0.1)
        #目標とする角速度 単位はたぶんラジアン 角速度マイナスなら逆向きに回転

        self.joint.setParam(ode.ParamFMax, 500)
        # 500ぐらいが最適値、10ぐらいだとふらつく、目標値を行き過ぎ、
        # また戻り...を繰り返す。この数値に落ち着くまで、 PID制御など
        # いろいろ検討した。
        #目標の角度と現在のジョイントの角度との差を求めている
        ##dParamVel      目標とする速度または角速度            
        ##dParamFMax     dParamVelを達成するために発生する最大の力またはトルク。
        ##直接 関節を目標の角度にするAPIがないので 上のように角度の差を求め
        ##て ...とする

    def    set_kaiten_kakudo(self):
        #-180と+180以上になるとキー入力をセーブしている。(範囲角以上になると)
        print " set_kaiten_kakudo      ",

        #if self.theta1>-self.hani_kakudo:
        #    self.flg1 = 1
        #if self.theta1< self.hani_kakudo:
        #    self.flg2 = 1
        self.tmp1 = self.joint.getAngle()
        self.z1 = self.theta1 - self.tmp1

        v = self.k * self.z1
        self.joint.setParam(ode.ParamVel, v)

        if field.scene.kb.keys:
            s = field.scene.kb.getkey()
            #if s=='j' and self.flg1==1:
            if s=='j':

                print "jjjjj   --------"
                print "self.hani_kakudo", self.hani_kakudo
                print "現在角度", self.tmp1
                print "目標角度",self.theta1
                print "目標角度と現在角度の差", self.z1
                self.theta1 -= math.pi/20
                if self.theta1<-self.hani_kakudo:
                    self.theta1 = -self.hani_kakudo + 0.1
                    #self.flg1 = 0

            #elif s=='k' and self.flg2==1:
            elif s=='k':
                print "kkkk   ++++++++"
                print "self.hani_kakudo", self.hani_kakudo
                print "getAngle", self.tmp1
                print "self.theta1",self.theta1
                print "目標角度と現在角度の差", self.z1
                self.theta1 += math.pi/20
                if self.theta1> self.hani_kakudo:
                    self.theta1 = self.hani_kakudo - 0.1
                    #self.flg2 = 0
            #print self.theta1

            ##動かすジョイントの選択
            ##いささか反応が遅い
            #if s=='1' or s=='2' or s=='3':
            #    index = int(s)

    def    set_kaiten_kakudo2(self, theta2):
        print "222222222222222"
        #-180と+180以上になるとキー入力をセーブしている。(範囲角以上になると)
        self.theta2 = theta2
        self.tmp2 = self.joint.getAngle()
        print self.tmp2
        self.z2 = self.theta2 - self.tmp2

        v = 5 * self.z2
        self.joint.setParam(ode.ParamVel, v)
        #self.joint.setParam(ode.ParamVel, 10000)


#次ここから jParaListのデータから判断してじどうで ジョイントを作成
def    metaSetJoint(field):
    #print "metaSetJoint"
    #print jParaList
    for list in jParaList:
        #print "list", list
        if list["name"]=="fixed":
            name = list["name"]
            obj1 = list["obj1"]
            obj2 = list["obj2"]
            jointList.append(SetFixedJoint(field, name, obj1, obj2))
            #print "metaSetJoint....fixed"
        if list["name"]=="hinge":
            name = list["name"]
            obj1 = list["obj1"]
            obj2 = list["obj2"]
            anchor0 = list["anchor"]
            j_axis0 = list["j_axis"]
            hani_kakudo = list["hani_kakudo"] 
            jointList.append(SetHingeJoint(field, name, obj1, obj2, anchor0, j_axis0, hani_kakudo))
            #print "metaSetJoint....hinge"

def control():
    p_b = ballList[0].pos
    print 'p_b %03.1f  %03.1f  %03.1f       ' % p_b,
    p_a = objList[3].pos
    print 'p_a %03.1f  %03.1f  %03.1f'      % p_a,
    #kakudo = math.atan2(p_b[0]-p_a[0], p_b[1]-p_a[1])
    #kakudo = math.atan2(p_b[1]-p_a[1], p_b[0]-p_a[0])
    #kakudo = math.atan2(p_b[0]-1, p_b[1]-0)
    kakudo = math.atan2(p_b[2], p_b[0])
    #kakudo = math.atan2(p_b[0], -p_b[2])
    print 'kakudo ', kakudo
    return kakudo


if __name__=='__main__':
    field = Field()
    ball = MetaBall(field)
    box = metaBox(field)
    #objListにオブジェクトが入った後jParaListに要素を入れる
    #そうでないとえらーが出る
    jParaList = [
            {"name":"fixed", "obj1":objList[0], "obj2":"env"}, #台座固定
            {"name":"hinge", "obj1":objList[0], "obj2":objList[1], "anchor":(0,       1, 0), "j_axis":(0, 1, 0), "hani_kakudo":3.1415},#台座1と台座2
            {"name":"hinge", "obj1":objList[1], "obj2":objList[2], "anchor":(0,       2, 0), "j_axis":(0, 0, 1), "hani_kakudo":3.1415},#台座2と第一アームの関節
            {"name":"hinge", "obj1":objList[2], "obj2":objList[3], "anchor":(0.35, 6.75, 0), "j_axis":(0, 0, 1), "hani_kakudo":1.0} #第一と第二アームの関節
            ]
            #{"name":"hinge", "obj1":objList[2], "obj2":objList[3], "anchor":(5.25, 6.75, 0), "j_axis":(0, 0, 1)}
            #{"name":"hinge", "obj1":objList[2], "obj2":objList[3], "anchor":(0.25, 6.75, 0), "j_axis":(0, 0, 1), "hani_kakudo":1.0} #第一と第二アームの関節
    #objList[3].geom.setCategoryBits(2)
    #objList[3].geom.setCollideBits(2)

    metaSetJoint(field)
    for list in jointList:
        if list.name=="hinge":
            #print "list.name",list.name
            list.kaitenn_para()



    index_m = 1
    #lb_a = label(pos=(0, 0, 12), text='joint')
    #lb_b = label(pos=(0, 0, 8), text='1')
    jn = '1'
    tx = 'joint ' + jn
    lb_b = label(pos=(0, 0, 8), text=tx)

    # aキーで「世界」の進行を止めたり進めたり
    print 'sキーで「世界」の進行を止め'
    print 'gキーで「世界」の進行を進めたり'
    Flg = False
    while True:    
        kakudo = control()
        jointList[1].set_kaiten_kakudo2(kakudo)
        #jointList[1].set_kaiten_kakudo2(0.011)
        #jointList[index_m].set_kaiten_kakudo()

        # キーボードを押し続けないと反応しない。苦し紛れに以下のfor文
        # for にしないとgetkey をあまりうけつけない
        # for in range以外のコードもいろいろ試してやっとこれに落ち着く
        for n in range(2000):
            if field.scene.kb.keys:
                jn = field.scene.kb.getkey()
                #動かすジョイントの選択
                #いささか反応が遅い
                if jn=="1" or jn=="2" or jn=="3":
                    index_m = int(jn)
                    tx = 'joint ' + jn
                    lb_b.text = tx
                    print("\007")   
                    #音をならして操作するジョイント
                    #が変わった事を知らせる

                #for n in range(20):
                #    elif jn=="s":
                #        Flg = True

        for n in range(20000):
            if field.scene.kb.keys:
                s1 = field.scene.kb.getkey()
                if s1 == 's':
                    Flg =True

        while Flg:
            if field.scene.kb.keys:
                s2 = field.scene.kb.getkey()
                if s2 == 'g':
                    Flg = False
                elif s2 != 's':
                    Flg = True
                else:
                    Flg = True

        #for n in range(200):
        #    if field.scene.kb.keys:
        #        s1 = field.scene.kb.getkey()
        #        if s1 == 's':
        #            Flg =True
        #            print 'soto_s'

        #while Flg:
        #    print '----------------------------'
        #    if field.scene.kb.keys:
        #        s2 = field.scene.kb.getkey()
        #        if s2 == 'g':
        #            Flg = False
        #            print 'g'
        #        elif s2 != 's':
        #            Flg = True
        #        else:
        #            Flg =True
               
        field.tick()

0 件のコメント:

コメントを投稿

About

参加ユーザー

連絡フォーム

名前

メール *

メッセージ *

ページ

Featured Posts