VTK与Python实现机械臂三维模型可视化详解


Posted in Python onDecember 13, 2017

三维可视化系统的建立依赖于三维图形平台, 如 OpenGL、VTK、OGRE、OSG等, 传统的方法多采用OpenGL进行底层编程,即对其特有的函数进行定量操作, 需要开发人员熟悉相关函数, 从而造成了开发难度大、 周期长等问题。VTK、 ORGE、OSG等平台使用封装更好的函数简化了开发过程。下面将使用Python与VTK进行机器人上位机监控界面的快速原型开发。

完整的上位机程序需要有三维显示模块、机器人信息监测模块(位置/角度/速度/电量/温度/错误信息...)、通信模块(串口/USB/WIFI/蓝牙...)、控制模块等功能模块。三维显示模块主要用于实时显示机器人的姿态(或位置)信息。比如机器人上肢手臂抬起,程序界面中的虚拟机器人也会同时进行同样的动作。三维显示模块也可以用于对机器人进行控制,实现良好的人机交互。比如在三维图像界面中可以点击拾取机器人某一关节,拖拽部件(肢体)控制真实的机器人完成同样的运动。Aldebaran Robotics的图形化编程软件Choregraphe可以完成上述的一些功能对NAO机器人进行控制。

VTK与Python实现机械臂三维模型可视化详解

对于简单的模型可以自己编写函数进行创建,但这种方法做出来的模型过于简单不够逼真。因此可以先在SolidWorks、Blender、3DMax、Maya、Rhino等三维设计软件中建立好模型,然后导出为通用的三维文件格式,再使用VTK将其读入并进行渲染。

在SolidWorks等三维设计软件中设计好机器人的大臂(upperarm)和小臂(forearm),然后创建装配体如下图所示。在将装配体导出为STL文件前需要注意几点:

1. 当从外界读入STL类型的模型时,其会按照它内部的坐标位置进行显示,因此它的位置和大小是确定的。为了以后的定位以及移动、旋转等操作的方便,需要先在SolidWorks中创建一个坐标系。如下图所示,坐标系建立在大臂关节中心点。

2. 如果将装配体整体输出为一个STL文件,则导入VTK后无法控制零部件进行相对运动。因此,需要将装配体各可动部件分别导出。

VTK与Python实现机械臂三维模型可视化详解

在SolidWorks的另存为STL对话框中,点开输出选项卡,如下图所示。注意之前提到的几点:如果勾选“在单一文件中保存装配体的所有零部件”则会将整个装配体导出为一个STL文件,否则就是分别命名的两个STL文件;输出坐标系下拉列表中选择之前创建的坐标系1,并勾选“不要转换STL输出数据到正的坐标空间”。

VTK与Python实现机械臂三维模型可视化详解

下面的Python代码简单实现了一个2自由度机械臂的三维仿真,可以拖动滑块或按键盘上的方向键控制肩关节或肘关节运动。当然程序还存在一些问题有待完善...

#!/usr/bin/env python
 import vtk
import math
from vtk.util.colors import *
filenames = ["upperarm.stl","forearm.stl"]
dt = 1.0    # degree step in rotation
angle = [0, 0] # shoulder and elbow joint angle
renWin = vtk.vtkRenderWindow()
assembly = vtk.vtkAssembly()
slider_shoulder = vtk.vtkSliderRepresentation2D()
slider_elbow = vtk.vtkSliderRepresentation2D()
actor = list() # the list of links
# Customize vtkInteractorStyleTrackballCamera 
class MyInteractor(vtk.vtkInteractorStyleTrackballCamera):
  def __init__(self,parent=None):
    self.AddObserver("CharEvent",self.OnCharEvent)
    self.AddObserver("KeyPressEvent",self.OnKeyPressEvent)
  # Override the default key operations which currently handle trackball or joystick styles is provided
  # OnChar is triggered when an ASCII key is pressed. Some basic key presses are handled here 
  def OnCharEvent(self,obj,event):
    pass
  def OnKeyPressEvent(self,obj,event):
    global angle
    # Get the compound key strokes for the event
    key = self.GetInteractor().GetKeySym()
    # Output the key that was pressed
    #print "Pressed: " , key
    # Handle an arrow key
    if(key == "Left"):
      actor[1].RotateY(-dt)      
    if(key == "Right"):
      actor[1].RotateY(dt)      
    if(key == "Up"):
      assembly.RotateY(-dt)
      angle[0] += dt
      if angle[0] >= 360.0:
        angle[0] -= 360.0
      slider_shoulder.SetValue(angle[0])  
    if(key == "Down"):
      assembly.RotateY(dt)
      angle[0] -= dt
      if angle[0] < 0.0:
        angle[0] += 360.0 
      slider_shoulder.SetValue(angle[0])
    # Ask each renderer owned by this RenderWindow to render its image and synchronize this process
    renWin.Render()
    return
def LoadSTL(filename):
  reader = vtk.vtkSTLReader()
  reader.SetFileName(filename)
  mapper = vtk.vtkPolyDataMapper() # maps polygonal data to graphics primitives
  mapper.SetInputConnection(reader.GetOutputPort())
  actor = vtk.vtkLODActor() 
  actor.SetMapper(mapper)
  return actor  # represents an entity in a rendered scene
def CreateCoordinates():
  # create coordinate axes in the render window
  axes = vtk.vtkAxesActor() 
  axes.SetTotalLength(100, 100, 100) # Set the total length of the axes in 3 dimensions 
  # Set the type of the shaft to a cylinder:0, line:1, or user defined geometry. 
  axes.SetShaftType(0) 
  axes.SetCylinderRadius(0.02) 
  axes.GetXAxisCaptionActor2D().SetWidth(0.03) 
  axes.GetYAxisCaptionActor2D().SetWidth(0.03) 
  axes.GetZAxisCaptionActor2D().SetWidth(0.03) 
  #axes.SetAxisLabels(0) # Enable:1/disable:0 drawing the axis labels
  #transform = vtk.vtkTransform() 
  #transform.Translate(0.0, 0.0, 0.0)
  #axes.SetUserTransform(transform)
  #axes.GetXAxisCaptionActor2D().GetCaptionTextProperty().SetColor(1,0,0)
  #axes.GetXAxisCaptionActor2D().GetCaptionTextProperty().BoldOff() # disable text bolding
  return axes
def ShoulderSliderCallback(obj,event):
  sliderRepres = obj.GetRepresentation()
  pos = sliderRepres.GetValue() 
  assembly.SetOrientation(0,-pos,0)

  renWin.Render()
def ElbowSliderCallback(obj,event):
  sliderRepres = obj.GetRepresentation()
  pos = sliderRepres.GetValue() 
  actor[1].SetOrientation(0,-pos,0)
  renWin.Render()
def ConfigSlider(sliderRep, TitleText, Yaxes):
  sliderRep.SetMinimumValue(0.0)
  sliderRep.SetMaximumValue(360.0)
  sliderRep.SetValue(0.0) # Specify the current value for the widget
  sliderRep.SetTitleText(TitleText) # Specify the label text for this widget
  sliderRep.GetSliderProperty().SetColor(1,0,0) # Change the color of the knob that slides
  sliderRep.GetSelectedProperty().SetColor(0,0,1) # Change the color of the knob when the mouse is held on it
  sliderRep.GetTubeProperty().SetColor(1,1,0) # Change the color of the bar 
  sliderRep.GetCapProperty().SetColor(0,1,1) # Change the color of the ends of the bar
  #sliderRep.GetTitleProperty().SetColor(1,0,0) # Change the color of the text displaying the value
  # Position the first end point of the slider
  sliderRep.GetPoint1Coordinate().SetCoordinateSystemToDisplay()
  sliderRep.GetPoint1Coordinate().SetValue(50, Yaxes) 
  # Position the second end point of the slider
  sliderRep.GetPoint2Coordinate().SetCoordinateSystemToDisplay()
  sliderRep.GetPoint2Coordinate().SetValue(400, Yaxes) 
  sliderRep.SetSliderLength(0.02) # Specify the length of the slider shape.The slider length by default is 0.05
  sliderRep.SetSliderWidth(0.02) # Set the width of the slider in the directions orthogonal to the slider axis
  sliderRep.SetTubeWidth(0.005)
  sliderRep.SetEndCapWidth(0.03)

  sliderRep.ShowSliderLabelOn() # display the slider text label
  sliderRep.SetLabelFormat("%.1f")

  sliderWidget = vtk.vtkSliderWidget()
  sliderWidget.SetRepresentation(sliderRep)
  sliderWidget.SetAnimationModeToAnimate()

  return sliderWidget
def CreateGround():
  # create plane source
  plane = vtk.vtkPlaneSource()
  plane.SetXResolution(50)
  plane.SetYResolution(50)
  plane.SetCenter(0,0,0)
  plane.SetNormal(0,0,1)  
  # mapper
  mapper = vtk.vtkPolyDataMapper()
  mapper.SetInputConnection(plane.GetOutputPort())
   
  # actor
  actor = vtk.vtkActor()
  actor.SetMapper(mapper)
  actor.GetProperty().SetRepresentationToWireframe()
  #actor.GetProperty().SetOpacity(0.4) # 1.0 is totally opaque and 0.0 is completely transparent
  actor.GetProperty().SetColor(light_grey)
  '''
  # Load in the texture map. A texture is any unsigned char image.
  bmpReader = vtk.vtkBMPReader() 
  bmpReader.SetFileName("ground_texture.bmp") 
  texture = vtk.vtkTexture() 
  texture.SetInputConnection(bmpReader.GetOutputPort()) 
  texture.InterpolateOn() 
  actor.SetTexture(texture)
  '''
  transform = vtk.vtkTransform()
  transform.Scale(2000,2000, 1)
  actor.SetUserTransform(transform)
  return actor  
def CreateScene():
  # Create a rendering window and renderer
  ren = vtk.vtkRenderer()
  #renWin = vtk.vtkRenderWindow()
  renWin.AddRenderer(ren)
  # Create a renderwindowinteractor
  iren = vtk.vtkRenderWindowInteractor()
  iren.SetRenderWindow(renWin)
  style = MyInteractor()
  style.SetDefaultRenderer(ren)
  iren.SetInteractorStyle(style)
  for id, file in enumerate(filenames):
    actor.append(LoadSTL(file))
    #actor[id].GetProperty().SetColor(blue)
    r = vtk.vtkMath.Random(.4, 1.0)
    g = vtk.vtkMath.Random(.4, 1.0)
    b = vtk.vtkMath.Random(.4, 1.0)
    actor[id].GetProperty().SetDiffuseColor(r, g, b)
    actor[id].GetProperty().SetDiffuse(.8)
    actor[id].GetProperty().SetSpecular(.5)
    actor[id].GetProperty().SetSpecularColor(1.0,1.0,1.0)
    actor[id].GetProperty().SetSpecularPower(30.0)
    assembly.AddPart(actor[id])
    # Add the actors to the scene
    #ren.AddActor(actor[id])
  # Also set the origin, position and orientation of assembly in space.
  assembly.SetOrigin(0, 0, 0) # This is the point about which all rotations take place 
  #assembly.AddPosition(0, 0, 0)
  #assembly.RotateX(45)
  actor[1].SetOrigin(274, 0, 0) # initial elbow joint position
  ren.AddActor(assembly)
  # Add coordinates
  axes = CreateCoordinates()
  ren.AddActor(axes)

  # Add ground
  ground = CreateGround()
  ren.AddActor(ground)

  # Add slider to control the robot
  sliderWidget_shoulder = ConfigSlider(slider_shoulder,"Shoulder Joint", 80)
  sliderWidget_shoulder.SetInteractor(iren)
  sliderWidget_shoulder.EnabledOn()
  sliderWidget_shoulder.AddObserver("InteractionEvent", ShoulderSliderCallback)

  sliderWidget_elbow = ConfigSlider(slider_elbow,"Elbow Joint", 160)
  sliderWidget_elbow.SetInteractor(iren)
  sliderWidget_elbow.EnabledOn()
  sliderWidget_elbow.AddObserver("InteractionEvent", ElbowSliderCallback)

  # Set background color
  ren.SetBackground(.2, .2, .2)

  # Set window size
  renWin.SetSize(600, 600)

  # Set up the camera to get a particular view of the scene
  camera = vtk.vtkCamera()
  camera.SetFocalPoint(300, 0, 0)
  camera.SetPosition(300, -400, 350)
  camera.ComputeViewPlaneNormal()
  camera.SetViewUp(0, 1, 0)
  camera.Zoom(0.4)
  ren.SetActiveCamera(camera)
  # Enable user interface interactor
  iren.Initialize()
  iren.Start()
if __name__ == "__main__":
  CreateScene()

VTK与Python实现机械臂三维模型可视化详解

下面是使用MFC搭建的机器人上位机监控平台,可以实现上述的一些基本功能。这个GIF动画使用开源软件ScreenToGif生成,非常好用!

VTK与Python实现机械臂三维模型可视化详解

VTK与Python实现机械臂三维模型可视化详解

总结

以上就是本文关于VTK与Python实现机械臂三维模型可视化详解的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站:

如有不足之处,欢迎留言指出。感谢朋友们对本站的支持!

Python 相关文章推荐
python实现的解析crontab配置文件代码
Jun 30 Python
Python中处理字符串之endswith()方法的使用简介
May 18 Python
python类的继承实例详解
Mar 30 Python
Python之使用adb shell命令启动应用的方法详解
Jan 07 Python
使用Python和OpenCV检测图像中的物体并将物体裁剪下来
Oct 30 Python
浅析python redis的连接及相关操作
Nov 07 Python
使用matlab 判断两个矩阵是否相等的实例
May 11 Python
python框架flask入门之路由及简单实现方法
Jun 07 Python
python 基于opencv 实现一个鼠标绘图小程序
Dec 11 Python
python通用数据库操作工具 pydbclib的使用简介
Dec 21 Python
浅析python实现动态规划背包问题
Dec 31 Python
python实现杨辉三角的几种方法代码实例
Mar 02 Python
python+pygame简单画板实现代码实例
Dec 13 #Python
Python实现简单的语音识别系统
Dec 13 #Python
关于反爬虫的一些简单总结
Dec 13 #Python
Python自动化运维_文件内容差异对比分析
Dec 13 #Python
Python实现自动发送邮件功能
Mar 02 #Python
django站点管理详解
Dec 12 #Python
Django 生成登陆验证码代码分享
Dec 12 #Python
You might like
yii2带搜索功能的下拉框实例详解
2016/05/12 PHP
在b/s开发中经常用到的javaScript技术
2006/08/23 Javascript
javaScript 关闭浏览器 (不弹出提示框)
2010/01/31 Javascript
jQuery 获取对象 定位子对象
2010/05/31 Javascript
jquery自动填充勾选框即把勾选框打上true
2014/03/24 Javascript
nodejs教程之异步I/O
2014/11/21 NodeJs
js实现点击左右按钮轮播图片效果实例
2015/01/29 Javascript
js实现可得到不同颜色值的颜色选择器实例
2015/02/28 Javascript
jQuery Ajax File Upload实例源码
2016/12/12 Javascript
JavaScript正则获取地址栏中参数的方法
2017/03/02 Javascript
利用Vue v-model实现一个自定义的表单组件
2017/04/27 Javascript
vue-router 中router-view不能渲染的解决方法
2017/05/23 Javascript
详解vue+vueRouter+webpack的简单实例
2017/06/17 Javascript
一个简易的js图片轮播效果
2017/07/22 Javascript
jQuery实现拼图小游戏(实例讲解)
2017/07/24 jQuery
DVA框架统一处理所有页面的loading状态
2017/08/25 Javascript
jquery自定义显示消息数量
2017/12/19 jQuery
jquery实现自定义树形表格的方法【自定义树形结构table】
2019/07/12 jQuery
详解json串反转义(消除反斜杠)
2019/08/12 Javascript
这样回答继承可能面试官更满意
2019/12/10 Javascript
vue中的计算属性和侦听属性
2020/11/06 Javascript
Python使用Matplotlib实现Logos设计代码
2017/12/25 Python
python保存数据到本地文件的方法
2018/06/23 Python
朴素贝叶斯分类算法原理与Python实现与使用方法案例
2018/06/26 Python
python+opencv实现高斯平滑滤波
2020/07/21 Python
利用python实现PSO算法优化二元函数
2019/11/13 Python
python turtle 绘制太极图的实例
2019/12/18 Python
Python列表list操作相关知识小结
2020/01/29 Python
keras实现多GPU或指定GPU的使用介绍
2020/06/17 Python
Django rest framework分页接口实现原理解析
2020/08/21 Python
英国买鞋网站:Charles Clinkard
2019/11/14 全球购物
竞选演讲稿范文
2013/12/28 职场文书
医院竞聘演讲稿
2014/05/16 职场文书
2014法院干警廉洁警示教育思想汇报
2014/09/13 职场文书
报名委托书
2015/01/29 职场文书
导游词之桂林山水
2019/09/20 职场文书