(四)关节控制

Joint controllers

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.

控制一个关节可以有很多方式,在接下来的章节中,我们将指出一个宽松控制器和一个精确控制器的区别:一个宽松控制器不能在每个可能的常规步长中提供新的控制值(例如,一些常规步长可能被跳过,但是仍然可以受控);而一个精确的关节控制器,则可以在每个可能的常规步长中提供控制值。

First, the approach to take for controlling a joint will depend on the joint mode:

首先,控制一个关节的方法取决于它的关节模式

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.

区别来自这样一个事实,即在力/转矩模式下运行的关节将由物理引擎处理。而物理引擎在默认情况下将执行比模拟循环多10倍的计算步骤:模拟循环以20Hz(在模拟时间)运行,而物理引擎以200Hz(也在模拟时间)运行。如果需要,可以完全配置默认行为。

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 API function (or similar, e.g. simxSetJointPosition for the B0-based remote API, or simxSetJointPosition for the legacy remote API). 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 child script.

如果关节不是处于力/力矩模式:如果关节不是处于力/力矩模式,那么你就可以直接(或者间接)地设置它的位置,通过sim.setJointPosition 接口函数 (或者类似的例如:simxSetJointPosition,B0-based 远程 API, 或者 simxSetJointPosition ,legacy 远程 API). 

In following threaded child script example, the joint is controlled loosely in position, and there is no synchronization with the simulation loop:

在下面这个threaded 子脚本例子中,关节以宽松位置控制模式控制,并且这个和仿真循环没有同步关系。

-- 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:

下面这个threaded子脚本例子中,关节以在每一步仿真中精确位置控制的方式控制,即线程和仿真循环是同步的。

-- Following script should run threaded:

local initialForbidLevel=sim.setThreadAutomaticSwitch(false) -- Automatic thread switching disabled
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.
sim.setThreadAutomaticSwitch(initialForbidLevel) -- Restore the automatic thread switching

-- 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 APIROS or BlueZero), then the external controller will run asynchronously to CoppeliaSim (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 CoppeliaSim in synchronous mode, and the external controller (e.g. the remote API client) will have to trigger each simulation step explicitely.

如果你想以外部应用(例如:远程API、ROS或BlueZero)的方式控制一个非力/力矩模式的关节,那么外部控制器和CoppeliaSim的运行是异步的(即和子脚本线程的非同步代码是类似的效果)。这个对于宽松控制是可以的,但是如果你想在每一个仿真循环中精确控制关节位置,那你就需要将CoppeliaSim运行在同步模式中,外部控制器(如远程API客户端)必须明确触发每个模拟step。

Following illustrates a C++ B0-based remote API client that does this:

下面是一个基于B0远程API的C++编写的客户端的例子:

bool doNextStep=false;

void simulationStepDone_CB(std::vector<msgpack::object>* msg)
{
    doNextStep=true;
}

int main(int argc,char* argv[])
{
    ...
    client.simxSynchronous(true); // enable the synchronous mode
    client.simxGetSimulationStepDone(client.simxDefaultSubscriber(simulationStepDone_CB)); // callback when step finished
    client.simxStartSimulation(client.simxDefaultPublisher()); // start the simulation

    client.simxSetJointPosition(jointHandle,90.0f*3.1415f/180.0f,client.simxDefaultPublisher()); // set the joint to 90 degrees
    client.simxSynchronousTrigger(); // start one simulation step
    while (!doNextStep) // wait until simulation step finished
        client.simxSpinOnce();
        
    doNextStep=false;
    client.simxSetJointPosition(jointHandle,180.0f*3.1415f/180.0f,client.simxDefaultPublisher()); // set the joint to 180 degrees
    client.simxSynchronousTrigger(); // start one simulation step
    while (!doNextStep) // wait until simulation step finished
        client.simxSpinOnce();
        
    doNextStep=false;
    client.simxSetJointPosition(jointHandle,0.0f*3.1415f/180.0f,client.simxDefaultPublisher()); // set the joint to 0 degrees
    client.simxSynchronousTrigger(); // start one simulation step
    while (!doNextStep) // wait until simulation step finished
        client.simxSpinOnce();
    ...
}

Refer to this page for details on how the B0-based remote API synchronous mode operates exactly. The approach is similar with ROS or BlueZero.

Following does the same, however with a legacy remote API client:

有关基于B0的远程API同步模式如何准确运行的详细信息,请参阅本页。这种方法与ROS或BlueZero类似。

下面的例子是同一个例子,只不过用的是传统的远程API客户端。

...
simxSynchronous(clientId,1); // enable the synchronous mode (client side). The server side (i.e. CoppeliaSim) also needs to be enabled.
simxStartSimulation(clientId,simx_opmode_oneshot); // start the simulation
simxSetJointPosition(clientId,jointHandle,90.0f*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.0f*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.0f*3.1415f/180.0f,simx_opmode_oneshot); // set the joint to 0 degrees
...

Refer to this page for details on how the legacy remote API synchronous mode operates exactly. The approach is similar with ROS or BlueZero.

有关传统远程API同步模式如何准确运行的详细信息,请参阅本页。这种方法与ROS或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:

The joint's motor is enabled, but the control loop is disabled. Use this mode when you want to precisely custom control your joint from an external application (e.g. force/torque control, PID, etc.). Use this mode also, when you want to loosely control your joint in force/torque mode, or for velocity control (e.g. robot wheel motors).
The joint's motor is enabled, and the control loop is enabled. Use this mode when your joint needs to act as a spring/damper, or if you want to precisely custom control your joint from within CoppeliaSim, or if you want to loosely control your joint in position control from an external application.

如果关节处于力/扭矩模式:如果关节在力/扭矩模式下工作并动态启用,则它将由物理引擎间接处理。如果关节的电机未启用,则关节不受控制(即,它将是自由的)。否则,关节可以处于以下两种动态模式:

关节的电机已启用,但控制回路已禁用。当您希望从外部应用程序(例如力/扭矩控制、PID等)精确自定义控制关节时,请使用此模式。当您想在力/扭矩模式下松散地控制关节,或用于速度控制(例如,机器人车轮马达)时,也可以使用此模式。

关节的电机已启用,控制回路已启用。当您的关节需要充当弹簧/阻尼器时,或者如果您希望从CoppeliaSim中精确自定义控制关节,或者如果您希望从外部应用程序的位置控制中松散地控制关节,请使用此模式。

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 B0-based remote API: simxSetJointTargetVelocity, or, in case of the legacy remote API: simxSetJointTargetVelocity), and the maximum force/torque with sim.setJointMaxForce (or for example, in case of the B0-based remote API: simxSetJointMaxForce, or in case of the the legacy remote API: simxSetJointMaxForce). 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 sensorsproximity sensorsdistance calculationsIK, 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 a joint callback function (or a dynamics callback function) as will be explained further down.

如果关节的电机已启用,但控制回路已禁用,则物理引擎将应用指定的最大力/扭矩,并加速关节,直到达到目标速度。如果负载很小和/或最大力/扭矩很高,将很快达到目标速度。否则,将需要一些时间,或者,如果力/扭矩不够大,将永远无法达到目标速度。您可以使用sim.setjointargetvelocity(例如,对于基于B0的远程API:simxsetjointargetvelocity,或者对于传统远程API:simxsetjointargetvelocity)以编程方式调整目标速度,并使用sim.setJointMaxForce(例如,对于基于B0的远程API:simxSetJointMaxForce,或者对于传统远程API:simxSetJointMaxForce)。在从子脚本为力/扭矩模式下的关节编写精确的关节控制器之前,应非常小心,原因如下:

默认情况下,模拟循环以50ms的时间步长运行(在模拟时间内)。但物理引擎将以5ms的时间步长运行,即10倍的频率。子脚本将在每个模拟步骤中调用,但不会在每个物理引擎计算步骤中调用。这意味着,如果以常规方式从子脚本控制关节,则只能为10个物理引擎计算步骤提供一次新的控制值:将缺少9个步骤。克服这一问题的一种方法是更改默认模拟设置,并将模拟时间步长指定为5ms,而不是50ms。这很好,但请记住,所有其他计算(例如,视觉传感器、接近传感器、距离计算、IK等)的运行频率也将提高10倍,最终会降低模拟速度(大多数情况下,其他计算模块不需要如此高的刷新率)。但是物理引擎需要如此高的刷新率)。

更好的选择是使用联合回调函数(或dynamics回调函数),下面将进一步解释。

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 CoppeliaSim 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++ B0-based remote API client that does this:

另一方面,如果您希望在外部(例如,从远程API客户端、ROS节点或BlueZero节点)运行精确和常规的联合控制器,那么您没有其他选择,只能将模拟循环设置为与物理引擎速率相同的速率,然后以同步模式运行CoppeliaSim,外部控制器(如远程API客户端)必须明确触发每个模拟步骤。

下面说明了一个基于C++ B0的远程API客户端程序:

bool doNextStep=false;

void simulationStepDone_CB(std::vector<msgpack::object>* msg)
{
    doNextStep=true;
}

int main(int argc,char* argv[])
{
    ...
    client.simxSynchronous(true); // enable the synchronous mode
    client.simxGetSimulationStepDone(client.simxDefaultSubscriber(simulationStepDone_CB)); // callback when step finished
    client.simxStartSimulation(client.simxDefaultPublisher()); // start the simulation

    // set the desired force and target velocity:
    client.simxSetJointMaxForce(jointHandle,1.0f,client.simxDefaultPublisher());
    client.simxSetJointTargetVelocity(jointHandle,180.0f*3.1415f/180.0f,client.simxDefaultPublisher());
    client.simxSynchronousTrigger(); // start one simulation step
    while (!doNextStep) // wait until simulation step finished
        client.simxSpinOnce();
        
    doNextStep=false;
    // set the desired force and target velocity:
    client.simxSetJointMaxForce(jointHandle,0.5f,client.simxDefaultPublisher());
    client.simxSetJointTargetVelocity(jointHandle,180.0f*3.1415f/180.0f,client.simxDefaultPublisher());
    client.simxSynchronousTrigger(); // start one simulation step 
    while (!doNextStep) // wait until simulation step finished
        client.simxSpinOnce();
        
    doNextStep=false;
    // set the desired force and target velocity:
    client.simxSetJointMaxForce(jointHandle,2.0f,client.simxDefaultPublisher());
    client.simxSetJointTargetVelocity(jointHandle,180.0f*3.1415f/180.0f,client.simxDefaultPublisher());
    client.simxSynchronousTrigger(); // start one simulation step
    while (!doNextStep) // wait until simulation step finished
        client.simxSpinOnce();
    ...
}

Refer to this page for details on how the B0-based remote API synchronous mode operates exactly. The approach is similar with ROS or BlueZero.

Following does the same, however with a legacy remote API client:

参考本页了解更多关于B0-based远程API同步操作模式的细节。ROS或BlueZero也是类似的方法。

下面的例子和上面的做的是同一件事,但是用的是传统的远程API客户端:

...
simxSynchronous(clientId,1); -- enable the synchronous mode (client side). The server side (i.e. CoppeliaSim) also needs to be enabled.
simxStartSimulation(clientId,simx_opmode_oneshot); // start the simulation
simxSetJointMaxForce(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
simxSetJointMaxForce(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
simxSetJointMaxForce(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
...

 

Refer to this page for details on how the legacy 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 B0-based remote API client: simxSetJointTargetPosition, or from a legacy remote API client: simxSetJointTargetPosition). When you need a precise custom controller, then you should use a joint callback function instead (or a dynamics callback function).

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, CoppeliaSim'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 CoppeliaSim in synchronous mode.

Following illustrates a C++ B0-based remote API client that does this:

有关传统远程API同步模式如何准确运行的详细信息,请参阅本页。这种方法与ROS或BlueZero类似。

如果关节的电机已启用,并且控制回路也已启用,则物理引擎将根据设置处理关节:关节可以在位置控制(即PID控制)、弹簧/减振器模式或自定义控制中操作。PID和spring/damper参数可以从子脚本、远程API客户端、ROS或BlueZero节点更新。请参阅对象参数IDs 2002-2004和2018-2019。可以使用sim.setJointTargetPosition(或,例如,从基于B0的远程API客户端:simxSetJointTargetPosition,或从传统远程API客户端:simxSetJointTargetPosition)设置所需的目标位置。当您需要一个精确的自定义控制器时,则应该使用联合回调函数(或动态回调函数)。

最后,如果需要在外部应用程序中实现的精确PID或自定义控制器,则需要确保模拟步骤与物理引擎计算步骤相同:默认情况下,CoppeliaSim的模拟循环以20Hz(模拟时间)运行,而物理引擎以200Hz运行。可以在“模拟”设置中调整模拟步长。您还需要确保在同步模式下运行CoppeliaSim。

下面说明了一个基于C++ B0的远程API客户端程序:

bool doNextStep=false;

void simulationStepDone_CB(std::vector<msgpack::object>* msg)
{
    doNextStep=true;
}

int main(int argc,char* argv[])
{
    ...
    client.simxSynchronous(true); // enable the synchronous mode
    client.simxGetSimulationStepDone(client.simxDefaultSubscriber(simulationStepDone_CB)); // callback when step finished
    client.simxStartSimulation(client.simxDefaultPublisher()); // start the simulation

    // set the desired target position:
    client.simxSetJointTargetPosition(jointHandle,90.0f*3.1415f/180.0f,client.simxDefaultPublisher());
    client.simxSynchronousTrigger(); // start one simulation step
    while (!doNextStep) // wait until simulation step finished
        client.simxSpinOnce();
        
    doNextStep=false;
    // set the desired target position:
    client.simxSetJointTargetPosition(jointHandle,180.0f*3.1415f/180.0f,client.simxDefaultPublisher());
    client.simxSynchronousTrigger(); // start one simulation step
    while (!doNextStep) // wait until simulation step finished
        client.simxSpinOnce();
        
    doNextStep=false;
    // set the desired target position:
    client.simxSetJointTargetPosition(jointHandle,0.0f*3.1415f/180.0f,client.simxDefaultPublisher());
    client.simxSynchronousTrigger(); // start one simulation step
    while (!doNextStep) // wait until simulation step finished
        client.simxSpinOnce();
    ...
}

Following does the same, however with a legacy remote API client:

下面的例子和上面的做的是同一件事,但是用的是传统的远程API客户端:

...
simxSynchronous(clientId,1); -- enable the synchronous mode (client side). The server side (i.e. CoppeliaSim) 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
...

You can also have a remote API client provide control values for a custom joint controller implemented in a joint callback function, by providing values, for instance via signals, to that joint callback function. For example, from a C++ B0-based remote API client:

您还可以通过向该联合回调函数提供值(例如通过信号)为在联合回调函数中实现的自定义联合控制器提供控制值。例如,基于C++ B0的远程API客户端:

 

bool doNextStep=false;

void simulationStepDone_CB(std::vector<msgpack::object>* msg)
{
    doNextStep=true;
}

int main(int argc,char* argv[])
{
    ...
    client.simxSynchronous(true); // enable the synchronous mode
    client.simxGetSimulationStepDone(client.simxDefaultSubscriber(simulationStepDone_CB)); // callback when step finished
    client.simxStartSimulation(client.simxDefaultPublisher()); // start the simulation

    // set the desired target position:
    simxSetFloatSignal("myDesiredTorque",1.0f,client.simxDefaultPublisher());
    simxSetFloatSignal("myDesiredTarget",90.0f*3.1415f/180.0f,client.simxDefaultPublisher());
    client.simxSynchronousTrigger(); // start one simulation step
    while (!doNextStep) // wait until simulation step finished
        client.simxSpinOnce();
    ...
}

In above example, your joint callback function could fetch those two signals (with sim.getDoubleSignal) before doing the control.

Following does the same, however with a legacy remote API client:

在上面的示例中,您的联合回调函数可以在执行控件之前获取这两个信号(使用sim.getDoubleSignal)。

下面的例子和上面的做的是同一件事,但是用的是传统的远程API客户端:

...
simxSynchronous(clientId,1); -- enable the synchronous mode (client side). The server side (i.e. CoppeliaSim) 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
...

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值