UnityVR--机械臂场景11-简单流水线应用3

news2024/11/28 2:30:17

目录

一. 前言

二. 设置一个定时器

三.  添加机械臂事件

四. 机械臂控制函数OnArmCtrl

五. 定义上面的3个机械臂移动方法

六. 机械臂各关节转动控制

七. 场景实现

八. 完整代码


一. 前言

  上一篇使用了DoTween插件,并且改写了事件的相关参数,本篇将实现在三自由度逆向解算3的基础上,使用发送事件的方式,实现机械臂的自动抓取、放置、回零,所有动作都通过逆向解算来完成。但在测试过程中也产生了不少问题,比如机械臂抖动,节奏不协调等,通过调试之后解决了部分问题,后期会继续测试,也大神请批评指正。

  以下是机械臂控制代码Robot_MoveEvent.cs,代码比较长,分段解释。

二. 设置一个定时器

  由于流水线涉及到很多部分需要相互通信,时常会有不协调的情况,因此先设定一个定时器,调节一下等待的时间:

  if (timer < waittime)
      timer += Time.deltaTime;
  else
  {
     //做一些事情
     timer = 0;
     return;
  }

三.  添加机械臂事件

  在Start()中定义一个机械臂事件,监听者为本类,回调OnArmCtrl函数:

EventManager.Instance.AddEvent(EventType.OnArmCtrl, this, OnArmCtrl);

四. 机械臂控制函数OnArmCtrl

  在这个函数中,根据传递的数据data不同,判断机械臂执行什么状态的动作,现在定义了回零、取工件和放置工件3个动作。

public void OnArmCtrl(EventDataBase data)
{//接收事件传输数据
    EventDataArm eventdata = data as EventDataArm;
    EArmMoveType armtype = eventdata.armType;
    Transform armTarget = eventdata.target;
    EClawType clawType = eventdata.clawType;
    float waittime = eventdata.waitTime;

    if (armtype == EArmMoveType.Home) //回零时发送手爪打开事件
        ArmHome(new EventDataClaw { clawType=EClawType.Open,waitTime=0});

    //if (armtype == EArmMoveType.Up) ArmUp(upPlace);  //移动到安全高度
    if (armtype == EArmMoveType.Get)
    {//去抓取物体时发送手爪关闭事件
      ArmDown(armTarget,new EventDataClaw { clawType=EClawType.Close,waitTime=waittime});
    }
    if (armtype == EArmMoveType.Put)
    {//启用定时器,推迟打开手爪的时间
      if (timer < waittime)
          timer += Time.deltaTime;
      else
      {
        ArmPutObject(putPlace,new EventDataClaw {clawType=EClawType.Open,waitTime=0.5f});
        timer = 0;
        return;
      }
    }
}

五. 定义上面的3个机械臂移动方法

  其中使用的逆解函数IKCaculator()和之前三自由度逆向解算3中一样,抄一下。如果在项目中经常需要使用到IK函数,也可以将之写到一个static的工具类中。

    public void ArmDown(Transform target,EventDataBase data)
    {//机械臂做抓取动作,target为hand的目标位置
        armDown = IKCaculator(target, armDown);
        armDown[3] = 90;    //手爪根部T_Axis_6再旋转90度
        MoveJoints(armDown, 0,data) ;
    }
    public void ArmHome(EventDataBase data)
    {//机械臂回零,存储位置的数组在一开始就已经定义
        MoveJoints(armHome, 0,data);
    }

    public void ArmUp(Transform upPlace,EventDataBase data)
    {//机械臂抬起位置
        armUp = IKCaculator(upPlace, armUp);
        armUp[3] = -90;
        MoveJoints(armUp, 0,data);
    }

    public void ArmPutObject(Transform place, EventDataBase data)
    {//机械臂放置物体,根据放置物体的位置逆向求解
        armPut = IKCaculator(place, armPut);
        armPut[3] = -90;
        MoveJoints(armPut, 0,data);
    }

六. 机械臂各关节转动控制

  上一篇中的测试脚本Test.cs的MoveJoints函数,添加一个回调事件,也就是在所有关节旋转到位时,发送一个手爪事件,让手爪张开或闭合

    public void MoveJoints(float[] angle, int i,EventDataBase data)
    {//遍历所有活动关节
        joints[i].SetAngle(angle[i], () =>
        {
            if (i == joints.Length-1)
            {
                EventManager.Instance.SendEvent(EventType.OnClawCtrl, data);
                return ;
            }
            i++;
            MoveJoints(angle, i,data);
        });
    }

七. 场景实现

  将这个机械臂控制代码挂在机械臂的根节点上,设置相应的旋转节点,还需要设置一个放置工件的PutPlace,可以在场景中定义一个空节点表示。如下图:

八. 完整代码

  代码有点长,本来想搞个下载的链接,无奈不知道怎么弄,就简单粗暴全部贴上吧。

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//作用:ABB机械臂加上机械爪,使用事件出发抓取动作
//Base:S_Axis_1
//Arm0:L_Axis_2
//Arm1:U_Axis_3
//Hand:T_Axis_6
//putPlace:放置位置
public class Robot_MoveEvent : MonoBehaviour
{
    public Transform Base, Arm0, Arm1, Hand,putPlace;
    public Robot_JointCtrl[] joints;  //数组存储需要控制的各个关节
    float[] armHome = { 0, 0, 0, 0,0 };  //机械臂零位
    float[] armUp= { 0, 0, 0, 0,0 };     //设置一个机械臂抬起的位置
    float[] armDown = { 0, 0, 0, 0, 0, 1 };  //数组记录机械臂下降坐标
    float[] armPut = { 0, 0, 0, 0, -1, 0 };   //数组记录机械臂放置物体的坐标
    private float L1, L2;  //从Base开始计算的手臂长
    private float sita_L1, sita_L2;//L1、L2初始位置的偏移角
    private Vector3 V_L1, V_L2;    //从Arm0开始计算的手臂长
    private float timer = 0;

    void Start()
    {
        L1 = Vector3.Distance(Arm0.position, Arm1.position);
        L2 = Vector3.Distance(Arm1.position, Hand.position);
        V_L1 = Arm1.position - Arm0.position;
        V_L2 = Arm1.position - Hand.position;
        sita_L1 = Vector3.Angle(V_L1, new Vector3(0, V_L1.y, V_L1.z));  //L1相对于yz平面的初始偏移角
        sita_L2 = Vector3.Angle(V_L2, new Vector3(0, V_L2.y, V_L2.z));//L2相对于yz平面的初始偏移角
        EventManager.Instance.AddEvent(EventType.OnArmCtrl, this, OnArmCtrl);
    }
    public void OnArmCtrl(EventDataBase data)
    {
        EventDataArm eventdata = data as EventDataArm;
        EArmMoveType armtype = eventdata.armType;
        Transform armTarget = eventdata.target;
        EClawType clawType = eventdata.clawType;
        float waittime = eventdata.waitTime;

        if (armtype == EArmMoveType.Home) //回零时发送手爪打开事件
            ArmHome(new EventDataClaw { clawType=EClawType.Open,waitTime=0});
        //if (armtype == EArmMoveType.Up) ArmUp(upPlace);  //设置移动到安全高度
        if (armtype == EArmMoveType.Get)
        {
            ArmDown(armTarget,new EventDataClaw { clawType=EClawType.Close,waitTime=waittime});
        }
        if (armtype == EArmMoveType.Put)
        {
            Debug.Log("准备调用放置方法");
            if (timer < waittime)
                timer += Time.deltaTime;
            else
        {
        ArmPutObject(putPlace,new EventDataClaw {clawType=EClawType.Open,waitTime=0.5f});
        timer = 0;
        return;
        }
        }
    }
    public void ArmDown(Transform target,EventDataBase data)
    {//机械臂做抓取动作,target为hand的目标位置
        armDown = IKCaculator(target, armDown);
        armDown[3] = 90;    //手爪根部T_Axis_6再旋转90度
        MoveJoints(armDown, 0,data) ;
    }
    public void ArmHome(EventDataBase data)
    {//机械臂回零
        Debug.LogError("开始回零");
        MoveJoints(armHome, 0,data);
    }

    public void ArmUp(Transform upPlace,EventDataBase data)
    {//机械臂抬起位置
        armUp = IKCaculator(upPlace, armUp);
        armUp[3] = -90;
        MoveJoints(armUp, 0,data);
    }

    public void ArmPutObject(Transform place, EventDataBase data)
    {//机械臂放置物体,根据放置物体的位置逆向求解
        Debug.Log("放置物体");
        armPut = IKCaculator(place, armPut);
        armPut[3] = -90;
        MoveJoints(armPut, 0,data);
    }

    public void MoveJoints(float[] angle, int i,EventDataBase data)
    {//遍历所有活动关节
        joints[i].SetAngle(angle[i], () =>
        {
            if (i == joints.Length-1)
            {
                EventManager.Instance.SendEvent(EventType.OnClawCtrl, data);
                return ;
            }
            i++;
            MoveJoints(angle, i,data);
        });
    }

    //适合ABB400的IK计算
    float[] IKCaculator(Transform target, float[] Scara)
    {
        float sita_1, sita_2, sita_3, sita_T, X, Y, Z, W, A, x_plus, y_plus, z_plus;
        //计算目标Target和第一个关节Base距离的三个分量X、Y、Z
        X = target.position.x - Base.transform.position.x;
        Y = target.position.y - Base.transform.position.y;
        Z = target.position.z - Base.transform.position.z;
        //计算目标Target和Arm0距离的三个分量x_plus、y_plus、z_plus
        x_plus = target.position.x - Arm0.position.x;
        y_plus = target.position.y - Arm0.position.y;
        z_plus = target.position.z - Arm0.position.z;
        //计算W和A,在图中为W'和A'
        W = Mathf.Sqrt(square(x_plus) + square(z_plus));
        A = Mathf.Sqrt(square(W) + square(y_plus));  //A的长度=根号(W平方+Y平方)
        sita_T = Mathf.Acos(W / A);  //辅助角T
        sita_1 = Mathf.Acos((square(L1) + square(W) + square(y_plus) - square(L2)) / (2 * L1 * A)) + sita_T;
        sita_2 = Mathf.Acos((square(W) + square(y_plus) - square(L1) - square(L2)) / (2 * L1 * L2));
        sita_3 = Mathf.Atan2(X, Z);

        sita_1 *= Mathf.Rad2Deg;
        sita_2 *= Mathf.Rad2Deg;
        sita_3 *= Mathf.Rad2Deg;

        Scara[0] = sita_3-90;    //ABB机械臂的误差排除
        Scara[1] =-(90- sita_1+sita_L1);
        Scara[2] = -(sita_2-sita_L2);

        return Scara;
    }
    static float square(float f)
    {
        return f * f;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/742089.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

jenkins 关闭关闭CSRF Protection(跨站请求伪造保护)

jenkins版本 我的jenkins版本是&#xff1a;2.332.4 背景 Jenkins版本自2.204.6以来的重大变更有&#xff1a;删除禁用 CSRF 保护的功能。 从较旧版本的 Jenkins 升级的实例将启用 CSRF 保护和设置默认的发行者&#xff0c;如果之前被禁用。 解决方法 老版本Jenkins的CSRF…

QT各种控件常用样式表qss示例

1、表格控件QTableWidget和QTableView 这个控件比较复杂&#xff0c;里面包含了滑动条、表头&#xff08;又细分为内容区/空白区&#xff09;、表格、整体、左上角按钮等多种不同的元素&#xff0c;他们之间有复杂的叠层关系。需要通过各种“选择器”来指定样式的作用范围。 …

解决openKylin显示不能自适应VMware窗口大小的教程

目录 前言解决步骤效果图 前言 相信很多朋友们使用VMware配置成功openKylin后对遇到不能全屏显示的问题很头痛&#xff0c;今天我用我自己的电脑为例&#xff0c;给大家示范一下。 网上对于虚拟机不能自适应VMware主要有两个方案 一个是编辑虚拟机设置----显示器----指定监…

如何用ChatGPT做咨询师,附Prompt

对基本问题研究得不深不透、得不到可靠的分析框架支持的情况下&#xff0c;仓促采取就事论事的应对措施 &#xff0c;由于未能触及事情的根本&#xff0c;往往非但不能获得预期的效果&#xff0c;相反可能引发新的矛盾。 ——吴敬琏&#xff08;著名经济学家&#xff0c;国务院…

【JavaEE初阶】JavaScript基础语法

摄影分享: 文章目录 1.初识JavaScript1.1JavaScript简介1.2JavaScript 和 HTML 和 CSS 之间的关系1.3JavaScript 运行过程1.4JavaScript 的组成 2.JS基础语法2.1在HTML中引入JS2.2第一个JS程序2.3变量2.4JS的内置类型2.4.1数字2.4.2字符串2.4.3布尔类型2.4.4undefined类型和nu…

821. 跳台阶

链接&#xff1a; 821. 跳台阶 - AcWing题库 题目&#xff1a; 一个楼梯共有 nn 级台阶&#xff0c;每次可以走一级或者两级&#xff0c;问从第 00 级台阶走到第 nn 级台阶一共有多少种方案。 输入格式 共一行&#xff0c;包含一个整数 nn。 输出格式 共一行&#xff0c;包含一…

性能测试:Jmeter压测过程中的短信验证码读取

目录 问题背景 解决思路 实现方法 1. 建立JDBC连接 2. 使用JDBC请求获取验证码 3. 使用正则将验证码提取并使用 问题背景 现如今国内的大部分软件或者网站应用&#xff0c;普遍流行使用短信业务&#xff0c;比如登录、注册以及特定的业务通知等。 对于这些业务&#xff…

1.Cesium介绍及环境配置

前言 鸽了半年&#xff0c;flag立的太多&#xff0c;稿子存了100多篇&#xff0c;都没有开始排版整理&#xff0c;这些天正好学习cesium&#xff0c;决定每天更新一篇&#xff0c;提提神&#xff01;&#x1f914;&#x1f632;&#x1f601; 一、Cesium简介 Cesium是一个用于显…

服务器该怎么预防勒索病毒

场景描述 随着互联网飞速发展&#xff0c;各企业不论大小基本都搭建了属于自己公司的服务器。例如文件服务器、代码服务器、业务系统服务器、数据库服务器等。虽然服务器种类各有不同&#xff0c;但共同点都在于&#xff1a;是公司重要的数据中心。一旦被勒索病毒侵占&#xf…

【二分查找】69. x 的平方根

69. x 的平方根 解题思路 使用二分查找注意 这里当计算的Mid在x的平方根附近之后&#xff0c;直接返回mid即可 class Solution {public int mySqrt(int x) {// 二分查找 查找区间 mid * mid xint left 0;int right x/ 2 1;int mid 0;if(x 0){return 0;}if(x 1){retur…

字符函数和字符串函数的模拟实现

求字符串长度 strlen 长度不受限制的字符串函数 strcpystrcmpstrcat长度受限制的字符串函数 strnlenstrncmpstrncpystrncat字符串查找 strstrstrtok错误信息报告 strerror内存操作函数 memcpymemmovememsetmemcmp 首先我们来看strlen字符串是以‘\0’为结束标志&#xff0c;str…

基于vant封装的动态表单(VFrom使用教程)

vant-ui是属于vue开发移动端中用的比较多的一个组件库了&#xff0c;网上基于它的一些组件的二次封装也数不胜数&#xff0c;但是却都是零零散散&#xff0c;不成体系。总不能用一个就去找类似的封装吧&#xff0c;这样拼凑起来的也不是我们想要的。尤其&#xff0c;涉及众多表…

Python单元测试

单元测试 测试可以保证你的代码在一系列给定条件下正常工作测试允许人们确保对代码的改动不会破坏现有的功能测试迫使人们在不寻常条件的情况下思考代码&#xff0c;这可能会揭示出逻辑错误良好的测试要求模块化&#xff0c;解耦代码&#xff0c;这是一个良好的系统设计的标志…

Oracle select 和read的权限

只有select权限&#xff0c;也可以for update锁表。 我懵了一下&#xff0c;不确认啊&#xff0c;我先测试了下&#xff0c;如下所示&#xff1a; create user test11 identified by test11; create user test12 identified by test12; grant connect,resource to test11,tes…

【2022HVV系列】蓝队手册更新版(建议收藏)

本文首发于【2022HVV系列】蓝队手册更新版&#xff08;建议收藏&#xff09; 综合篇 青藤云安全出品&#xff1a; 倒计时&#xff01;你需要这份《2022攻防演练蓝队防守指南》 攻防演练合集 | 3个阶段&#xff0c;4大要点&#xff0c;蓝队防守全流程纲要解读网络安全实战攻防…

[QT编程系列-1]:C++图形用户界面编程,QT框架快速入门培训 - 0- 总述

目录 导言 主要内容 附录&#xff1a; 导言 1. 在这里强调为啥选择 PPT 方式&#xff0c;而不是直接讲解代码 2. 重原理和方法 3. 种 QT 的框架和 QT 的开发流程 4. 轻 UI 界面美观&#xff08; UI 设计单独课程&#xff09; 5. 请代码具体实现&#xff08;后期自学&#xf…

JavaScript 导出csv文件页面卡死时的处理方式

文章概叙 当我在使用xlsx包导出csv文件时候&#xff0c;数据过大导致页面卡死&#xff0c;因此才使用blob的方式来导出csv文件&#xff0c;如果没有出现这个问题&#xff0c;建议还是使用xlsx包 XLSX包 npm的地址&#xff0c;对于前后端开发的同学来说&#xff0c;文档难度不…

【SCI一区】互联燃料电池混合动力汽车通过信号交叉口的生态驾驶双层凸优化(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

LCD显示方向

LCD显示方向 一、ILI9341内存到显示地址的映射 本文只讨论“正常显示”&#xff0c;不讨论“垂直滚动显示”模式。 可以看到物理内存被两个指针访问&#xff0c;行指针和列指针&#xff0c;行指针范围从000h到013Fh&#xff0c;列指针范围为0000h到00EFh。也就是说&#xff0c…

naive-ui 的n-date-picker改变成中文

如下 那改完之后就要变成 如下 其实蛮简单的 修改app.vue import { zhCN,dateZhCN } from "naive-ui";<n-config-provider :locale"zhCN" :date-locale"dateZhCN"><RouterView /></n-config-provider> 你要是卡到前端难题&a…