Stewart Platform: Design & Analyze Parallel Manipulator

Introduction

This model simulates a six-degree-of-freedom Stewart platform. The position and orientation of the platform is determined by the lengths of six legs. Each leg comprises a linear actuator and five rotational degrees of freedom. The model is constructed from components in the Modelica.Mechanics library. Mathematica is used to plan the 3D geometry of the model, and to produce custom visualizations of simulation results.

To open each subsection in this notebook, double click on its cell bracket in the right margin.

Explore the available scenarios by evaluating each cell in order. Evaluation is done by selecting a cell and pressing Shift+Enter.

In order to get the full experience of this example, you need the following:

Wolfram SystemModeler; a free trial download is available at www.wolfram.com/system-modeler

The model that came in the same bundle as this notebook

For more information about Wolfram SystemModeler, visit www.wolfram.com/system-modeler.

Initialize

Define the model name:

mmodel="IndustryExamples.IndustrialManufacturing.StewartPlatform.StewartPlatformControlScenario";

Design: 3D Geometry

To open each subsection in this notebook, double click on its cell bracket in the right margin.

Hexagonal base

Arrange the fixed endpoints of the six legs in an irregular hexagon. The hexagon is formed by truncating an equilateral triangle.

Angles of corners of an equilateral triangle with one corner lying on x-axis:

baseTriangleAngles={0,120,240};

Half-angle to truncate each corner by:

baseTruncationAngle=15;

Angle of each corner of hexagonal base:

baseAngles=SortBy[Flatten[Table[Mod[{θ+baseTruncationAngle,θ-baseTruncationAngle},360] °,{θ,baseTriangleAngles}],1],Less]

Radius of hexagonal base:

baseRadius=0.15;

Coordinates {x,y,z} of each corner of the hexagonal base:

basePoints=Table[{baseRadius Cos[θ],0,baseRadius Sin[θ]},{θ,baseAngles}]

These coordinates can be manually inserted into the model “StewartPlatform.ExampleModel” by copying and pasting them into the parameter “legBasePoints” of the component “base”.

Alternatively, insert the coordinates into the model programmatically:

SetSystemModel["StewartPlatform.Components.Base",<|"ParameterValues"-> {"legBasePoints"->basePoints}|>]

(SystemModeler verifies that the new parameter values fit in the model. This takes several seconds.)

Visualize shape of hexagonal base:

Graphics3D[Polygon[basePoints],PlotRange->baseRadius,ImageSize->160]

Hexagonal platform

Also arrange the endpoints of the six legs that are attached to the moveable platform in an irregular hexagon.

Offset the platform from the base by 60 °:

platformTriangleAngles=baseTriangleAngles+60

Half-angle to truncate each corner by:

platformTruncationAngle=15;

Angle of each corner of hexagonal platform:

platformAngles=SortBy[Flatten[Table[Mod[{θ+platformTruncationAngle,θ-platformTruncationAngle},360] °,{θ,platformTriangleAngles}],1],Less]

Radius of hexagonal platform:

platformRadius=0.1;

Coordinates {x,y,z} of each corner of the hexagonal platform relative to the center of the platform:

relativePlatformPoints=Table[{platformRadius Cos[θ],0,platformRadius Sin[θ]},{θ,platformAngles}]

These coordinates can be manually inserted into the model “StewartPlatform.ExampleModel” by copying and pasting them into the parameter “legPlatformRelativePositions” of the component “platform”.

Alternatively, insert the coordinates into the model programmatically:

SetSystemModel["StewartPlatform.Components.Platform",<|"ParameterValues" ->{"legPlatformRelativePositions"->relativePlatformPoints}|>]

(SystemModeler verifies that the new parameter values fit in the model. This takes several seconds.)

Initial height of hexagonal platform:

platformHeight=0.15;

Absolute coordinates {x,y,z} of each corner of the hexagonal platform:

platformPoints=Table[p+{0,platformHeight,0},{p,relativePlatformPoints}]

Visualize base, platform, and legs together:

Graphics3D[{Polygon[{basePoints,platformPoints}],{Thick,MapThread[Line[{##}]&,{basePoints,platformPoints}]}},PlotRange->{{-baseRadius,baseRadius},All,{-baseRadius,baseRadius}},Axes->True,ViewVertical->{0,1,0},ViewPoint->{1,1,2}]

Platform mass and moment of inertia

Compute the mass and moment of inertia of the moveable platform by integrating over its volume.

Thickness and density of hexagonal platform:

platformThickness=0.01;
platformDensity=8000;

A utility function giving a predicate representing the region enclosed in a two-dimensional polygon:

LineSign[{a_,b_}][p_]:=Sign[First[RotationTransform[{b-a,{0,1}}][p-a]]]
PolygonPredicate[Polygon[pts_],{x_,y_}]:=Apply[And,Table[LineSign[pair][{x,y}]==-1,{pair,Partition[Append[pts,First[pts]],2,1]}]]

For example, the predicate of a unit square:

Simplify[PolygonPredicate[Polygon[{{0,0},{1,0},{1,1},{0,1}}],{x,y}],{x,y}∈Reals]

Two-dimensional polygonal shape of the moveable platform:

platformPolygon=Polygon[platformPoints[[All,{1,3}]]];

Compute platform mass by integrating over the volume:

platformMass=platformDensity*Integrate[Boole[PolygonPredicate[platformPolygon,{x,y}]&&((-platformThickness)/2)<z<(platformThickness/2)],{x,-∞,∞},{y,-∞,∞},{z,-∞,∞}]

This value can be manually inserted into the model “StewartPlatform.ExampleModel” by copying and pasting it into the parameter “mass” of the component “platform”. Alternatively, the value can be inserted programmatically (see below).

Definition of three-dimensional moment of inertia tensor for a specified density field ρ:

momentOfInertia[ρ_,{x_,y_,z_}]:=Integrate[ρ(((x^2)+(y^2)+(z^2))IdentityMatrix[3]-{{(x^2),x y,x z},{x y,(y^2),y z},{x z,y z,(z^2)}}),{x,-∞,∞},{y,-∞,∞},{z,-∞,∞}]

Compute moment of inertia tensor of the moveable platform:

platformInertia=momentOfInertia[platformDensity*Boole[PolygonPredicate[platformPolygon,{x,y}]&&((-platformThickness)/2)<z<(platformThickness/2)],{x,y,z}]//Chop

The canonical axis of physical gravity in Modelica is y and not z, so swap these coordinates:

platformInertia=platformInertia[[{1,3,2},{1,3,2}]]

This value can be manually inserted into the model “StewartPlatform.ExampleModel” by copying and pasting it into the parameter “inertiaTensor” of the component “platform”.

Alternatively, insert the platform parameters programmatically:

SetSystemModel["StewartPlatform.Components.Platform",<|"ParameterValues" ->{"mass"->platformMass,"inertiaTensor"->platformInertia,"platformHeight"->platformHeight}|>]

(SystemModeler verifies that the new parameter values fit in the model. This takes several seconds.)

Initial leg geometry

Specify initial conditions for the length and orientation of each of the six-degree-of-freedom legs.

legLengths=Map[Norm,platformPoints-basePoints]

legDirections=Map[Normalize,platformPoints-basePoints]

These parameters can be manually inserted into the model “StewartPlatform.ExampleModel” by copying and pasting them into the parameters “initialLengths” and “legDirections” of the component “legs”.

Alternatively, insert the leg parameters programmatically:

SetSystemModel["StewartPlatform.Components.Legs",<|"ParameterValues" -> {"initialLengths"->legLengths,"legDirections"->legDirections}|>]

(SystemModeler verifies that the new parameter values fit in the model. This takes several seconds.)

3D geometry files for SystemModeler animations

Create custom 3D graphics representing the base and platform and export them as OBJ 3D geometry files for use by SystemModeler.

Checkerboard mesh specification:

checkerboard=ConstantArray[Range[-baseRadius,baseRadius,(baseRadius/4)],2];

Create and export platform graphics:

platformGraphics=RegionPlot3D[Evaluate[PolygonPredicate[platformPolygon,{x,y}]&&-(platformThickness/2)<z<(platformThickness/2)],{x,-platformRadius,platformRadius},{y,-platformRadius,platformRadius},{z,-platformThickness,platformThickness},BoxRatios->Automatic,PlotPoints->50,Mesh->checkerboard,MeshFunctions->{#1&,#2&},MeshShading->{{GrayLevel[0.6],GrayLevel[0.8]},{GrayLevel[0.8],GrayLevel[0.6]}},Lighting->"Neutral"]

Export[FileNameJoin[{NotebookDirectory[],"DocumentationFiles","Platform.obj"}],platformGraphics];

Create and export base graphics:

baseThickness=0.02;

baseGraphics=RegionPlot3D[Evaluate[PolygonPredicate[Polygon[basePoints[[All,{1,3}]]],{x,y}]&&-(baseThickness/2)<z<(baseThickness/2)],{x,-baseRadius,baseRadius},{y,-baseRadius,baseRadius},{z,-baseThickness,baseThickness},BoxRatios->Automatic,PlotPoints->50,Mesh->checkerboard,MeshFunctions->{#1&,#2&},MeshShading->{{GrayLevel[0.6],GrayLevel[0.8]},{GrayLevel[0.8],GrayLevel[0.6]}},Lighting->"Neutral"]

Export[FileNameJoin[{NotebookDirectory[],"DocumentationFiles","Base.obj"}],baseGraphics];

Design: Motion Planning

Plan the motion of the Stewart platform by solving the inverse kinematics problem giving the lengths of each leg as a function of the desired configuration. Export the result to a file in “ModelicaCombiTimeTable” format for use by the SystemModeler model.

Inverse kinematics

The “pose” of the moveable platform is defined by 6 coordinates {x,y,z,θ,ϕ,τ}. The position of the center of the platform is {x,y,z}. The normal vector pointing upwards out of the platform points in the direction {θ,ϕ} in spherical coordinates. The rotation of the platform about the normal vector is the angle τ.

Function giving the position of the moveable endpoint of each leg as a function of the pose coordinates:

PoseLegEndpoints[{x_,y_,z_,θ_,ϕ_,τ_}]:=Table[Composition[TranslationTransform[{x,y,z}+{0,platformHeight,0}],RotationTransform[{{0,1,0},{Sin[θ]Cos[ϕ],Cos[θ],Sin[θ]Sin[ϕ]}}],RotationTransform[τ,{0,1,0}]][p],{p,relativePlatformPoints}]

Function giving the lengths of each leg as a function of the pose coordinates:

PoseLegLengths[pose_]:=Map[Norm,PoseLegEndpoints[N[pose]]-basePoints]

For example, compare the initial pose with a pose in which the platform is uniformly raised by 1 cm:

{PoseLegLengths[{0,0,0,0,0,0}],PoseLegLengths[{0,0.01,0,0,0,0}]}

Utility function for generating a sequence of steps between two poses:

PoseRange[{pose1_,pose2_},steps_]:=Table[pose1+t(pose2-pose1),{t,Range[0,1,(1/steps)]}]

Function to tabulate and export a sequence of leg lengths corresponding to a platform shifting between a list of poses at the rate of one pose per second, with a specified fixed offset applied to the length each leg:

ExportMotionPlan[poses_List,legoffsets_:{0,0,0,0,0,0}]:=
Module[{framerate=10,lengths,tabledata},
lengths={First[poses]};
Do[lengths=Join[lengths,Rest@PoseRange[{Last[lengths],wp},framerate]],{wp,Rest[poses]}];AppendTo[lengths,Last[lengths]];
lengths=Map[PoseLegLengths,lengths];
lengths=Map[#+legoffsets&,lengths];
tabledata=MapIndexed[Prepend[#,((First[#2]-1)/framerate)]&,lengths];{Export[FileNameJoin[{NotebookDirectory[],"DocumentationFiles","PlatformPath.txt"}],{{"LegLengths",tabledata}},"MCTT"],First[Last[tabledata]]}
]

Short test motion plan

A short test motion plan consisting of a single change of platform pose:

testMotionPlan=
Block[{dx=0.05,dθ=0.5},
{
{0,0,0,0,0,0},
{0,0,0,0,0,0},
{dx,dx,0,dθ,0,dθ},
{dx,dx,0,dθ,0,dθ}
}
];

Evaluate the following line to specify the short test motion plan:

ExportMotionPlan[testMotionPlan];

Evaluate the following line to include a fixed offset (error) for one of the legs of the Stewart platform:

ExportMotionPlan[testMotionPlan,{0.02,0,0,0,0,0}];

Long demonstration motion plan

A long demonstration motion plan:

demoMotionPlan=
Block[{dx=0.05,dθ=0.5},
{
{0,0,0,0,0,0},
{0,0,0,0,0,0},
{dx,dx,0,0,0,-2dθ},
{dx,0,0,dθ,0,0},
{dx,0,0,dθ,0,0},
{-dx,dx,0,-dθ,0,0},
{-dx,dx,0,-dθ,0,0},
{-dx,dx,0,-dθ,0,2dθ},
{-dx,dx,0,-dθ,0,0},
{-dx,dx,dx,-dθ,0,0},
{dx,dx,dx,-dθ,0,0},
{dx,dx,-dx,-dθ,0,0},
{-dx,dx,-dx,-dθ,0,0},
{-dx,dx,dx,-dθ,0,0},
{0,0,0,0,0,2dθ},
{0,0,0,0,0,0},
{0,0,0,0,0,0}
}
];

Evaluate the following line to specify the long demonstration motion plan:

ExportMotionPlan[demoMotionPlan];

Visualization: Custom 3D Animation

Generate a custom animation of the Stewart platform from the SystemModeler simulation results by using Mathematica’s Graphics3D function.

Simulation results

Simulate the Stewart platform using the long demonstration motion plan:

demoSimulation:=demoSimulation=
(
ExportMotionPlan[demoMotionPlan];
SystemModelSimulate[mmodel,{0,20}]
);

(The “memoization” idiom f := f = expr ensures that expr evaluates only the first time f is used, and the result is remembered for subsequent uses of f.)

Run the simulation to precompute results:

demoSimulation

Define a function to extract the position of the moveable endpoint of each of the six legs as a function of time t:

LegEndpointsFunction[sim_]:=
Block[{t},Function@@{t,Partition[sim[Flatten[Table["platform.frame_b["<>ToString[i]<>"].r_0["<>ToString[a]<>"]",{i,6},{a,3}]],t],3][[All,{1,3,2}]]}]

For example, the position of the endpoints of the legs at time t=3:

LegEndpointsFunction[demoSimulation][3]

Graphics3D

The following function returns a function of t that draws the Stewart platform at the specified time. The visualization comprises three parts: the base, drawn by BaseShape; the platform, drawn by PlatformShape; and the n legs, drawn by LegShape. Each function is capable of drawing the platform using different named styles, defined below.

StewartPlatformGraphicsFunction[sim_,style_]:=
With[
{platformf=LegEndpointsFunction[sim]},
Function[t,
Module[{base,platform,n=0},
base=basePoints[[All,{1,3,2}]];
platform=platformf[t];
Graphics3D[
{
BaseShape[base,style],
PlatformShape[platform,style],
MapThread[LegShape[{##},style,++n]&,{base,platform}]
},
PlotRange->{{-1/6,1/6},{-1/6,1/6},{-(baseThickness/2),1/4}},
Lighting->"Neutral",
Boxed->False,
BoxRatios->Automatic,
ViewPoint->{1,-4,2}
]
]
]
]

“Minimal” style

Represent the base, platform, and legs with plain polygons and lines:

BaseShape[corners_List,"Minimal"]:=Polygon[corners]

PlatformShape[corners_List,"Minimal"]:=Polygon[corners]

LegShape[{a_,b_},"Minimal",n_]:=Line[{a,b}]

Example frame at time t=3:

StewartPlatformGraphicsFunction[demoSimulation,"Minimal"][3]

Extrude 2D polygon to 3D

Utility function to thicken a 2D polygon into a 3D shape, which is useful for more complex styles:

ExtrudedPolygonFaces[corners_,z_]:=
Module[
{normal,a,b,c,dz},
{a,b,c}=Take[corners,3];
normal=Normalize[Cross[c-b,a-b]];
dz=Table[z*normal,{corners}];
{corners-dz,corners+dz}
]

JoinPolygonPoints[corners1_,corners2_]:=MapThread[Join,Table[Thread[{c,RotateRight[c]}],{c,{corners1,corners2}}]][[All,{1,2,4,3}]]

ExtrudedPolygonPoints[corners_,z_]:=
Module[{faces},
faces=ExtrudedPolygonFaces[corners,z];
Join[faces,JoinPolygonPoints@@faces]
]

For example, an extruded version of the hexagonal base of the Stewart platform:

Graphics3D[Map[Polygon,ExtrudedPolygonPoints[basePoints,baseThickness]],ImageSize->160]

“Wireframe” style

Represent the base and platform as the wireframes of extruded polygons.

BaseShape[corners_List,style:"Wireframe"]:=
{FaceForm[],Polygon/@ExtrudedPolygonPoints[corners,(baseThickness/2)]}

PlatformShape[corners_List,style:"Wireframe"]:=
{FaceForm[],Polygon/@ExtrudedPolygonPoints[corners,(platformThickness/2)]}

LegShape[{a_,b_},"Wireframe",n_]:=Line[{a,b}]

Example frame at time t=3:

StewartPlatformGraphicsFunction[demoSimulation,"Wireframe"][3]

“Solid” style

Represent the base and platform as solid extruded polygons and represent the legs with cylinders.

BaseShape[corners_List,style:"Solid"]:=
Polygon/@ExtrudedPolygonPoints[corners,(baseThickness/2)]

PlatformShape[corners_List,style:"Solid"]:=
Polygon/@ExtrudedPolygonPoints[corners,(platformThickness/2)]

LegShape[{a_,b_},"Solid",n_]:=Cylinder[{a,b},0.005]

Example frame at time t=3:

StewartPlatformGraphicsFunction[demoSimulation,"Solid"][3]

“Full” style

Represent the base and platform using the previously generated checkerboard graphics and represent the legs with cylinders and a rotating nut.

BaseShape[corners_List,"Full"]=First[baseGraphics];

Use FindGeometricTransform to find the correct linear transformation to apply to the previously generated checkboard-style graphics for the moveable platform:

PlatformShape[corners_List,"Full"]:=
Module[{transform},
transform=Last[FindGeometricTransform[corners,relativePlatformPoints[[All,{1,3,2}]],TransformationClass->"Rigid",Method->"Linear"]];
{GeometricTransformation[First[platformGraphics],transform]}
]

Custom visualization of a single telescopic leg, with a rotating hexagonal nut for visual effect only:

FullLegShape[{a_,b_},color_]:=
Module[
{
midpoint,
pistonlength=0.125,
pistonradius=0.006,
extensionradius=0.0035,
rotationspeed=200
},
midpoint=a+pistonlength Normalize[b-a];
{
{color,Cylinder[{a,midpoint},pistonradius]},
{Lighter@Gray,Cylinder[{midpoint,b},extensionradius]},
{Gray,Sphere[a,1.5pistonradius],Sphere[b,1.5extensionradius]},
{Lighter@Gray,Translate[Rotate[Rotate[Polygon/@ExtrudedPolygonPoints[Table[2pistonradius{Cos[θ],Sin[θ],0},{θ,Most@Range[0,2π,((2π)/6)]}],(pistonradius/2)],rotationspeed*Norm[b-a],{0,0,1}],{{0,0,1},Normalize[b-a]}],midpoint]}
}
]

LegShape[{a_,b_},"Full",n_]:=FullLegShape[{a,b},Darker[Red]]

Example frame at time t=3:

StewartPlatformGraphicsFunction[demoSimulation,"Full"][3]

Export movie file

Generate individual movie frames at a specified frame rate and export an AVI movie:

frameRate=15;

Export[FileNameJoin[{NotebookDirectory[],"StewartPlatformDemo.avi"}],Monitor[With[{frame=StewartPlatformGraphicsFunction[demoSimulation,"Full"]},Table[Image[frame[N[t]]],{t,0,18,(1/frameRate)}]],ProgressIndicator[t,{0,18}]],"FrameRate"->frameRate];

Open the movie with the system’s default application for AVI files:

SystemOpen[FileNameJoin[{NotebookDirectory[],"StewartPlatformDemo.avi"}]]

Visualization: Leg Length Offset

Visualize the effect of a leg length offset (error) by combining a “Full” style visualization with a “Wireframe” style visualization.

Simulation results

Perform simulations with the short test motion plan with and without leg offsets:

nonzeroOffsetSimulation:=nonzeroOffsetSimulation=
(
ExportMotionPlan[testMotionPlan,{0.02,0,0,0,0,0}];
SystemModelSimulate[mmodel,{0,4}]
);

zeroOffsetSimulation:=zeroOffsetSimulation=
(
ExportMotionPlan[testMotionPlan];
SystemModelSimulate[mmodel,{0,4}]
);

(The “memoization” idiom f := f = expr ensures that expr evaluates only the first time f is used, and the result is remembered for subsequent uses of f.)

Run the simulations to precompute results:

{nonzeroOffsetSimulation,zeroOffsetSimulation}

Custom visualization

Visualize the zero-offset scenario with “Full” style while temporarily changing the color of the first leg (which is the offset leg):

zeroOffsetGraphics:=zeroOffsetGraphics=
Module[{result},
LegShape[{a_,b_},"Full",1]:=FullLegShape[{a,b},Green];
result=StewartPlatformGraphicsFunction[zeroOffsetSimulation,"Full"][3];
LegShape[{a_,b_},"Full",1]=.;
result
];

Pre-compute and show the visualization:

Show[zeroOffsetGraphics,ImageSize->240]

Visualize the nonzero-offset scenario with a modified “Wireframe” style:

nonzeroOffsetGraphics:=nonzeroOffsetGraphics=Replace[StewartPlatformGraphicsFunction[nonzeroOffsetSimulation,"Wireframe"][3],Graphics3D[first_,rest___]:>Graphics3D[{EdgeForm[Directive[Thick,Blue]],Thick,Blue,first},rest]];

Pre-compute and show the visualization:

Show[nonzeroOffsetGraphics,ImageSize->240]

Combine the two visualizations to show the effect of the leg length offset:

Show[zeroOffsetGraphics,nonzeroOffsetGraphics,PlotRange->All]

Terms and Conditions of Use

This domain example is an informational resource made freely available by Wolfram Research.

Use of This Example

• You may not use this example for any purpose that is unlawful or dangerous.
• You assume total responsibility and risk for your use of this example.
• You may not present this example with any alteration to its trade dress, trademarks, or design.

License

All content in this bundle is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. By accessing the content or using it in any way, you accept and agree to be bound by the terms of this license. If you do not agree to these Terms of Use, you may not use this content. Wolfram Research reserves the right to change, modify, add to, or remove portions of these Terms of Use at any time without notice. Please refer back to www.wolfram.com for the latest Terms of Use.

A summary of the licensing terms can be found at:
http://creativecommons.org/licenses/by-nc-sa/3.0

The full legal code can be found at: 
http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode