目录
一. 手爪
二. 红外线传感器
三. 工件生成器
四. 总结
上一篇已经实现了机械臂各种动作的控制,本篇实现一下其余的组成部分,比如手爪、传感器和自动放置工件等。
一. 手爪
手爪的模型调整就不多说了,需要设置的是Rigidbody、Collider,还需要判断手爪开合的方向和距离等, 详见机械臂场景3-手爪一文。
实现的主要功能有:
1. 当手爪上的碰撞器触发时,将工件设为手爪的子物体,跟随手爪一起移动;
2. 当手爪打开时,将工件的父物体设置为空,并且发送给机械臂回零事件;
这里直接放一下手爪的代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Robot_ClawCtrl : MonoBehaviour
{
public Transform finger1, finger2;//两个手指
float MoveDis, OpenDis = 0.1f, CloseDis = 0.02f, curDir;
//需要移动的距离,打开时的距离,闭合时的距离,当前距离
public bool isHold = false; //是否抓到物体
public Transform ClawTarget = null; //手爪抓到的目标
void Start()
{
EventManager.Instance.AddEvent(EventType.OnClawCtrl,this, OnClawControl);
}
public void OnClawControl(EventDataBase data)
{
EventDataClaw eventData = data as EventDataClaw;
EClawType clawType = eventData.clawType;
Transform target = eventData.target;
float settime = eventData.waitTime;
if(clawType==EClawType.Close)
OnCloseClaw(); //如果闭合的时间有误,可以添加一个定时器
if (clawType == EClawType.Open) //添加了定时器的打开命令
{
if (timer <= settime)
timer += Time.deltaTime;
else
{
OnOpenClaw();
timer = 0;
return;
}
}
}
public void OnOpenClaw()
{//打开手爪
MoveDis = OpenDis;
finger1.localPosition = new Vector3(0, 0, -OpenDis);
finger2.localPosition = new Vector3(0, 0, OpenDis);
if(ClawTarget!=null||isHold)
{
ClawTarget.transform.GetComponent<Rigidbody>().isKinematic = false;
ClawTarget.transform.SetParent(null);
isHold = false;
//发送回零事件
EventManager.Instance.SendEvent(EventType.OnArmCtrl, new EventDataArm
{
armType = EArmMoveType.Home,
clawType = EClawType.Close,
target = null,
waitTime = 0
});
ClawTarget = null;
}
}
public void OnCloseClaw()
{//关闭手爪
MoveDis = CloseDis;
finger1.localPosition = new Vector3(0, 0, MoveDis);
finger2.localPosition = new Vector3(0, 0, -MoveDis);
}
private void OnTriggerStay(Collider other)
{
ClawTarget = other.transform;
//如果判断物体是工件
//这里设置的是layer,判断tag也是可以的
if (ClawTarget.gameObject.layer == LayerMask.NameToLayer("Load")&& ClawTarget!= null)
{
ClawTarget.transform.SetParent(transform);
ClawTarget.transform.GetComponent<Rigidbody>().isKinematic = true;
isHold = true;
EventManager.Instance.SendEvent(EventType.OnArmCtrl, new EventDataArm
{ armType = EArmMoveType.Put,target=ClawTarget,clawType=EClawType.Close,waitTime=0.1f});
}
else return;
}
可以将手爪的脚本挂在手爪节点上,和它的刚体放在一起。需要指明两个移动的手指,IsHold和ClawTarget两个参数不需要赋初值,仅用于调试时参看。
二. 红外线传感器
当有工件经过传感器时发送事件,通知传送带停止和机械臂抓取,可以参照激光门伤害文中的射线检测,其他检测方式也很多。
1. 建立两个立柱RayEmitter和RayReciever,用于指明发射和接收射线的位置。在这两个节点下再设置两个空节点point1和point2,作为真正的发射和接收位置;
2. 传感器代码,其中最重要的API是物理系统中的射线检测LineCast。代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//传感器
public class RaySensor : MonoBehaviour
{
public Transform emitter;
public Transform receiver;
void Start()
{//使用工具包中的划线工具,画一条线段代表红外线
Tools.DrawLine(transform, emitter.position, receiver.position, Color.cyan);
}
void FixedUpdate()
{
Physics.Linecast(emitter.position, receiver.position, out RaycastHit hit, 64);//64是Layer号
if (hit.collider != null)
{//当检测到工件时,发送传送带停止事件
EventManager.Instance.SendEvent(EventType.OnConveyerCtrl, new EventDataConveyer
{
conveyerType = EConveyerType.Stop //通知传送带停止
});
EventManager.Instance.SendEvent(EventType.OnArmCtrl, new EventDataArm
{//发送机械臂抓取事件
armType = EArmMoveType.Get,
target = hit.transform, //传输给机械臂抓取命令和目标
clawType=EClawType.Close, //发送手爪闭合的数据
waitTime=0.2f
});
}
else
{
EventManager.Instance.SendEvent(EventType.OnConveyerCtrl, new EventDataConveyer
{//传送带移动
conveyerType = EConveyerType.Move
});
}
}
}
三. 工件生成器
它的作用是源源不断地将工件放在传送带上,使用在ResourceManager--资源管理中写的资源管理工具Resload.cs,将放置在Assets->Recources->BeltLoaders文件夹下的工件生成到传送带上去
1. 场景中的设置:随便放置一个模型,作为生成器。重点是放置一个空节点(粉色的),所有工件都是从这个空节点中生成出来的。
2. 工件生成器的代码LoadersManager:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Pool;
public class LoadersManager : MonoBehaviour
{//Dispenser管理器
//挂在控制节点上,管理所有生成的传送带负载工件
public GameObject[] loaders; //所有的负载工件放入数组
public float passedTime=0, loaderRate=10; //计时器和间隔放负载的时间
public bool ifLoad = true; //是否可以发放工件的标记
void Update()
{
passedTime+= Time.deltaTime;
if (loaders.Length>0 && passedTime>=loaderRate&&ifLoad)
{//当数组中有工件物体&时间到&红外线发送信号
GameObject loader = Resources.Load<GameObject>("BeltLoaders/" + loaders[Random.Range(0,loaders.Length)].name); //随机加载一个工件
if (loader == null)
Debug.Log("没有找到物体");
else
{
var go=GameObject.Instantiate(loader); //实例化物体
go.transform.SetParent(transform);
go.transform.localPosition = Vector3.zero;
go.SetActive(true);
go.AddComponent<Loaders>(); //生成的每一个工件都要挂上Loaders脚本
passedTime = 0;
}
}
}
}
3. 工件的脚本:上面的生成器在每生成一个工件时,除了设置工件的初始位置、父节点外,还要给每一个工件挂上Loaders.cs脚本,脚本中需要给工件加上刚体和碰撞体,代码如下:
public class Loaders : MonoBehaviour
{//挂在每一个被抓取工件上
private Rigidbody rig;
void Start()
{
rig = gameObject.AddComponent<Rigidbody>();
rig.useGravity = true;
gameObject.GetComponent<MeshCollider>().convex = true;
gameObject.layer = LayerMask.NameToLayer("Load");
}
4. 最终效果:
四. 总结
至此,传送带的场景搭建完毕,这只是一个简单的示例,很多情况都作为最简模型处理,并且在调试过程中还有累计误差和节拍误差等问题仍待解决。