Actors有时也会组合在一起形成层次结构,当其中的某个Actor运动时,会影响到其他Actor的位置。例如,一个机械手臂可能由上臂、前臂、手腕和末端等部分通过关节连接起来。当上臂绕着肩关节旋转时,我们希望的是其他部分也会跟着运动。这种行为的实现就要用到Assembly,vtkAssembly是vtkActor的子类,它在构建机器人模型里非常有用。对串联机器人或串并联机器人来说,机构中存在着许多运动链,当前连杆的位置与姿态与它的父节点有关,父节点转动一定的角度,子节点也必须转动,否则模型就会断开。
可以类比VREP中的三连杆机构,左侧的层次结构图中可以看到这样一条父子关系链:J1→L1→J2→L2→J3→L3。即基座关节J1可以看成运动链的根节点,转动J1剩下的两根连杆L2和L3也会同样旋转。
下面我们在Python中实现用键盘控制三连杆机构的运动。首先在SolidWorks中绘制一个长度为100mm的连杆,然后创建装配体,依次将3个连杆首尾相连。按照之前的方法将装配体导出为3个STL模型文件。使用VTK的vtkSTLReader类读入STL文件,并编写事件处理的类处理键盘事件:
#!/usr/bin/env python import vtk import math from vtk.util.colors import * filenames = ["link-1.stl","link-2.stl","link-3.stl"] dt = 1.0 # degree step in rotation renWin = vtk.vtkRenderWindow() actor = list() # the list of links joint1 = vtk.vtkAssembly() joint2 = vtk.vtkAssembly() joint3 = vtk.vtkAssembly() # Customize vtkInteractorStyleTrackballCamera class MyInteractor(vtk.vtkInteractorStyleTrackballCamera): def __init__(self,parent=None): self.AddObserver("CharEvent",self.OnCharEvent) self.AddObserver("KeyPressEvent",self.OnKeyPressEvent) 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()
# Handle an arrow key if(key == "Left"): joint1.RotateY(-dt) if(key == "Right"): joint1.RotateY(dt) if(key == "Up"): joint2.RotateY(-dt) if(key == "Down"): joint2.RotateY(dt) if(key == "a"): joint3.RotateY(-dt) if(key == "d"): joint3.RotateY(dt) # 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() mapper.SetInputConnection(reader.GetOutputPort()) actor = vtk.vtkLODActor() actor.SetMapper(mapper) return actor def CreateScene(): # Create a rendering window and renderer ren = vtk.vtkRenderer() 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)) 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) joint1.AddPart(actor[0]) joint1.AddPart(joint2) joint2.AddPart(actor[1]) joint2.AddPart(joint3) joint3.AddPart(actor[2]) joint2.SetOrigin(100, 0, 0) # initial elbow joint position joint3.SetOrigin(200, 0, 0) # initial wrist joint position ren.AddActor(joint1)
# Set background color ren.GradientBackgroundOn() ren.SetBackground(.1, .1, .1) ren.SetBackground2(0.8,0.8,0.8) # Set window size renWin.SetSize(600, 600) # Enable user interface interactor iren.Initialize() iren.Start() if __name__ == "__main__": CreateScene()
运行结果如下图所示。按键盘上的← →键可以控制连杆1,按↑ ↓键可以控制连杆2,按a、d键可以控制连杆3。在添加装配体后能保证旋转单个关节时其后面的子装配体也能跟着一起旋转。
参考: