三维可视化系统的建立依赖于三维图形平台, 如 OpenGL、VTK、OGRE、OSG等, 传统的方法多采用OpenGL进行底层编程,即对其特有的函数进行定量操作, 需要开发人员熟悉相关函数, 从而造成了开发难度大、 周期长等问题。VTK、 ORGE、OSG等平台使用封装更好的函数简化了开发过程。下面将使用Python与VTK进行机器人上位机监控界面的快速原型开发。
完整的上位机程序需要有三维显示模块、机器人信息监测模块(位置/角度/速度/电量/温度/错误信息...)、通信模块(串口/USB/WIFI/蓝牙...)、控制模块等功能模块。三维显示模块主要用于实时显示机器人的姿态(或位置)信息。比如机器人上肢手臂抬起,程序界面中的虚拟机器人也会同时进行同样的动作。三维显示模块也可以用于对机器人进行控制,实现良好的人机交互。比如在三维图像界面中可以点击拾取机器人某一关节,拖拽部件(肢体)控制真实的机器人完成同样的运动。Aldebaran Robotics的图形化编程软件Choregraphe可以完成上述的一些功能对NAO机器人进行控制。
对于简单的模型可以自己编写函数进行创建,但这种方法做出来的模型过于简单不够逼真。因此可以先在SolidWorks、Blender、3DMax、Maya、Rhino等三维设计软件中建立好模型,然后导出为通用的三维文件格式,再使用VTK将其读入并进行渲染。
在SolidWorks等三维设计软件中设计好机器人的大臂(upperarm)和小臂(forearm),然后创建装配体如下图所示。在将装配体导出为STL文件前需要注意几点:
1. 当从外界读入STL类型的模型时,其会按照它内部的坐标位置进行显示,因此它的位置和大小是确定的。为了以后的定位以及移动、旋转等操作的方便,需要先在SolidWorks中创建一个坐标系。如下图所示,坐标系建立在大臂关节中心点。
2. 如果将装配体整体输出为一个STL文件,则导入VTK后无法控制零部件进行相对运动。因此,需要将装配体各可动部件分别导出。
在SolidWorks的另存为STL对话框中,点开输出选项卡,如下图所示。注意之前提到的几点:如果勾选“在单一文件中保存装配体的所有零部件”则会将整个装配体导出为一个STL文件,否则就是分别命名的两个STL文件;输出坐标系下拉列表中选择之前创建的坐标系1,并勾选“不要转换STL输出数据到正的坐标空间”。
下面的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()
下面是使用MFC搭建的机器人上位机监控平台,可以实现上述的一些基本功能。这个GIF动画使用开源软件ScreenToGif生成,非常好用!
总结
以上就是本文关于VTK与Python实现机械臂三维模型可视化详解的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站:
python+pygame简单画板实现代码实例
Python实现简单的语音识别系统
Python内置模块turtle绘图详解
如有不足之处,欢迎留言指出。感谢朋友们对本站的支持!
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!
稳了!魔兽国服回归的3条重磅消息!官宣时间再确认!
昨天有一位朋友在大神群里分享,自己亚服账号被封号之后居然弹出了国服的封号信息对话框。
这里面让他访问的是一个国服的战网网址,com.cn和后面的zh都非常明白地表明这就是国服战网。
而他在复制这个网址并且进行登录之后,确实是网易的网址,也就是我们熟悉的停服之后国服发布的暴雪游戏产品运营到期开放退款的说明。这是一件比较奇怪的事情,因为以前都没有出现这样的情况,现在突然提示跳转到国服战网的网址,是不是说明了简体中文客户端已经开始进行更新了呢?
更新日志
- 凤飞飞《我们的主题曲》飞跃制作[正版原抓WAV+CUE]
- 刘嘉亮《亮情歌2》[WAV+CUE][1G]
- 红馆40·谭咏麟《歌者恋歌浓情30年演唱会》3CD[低速原抓WAV+CUE][1.8G]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[320K/MP3][193.25MB]
- 【轻音乐】曼托凡尼乐团《精选辑》2CD.1998[FLAC+CUE整轨]
- 邝美云《心中有爱》1989年香港DMIJP版1MTO东芝首版[WAV+CUE]
- 群星《情叹-发烧女声DSD》天籁女声发烧碟[WAV+CUE]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[FLAC/分轨][748.03MB]
- 理想混蛋《Origin Sessions》[320K/MP3][37.47MB]
- 公馆青少年《我其实一点都不酷》[320K/MP3][78.78MB]
- 群星《情叹-发烧男声DSD》最值得珍藏的完美男声[WAV+CUE]
- 群星《国韵飘香·贵妃醉酒HQCD黑胶王》2CD[WAV]
- 卫兰《DAUGHTER》【低速原抓WAV+CUE】
- 公馆青少年《我其实一点都不酷》[FLAC/分轨][398.22MB]
- ZWEI《迟暮的花 (Explicit)》[320K/MP3][57.16MB]