目录
一. 整体介绍
二. 准备工作
三. 建立流水线
1. 流水线包含的功能:
2. 流水线的代码结构
3. 场景实现
4. 测试运行
一. 整体介绍
流水线是在空间和时间上合理安排和组织工艺线路的一种形式,它涉及到一种或多种生产设备、传感器、物料传输设备等,并且有明显的生产节拍,是一个比较复杂的生产系统。当然,流水线的设计不是本篇的研究对象,这里记录一个机械臂和传送带配合运送工件的简单示例。
传送带IK演示
这里只涉及到传送带、传感器、机械臂和工件相互间的通信,也就是通过传送带搬运工件,当传感器检测到工件的到来时,就通知传送带停止、机械臂去抓取工件,当机械臂将工件抓出传感器检测范围后,传送带继续搬运、机械臂回零。
二. 准备工作
1. 考虑到在以上四个要素之间有不少信息需要通信传输,所以计划使用事件发送来实现,不然需要设立很多个判断,影响程序的可读性。关于事件中心的建立,请详见事件中心2(实在是不愿意看的同学只要复制粘贴这里面的4个类,放在工程文件中就可以使用了)。
2. 关节旋转中需要用到DoTween插件,可以去AssetStore下载,免费的那个就行。不用也可以,只是为了好看。
3. 本篇在上一篇ABB机械臂的逆向解算的基础上,请先将上一篇的代码测试成功。
下面正式开始:
三. 建立流水线
1. 流水线包含的功能:
流水线包含了2个方面的功能:
A. 动画功能:使用材质(renderer.material)流动实现流水线向前运行的效果
B. 物理功能:使用刚体(Rigidbody)实现让工件物理移动的效果。
2. 流水线的代码结构
2.1 机械基类MechanicBase.cs:
由于考虑到工程场景中涉及到的机械部件较多,因此先建立一个机械基类,将机械部件需要的变量、方法等提炼出来,下面是本篇建立的机械基类。由于不同的机械单元驱动方式不同,因此将基类定义为抽象类,以便于在子类中重写它的抽象方法:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
//机械基类
[Serializable]
public struct MechanicCell
{
public MechanicBase unit; //一个机械单元
public float ratio; //比率,控制这个单元的速度
public MechanicCell(MechanicBase cell, float ratio)
{
unit = cell;
this.ratio = ratio;
}
}
public abstract class MechanicBase : MonoBehaviour
{//在基类中定义一个驱动引擎的方法
public abstract void EngineDriver(float velocity);
}
public enum Direction //枚举,机械单元运动的方向
{
forward, back, right, left
}
2.2 定义一个引擎脚本BeltEngine.cs:
BeltEngine.cs用于驱动本系统内所有的机械单元。我的设想参照了MGS-MechanicalDrive插件包,一个Engine的作用域仅限于当前的机械系统,类似于一个电机。比如一个Engine.cs实例,在传送带的系统中用于驱动皮带轮和同步带:
代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//使用engine驱动所有部件
public class BeltEngine : MechanicBase
{//定义机械系统,将所有活动部件放入数组中
public MechanicCell[] cells;
public float enginePower = 50; //动力系数
void Start()
{//注册一个停止事件,当收到停止命令时,将enginePower设置为0
EventManager.Instance.AddEvent(EventType.OnConveyerCtrl, this, data=> {
if ((data as EventDataConveyer).conveyerType == EConveyerType.Stop) enginePower = 0;
else enginePower = 50; });
}
void FixedUpdate()
{
EngineDriver(enginePower);
}
public override void EngineDriver(float velocity)
{//一旦方法被调用,就将本机械系统中的所有单元都驱动一遍
foreach(var cell in cells)
{
cell.unit.EngineDriver(velocity * cell.ratio);
}
}
}
2.3 传送带脚本BeltMove.cs
传送带的实现需要用到传送带上的材质(Renderer)和刚体(Rigidbody),代码还是比较简单的:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(Renderer))] //绑定Renderer组件
[RequireComponent(typeof(Rigidbody))] //绑定Renderer组件
public class BeltMove : MechanicBase
{
protected Renderer renderer; //定义材质
private Rigidbody rig; //定义刚体
private float loadRate = 0.2f; //负载的速率系数
private float renderDir=1; //传送带运动方向的系数
void Start()
{
renderer = GetComponent<Renderer>(); //获取材质组件
rig = GetComponent<Rigidbody>(); //获取刚体组件
}
public override void EngineDriver(float velocity)
{//重写基类的EngineDriver方法
x = velocity * Mathf.Deg2Rad * Time.fixedDeltaTime;
y = 0;
//实现传送带的材质流动,如果方向反了,可以将系数renderDir改为-1
renderer.material.mainTextureOffset += new Vector2(y, x) *renderDir;
//实现刚体之间的相对运动,如果方向不对,可修改Vector3.back
Vector3 ori_pos = rig.position; //传送带实际位置不改变
rig.position += Vector3.back* velocity*loadRate* Time.fixedDeltaTime;
rig.MovePosition(ori_pos);
}
}
3. 场景实现
3.1 没有找到合适的流水线模型,建立了两个Cube并拉长成合适的大小,随便贴个材质:
3.2 给两条传送带分别挂上Collider和Rigidbody,由于本来就是Cube改造的,自带有BoxCollider,最重要的是将Rigidbody的IsKinematic打勾。
原理是利用刚体运动"Rigidbody.MovePosition(position)",并且利用物理系统模拟的摩擦力实现两个刚体间的相对运动。但只需要工件移动,传送带的物理位置不能改变,因此传送带需要勾选IsKinematic
3.3 建立相应的节点
以下是本篇的一个传送带系统,包含了3条传送带和1个驱动引擎,其中每一条传送带都需要挂上BeltMove.cs脚本,并且修改脚本中的运动方向(上文中的Vector3.back):
另外,Engine是个空节点,需要挂上BeltEngine.cs,并且将需要驱动的机械部件填入列表中:
4. 测试运行
以上就完成了传送带部分的建立。加一个工件测试运行一下(别忘了给工件加上碰撞器和刚体),并将EnginPower、Ratio等参数调整到适合状态
(总之就是一个非常简单的应用案例,如果有错误请批评指正)