CoppeliaSim 脚本(3)Customization scripts and Callback function

本文详细介绍了CoppeliaSim中的自定义脚本功能,包括如何使用自定义脚本来个性化模拟场景,以及各种回调函数如动力学、关节、接触和视觉回调的使用方法。了解这些功能可以帮助用户更灵活地控制模拟过程。

本文为官方文档翻译,仅供个人参考,如有侵权,联系删除

3.Customization scripts

自定义脚本是嵌入式脚本,可在很大程度上用于自定义模拟场景。 它们被附加到场景对象(或与场景对象相关联),并且可以通过场景层次结构中的黑色脚本图标轻松识别它们:
在这里插入图片描述
以下是自定义脚本的主要属性:
他们是非线程脚本。
它们始终在同一场景中执行:在运行模拟时以及在不运行模拟时。
它们附加到场景对象(或与场景对象关联)(即它们是关联的脚本)。关联脚本构成了CoppeliaSim分布式控制体系结构的基础,并且共享了方便的属性,如果关联对象被复制,该属性将被自动复制。
以上属性允许自定义脚本共享附加组件和子脚本的某些最佳功能。自定义脚本允许创建可自定义的模型,例如:想象一下一个模型,该模型已放入场景中,并且即使没有运行模拟也能够自行配置或调整。这可能是一个机器人,用户可以在其中通过单个滑块重新定位来调整各种链接长度。
自定义脚本包含阻塞型函数。这意味着每次调用它们时,它们都应执行一些任务,然后返回控制权。如果未返回控制,则整个应用程序将暂停。定制脚本函数经常被系统调用,但主脚本也会遵循精确的执行顺序进行调用。定制脚本还支持回调功能。
自定义脚本通常分为以下几个函数:

-- This is a customization script. It is intended to be used to customize a scene in
-- various ways, mainly when simulation is not running. When simulation is running,
-- do not use customization scripts, but rather child scripts if possible

function sysCall_init()
    -- do some initialization here
end

function sysCall_nonSimulation()
    -- is executed when simulation is not running
end

function sysCall_cleanup()
    -- do some clean-up here
end

-- You can define additional system calls here:
function sysCall_beforeSimulation()
end

function sysCall_actuation()
end

function sysCall_sensing()
end

function sysCall_suspend()
end

function sysCall_suspended()
end

function sysCall_resume()
end

function sysCall_afterSimulation()
end

function sysCall_beforeInstanceSwitch()
end

function sysCall_afterInstanceSwitch()
end

function sysCall_userConfig()
end

function sysCall_dynCallback(inData)
    -- See the dynamics callback function section in the user manual for details about the input argument
end

function sysCall_jointCallback(inData)
    -- See the joint callback function section in the user manual for details about input/output arguments
    return outData
end

function sysCall_contactCallback(inData)
    -- See the contact callback function section in the user manual for details about input/output arguments
    return outData
end

function sysCall_beforeCopy(inData)
    for key,value in pairs(inData.objectHandles) do
        print("Object with handle "..key.." will be copied")
    end
end

function sysCall_afterCopy(inData)
    for key,value in pairs(inData.objectHandles) do
        print("Object with handle "..key.." was copied")
    end
end

function sysCall_beforeDelete(inData)
    for key,value in pairs(inData.objectHandles) do
        print("Object with handle "..key.." will be deleted")
    end
    -- inData.allObjects indicates if all objects in the scene will be deleted
end

function sysCall_afterDelete(inData)
    for key,value in pairs(inData.objectHandles) do
        print("Object with handle "..key.." was deleted")
    end
    -- inData.allObjects indicates if all objects in the scene were deleted
end

function sysCall_beforeMainScript()
    -- Can be used to step a simulation in a custom manner.
    local outData={doNotRunMainScript=false} -- when true, then the main script won't be executed
    return outData
end

function sysCall_afterCreate(inData)
    for i=1,#inData.objectHandles,1 do
        print("Object with handle "..inData.objectHandles[i].." was created")
    end
end

4.Callback function

回调函数是特殊的脚本函数,可以由CoppeliaSim在模拟步骤的特定状态下调用:

  1. 在每个动力学模拟步骤之前和之后,物理引擎都会调用动力学回调函数。
  2. 物理引擎为每个接触对调用接触回调函数。
  3. 物理引擎为关节控制调用关节回调函数
  4. 每当获取或应用新图像时,系统都会调用视觉回调函数。

4.1 Dynamics callback functions

非线程子脚本或自定义脚本可以包含动力学回调函数。 如果存在,则物理引擎将在每个动力学模拟步骤之前和之后使用适当的参数调用回调函数。 动态回调函数可能经常被调用,通常每个模拟步骤调用10 * 2次(请记住,默认情况下,物理引擎时间步骤比模拟时间步骤小10倍)。 因此,脚本尽可能简单,以避免减慢仿真速度。
以下是一个简单的动态回调函数:

function sysCall_dynCallback(inData)
    -- This function gets called often, so it might slow down the simulation
    --     (this is called twice at each dynamic simulation step, by default 20x more often than a child script)
    -- We have:
    -- inData.passCnt : the current dynamics calculation pass. 1-10 by default. See next item for details.
    -- inData.totalPasses : the number of dynamics calculation passes for each "regular" simulation pass.
    --                      10 by default (i.e. 10*5ms=50ms which is the default simulation time step)
    -- inData.dynStepSize : the step size used for the dynamics calculations (by default 5ms)
    -- inData.afterStep : false when called before, and true after a dynamics step was computed.

    local txt=string.format(" the %ith dynamics calculation step (out of %i steps)",inData.passCnt,inData.totalPasses)
    if inData.afterStep then
        txt="After"..txt
    else
        txt="Before"..txt
    end
    print(txt)
end

4.Joint callback functions

非线程子脚本或自定义脚本可以包含关节回调函数。 当存在给定关节(必须动态启用并且还必须启用其控制循环)时,物理引擎将使用适当的参数调用回调函数,从而允许用户自定义相关关节的控制环以进行编写 低级控制算法。 可能会经常调用联合回调函数,对于给定的联合,通常每个模拟步骤调用10次(请记住,默认情况下,物理引擎时间步骤比模拟时间步骤小10倍)。 因此,脚本尽可能简单,以避免减慢仿真速度。
以下是一个简单的PID联合回调函数:

function sysCall_jointCallback(inData)
    -- This function gets called often, so it might slow down the simulation
    --     (this is called at each dynamic simulation step, by default 10x more often than a child script)
    -- We have:
    -- inData.first : whether this is the first call from the physics engine, since the joint
    --                was initialized (or re-initialized) in it.
    -- inData.revolute : whether the joint associated with this script is revolute or prismatic
    -- inData.cyclic : whether the joint associated with this script is cyclic or not
    -- inData.handle : the handle of the joint associated with this script
    -- inData.lowLimit : the lower limit of the joint associated with this script (if the joint is not cyclic)
    -- inData.highLimit : the higher limit of the joint associated with this script (if the joint is not cyclic)
    -- inData.passCnt : the current dynamics calculation pass. 1-10 by default. See next item for details.
    -- inData.totalPasses : the number of dynamics calculation passes for each "regular" simulation pass.
    --                      10 by default (i.e. 10*5ms=50ms which is the default simulation time step)
    -- inData.currentPos : the current position of the joint
    -- inData.targetPos : the desired position of the joint
    -- inData.errorValue : targetPos-currentPos (with revolute cyclic joints we take the shortest cyclic distance)
    -- inData.effort : the last force or torque that acted on this joint along/around its axis. With Bullet,
    --                 torques from joint limits are not taken into account
    -- inData.dynStepSize : the step size used for the dynamics calculations (by default 5ms)
    -- inData.targetVel : the joint target velocity (as set in the user interface)
    -- inData.maxForce : the joint maximum force/torque (as set in the user interface)
    -- inData.velUpperLimit : the joint velocity upper limit (as set in the user interface)
    --
    -- Make sure that the joint is dynamically enabled, is in force/torque mode, motor enabled and
    -- control loop enabled, otherwise this function won't be called

    if inData.first then
        PID_P=0.1
        PID_I=0
        PID_D=0
        pidCumulativeErrorForIntegralParam=0
    end
    
    -- The control happens here:
    -- 1. Proportional part:
    local ctrl=inData.errorValue*PID_P
    
    -- 2. Integral part:
    if PID_I~=0 then
        pidCumulativeErrorForIntegralParam=pidCumulativeErrorForIntegralParam+inData.errorValue*inData.dynStepSize
    else
        pidCumulativeErrorForIntegralParam=0
    end
    ctrl=ctrl+pidCumulativeErrorForIntegralParam*PID_I
    
    -- 3. Derivative part:
    if not inData.first then
        ctrl=ctrl+(inData.errorValue-pidLastErrorForDerivativeParam)*PID_D/inData.dynStepSize
    end
    pidLastErrorForDerivativeParam=inData.errorValue
    
    -- 4. Calculate the velocity needed to reach the position in one dynamic time step:
    local maxVelocity=ctrl/inData.dynStepSize -- max. velocity allowed.
    if (maxVelocity>inData.velUpperLimit) then
        maxVelocity=inData.velUpperLimit
    end
    if (maxVelocity<-inData.velUpperLimit) then
        maxVelocity=-inData.velUpperLimit
    end
    local forceOrTorqueToApply=inData.maxForce -- the maximum force/torque that the joint will be able to exert

    -- 5. Following data must be returned to CoppeliaSim:
    firstPass=false
    local outData={}
    outData.velocity=maxVelocity
    outData.force=forceOrTorqueToApply
    return outData
end

4.3The contact callback function

687/5000
非线程子脚本或自定义脚本可以包含接触回调函数。 如果存在,并且物理引擎检测到两个可响应形状之间发生接触,则将使用适当的参数调用接触回调函数,从而允许用户自定义接触的处理方式。接触回调函数可能经常被调用,有时每个仿真步骤被调用数百次(还请记住,默认情况下,对于一个仿真步骤,物理引擎将被调用10次)。 因此,请保持简单,以避免减慢仿真速度。
以下是典型的接触回调函数:

function sysCall_contactCallback(inData)
    -- Will objects with inData.handle1 and inData.handle2 respond to dynamic collision?
    local retData={}
    retData.ignoreContact=false -- handle contact here
    retData.collisionResponse=true -- shapes will collide

    if inData.engine==sim.physics_bullet then
        retData.bullet={}
        retData.bullet.friction=0
        retData.bullet.restitution=0
    end

    if inData.engine==sim.physics_ode then
        retData.ode={}
        retData.ode.maxContacts=16
        retData.ode.mu=0
        retData.ode.mu2=0
        retData.ode.bounce=0
        retData.ode.bounceVel=0
        retData.ode.softCfm=0
        retData.ode.softErp=0
        retData.ode.motion1=0
        retData.ode.motion2=0
        retData.ode.motionN=0
        retData.ode.slip1=0
        retData.ode.slip2=0
        retData.ode.fDir1={0,0,0}
        local mode=1 -- bit-coded. See below
        -- 1=dContactMu2
        -- 2=dContactFDir1
        -- 4=dContactBounce
        -- 8=dContactSoftERP
        -- 16=dContactSoftCFM
        -- 32=dContactMotion1
        -- 64=dContactMotion2
        -- 128=dContactSlip1
        -- 256=dContactSlip2
        -- 512=dContactApprox1_1
        -- 1024=dContactApprox1_2
        -- 2048=dContactApprox1
        retData.ode.contactMode=mode
    end

    if inData.engine==sim.physics_vortex then
    end

    if inData.engine==sim.physics_newton then
        retData.newton={}
        retData.newton.staticFriction=0
        retData.newton.kineticFriction=0
        retData.newton.restitution=0
    end

    return(retData)
end

4.4Vision callback functions

当与视觉传感器相关联时,子脚本或自定义脚本可以包括视觉回调功能。当存在给定视觉传感器时,则系统将在每次获取或应用新图像时调用回调函数,从而允许用户执行图像处理。以下API函数就是这种情况:sim.handleVisionSensor,sim.checkVisionSensor,sim.checkVisionSensorEx,sim.setVisionSensorImage和sim.setVisionSensorCharImage。
视觉回调函数的位置适用一些条件:通常,它应位于自定义脚本或非线程子脚本中。但是,如果从线程子脚本中调用触发API函数(例如sim.handleVisionSensor),则视觉回调函数也应位于线程子脚本中。如果在非线程子脚本和自定义脚本中都存在视觉回调函数,并且两者都附加到视觉传感器,则将首先调用该子脚本,然后再调用自定义脚本。
以下表示一个空的视觉回调函数:

function sysCall_vision(inData)
    -- We have:
    -- inData.handle : the handle of the vision sensor.
    -- inData.resolution : the x/y resolution of the vision sensor
    -- inData.clippingPlanes : the near and far clipping planes of the vision sensor
    -- inData.viewAngle : the view angle of the vision sensor (if in persp. proj. mode)
    -- inData.orthoSize : the ortho size of the vision sensor (if in orth. proj. mode)
    -- inData.perspectiveOperation : true if the sensor is in persp. proj. mode

    local outData={}
    outData.trigger=false -- true if the sensor should trigger
    outData.packedPackets={} -- a table of packed packets. Can be accessed via e.g. sim.readVisionSensor
    return outData
end

可以通过使用各种API函数来执行图像处理。 视觉插件导出了一些非常简单的图像处理功能。 通过图像插件(OpenCV包装器)支持更多图像处理功能。
以下是一个简单的边缘检测视觉回调函数,该函数触发并返回一个数据包(基于视觉插件功能):

function sysCall_vision(inData)
    simVision.sensorImgToWorkImg(inData.handle)
    simVision.edgeDetectionOnWorkImg(inData.handle,0.1)
    simVision.workImgToSensorImg(inData.handle)

    local outData={}
    outData.trigger=true
    local packetData={1.0,42.123,129.3}
    outData.packedPackets={sim.packFloatTable(packetData)}
    return outData
end

以下是视觉回调函数,该函数在获取的图像上绘制一个圆圈(基于图像插件函数):

function sysCall_vision(inData)
    local imgHandle=simIM.readFromVisionSensor(inData.handle)
    local center={inData.resolution[1]/2,inData.resolution[2]/2}
    local radius=(inData.resolution[1]+inData.resolution[2])/8
    simIM.circle(imgHandle,center,radius,{255,0,255},4)
    simIM.writeToVisionSensor(imgHandle,inData.handle)
    simIM.destroy(imgHandle)
end

4.5Trigger callback functions

力/扭矩传感器相关联时,子脚本或自定义脚本可以包括触发器回调函数。
某些条件适用于触发器回调函数的位置:通常,它应位于自定义脚本或非线程子脚本中。 但是,如果从线程子脚本中调用了触发API函数(例如sim.handleVisionSensor或sim.handleProximitySensor),则触发器回调函数也应位于线程子脚本中。 如果在非线程子脚本以及自定义脚本中都存在触发器回调函数,这两个都附加在对象触发上,那么将首先调用子脚本,然后再调用自定义脚本。
视觉传感器可以在视觉回调函数内部生成触发信号。 然后按以下示例调用触发器回调(如果存在)

function sysCall_trigger(inData)
    -- We have:
    -- inData.handle : the handle of the vision sensor.
    -- inData.packedPackets : an array of data packets, packed (use sim.unpackFloatTable to unpack)
    --    the first data packet always contains 15 auxiliary values about the acquired image:
    --    - minimum of {intensity, red, green blue and depth value}
    --    - maximum of {intensity, red, green blue and depth value}
    --    - average of {intensity, red, green blue and depth value}

    local outData={}
    outData.trigger=true
    return outData
end

当检测到物体时,接近传感器会生成触发信号。 然后按以下示例调用触发器回调(如果存在):

function sysCall_trigger(inData)
    -- We have:
    -- inData.handle : the handle of the proximity sensor.
    -- inData.detectedObjectHandle : handle of detected object
    -- inData.detectedPoint : detected point, relative to sensor frame
    -- inData.normalVector : normal vector at detected point, relative to sensor frame

    local outData={}
    outData.trigger=true
    return outData
end

以下是力/转矩传感器触发回调函数的示例,其中力/转矩传感器已损坏:

function sysCall_trigger(inData)
    -- We have:
    -- inData.handle : the handle of the force/torque sensor.
    -- inData.force : current force
    -- inData.torque : current torque
    -- inData.filteredForce : current filtered force
    -- inData.filteredTorque : current filtered torque
    sim.breakForceSensor(inData.handle)
end
### CoppeliaSim Vision Sensor Script Location and Example In CoppeliaSim, vision sensors play a crucial role in simulating scenarios where visual data processing is necessary. The scripts associated with these sensors can be located within specific scenes that utilize them. Vision sensor-related Lua scripts typically reside inside the scene hierarchy under the respective vision sensor object. Users may access these scripts directly from the simulation environment by selecting the vision sensor object and navigating to the `Script` tab[^1]. This allows for customization of how the sensor captures images or processes visual information. An illustrative example demonstrates initializing and utilizing a vision sensor: ```lua -- Initialize the vision sensor function sysCall_init() -- Handle to the vision sensor local visionSensorHandle = sim.getObjectAssociatedWithScript(sim.handle_self) -- Enable the vision sensor sim.setObjectInt32Parameter(visionSensorHandle, sim.visionintparam_resolution_x, 128) sim.setObjectInt32Parameter(visionSensorHandle, sim.visionintparam_resolution_y, 128) end -- Capture image during runtime function captureImage(sensorHandle) -- Get resolution and image array local resolution, image = sim.getVisionSensorImage(sensorHandle, {0, 0}, 128*128*3) if (resolution ~= nil) then print("Captured Image Resolution:", table.unpack(resolution)) end end -- Main loop function function sysCall_actuation() local handle = sim.getObjectAssociatedWithScript(sim.handle_self) captureImage(handle) end ``` This code snippet initializes a vision sensor upon starting the simulation and defines functions to capture images periodically while running. Note that this script assumes association with a particular vision sensor instance within the scene file[^2].
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值