Joint types and operationCompared to another object, a joint has two reference frames (visible only if the joint is selected). The first one is the regular reference frame that is fixed and that other objects also have. The second reference frame is not fixed, and will move relative to the first reference frame depending on the joint position (or joint value) that defines its configuration.html
4 types of joints are supported:node [Revolute joint, prismatic joint, screw and spherical joint]spring
[Two equivalent mechanisms (in this configuration): spherical joint (left) and 3 revolute joints (right)]api [Two non-equivalent mechanisms: the right configuration is close to a singularity]app A joint is used to allow for a relative movement between its parent and its children. When a parent-child relationship is built between a joint and an object, the object is attached to the joint's second reference frame, thus, a change of the joint's configuration (intrinsic position) will directly be reflected onto its children. New joints can be added to a scene with [Menu bar --> Add --> Joints].async
A joint can be in one of following modes:ide
When the joint is in passive mode, inverse kinematics mode or dependent mode, it can optionally also be operated in a hybrid fashion: hybrid operation allows the joint to operate in a regular way, but additionally, just before dynamics calculations, the current joint position will be copied to the target joint position, and then, during dynamics calculations, the joint will be handled as a motor in position control (if and only if it is dynamically enabled (refer to the section on designing dynamic simulations for more information)). This feature allows for instance to control the leg of a humanoid robot by simply specifying the desired foot position (as an inverse kinematics task); the corresponding calculated joint positions will then be applied as position control values for the leg dynamic motion.oop
There are many different ways a joint can be controlled. In following section, we differentiate betwen a loose controller and a precise controller: a loose joint controller will not be able to provide new control values in each possible regulation step (e.g. some regulation steps might/will be skipped, but control is still possible). A precise joint controller on the other hand, will be able to provide control values in each possible regulation step.fetch First, the approach to take for controlling a joint will depend on the joint mode:flex The differentiation comes from the fact that a joint that operates in force/torque mode will be handled by the physics engine. And the physics engine will perform by default 10 times more calculation steps than the simulation loop: the simulation loop runs at 20Hz (in simulation time), while the physics engine runs at 200Hz (also in simulation time). That default behaviour can entirely be configured if required. If the joint is not in force/torque mode: if the joint is not in force/torque mode, then you can directly (and instantaneously) set its position via the sim.setJointPosition (or similar, e.g. simxSetJointPosition for the remote API) API function. You can do this from a child script, from a plugin, from a ROS node, from a BlueZero node, or from a remote API client. If you do this from a child script, then it should be done inside of the actuation section of the non-threaded child script, or from a threaded child script that executes before the sensing phase of the main script (default). In the latter case however, make sure to have your threaded child script synchronized with the simulation loop for precise control. In following threaded child script example, the joint is controlled loosley in position, and there is no synchronization with the simulation loop: -- Following script should run threaded: jointHandle=sim.getObjectHandle('Revolute_joint') sim.setJointPosition(jointHandle,90*math.pi/180) -- set the position to 90 degrees sim.wait(2) -- wait 2 seconds (in simulation time) sim.setJointPosition(jointHandle,180*math.pi/180) -- set the position to 180 degrees sim.wait(1) -- wait 1 second (in simulation time) sim.setJointPosition(jointHandle,0*math.pi/180) -- set the position to 0 degrees etc. In following threaded child script example, the joint is controlled precisely in position in each simulation step, i.e. the thread is synchronized with the simulation loop: -- Following script should run threaded: sim.setThreadSwitchTiming(200) -- Automatic thread switching to a large value (200ms) jointHandle=sim.getObjectHandle('Revolute_joint') sim.setJointPosition(jointHandle,90*math.pi/180) -- set the position to 90 degrees sim.switchThread() -- the thread resumes in next simulation step (i.e. when t becomes t+dt) sim.setJointPosition(jointHandle,180*math.pi/180) -- set the position to 180 degrees sim.switchThread() -- the thread resumes in next simulation step sim.setJointPosition(jointHandle,0*math.pi/180) -- set the position to 0 degrees sim.switchThread() -- the thread resumes in next simulation step -- etc. -- In above code, a new joint position is applied in each simulation step When you try to control a joint that is not in force/torque mode from an external application (e.g. via the remote API, ROS or BlueZero), then the external controller will run asynchronously to V-REP (i.e. similar to the non-synchronized code of a threaded child script). This is fine most of the time for loose control, but if you wish to control the position of the joint precisely in each simulation loop, you will have to run V-REP in synchronous mode, and the external controller (e.g. the remote API client) will have to trigger each simulation step explicitely. Following illustrates a C/C++ remote API client that does this: simxSynchronous(clientId,1); -- enable the synchronous mode (client side). The server side (i.e. V-REP) also needs to be enabled. simxStartSimulation(clientId,simx_opmode_oneshot); // start the simulation simxSetJointPosition(clientId,jointHandle,90.0*3.1415f/180.0f,simx_opmode_oneshot); // set the joint to 90 degrees simxSynchronousTrigger(clientId); // trigger next simulation step. Above commands will be applied simxSetJointPosition(clientId,jointHandle,,180.0*3.1415f/180.0f,simx_opmode_oneshot); // set the joint to 180 degrees simxSynchronousTrigger(clientId); // next simulation step executes. Above commands will be applied simxSetJointPosition(clientId,jointHandle,,0.0*3.1415f/180.0f,simx_opmode_oneshot); // set the joint to 0 degrees etc. Refer to this page for details on how the remote API synchronous mode operates exactly. The approach is similar with ROS or BlueZero. If the joint is in force/torque mode: if the joint operates in force/torque mode and is dynamically enabled, then it will be indirectly handled by the physics engine. If your joint's motor is not enabled, then your joint is not controlled (i.e. it will be free). Otherwise, your joint can be in following two dynamic modes:
If your joint's motor is enabled, but the control loop is disabled, then the physics engine will apply the specified Maximum force/torque, and accelerate the joint until the target velocity is reached. If the load is small and/or the maximum force/torque high, that target velocity will be reached quickly. Otherwise, it will take some time, or, if the force/torque is not large enough, the target velocity will never be reached. You can programmatically adjust the target velocity with sim.setJointTargetVelocity (or for example, in case of the remote API, simxSetJointTargetVelocity), and the maximum force/torque with sim.setJointForce (or for example, in case of the remote API, simxSetJointForce). You should be very careful before writing a precise joint controller for a joint in force/torque mode from a child script for following reason: By default, the simulation loop runs with a time step of 50ms (in simulation time). But the physics engine will run with a time step of 5ms, i.e. 10 times more often. A child script will be called in each simulation step, but not in each physics engine calculation step. This means that if you control a joint from a child script in a regular way, you will only be able to provide new control values once for 10 physics engine calculation steps: you will be missing 9 steps. One way to overcome this would be to change the default simulation settings and to specify a simulation time step of 5ms, instead of 50ms. This works fine, but remember that all other calculations (e.g. vision sensors, proximity sensors, distance calculations, IK, etc.) will also run 10 times more often, and finally slow down your simulation (most of the time you won't need such a high refresh rate for the other calculation modules. But the physics engine requires such a high refresh rate). Another, much better option, would be to use joint callback functions as will be explained further down. If, one the other hand, you want to run a precise and regular joint controller externally (e.g. from a remote API client, a ROS node or a BlueZero node), then you have no other option than to set the simulation loop to the same rate as the physics engine rate, then run V-REP in synchronous mode, and the external controller (e.g. the remote API client) will have to trigger each simulation step explicitely. Following illustrates a C/C++ remote API client that does this: simxSynchronous(clientId,1); -- enable the synchronous mode (client side). The server side (i.e. V-REP) also needs to be enabled. simxStartSimulation(clientId,simx_opmode_oneshot); // start the simulation simxSetJointForce(clientId,jointHandle,1.0f,simx_opmode_oneshot); // set the joint force/torque simxSetJointTargetVelocity(clientId,jointHandle,180.0f*3.1415f/180.0f,simx_opmode_oneshot); // set the joint target velocity simxSynchronousTrigger(clientId); // trigger next simulation step. Above commands will be applied simxSetJointForce(clientId,jointHandle,0.5f,simx_opmode_oneshot); // set the joint force/torque simxSetJointTargetVelocity(clientId,jointHandle,180.0f*3.1415f/180.0f,simx_opmode_oneshot); // set the joint target velocity simxSynchronousTrigger(clientId); // next simulation step executes. Above commands will be applied simxSetJointForce(clientId,jointHandle,2.0f,simx_opmode_oneshot); // set the joint force/torque simxSetJointTargetVelocity(clientId,jointHandle,180.0f*3.1415f/180.0f,simx_opmode_oneshot); // set the joint target velocity etc. Refer to this page for details on how the remote API synchronous mode operates exactly. The approach is similar with ROS or BlueZero. If your joint's motor is enabled, and the control loop is also enabled, then the physics engine will handle the joint according to the setting: your joint can operate in position control (i.e. PID control), in a spring/damper mode, or in custom control. PID and spring/damper parameters can be updated from a child script, from a remote API client, from a ROS or BlueZero node. Refer to object parameter IDs 2002-2004, and 2018-2019. Desired target positions can be set with sim.setJointTargetPosition (or, for example, from a remote API client, simxSetJointTargetPosition). When you need a precise custom controller, then you should use a joint callback function instead. Finally, if you need a precise PID or custom controller that is implemented in an external application, you need to make sure that the simulation step is the same as the physics engine calculation step: by default, V-REP's simulation loop runs at 20Hz (in simulation time), while the physics engine runs at 200Hz. You can adjust the simulation step size in the simulation setting. You also need to make sure you run V-REP in synchronous mode. Following illustrates a C/C++ remote API client that does this: simxSynchronous(clientId,1); -- enable the synchronous mode (client side). The server side (i.e. V-REP) also needs to be enabled. simxStartSimulation(clientId,simx_opmode_oneshot); // start the simulation simxSetJointTargetPosition(clientId,jointHandle,90.0f*3.1415f/180.0f,simx_opmode_oneshot); // set the desired joint position simxSynchronousTrigger(clientId); // trigger next simulation step. Above commands will be applied simxSetJointTargetPosition(clientId,jointHandle,180.0f*3.1415f/180.0f,simx_opmode_oneshot); // set the desired joint position simxSynchronousTrigger(clientId); // next simulation step executes. Above commands will be applied simxSetJointTargetPosition(clientId,jointHandle,0.0f*3.1415f/180.0f,simx_opmode_oneshot); // set the desired joint position etc. You can also have a remote API client provide control values for a custom joint controller implemented in a joint callback function, by providing signals to that joint callback function, like in following example: simxSynchronous(clientId,1); -- enable the synchronous mode (client side). The server side (i.e. V-REP) also needs to be enabled. simxStartSimulation(clientId,simx_opmode_oneshot); // start the simulation simxSetFloatSignal(clientId,"myDesiredTorque",1.0f,simx_opmode_oneshot); // set the signal value simxSetFloatSignal(clientId,"myDesiredTarget",90.0f*3.1415/180.0f,simx_opmode_oneshot); // set the signal value simxSynchronousTrigger(clientId); // trigger next simulation step. Above commands will be applied etc. In above example, your joint callback function could fetch those two signals (with sim.getFloatSignal) before doing the control. Recommended topics |