4. 举例

4.1. 焊接 举例

Basic example from the website (offline programming section) that shows how to generate a hexagonal path given one target. This example uses a KUKA KR 6 R900 sixx. 从网站(离线编程部分)演示了如何生成一个给予的目标六边形路径的基本实例。本例使用的是一个KUKA KR 6R900 sixx.。 More information here:

点击此处了解详情:
离线编程 - 焊接实验
from robolink import *    # API与RoboDK沟通
from robodk import *      # robodk机器人工具箱

# 与RoboDK任何互动都必须通过RDK做:
RDK = Robolink()

# 按名称获取机器人:
robot = RDK.Item('KUKA KR 6 R900 sixx')

# 获得原始目标和焊接目标:
home = RDK.Item('Home')
target = RDK.Item('Target 1')

# 获得目标的姿态 (代表位置和方向的4x4矩阵):
poseref = target.Pose()

# 机器人运动到原始位置,然后向目标1:
robot.MoveJ(home)
robot.MoveJ(target)

# 围绕目标1制作一个六边形:
for i in range(7):
    ang = i*2*pi/6 #angle: 0, 60, 120, ...
    posei = poseref*rotz(ang)*transl(200,0,0)*rotz(-ang)
    robot.MoveL(posei)

# 移回中心,然后返回到原处:
robot.MoveL(target)
robot.MoveJ(home)

4.2. 拾取和放置

This example shows an advanced pick and place application using a Fanuc M-710iC/50 robot (Example 2 from the RoboDK library). 这个例子显示了用Fanuc M-710iC/50机器人进行先进的拾取和放置的应用(例2来自RoboDK库) In this example all the robot movements of the Fanuc robot are managed by the Python program. Using the Python API it is possible to create, move, modify and delete any object, reference frame, robot or other items in the station. 在这个例子中Fanuc的机器人的所有机器人运动由Python程序管理。使用Python API能够创建,移动,修改和删除站中的任何对象,参考系,机器人或其他物品。

Example 02-1 - 用python进行拾取和放置
from robolink import *    # API与roboDk沟通
from robodk import *      # robodk机器人工具箱

#设置球体参数
BALL_DIAMETER = 100 # 一个球的直径
APPROACH = 100      # 抓取每一部分的接触距离, 以毫米
nTCPs = 6           # TCP的刀具号

#----------------------------------------------
# 函数定义

def box_calc(BALLS_SIDE=4, BALLS_MAX=None):
    """计算点(球中心)的列表,如果球被存放在一个盒子中"""
    if BALLS_MAX is None: BALLS_MAX = BALLS_SIDE**3
    xyz_list = []
    for h in range(BALLS_SIDE):
        for i in range(BALLS_SIDE):
            for j in range(BALLS_SIDE):
                xyz_list = xyz_list + [[(i+0.5)*BALL_DIAMETER, (j+0.5)*BALL_DIAMETER, (h+0.5)*BALL_DIAMETER]]
                if len(xyz_list) >= BALLS_MAX:
                    return xyz_list
    return xyz_list

def pyramid_calc(BALLS_SIDE=4):
    """计算点(球中心)的列表,如果球被存放在一个坠塔中"""
    #球的数量可以被计算当: int(BALLS_SIDE*(BALLS_SIDE+1)*(2*BALLS_SIDE+1)/6)
    BALL_DIAMETER = 100
    xyz_list = []
    sqrt2 = 2**(0.5)
    for h in range(BALLS_SIDE):
        for i in range(BALLS_SIDE-h):
            for j in range(BALLS_SIDE-h):
                height = h*BALL_DIAMETER/sqrt2 + BALL_DIAMETER/2
                xyz_list = xyz_list + [[i*BALL_DIAMETER + (h+1)*BALL_DIAMETER*0.5, j*BALL_DIAMETER + (h+1)*BALL_DIAMETER*0.5, height]]
    return xyz_list

def balls_setup(frame, positions):
    """在参考系中放置球列表。参考对象(球)先前必须已复制到剪贴板。"""
    nballs = len(positions)
    step = 1/(nballs - 1)
    for i in range(nballs):
        newball = frame.Paste()
        newball.setName('ball ' + str(i)) #设置项目名
        newball.setPose(transl(positions[i])) #设置相对于父项的项目位置
        newball.setVisible(True, False) #使项目可见,但隐藏其参考系
        newball.Recolor([1-step*i, step*i, 0.2, 1]) #设置RGBA颜色

def cleanup_balls(parentnodes):
    """从父项提供的列表中,删除其名称以\"ball\"开头的所有子项。"""
    todelete = []
    for item in parentnodes:
        todelete.append(item.Childs())
    todelete = robottool.Childs() + frame1.Childs() + frame2.Childs()
    for item in todelete:
        if item.Name().startswith('ball'):
            item.Delete()

def TCP_On(toolitem, tcp_id):
    """分离附着在toolitem Htool姿势的最接近的目标。,
    此外, 它将输出合适的功能函数,调用已经生成的机器人程序 (调用 TCP_On)"""
    toolitem.AttachClosest()
    toolitem.RDK().RunMessage('Set air valve %i on' % (tcp_id+1))
    toolitem.RDK().RunProgram('TCP_On(%i)' % (tcp_id+1));
        
def TCP_Off(toolitem, tcp_id, itemleave=0):
    """分离附着在toolitem Htool姿势的最接近的目标,
    此外,它将输出生成的机器人程序相应的函数调用(调用TCP_Off)"""
    toolitem.DetachClosest(itemleave)
    toolitem.RDK().RunMessage('Set air valve %i off' % (tcp_id+1))
    toolitem.RDK().RunProgram('TCP_Off(%i)' % (tcp_id+1));


#----------------------------------------------------------
# 这个程序从这里开始:

# 与RoboDK的任何互动都必须通过RDK:
RDK = Robolink()

# 关闭自动渲染(快)
RDK.Render(False)

#RDK.Set_Simulation_Speed(500); # 设置仿真速度

# 从工作站树中收集所需物品
robot = RDK.Item('Fanuc M-710iC/50')
robottool = RDK.Item('Tool')
frame1 = RDK.Item('Table 1')
frame2 = RDK.Item('Table 2')

# 复制一个球作为一个对象 (同 CTRL+C)
ballref = RDK.Item('参考球')
ballref.Copy()

# 运行一个预定站程序(在RoboDK中)取代这两个表
prog_reset = RDK.Item('Replace objects')
prog_reset.RunProgram()

# 调用自定义过程来删除旧对象
cleanup_balls([robottool, frame1, frame2])

# 制作一个位置列表以放置这个对象
frame1_list = pyramid_calc(4)
frame2_list = pyramid_calc(4)

# 编程处的对象有一个特制的程序
balls_setup(frame1, frame1_list)

# 开启自动渲染
RDK.Render(True)

# 为6个吸盘工具计算工具框架
TCPs = []
for i in range(nTCPs):
    TCPs = TCPs + [transl(0,0,100)*rotz((360/nTCPs)*i*pi/180)*transl(125,0,0)*roty(pi/2)]

# 移动球    
robot.setTool(robottool) # 如果仅存在一个工具,这就是自动的
nballs_frame1 = len(frame1_list)
nballs_frame2 = len(frame2_list)
idTake = nballs_frame1 - 1
idLeave = 0
idTCP = 0
target_app_frame = transl(2*BALL_DIAMETER, 2*BALL_DIAMETER, 4*BALL_DIAMETER)*roty(pi)*transl(0,0,-APPROACH)

while idTake >= 0:
    # ------------------------------------------------------------------
    # 首要任务:抓住尽可能多的球
    # 在这点上该工具是空的,所以尽可能采取多的球(最多为6 - > nTCPs)
    ntake = min(nTCPs, idTake + 1)

    # 抓1帧的方法
    robot.setFrame(frame1)
    robottool.setHtool(TCPs[0])
    robot.MoveJ([0,0,0,0,10,-200])
    robot.MoveJ(target_app_frame)

    # 从1帧中抓取ntake球
    for i in range(ntake):
        Htool = TCPs[i]
        robottool.setHtool(Htool)
        # #计算目标WRT1帧:Y轴旋转是必要的,因为Z和X轴被反转  
        target = transl(frame1_list[idTake])*roty(pi)*rotx(30*pi/180)
        target_app = target*transl(0,0,-APPROACH)
        idTake = idTake - 1        
        robot.MoveL(target_app)
        robot.MoveL(target)
        TCP_On(robottool, i)
        robot.MoveL(target_app)
 
    # ------------------------------------------------------------------
    # 第二优先:卸载工具      
    # 方法2帧,并将该工具球放置到表2 
    robottool.setHtool(TCPs[0])
    robot.MoveJ(target_app_frame)
    robot.MoveJ([0,0,0,0,10,-200])
    robot.setFrame(frame2)    
    robot.MoveJ(target_app_frame)
    for i in range(ntake):
        Htool = TCPs[i]
        robottool.setHtool(Htool)
        if idLeave > nballs_frame2-1:
            raise Exception("没有剩余空间来放置在表2中的对象")
        
        # 计算目标WRT的1帧:因为Z轴和X轴被反转,所以Y轴需要旋转180度
        target = transl(frame2_list[idLeave])*roty(pi)*rotx(30*pi/180)
        target_app = target*transl(0,0,-APPROACH)
        idLeave = idLeave + 1        
        robot.MoveL(target_app)
        robot.MoveL(target)
        TCP_Off(robottool, i, frame2)
        robot.MoveL(target_app)

    robot.MoveJ(target_app_frame)

# 当机器人完成工作时,返回原点
robot.MoveJ([0,0,0,0,10,-200])

4.3. 绘制一个图像

A robot is programmed given an SVG image to simulate a drawing application. An ABB IRB 4600-20/2.50 is used in this example. 机器人编程给出SVG图像来模拟绘图应用程序。本实施例中使用的是ABB的IRB4600-20/2.50 3D HTML simulation of this example: https://www.robodk.com/simulations/Robot-Drawing.html 本实例是三维HTML仿真: https://www.robodk.com/simulations/Robot-Drawing.html

Example 03 - 用机器人绘图
from robolink import *    # API与RoboDK沟通
from robodk import *      # robodk机器人工具箱

import sys 
import os
import re
   
#--------------------------------------------------------------------------------
# 函数定义:

def point2D_2_pose(point, tangent):
    """在XY平面上转换一个2D点为3D姿态 (4x4同质矩阵)"""
    return transl(point.x, point.y, 0)*rotz(tangent.angle())

def svg_draw_quick(svg_img, board, pix_ref):
    """迅速显示没有检查机器人动作的图像结果"""
    RDK.Render(False)
    count = 0
    for path in svg_img:
        count = count + 1
        # 使用像素参考设置路径颜色,设置像素宽度并复制作为参考
        pix_ref.Recolor(path.fill_color)
        np = path.nPoints()
        print('drawing path %i/%i' % (count, len(svg_img)))
        for i in range(np):
            p_i = path.getPoint(i)
            v_i = path.getVector(i)
            pt_pose = point2D_2_pose(p_i, v_i)
            
            # 在计算像素构成时,添加像素几何体到画板对象
            board.AddGeometry(pix_ref, pt_pose)
            
    RDK.Render(True)

def svg_draw_robot(svg_img, board, pix_ref, item_frame, item_tool, robot):
    """用机器人绘制图像。它比svg_draw_quick慢,但它可确保该图像可以用机器人绘制."""

    APPROACH = 100  # 接近距离MM中的每个路径    
    home_joints = [0,0,0,0,90,0] # 原始关节,为度
    
    robot.setFrame(item_frame)
    robot.setTool(item_tool)
    robot.MoveJ(home_joints)
    
    # 根据原始位置刀具方向获取目标方向
    orient_frame2tool = invH(item_frame.Pose())*robot.SolveFK(home_joints)*item_tool.Pose()
    orient_frame2tool[0:3,3] = Mat([0,0,0])
    # 替代方案:  orient_frame2tool = roty(pi)

    for path in svg_img:
        # 使用像素参考设置路径颜色,设置像素宽度并复制作为参考
        print('Drawing %s, RGB color = [%.3f,%.3f,%.3f]'%(path.idname, path.fill_color[0], path.fill_color[1], path.fill_color[2]))
        pix_ref.Recolor(path.fill_color) 
        np = path.nPoints()

        # 机器人的运动:到第一目标的方法
        p_0 = path.getPoint(0)
        target0 = transl(p_0.x, p_0.y, 0)*orient_frame2tool
        target0_app = target0*transl(0,0,-APPROACH)
        robot.MoveL(target0_app)
        RDK.RunMessage('Drawing %s' % path.idname);
        RDK.RunProgram('SetColorRGB(%.3f,%.3f,%.3f)' % (path.fill_color[0], path.fill_color[1], path.fill_color[2]))
        for i in range(np):
            p_i = path.getPoint(i)
            v_i = path.getVector(i)
            pt_pose = point2D_2_pose(p_i, v_i)            
        
            # 机器人运动:
            target = transl(p_i.x, p_i.y, 0)*orient_frame2tool #保持刀具方向不变
            #目标 = pt_pose*orient_frame2tool #沿着路径移动的工具 (达到的限制是六轴)
            robot.MoveL(target)

            # 用计算出的像素姿态创建一个新像素对象
            board.AddGeometry(pix_ref, pt_pose)

        target_app = target*transl(0,0,-APPROACH)
        robot.MoveL(target_app)
        
    robot.MoveL(home_joints)

#--------------------------------------------------------------------------------
# 程序开始
RDK = Robolink()

# 找到并导入SVG PY模块
path_stationfile = RDK.getParam('PATH_OPENSTATION')
sys.path.append(os.path.abspath(path_stationfile)) # 临时添加路径到进出站模块
from svgpy.svg import *

# 选择文件绘制
svgfile = path_stationfile + '/World map.svg'
#svgfile = path_stationfile + '/RoboDK text.svg'

# 导入SVG文件
svgdata = svg_load(svgfile)

IMAGE_SIZE = Point(1000,2000)   # 在MM中的图像的大小
MM_X_PIXEL = 10                 # 根据像素的大小毫米级的路径将被分割
svgdata.calc_polygon_fit(IMAGE_SIZE, MM_X_PIXEL)
size_img = svgdata.size_poly()  # 返回当前多边形的大小

# 得到机器人,框架和工具对象
robot = RDK.Item('ABB IRB 4600-20/2.50')
framedraw = RDK.Item('Frame draw')
tooldraw = RDK.Item('Tool')

# 拿到像素参考画
pixel_ref = RDK.Item('pixel')

# 删除以前的任何形象
image = RDK.Item('Board & image')
if image.Valid() and image.Type() == ITEM_CASE_OBJECT: image.Delete()

# 基于项目参考系制作一个绘图板“250毫米黑板”
board_1m = RDK.Item('Blackboard 250mm')
board_1m.Copy()
board_draw = framedraw.Paste()
board_draw.setName('Board & image')
board_draw.Scale([size_img.x/250, size_img.y/250, 1]) # 调整电路板尺寸和图像尺寸(比例)

# 快速显示没有检查机器人动作的最终结果
#svg_draw_quick(svgdata, board_draw, pixel_ref)

# 绘制机器人图像:
svg_draw_robot(svgdata, board_draw, pixel_ref, framedraw, tooldraw, robot)

4.4. 3个机器人同步

This example shows to synchronize multiple robots at the same time. The robots can be synchronized together given keypoints and using Python threads. This example is similar to 焊接 举例 but updated to support moving multiple robots at the same time. The robots can be connected to the computer using appropriate robot drivers and switch from the simulation to moving the real robots. 这个例子显示了多个机器人在同一时间同步。机器人可以一起同步给予重点和使用Python线程。这个例子是类似焊接的例子,且不断更新,以支持在同一时间移动多个机器人。

Offline programming - 3个机器人同步
from robolink import *    # API与RoboDK沟通
from robodk import *      # 基本的矩阵运算
import threading
import queue

#----------------------------------------------
# 函数定义和全局变量声明

# 用于同步机器人动作的全局变量
# 这些变量由SyncSet() and SynchWait()管理

global SYNC_COUNT = 0
global SYNC_TOTAL = 0
global SYNC_ID = 0
lock = threading.Lock()

def SyncSet(total_sync):
    """SyncSet将设置必须同步运行的总机器人程序(线程).
    每次SyncSet 被调用 SYNC_ID 则加一"""
    with lock:
        SYNC_COUNT = 0
        SYNC_TOTAL = total_sync
        SYNC_ID = SYNC_ID + 1
        #print('SyncSet')

def SyncWait():
    """SyncWait will block the robot movements for a robot when necessary, synchronizing the movements sequentially.
    Use SyncSet(nrobots) to define how many robots must be synchronized together."""
    # 保存局部变量与同步事件ID
    sync_id = SYNC_ID
    with lock:
        # 增加被同步的线程数
        SYNC_COUNT += 1

    # 如果所有线程达到移动到下一个sync事件这个 SyncWait(SYNC_COUNT = SYNC_TOTAL)
    if SYNC_COUNT >= SYNC_TOTAL:
        SyncSet(SYNC_TOTAL)
        return

    # 等待SynchSet前移
    while sync_id >= SYNC_ID:
        time.sleep(0.0001)


# 移动每个机器人主要程序   
def DoWeld(q, robotname):
    # 任何与RoboDk接触必须通过Robolink()来做
    # 每一个机器人运动要求一个新的 Robolink() 项目 (新的沟通链接)
    # 两个机器人不能在相同的通信链路上移动
    
    rdk = Robolink()

    # 得到机器人项目:
    robot = rdk.Item(robotname)

    # 得到原始节点目标
    home = robot.JointsHome()

    # 得到焊接目标参数
    target = rdk.Item('Target')

    # 得到的参考帧,并将其设置到机器人
    reference = target.Parent()
    robot.setFrame(reference)

    # 获得目标(4x4矩阵)的姿势:
    poseref = target.Pose()
    pose_approach = poseref*transl(0,0,-100)

    # 移动机器人到原始位置,然后到中心:
    robot.MoveJ(home)
    robot.MoveJ(pose_approach)
    SyncWait()
    robot.MoveL(target)

    # 制作一个围绕中心的六边形:
    for i in range(7):
        ang = i*2*pi/6 #angle: 0, 60, 120, ...
        posei = poseref*rotz(ang)*transl(200,0,0)*rotz(-ang)
        SyncWait()
        robot.MoveL(posei)

    # 移回中心,然后返回原位:
    SyncWait()
    robot.MoveL(target)
    robot.MoveL(pose_approach)
    robot.MoveJ(home)
    q.put('Robot %s finished' % robotname)

#----------------------------------------
# Python程序启动
    
# 检索在RoboDK站的所有可用的机器人(作为名字的列表)
RDK = Robolink()
robots = RDK.ItemList(ITEM_TYPE_ROBOT)
print(robots)

# 检索机器人的数量一起同步
nrobots = len(robots)
SyncSet(nrobots)

# 队列允许线程之间共享信息
q = queue.Queue()

# 启动所有机器人下载程序。每个机器人将在一个单独的线程中运行。
threads = []
for i in range(nrobots):
    robotname = robots[i]
    t = threading.Thread(target=DoWeld, args = (q, robotname))
    t.daemon = True
    t.start()
    threads.append(t)

# 等待每一个线程完成
for x in threads:
    x.join()
    print(q.get())

print('Main program finished')



    
    
    



4.5. 机器人建模

Example that models the forward and inverse kinematics of an ABB IRB 120 robot using the RoboDK API for Python. Reference frames are placed according to an existing robot in the station. 例子模型使用Python的RoboDK API编程的ABB IRB 120机器人的正向和反向运动

Robot Model - 镜像测试
from robolink import *    # API与RoboDK沟通
from robodk import *      # robodk 机器人工具箱


#----------------------------------------------
# 函数定义

def FK_Robot(dh_table, joints):
    """计算机器人的正向运动学.
    h_table必须在毫米和弧度,关节数组必须以度给出"""
    Habs = []
    Hrel = []    
    nlinks = len(dh_table)
    HiAbs = eye(4)
    for i in range(nlinks):
        [rz,tx,tz,rx] = dh_table[i]
        rz = rz + joints[i]*pi/180
        Hi = dh(rz,tx,tz,rx)
        HiAbs = HiAbs*Hi
        Hrel.append(Hi)
        Habs.append(HiAbs)

    return [HiAbs, Habs, Hrel]

def Frames_setup_absolute(frameparent, nframes):
    """添加nframes参考帧级联到父项参考帧"""
    frames = []
    for i in range(nframes):
        newframe = frameparent.RDK().AddFrame('frame %i' % (i+1), frameparent)
        newframe.setPose(transl(0,0,100*i))
        frames.append(newframe)

    return frames

def Frames_setup_relative(frameparent, nframes):
    """添加nframes参考帧级联到父项参考帧"""
    frames = []
    parent = frameparent
    for i in range(nframes):
        newframe = frameparent.RDK().AddFrame('frame %i' % (i+1), parent)
        parent = newframe
        newframe.setPose(transl(0,0,100))
        frames.append(newframe)

    return frames

def Set_Items_Pose(itemlist, poselist):
    """设置在项目类表中的每个项目的姿态(三维位置)"""
    for item, pose in zip(itemlist,poselist):
        item.setPose(pose)

def are_equal(j1, j2):
    """如果J1和J2相等则返回为true否则返回False"""
    if j1 is None or j2 is None:
        return False
    sum_diffs_abs = sum(abs(a - b) for a, b in zip(j1, j2))
    if sum_diffs_abs > 1e-3:
        return False
    return True
        
#----------------------------------------------------------
# 该计划从这里开始:
RDK = Robolink()        
        
#-----------------------------------------------------
# 机器人的DH表: ABB IRB 120-3/0.6
DH_Table = []
#                 rZ (theta),   tX,   tZ,   rX (alpha)
DH_Table.append([          0,    0,  290,  -90*pi/180])
DH_Table.append([ -90*pi/180,  270,    0,           0])
DH_Table.append([          0,   70,    0,  -90*pi/180])
DH_Table.append([          0,    0,  302,   90*pi/180])
DH_Table.append([          0,    0,    0,  -90*pi/180])
DH_Table.append([ 180*pi/180,    0,   72,           0])

# 自由度:  (6 for ABB IRB 120-3/0.6)
DOFs = len(DH_Table)

# get the robot:
robot = RDK.Item('ABB IRB 120-3/0.6')

# 从以前的测试项目中,清理所有含“镜测试”的项目
while True:
    todelete = RDK.Item('Robot base')
    # 确保一个项目被发现
    if not todelete.Valid():
        break
    # 仅删除帧
    if todelete.Type() == ITEM_CASE_FRAME:
        print('Deleting: ' + todelete.Name())
        todelete.Delete()

# 设置用于测试的父框架:
parent_frameabs = RDK.AddFrame('Robot base (absolute frames)')
parent_framerel = RDK.AddFrame('Robot base (relative frames)')

# 设立试验的子框架:
frames_abs = Frames_setup_absolute(parent_frameabs, DOFs)
frames_rel = Frames_setup_relative(parent_framerel, DOFs)

# 当有必要时记住更新的最后几个机器人关节
last_joints = None

# 无限循环
while True:
    # 获得当前机器人关节为浮动阵列(列表)
    joints = robot.Joints().tolist()

    # 如果关节和以前一样不更新
    if are_equal(joints, last_joints):
        continue

    # 如果关节改变,计算该位置的正向运动学
    [Hrobot, HabsList, HrelList] = FK_Robot(DH_Table, joints)

    # 当我们更新所有帧时关闭渲染:
    RDK.Render(False)
    # 更新所有帧
    Set_Items_Pose(frames_abs, HabsList)
    Set_Items_Pose(frames_rel, HrelList)
    
    # 渲染和打开渲染
    RDK.Render(True)

    # 记住上次机器人关节
    last_joints = joints

    print('当前机器人关节:')    
    print(joints)
    print('机器人姿态 (正向运动):')
    print(Hrobot)
    print('\n\n')