Start a new topic
Answered

Script for Changing Collision Detection from Touch Sensor to Rigid Body

Good Day.


I need help.


I am new on using Vortex Studio Editor and I am not fully familiar on creating Python scripts for simulation.

How can I make a script to change the collision detection of a  Capsule Collision Geometry from Touch Sensor to Rigid Body?


I hope for a positive response.


Thank you and have a nice day ahead!


Regards,


Mark Jayvhie Belbar,

Mechanical Engineer, Crane Motion Simulation


Best Answer

Hi Mark,


In your script, add a parameter of type "CollisionGeometry" (right-click on the script in the explorer tree, then select "edit").

Here I created a parameter of the appropriate type and gave it the name "CG" for collision geometry:


image


Once you have done that, set your "CG" parameter to the collision geometry you would like to modify (the capsule), and then use the following script to modify the collision mode:

   

from VxSim import *


def on_add_to_universe(self, universe):
    """ Called when the script is added to the universe.
    Use this method to define specific dynamics actions that must be taken at initialization.""" 
    pass

def on_remove_from_universe(self, universe):
    """ Called when the script is removed from the universe.
    Use this method to define specific dynamics actions that must be taken at shutdown.""" 
    pass

def pre_step(self):
    """ Called before the collision detection and before the dynamic solver.
    Use this method to get inputs or set values to dynamics objects.""" 

    # The following line prints all the available functions and parameters on the value in the parameter CG
    # The "value" member accesses the actual object in the parameter field.
    #print dir(self.parameters.CG.value)

    # sets mode to "Rigid Body" (=0)
    self.parameters.CG.value.parameterContactMode.value = 0

    # sets mode to "Touch Sensor" (=1)
    #self.parameters.CG.value.parameterContactMode.value = 1

    # sets mode to "Contact Sensor" (=2)
    #self.parameters.CG.value.parameterContactMode.value = 2
    pass

def post_step(self):
    """ Called after the collision detection and after the dynamic solver.
    Use this method to set outputs or get values from dynamics objects.""" 
    pass
 

If you want to know which functions and members (usually parameters, inputs and outputs) are available on some extension (Part, CollisionGeometry, Assembly, or anything else), you can use the dir() command in python. See the script above for an example (commented out in pre_step).


Hope this helps.


Cheers,

Daniel


Answer

Hi Mark,


In your script, add a parameter of type "CollisionGeometry" (right-click on the script in the explorer tree, then select "edit").

Here I created a parameter of the appropriate type and gave it the name "CG" for collision geometry:


image


Once you have done that, set your "CG" parameter to the collision geometry you would like to modify (the capsule), and then use the following script to modify the collision mode:

   

from VxSim import *


def on_add_to_universe(self, universe):
    """ Called when the script is added to the universe.
    Use this method to define specific dynamics actions that must be taken at initialization.""" 
    pass

def on_remove_from_universe(self, universe):
    """ Called when the script is removed from the universe.
    Use this method to define specific dynamics actions that must be taken at shutdown.""" 
    pass

def pre_step(self):
    """ Called before the collision detection and before the dynamic solver.
    Use this method to get inputs or set values to dynamics objects.""" 

    # The following line prints all the available functions and parameters on the value in the parameter CG
    # The "value" member accesses the actual object in the parameter field.
    #print dir(self.parameters.CG.value)

    # sets mode to "Rigid Body" (=0)
    self.parameters.CG.value.parameterContactMode.value = 0

    # sets mode to "Touch Sensor" (=1)
    #self.parameters.CG.value.parameterContactMode.value = 1

    # sets mode to "Contact Sensor" (=2)
    #self.parameters.CG.value.parameterContactMode.value = 2
    pass

def post_step(self):
    """ Called after the collision detection and after the dynamic solver.
    Use this method to set outputs or get values from dynamics objects.""" 
    pass
 

If you want to know which functions and members (usually parameters, inputs and outputs) are available on some extension (Part, CollisionGeometry, Assembly, or anything else), you can use the dir() command in python. See the script above for an example (commented out in pre_step).


Hope this helps.


Cheers,

Daniel


1 person likes this
Hi Mark,

Just by scanning through the script I already found one error.

Motor.lock.velocity.value = Constraint.kControlLocked

You are assigning an enum value defining the control mode of a coordinate to a lock velocity field. That is not correct. The lock.velocity field defines the desired velocity you want a coordinate to move if the coordinate is in kControlLocked mode.
In order to switch the coordinate to be in lock mode, you have to navigate to the coordinate you want to actuate first.

Let's say your "Motor" constraint in your script is a Hinge joint. In this case, it will have one angular coordinate that is controllable. All the input properties of this coordinate are in the Hinge's input container called inputAngularCoordinate. In the inputAngularCoordinate container, you have the ability to change the control to kControlLocked. And then you can set the desired lock velocity. The locked control mode models a positional control where you define a target position (lock.position.value = ...) or a desired speed (lock.velocity.value = ...). The two inputs (position and velocity) can be used together or exclusively, in the sense that lock.velocity.value will automatically update lock.position.value according to the provided speed in each time step. So, the lock.position.value will be driven automatically with this speed and will be changed automatically during simulation. But you can also just set the lock.position.value manually each frame if that is what you prefer.
A locked coordinate uses a visco-elastic constraint model. So it acts like a spring-damper and has stiffness and damping coefficients. The set point of the spring is defined by lock.position.value.

In the example with the Hinge constraint, to change the control mode of the hinge's controllable angular coordinate (the hinge has only one controllable coordinate, but other constraints, like a universal joint, have multiple), you can do the following in your script. Let's say your "Motor" parameter is the Hinge constraint.

# set hinge's angular coordinate to locked mode
self.parameters.Motor.value.inputAngularCoordinate.control.value = VxSim.Constraint.kControlLocked
# set hinge's lock velocity to 3 radian per second
self.parameters.Motor.value.inputAngularCoordinate.lock.velocity.value = 3.0

To see which input, output and parameter fields are available on the "Motor" constraint that you specify in your script's parameters, simply add the line "print dir(self.parameters.Motor.value)" to your script.

Or have a look at the C++ SDK reference manual of the Hinge, here:
https://www.cm-labs.com/vortexstudiodocumentation/Vortex_Technical_Documentation/class_vx_dynamics_1_1_hinge.html

Likewise, if you want to know what is available on the hinge's angular coordinate container, in your script add the line "print dir(self.parameters.Motor.value.inputAngularCoordinate)"

Or check the C++ SDK reference of this coordinate container here:
https://www.cm-labs.com/vortexstudiodocumentation/Vortex_Technical_Documentation/class_vx_dynamics_1_1_constraint_coordinate_container.html

Hope this helps.

Cheers,
Daniel

1 person likes this

Hi Daniel,

Thank you for your technical advise. The script is functioning now. I highly appreciated your help!

Highly commended!


Warm Regards,


Mark


1 person likes this

Hi Mark,


I am glad I could help you out. And thanks for your appreciation.

If you don't mind, could you post the functioning version of your script here in this thread, so that others can benefit from this discussion to the full extend?


Thanks!


Daniel



1 person likes this

 Hi Daniel,


Attached here is the functioning script for  the control mode of the prismatic's controllable linear coordinate.

I hope it will help everyone. 

 



from VxSim import *
# import pyvx

def on_add_to_universe(self, universe):
    # print dir(Types)
    CreateInput(self, 'LS Locked', Types.Type_Bool)
    CreateInput(self, 'LS Unlocked', Types.Type_Bool)

    CreateOutput(self, 'Locked', Types.Type_Bool)
    CreateOutput(self, 'FL Wheel', Types.Type_Part)
    
    CreateParameter(self, 'Intersection Sensor', Types.Type_ExtensionPtr)
    CreateParameter(self, 'Motor', Types.Type_Constraint)

    parts = [self.parameters.Intersection_Sensor.value]

    self.sensors = []
    for part in parts:
        sensor = IntersectionSensor(universe)
        sensor.setSensorExtension(part)
        sensor.setCollectingIntersections(True)
        self.sensors.append(sensor)

    self.intersections = None

def pre_step(self):
    
    # Locked must be tested in the post step, so use the outputs, which is set there
    Locked = self.outputs.Locked.value
    FL_Wheel = self.outputs.FL_Wheel.value
    Motor = self.parameters.Motor.value.inputLinearCoordinate

    if Locked and self.inputs.LS_Locked.value:
        connect(self.parameters.Motor.value, FL_Wheel, VxVector3(0.0,0.0,0.0))
        self.parameters.Motor.value.inputLinearCoordinate.lock.velocity.value = 0.0

    else:
        self.parameters.Motor.value.inputLinearCoordinate.control.value = VxSim.Constraint.kControlLocked
      
def post_step(self):

    Locked = True
    for sensor in self.sensors:
        if not sensor.hasIntersected():
            Locked = False
            break
        
    if Locked:
        intersections = self.sensors[0].getIntersections()
        self.outputs.FL_Wheel.value = intersections[0].triggerPart
        
    else:
        self.outputs.FL_Wheel.value = None
         
    self.outputs.Locked.value = Locked

    
def connect(constraint, FL_Wheel, position):
    if constraint is None or constraint.inputEnable.value == True:
        return
    constraint.inputAttachments[0].part.value = FL_Wheel
    constraint.inputAttachments[0].position.value = position
    constraint.inputEnable.value = True

# Create functions create fields in the Editor
def CreateInput(extension, name, type):
    input = extension.getInput(name)
    if input is None:
        input = extension.addInput(name, type)

def CreateOutput(extension, name, type):
    output = extension.getOutput(name)
    if output is None:
        output = extension.addOutput(name, type)

def CreateParameter(extension, name, type):
    param = extension.getParameter(name)
    if param is None:
        param = extension.addParameter(name, type)

# Signal processing
def clamp(value, minValue, maxValue):
    clampedValue = max(min(value, maxValue), minValue)
    return clampedValue

def lowPassFilter(inputSignal, outputSignal, timeConstant):
    stepTime = self.getApplicationContext().getSimulationTimeStep()
    value = ( (deltaTime * inputSignal) + (timeConstant * outputSignal) ) / (deltaTime + timeConstant )
    return value 

 ありがとうございます!


Regards,


Mark


1 person likes this

Hi Daniel,


Thank you for your response! It was a great help for me.

Aside from this concern, I encounter some difficulty on the script I've made.

The script below was made to act as a limit switch sensor for a trolley. Everytime the wheel intersect the sensor, and presses the "LS Locked input" via gamepad connection,  it will stop suddenly.


The script performed its goal but the problem was when I determining the constraint for the parameter "Motor", the constraint which have a gamepad control connection loses. When I start the simulation, It automatically moves even  if I don`t press the button for the movement.




image


  

from VxSim import *
# import pyvx

def on_add_to_universe(self, universe):
    # print dir(Types)
    CreateInput(self, 'LS Locked', Types.Type_Bool)
    CreateInput(self, 'LS Unlocked', Types.Type_Bool)

    CreateOutput(self, 'Locked', Types.Type_Bool)
    CreateOutput(self, 'FL Wheel', Types.Type_Part)
    
    CreateParameter(self, 'Intersection Sensor', Types.Type_ExtensionPtr)
    CreateParameter(self, 'Motor', Types.Type_Constraint)

    parts = [self.parameters.Intersection_Sensor.value]

    self.sensors = []
    for part in parts:
        sensor = IntersectionSensor(universe)
        sensor.setSensorExtension(part)
        sensor.setCollectingIntersections(True)
        self.sensors.append(sensor)

    self.intersections = None

def pre_step(self):
    
    # Locked must be tested in the poststep, so use the outputs, which is set there
    Locked = self.outputs.Locked.value
    FL_Wheel = self.outputs.FL_Wheel.value
    Motor = self.parameters.Motor.value.inputLinearCoordinate

    if Locked and self.inputs.LS_Locked.value:
        connect(self.parameters.Motor.value, FL_Wheel, VxVector3(0.0,0.0,0.0))
        Motor.lock.velocity.value = 0.0

    else:
        Motor.lock.velocity.value = Constraint.kControlLocked
      
def post_step(self):

    Locked = True
    for sensor in self.sensors:
        if not sensor.hasIntersected():
            Locked = False
            break
        
    if Locked:
        intersections = self.sensors[0].getIntersections()
        self.outputs.FL_Wheel.value = intersections[0].triggerPart
        
    else:
        self.outputs.FL_Wheel.value = None
         
    self.outputs.Locked.value = Locked

    
def connect(constraint, FL_Wheel, position):
    if constraint is None or constraint.inputEnable.value == True:
        return
    constraint.inputAttachments[0].part.value = FL_Wheel
    constraint.inputAttachments[0].position.value = position
    constraint.inputEnable.value = True

# Create functions create fields in the Editor
def CreateInput(extension, name, type):
    input = extension.getInput(name)
    if input is None:
        input = extension.addInput(name, type)

def CreateOutput(extension, name, type):
    output = extension.getOutput(name)
    if output is None:
        output = extension.addOutput(name, type)

def CreateParameter(extension, name, type):
    param = extension.getParameter(name)
    if param is None:
        param = extension.addParameter(name, type)

# Signal processing
def clamp(value, minValue, maxValue):
    clampedValue = max(min(value, maxValue), minValue)
    return clampedValue

def lowPassFilter(inputSignal, outputSignal, timeConstant):
    stepTime = self.getApplicationContext().getSimulationTimeStep()
    value = ( (deltaTime * inputSignal) + (timeConstant * outputSignal) ) / (deltaTime + timeConstant )
    return value     
 

  I think that the script below have errors? But i cannot determined it.


Thank you for your support.


Regards,


Mark

Hi Mark,

Just like this it is hard for me to find potential errors in the script.
When the problem occurs, can you navigate to the script and see if it prints some errors in the script log widget of the editor's properties view?

Daniel

 

Thanks a lot, Mark!
Login to post a comment