游戏或者仿真中要提供接近于真实世界的完整触觉反馈体验,需要VR头戴设备、控制器、外骨骼甚至是行走模拟装置的配合。然而,人类的触觉系统极其敏感,普通人打麻将就能用手指轻轻松松地摸出牌面。在目前的技术基础上,机器很难还原真实的人类触觉反馈,能做的只是在特定的内容和场景中尽量满足用户的反馈体验。比如在VR游戏中抓取武器和物品时,几厘米的偏差,没有准确还原物体材质和纹路,对用户的实际体验影响并不大。除了高精度的外设,要实现触觉或力反馈,还需要强大的物理引擎,虚拟世界中的复杂物体的建模、与用户肢体的触碰非常消耗计算资源。

接触力的计算

[Using a two arms exoskeleton while wearing a VR head-mounted display creating an immersed environment

  • 牛顿运动定律与物理引擎
  1. 惯性定律:如果物体没有受到外力作用,则运动状态(静止或匀速直线运动)不会发生改变;
  2. 牛顿第二定律:物体的加速度跟其所受合外力成正比,跟物体质量成反比,加速度方向与合外力方向相同,即$F=ma$;
  3. 牛顿第三定律:力是物体与物体之间的相互作用,作用力与反作用力总是大小相等方向相反,作用在同一直线上。

  根据牛顿三大运动定律,我们可以创造物理引擎来使虚拟场景中的物体产生动态行为,提供沉浸式体验。对于一个交互式游戏来说其基本运行流程如下:

接触力的计算

  计算物体间的碰撞是物理引擎最核心的部分。一般来说,先要解决如何高效地检测到碰撞的产生(碰撞检测),以及如何确定碰撞点及方向,之后我们就可以求得碰撞体的受力情况,从而根据牛顿运动定律计算出它们将要产生的平动和转动。最后将场景中物体的位置和姿态输出给图形引擎去渲染。

接触力的计算

  但如何求得力和力矩?这便是一个很复杂的问题了。像重力可以直接影响物体所受力,摩擦可以直接影响物体所受力矩,这些都很简单。较为复杂的就是两个物体间的碰撞了,物体引擎中有一半以上的代码是用来计算碰撞的。

  在两物体接触面上存在着接触面法向力(冲击力或接触力)和接触面切向力(摩擦力)两种作用。当接触不连续时产生的接触法向力就是冲击力。由于摩擦力本身也是很复杂的问题,很多仿真软件中对它作了简化,采用比较简单的库伦摩擦力模型来计算:$$F_f=\mu F_n$$

 

接触力的计算

  • 用于碰撞的动力学基本定理

  由于碰撞时间极短,通常只有千分之一甚至万分之一秒,因此所产生的力非常巨大。这种产生在碰撞中,作用时间极短,数值巨大的力称为碰撞力瞬时力。瞬时力的冲量称之为碰撞冲量。瞬时力不仅数值巨大,而且随时间迅速变化,其规律非常复杂,难以确定。碰撞过程中除了由碰撞力引起物体塑性变形外,同时还伴随着发声、发光和发热等机械能转换为其它形式能量的现象。因此,在研究碰撞问题时,一般并不去讨论瞬时力本身,而只讨论它的冲量及产生的总效果。研究碰撞问题,各微分形式的动力学基本定理不能直接应用,一般用积分形式的动量定理和动量矩定理。

  当两物体碰撞时,过接触点作物体表面的公法线,此法线称为碰撞法线。若两物体的质心均位于碰撞接触点的公法线上,则为对心碰撞;否则为偏心碰撞。 

  1.  冲量定理

  对于由$n$个质点组成的质点系,其中第$i$个质点受到的碰撞冲量可分为外碰撞冲量$\bf I_i^{(e)}$和内碰撞冲量$\bf I_i^{(i)}$,由于内碰撞冲量大小相等方向相反,则质点系在碰撞开始和结束时的动量的改变等于作用于质点系的外碰撞冲量的矢量和:$$  m{\bf u_c} -  m \bf v_c=\sum I_i^{(e)}$$

  其中,$m$为质点系质量,$\bf v_c$和$\bf u_c$分别为质心碰撞开始和结束时的速度。

  2.  冲量矩定理

  质点系对质心的动量矩的改变等于外碰撞冲量对质心矩的矢量和:$$ \bf L_{c2}-L_{c1}=\sum M_c (I_i^{(e)})$$

  式中,$\bf L_{c1}$和$\bf L_{c2}$分别表示碰撞开始和结束时质点系对质心的动量矩矢;$\sum M_c (I_i^{(e)})$为外碰撞冲量对质心之矩的矢量和。

  对于平面运动刚体的碰撞问题,可应用质点系相对于质心的冲量矩定理来描述转动部分。根据刚体平面运动特征,相对于质心的冲量矩定理在其平面内视为代数量,即有$$L_c=J_c \omega$$

  式子中$J_c$为刚体对过质心且与其对称平面垂直的轴的转动惯量,$\omega$为刚体转动的角度。由冲量矩定理有:$$L_{c2}-L_{c1}=J_c \omega_2-J_c \omega_1=\sum M_c (I_i^{(e)})$$

  式中,$\omega_1$和$\omega_2$分别为平面运动刚体碰撞前、后的角速度。

  •  恢复系数

  碰撞过程可分为两个阶段。开始碰撞到物体速度为零的过程为第一阶段,称为变形阶段。碰撞冲量为$\bf I_1$,则根据冲量定理:$$0-(-mv)=\it I_1$$

  之后,物体恢复弹性变形到碰撞结束的过程为第二阶段,称为变形恢复阶段。设碰撞冲量为$\bf I_2$,则根据冲量定理:$$mu-0=\it I_2$$

   经研究发现,对于材料确定的物体发生正碰撞时,碰撞前后的速度大小之比几乎是不变的,等于一个常数$e$,即$$e=\frac{u}{v}=\frac{I_2}{I_1}$$

   $e$称为恢复系数。恢复系数$e$表示物体在碰撞前后速度的恢复程度和物体变形的恢复程度,也反映了碰撞中机械能损失的程度。$e$越小,动能损失越大;反之,动能损失越小。一般情况下,$u<v$,恢复系数小于1。但理论上,恢复系数也可以大于1。例如当两个手雷碰撞在一起产生爆炸,化学能转换为机械能(能量增加),碰撞后的速度大于碰撞前速度。另外需要注意:恢复系数是两个碰撞物体之间的共同性质,但在许多文献中恢复系数常写为单个物体固有属性(没有提这物体到底是与哪个物体相互碰撞),在这种情况下,第二个物体被假定为完全弹性刚体。

  •  ADAMS中的冲击力模型

  We can either use information about the depth of the collision and generate a very large force acting on the two objects (like a very stiff spring due to the “compression” of the object’s material as one penetrates the other), or we can use an impulse response which means we simply modify the objects’ momentum without any regard for the forces involved.

接触力的计算

  ADAMS中计算法向接触力大小有两种模型:冲击函数模型(Impact)和恢复系数模型(Restitution)。恢复系数模型基于冲量理论。冲击函数模型用一个弹簧-阻尼模型来表示(碰撞力由两个部分组成:一个是由于两个构件之间的相互切入而产生的弹性力;另一个是由于相对速度产生的阻尼力),冲击函数表示为:$$F_n=k\cdot g^e+step(g,0,0,d_{max},c_{max}) \cdot\frac{dg}{dt}$$

接触力的计算

   式中:$g$——接触物体之间的穿透深度;$F_n$——法向接触力大小;$k$——刚度系数;$e$——碰撞系数,反映了材料的非线性程度;$c_{max}$——最大阻尼系数,表示物体碰撞时的能量损失;$d_{max}$——最大切入深度,它决定了何时阻尼力达到最大;为了防止碰撞过程中阻尼力的不连续,式中采用了step函数,其形式为$step(x,x_0,h_0,x_1,h_1)$

接触力的计算

[Damping Coefficient versus Penetration]

  •  VREP中获取接触力的方法

   机器人仿真中常常需要测量脚底压力分布,或是手爪夹持力等接触力。在VREP中获取接触力有两种方法:一种是通过在末端添加力传感器进行测量,另一种是直接利用物理引擎计算结果获取接触力信息,相关函数为simGetContactInfo:

接触力的计算

  使用simGetContactInfo函数可以直接获取接触力信息,不用添加力传感器等物体,但是用法稍微复杂。VREP中默认情况下物理引擎运行速度是仿真速度的10倍,就是说如果仿真一步需要50ms,那么物理引擎已经运算了10步(5ms一步),如果dynamicPass参数设为sim_handle_all,则一步仿真将会返回10次接触力的结果。如果要利用接触力的信息,需要解析这些力属于哪两个接触体。参考官方论坛中的帖子Get Friction between leg-tip and ground

 Is it possible to detect the friction between the tip of a robot-leg and the ground?

 You have several ways of doing this:

  • you can attach a force sensor at the tip of your leg, then attach a contact sphere to the force sensor (i.e. legTip --> forceSensor --> contactSphere). Then you can read the force/torque in the force/torque sensor
  • you can parse all contact points and vectors for one simulation step and try to figure out which one(s) correspond to a specific leg/ground pair. This is more complicated, but you do not require an additional contact sphere as in above's first point. For that, have a look at the demo model Models/other/contact display.ttm (which uses the API function simGetContactInfo)

   VREP中提供了一个contact display模型(Model browser --> other文件夹下),利用simGetContactInfo函数方便的为用户显示接触点和接触力的大小以及方向。修改其lua代码,在状态栏中显示每次获取的接触力信息。从图中可以看出立方体与地面接触点有4个,将这些接触点对应的接触力加起来就是立方体重量。

接触力的计算

  核心代码是下面这句,返回值objectsInContact是相互接触两物体的句柄,contactPt是接触点的三维坐标,forceDirectionAndAmplitude是力向量的三维坐标,幅值代表大小:

objectsInContact,contactPt,forceDirectionAndAmplitude = simGetContactInfo(sim_handle_all, sim_handle_all, index)

  仿真时可以看到四个点的接触力数值是一直在跳动的(理论上如果接触面是平面,四个点接触力应该一样),也许是三点确定一个平面,多一个冗余的接触点就会导致计算不稳定?下图中的球与地面只有一个接触点,仿真时的接触力要稳定许多。而且接触力的仿真结果还与具体的物理引擎有关,Bullet和ODE的结果就比Vortex和Newton差很多...

接触力的计算

  为了得到稳定可靠地接触力,可以对一次仿真中的所有接触力求平均值(参考官方论坛帖子Impact force on collision),下面修改了contact display中的代码,将每一步获取的力(对于单个立方体与地面接触模型,每一步仿真共有40个接触点的接触力)求均值,计算立方体重量:

if (sim_call_type==sim_childscriptcall_initialization) then 
    black={0,0,0}
    purple={1,0,1}
    lightBlue={0,1,1}
    options=0
    forceVectorScaling=simGetScriptSimulationParameter(sim_handle_self,'forceVectorScaling')
    forceVectorWidth=simGetScriptSimulationParameter(sim_handle_self,'forceVectorWidth')
    contactPointSize=simGetScriptSimulationParameter(sim_handle_self,'contactPointSize')
    overlayDisplay=simGetScriptSimulationParameter(sim_handle_self,'overlayDisplay')
    
    --sim_drawing_overlay: if specified, then items are drawn on top of other objects and are (almost) always visible
    if (overlayDisplay) then
        options=options+sim_drawing_overlay
    end

    -- Add a line and a sphere container:
        --sim_drawing_lines:items are pixel-sized lines. 6 values per item (x0,y0,z0,x1,y1,z1) 
    lineContainer = simAddDrawingObject(sim_drawing_lines+options,forceVectorWidth,0,-1,1000,black,black,black,purple)
        --items are "sphere points". 3 values per item (x,y,z) 
    sphereContainer = simAddDrawingObject(sim_drawing_spherepoints+options,contactPointSize,0,-1,1000,black,black,black,lightBlue)

    ObjectHandle = simGetObjectHandle("Cuboid")
end 


if (sim_call_type==sim_childscriptcall_cleanup) then 
    -- Remove the containers:
    simRemoveDrawingObject(lineContainer)
    simRemoveDrawingObject(sphereContainer)
end 

if (sim_call_type==sim_childscriptcall_sensing) then 
    -- empty the containers:
    simAddDrawingObjectItem(lineContainer,nil) 
    simAddDrawingObjectItem(sphereContainer,nil) 
    
    -- Fill the containers with contact information:
    index = 0 -- zero-based index of the contact to retrieve
    sum = 0
    while (true) do
        -- Retrieves contact point information of a dynamic simulation pass
        objectsInContact,contactPt,forceDirectionAndAmplitude = simGetContactInfo(sim_handle_all, ObjectHandle, index)

        if (objectsInContact) then

            sum = sum + forceDirectionAndAmplitude[3]
            if (index == 39) then
                str = string.format("%f N", sum/10)
                simAddStatusbarMessage(str)  -- Adds a message to the status bar
            end

            line={contactPt[1],contactPt[2],contactPt[3], 0,0,0}

            line[4]=contactPt[1]+forceDirectionAndAmplitude[1]*forceVectorScaling
            line[5]=contactPt[2]+forceDirectionAndAmplitude[2]*forceVectorScaling
            line[6]=contactPt[3]+forceDirectionAndAmplitude[3]*forceVectorScaling
            simAddDrawingObjectItem(lineContainer,line) -- Draw force vector
            simAddDrawingObjectItem(sphereContainer,{contactPt[1],contactPt[2],contactPt[3]}) -- Draw contact point

            index = index + 1
        else
            break
        end
    end
end 
View Code

相关文章:

  • 2022-12-23
  • 2022-02-05
  • 2022-02-10
  • 2022-01-17
  • 2021-12-05
  • 2021-06-25
  • 2021-08-28
  • 2022-12-23
猜你喜欢
  • 2021-09-06
  • 2021-08-17
  • 2021-10-19
  • 2021-08-24
  • 2021-11-18
  • 2022-02-27
  • 2022-02-01
相关资源
相似解决方案