Document toolboxDocument toolbox

WIP 02 | Multi-Joint Systems

Goal: Learn how to prepare scenes for connections to external controllers (PLC’s)

In this tutorial we learn how to prepare our scenes to be controlled by an external controller. In previous tutorials we made Actors responsible for their own velocities. Here we look al how we can use a controller component to do that for those Actors.
This scene features a simplistic setup of a crane. It features nested Actors (Actors in Actors) and a controller component that controls them.

ActorVelocityController script

#if PRESPECTIVE_SOURCE || ALPHA using System.Collections.Generic; using u040.prespective.core.spline; using u040.prespective.prescripted.des; using u040.prespective.prescripted.des.activities.motiontensor; using u040.prespective.prescripted.des.events; using u040.prespective.prescripted.des.participant; using u040.prespective.prescripted.des.participant.actor; namespace u040.prespective.tutorials.desintermediate { /// <summary> /// Demo script to show how to use forward kinematics in DES /// </summary> public class ActorVelocityController : DESParticipant { /// <summary> /// Actor to control the Crane Arm /// </summary> public DESActor ArmActor; /// <summary> /// Actor to control the Load /// </summary> public DESActor LoadActor; /// <summary> /// Spline to describe the path on the Base /// </summary> public DSpline BaseSpline; /// <summary> /// Spline to describe the path on the Arm /// </summary> public DSpline ArmSpline; /// <summary> /// The velocity applied to the Arm Actor /// </summary> public double ArmVelocity; /// <summary> /// The velocity applied to the Load Actor /// </summary> public double LoadVelocity; /// <summary> /// Motion Tensor object for the Arm Actor /// </summary> private MotionTensor armMotionTensor; /// <summary> /// Motion Tensor object for the Load Actor /// </summary> private MotionTensor loadMotionTensor; /// <summary> /// Previously set velocity for the Arm Actor to detect a change in the Arm Velocity /// </summary> private double previousArmVelocity; /// <summary> /// Previously set velocity for the Load Actor to detect a change in the Load Velocity /// </summary> private double previousLoadVelocity; /// <summary> /// The priority this Participant has for the solver /// </summary> public override int ParticipantSolverPriority => 1; /// <summary> /// Called before starting the Simulation. Usually used to create objects required for the simulation /// </summary> /// <param name="_simController"></param> protected override void initializeParticipant(ADESController _simController) { // Create MotionTensors for both the arm and load actors armMotionTensor = new MotionTensor(new List<MotionTensorComponent>() { new MotionTensorComponent(BaseSpline, MotionMode.Relative, MotionType.Translation, 0d, ArmVelocity, 1d) }) { IsRunning = true }; loadMotionTensor = new MotionTensor(new List<MotionTensorComponent>() { new MotionTensorComponent(ArmSpline, MotionMode.Relative, MotionType.Translation, 0d, LoadVelocity, 1d) }) { IsRunning = true }; } /// <summary> /// OnSimulationStart is run once at the start of the simulation. Any preparations necessary for the simulation can be done here. /// </summary> /// <param name="_resultingEvents">Container for the events that are calculated during start (they are invoked after the start on all simulated objects are completed)</param> /// <param name="_resultRecords">Container for the records that are created during start (they are processed for events after the start on all simulated objects are completed)</param> public override void OnSimulationStart(ref List<DESEvent> _resultingEvents, ref List<DESRecord> _resultRecords) { // Add the previously created Motion Tensors to the arm and load Actors ArmActor.AddActivity(armMotionTensor); LoadActor.AddActivity(loadMotionTensor); } /// <summary> /// OnSimiluationFrameUpdate is called once every frame pass update /// </summary> /// <param name="_passedFrameTime">The passed time in the current frame</param> /// <param name="_totalFrameTime">The total time of a single frame</param> /// <param name="_frameID">The ID of the current frame</param> /// <param name="_framePass">The current frame pass count</param> /// <param name="_resultingEvents">Container for the events that are calculated during start (they are invoked after the start on all simulated objects are completed)</param> /// <param name="_resultRecords">Container for the records that are created during start (they are processed for events after the start on all simulated objects are completed)</param> public override void OnSimulationFrameUpdate(double _passedFrameTime, double _totalFrameTime, long _frameID, int _framePass, ref List<DESEvent> _resultingEvents, ref List<DESRecord> _resultRecords) { // If the ArmVelocity has changed, set it for the ArmActor's Motion Tensor if(previousArmVelocity != ArmVelocity) { armMotionTensor.ForceVelocity(ArmVelocity, SimulationController, _passedFrameTime); previousArmVelocity = ArmVelocity; } // If the ArmVelocity has changed, set it for the ArmActor's Motion Tensor if (previousLoadVelocity != LoadVelocity) { loadMotionTensor.ForceVelocity(LoadVelocity, SimulationController, _passedFrameTime); previousLoadVelocity = LoadVelocity; } // Run the inherited code for OnSimulationFrameUpdate base.OnSimulationFrameUpdate(_passedFrameTime, _totalFrameTime, _frameID, _framePass, ref _resultingEvents, ref _resultRecords); } } } #endif

 

 

public class ActorVelocityController : DESParticipant

The ActorVelocityController is a DESParticipant. This makes this script part of the simulation and it receives the similation callbacks such as OnSimulationStart and OnSimulationFrameUpdate.

 

protected override void initializeParticipant(ADESController _simController) { // Create MotionTensors for both the arm and load actors armMotionTensor = new MotionTensor(new List<MotionTensorComponent>() { new MotionTensorComponent(BaseSpline, MotionMode.Relative, MotionType.Translation, 0d, ArmVelocity, 1d) }) { IsRunning = true }; loadMotionTensor = new MotionTensor(new List<MotionTensorComponent>() { new MotionTensorComponent(ArmSpline, MotionMode.Relative, MotionType.Translation, 0d, LoadVelocity, 1d) }) { IsRunning = true }; }

A DESParticipant is required to override the initializeParticipant method. This is similar to an Awake method. This can be used to prepare any objects required for the simulation.
In this case it is used to create the Motion Tensors for both Actors. There are not added to the Actors yet since this needs to be done at the start of the simulation. The initialization of this DESParticipant happens before the start the of the simulation.

 

During the OnSimulationStart the previously created Motion Tensors are added to their corresponding Actors.

 

The Actor Velocity Controller is responsible for controller the velocities of the Arm Actor and the Load Actor. To make sure that their velocities are always up to date, the controller verifies those velocities in the OnSimulationFrameUpdate. For each frame it checks whether the current velocity was changed in the controller by comparing it to the last set velocity for the Actor. If these are not equal, it will set the new velocity for the Actor and store that value as the last set velocity. It does this for both the Arm Actor and the Load Actor.

In this tutorial, this controller is responsible for setting the velocities of the Actors through its own properties (ArmVelocity and LoadVelocity). When an external controller (PLC) would be connected, the velocity values would come through external connections. A controller can still be responsible for receiving and setting the velocities but in that case it would not stores those values itself.


Play Mode

When we press play, the Actor Velocity Controller script on the Crane Base continuously updates the velocities for both the Arm Actor and the Load Actor. Alternating between positive and negative velocity values will move the Actor back and forth over its Spline. Since the Load Actor is nested in the Arm Actor, the Load Actor will always move along with the Arm Actor. The Load Actor will also always move relative to the Arm Actor.

 

 

Prespective Documentation