使用three.js实现机器人手臂的运动效果
Three.js 是一款运行在浏览器中的 3D 引擎,你可以用它创建各种三维场景,包括了摄影机、光影、材质等各种对象。你可以在它的主页上看到许多精彩的演示。不过,这款引擎目前还处在比较不成熟的开发阶段,其不够丰富的 API 以及匮乏的文档增加了初学者的学习难度(尤其是文档的匮乏)three.js的代码托管在github上面。——百度百科
Three.js封装了OpenGl ES 2.0 的API,使得我们更容易在浏览器上开发各种图形效果。比起使用winform,MFC等形式,这个代码库可以让我们避开一些winform和MFC的细节问题,而专注于图形的绘制。不过由于这个库也并没有封装到非常完美,所以有些部分还需要我们自己来实现。
在此贴上Three.js中文文档的连接地址Three.js 中文文档 | 参考手册 | 使用指南 | 动画特效实例 | 踏得网
虽然这个链接上的文档有一些错误,还有许多坑,很多东西都没有说明清楚,不过,也是可以参考的。
如果要直接看效果,可以直接下载文章末尾的源代码,附带了操作方式。还需要使用电脑浏览器打开代码,因为没有对移动端做适配,所以移动端效果不好。
接下来,让我们实现一个机器人的手臂运动效果。效果如下图
很丑对不对,但这只是一个教程,所以美化可以自己来做,过多的美化工作反而会增加学习的复杂程度。
我们制作这个机器人,需要解决的一些问题是:
1.如何让我们的图形沿着任意轴转动;
2.如何使用键盘控制其转动;
这两个问题解决了,那么我们的机器人也就非常容易绘制了。接下来,就开始详解代码以及思路了。
首先,我们需要生成一个canvas,因为在html5中,所有的绘图工作都是在canvas中进行。我们可以写一个canvas标签,也可以让Three.js帮我们生成一个。这里我们让Three.js帮我们生成:
1 var scene = new THREE.Scene(); 2 var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 1000); 3 camera.position.z = 5; 4 camera.position.y = 1; 5 camera.position.x = 1.5; 6 camera.lookAt(new THREE.Vector3(0, 0, 0)); 7 scene.add(camera); 8 var renderer = new THREE.WebGLRenderer(); 9 renderer.setSize(window.innerWidth, window.innerHeight); 10 document.body.appendChild(renderer.domElement);
为什么相机要进行位置的调整呢?这是因为,如果我们不调整位置,那么我们的视角是正对着物体的,物体的3D效果会因为视角问题而显得不那么明显。
接下来,我们要绘制机器人的头,腿,身体。需要注意的是,如果我们为我们生成的图形选择了不合适的材质,会导致3D效果观察不明显。关于材质的选择,已经超出了要讲述的范围,所以为了演示简单,我们只使用基本材质。代码如下:
1 //机器人身体 2 var geometry = new THREE.BoxGeometry(1, 1, 1, 8, 8, 8); 3 var material = new THREE.MeshBasicMaterial({ 4 color: 0x00ff00, 5 wireframe: true //这里是为了让其变成网格状态,从而使其更容易观察,默认为false 6 }); 7 var cube = new THREE.Mesh(geometry, material); 8 cube.position.y = -1; 9 //机器人头部 10 var head = new THREE.SphereGeometry(0.5, 32, 32); 11 var material = new THREE.MeshBasicMaterial({ 12 color: 0x0909FD, 13 wireframe: true 14 }); 15 var sp = new THREE.Mesh(head, material); 16 //机器人双腿 17 var geometry = new THREE.BoxGeometry(0.1, 1, 0.1); 18 var material = new THREE.MeshBasicMaterial({ 19 color: 0xFF2E67, 20 wireframe: true 21 }); 22 var left_leg = new THREE.Mesh(geometry, material); 23 var right_leg = new THREE.Mesh(geometry, material); 24 left_leg.position.x = -0.3; 25 left_leg.position.y = -2; 26 right_leg.position.x = 0.3; 27 right_leg.position.y = -2;
我们的机器人就画成了这样:
接下来,就到了最关键的地方:机器人手臂的转动。很多刚学习的同学都发现,我们的图形的旋转轴是在图形的正中心。什么意思呢?看这个图:
我在图中已经标识了我们生成物体的坐标轴,可以看出,坐标轴实际上是在位置的重心。我们通过函数或者设置属性的方法,只能绕着这三个轴转。有同学问,可不可以通过先移位,再旋转,再移回原位置的方式来解决这个问题呢?我们的计算机图形学课本上是这么教的,在winfoem和MFC上也是可以做到的,但由于three.js生成的物体的坐标轴的特殊性,我们无法通过这种形式来解决(亲测)。有兴趣的同学可以自己试一试。
那么,我们该怎么办呢?我们查找手册,发现three.js中有一个类,Object3D()。这个类生成一个3D的模型,也可以说是容器。那么,我们可不可以通过生成一个容器,然后再将我们需要的图形放入这个容器,再调整容器和图形的位置来实现图形绕任意轴旋转呢?很显然,是可以的,如果你能想到,说明你的智商还是值得称道的。不过想不到也没有任何问题,因为我们需要承认自己的平庸,才能激励自己努力学习。接下来,我们来探讨,如何进行旋转操作。
首先,我们先生成一个容器对象,并且加入坐标轴显示,看看我们生成的容器是什么样的
1 var upbox = new THREE.Object3D(); 2 scene.add(upbox); 3 upbox.add(new THREE.AxisHelper(3));
效果如下:
可以看出,虽然我们无法看到这个容器,但这个容器还是有其对应的坐标位置。我们可以通过设置其position属性来移动它的位置,或者旋转它。接下来,我们把我们生成的立方体放入这个容器,同时也显示我们立方体的坐标轴。代码如下:
1 var geometry = new THREE.BoxGeometry(1, 1, 1, 8, 8, 8); 2 var material = new THREE.MeshBasicMaterial({ 3 color: 0x00ff00, 4 wireframe: true //这里是为了让其变成网格状态,从而使其更容易观察,默认为false 5 }); 6 var cube = new THREE.Mesh(geometry, material); 7 cube.add(new THREE.AxisHelper(3)); 8 9 var upbox = new THREE.Object3D(); 10 scene.add(upbox); 11 upbox.add(new THREE.AxisHelper(3)); 12 13 var render = function() { 14 requestAnimationFrame(render); 15 upbox.add(cube); //将立方体放入容器 16 renderer.render(scene, camera); 17 }
效果如图:
可以看到,新加入的这个立方体的坐标轴和这个容器的坐标轴重叠了。而此时,关键的来了,此时我们的立方体的位移已经不是相对于世界坐标了,而是相对于其父容器的坐标。也就是说,我们的立方体现在已经不是相对于世界坐标系的原点来移动了,而是相对于容器的坐标原点。由于截图来表示世界坐标系不是很直观,所以大家可以试着移动一下容器的位置,移动容器的位置,立方体的位置也会跟着容器的变化而变化。
接下来,我们来移动立方体的位置:
1 cube.position.x = 1; 2 cube.position.y = -1;
效果如下:
可以看出,我们的立方体已经移动了,而且是相对于容器的原点来移动的。那么,我们来试一下旋转我们的容器:
1 upbox.rotation.z = 1;
效果如下:
我们的立方体就跟着容器转了起来。
那么,我么就可以得到一个结论:我们可以将我们的容器的坐标轴当作我们需要移动的坐标轴,然后把我们的图形移动到适当的的位置,我们需要让立方体按x=1旋转,就把容器移动到x=1的位置;我们需要让立方体按y=1转,就把我们的容器移动到y=1的位置来旋转。不过这样也有一个问题,那就是我们的立方体的坐标已经不是相对于世界坐标了,这里需要注意。之前说道,只要把立方体移到适当的位置就可以了,那么这个适当的位置是哪里呢?文字描述非常复杂,我们用代码和效果来说明:
1 cube.add(new THREE.AxisHelper(1)); 2 cube.position.x = 0.5; 3 cube.position.y = 0.5; 4 cube.position.z = -0.5;
效果图:
这个位置就可以说成是适当的位置,我们直接设置好容器的坐标,再让容器旋转,就达到了我们绕任意轴旋转的功能。当然,按照个人的需求,这个适当位置是可以调整的。
如果我们需要实现复杂的控制,那么我们就可以采用容器里套容器的方法。这个和动画中的骨骼非常像,不过也不一样。接下来,我们就可以继续接着之前的那个机器人来绘制我们的机器人手臂了。
1 //机器人上臂 2 var upbox = new THREE.Object3D(); 3 upbox.position.x = 0.5; 4 upbox.position.y = -1.0; 5 scene.add(upbox); 6 upbox.add(new THREE.AxisHelper(2)); 7 var geometry = new THREE.BoxGeometry(1, 0.1, 0.1); 8 var material = new THREE.MeshBasicMaterial({ 9 color: 0xFF2E67, 10 wireframe: true 11 } 12 13 ); 14 var ge = new THREE.Mesh(geometry, material); 15 ge.position.x = 0.5; 16 ge.position.y = 0; 17 //机器人下臂 18 var box = new THREE.Object3D(); 19 box.position.x = 1.0; 20 box.position.y = 0.0; 21 scene.add(box); 22 //box.add(new THREE.AxisHelper(2)); 23 var geometry = new THREE.BoxGeometry(0.1, 1, 0.1); 24 var material = new THREE.MeshBasicMaterial({ 25 color: 0xFF2E67, 26 wireframe: true 27 } 28 29 ); 30 var ge_next = new THREE.Mesh(geometry, material); 31 ge_next.position.y = 0.5; 32 box.add(ge_next); 33 //机器人手掌 34 var handbox = new THREE.Object3D(); 35 handbox.position.x = 0; 36 handbox.position.y = 0.0; 37 scene.add(handbox); 38 //handbox.add(new THREE.AxisHelper(2)); 39 var geometry = new THREE.BoxGeometry(0.5, 0.5, 0.5, 4, 4, 4); 40 var material = new THREE.MeshBasicMaterial({ 41 color: 0xFF2E67, 42 wireframe: true 43 } 44 45 ); 46 var hand = new THREE.Mesh(geometry, material); 47 hand.position.y = 1.2; 48 //机器人中指 49 var zhongzhibox = new THREE.Object3D(); 50 zhongzhibox.position.x = 0; 51 zhongzhibox.position.y = 1.5; 52 scene.add(zhongzhibox); 53 //zhongzhibox.add(new THREE.AxisHelper(2)); 54 var geometry = new THREE.BoxGeometry(0.1, 0.5, 0.1); 55 var material = new THREE.MeshBasicMaterial({ 56 color: 0xFF2E67, 57 wireframe: true 58 } 59 60 ); 61 var zhongzhi = new THREE.Mesh(geometry, material); 62 zhongzhi.position.y = 0.2; 63 scene.add(cube); 64 scene.add(sp); 65 scene.add(ge); 66 scene.add(ge_next); 67 scene.add(hand); 68 scene.add(zhongzhi); 69 scene.add(left_leg); 70 scene.add(right_leg);
其中,也有容器中嵌套容器的操作。
那么,就该解决第二个问题了。我们如何通过键盘控制我们的机器人手臂呢?
很简单,使用事件绑定。我们可以给onkeydown这个事件绑定操作,来实现控制机器人的手臂。类似于:
1 onkeydown = function(event) { 2 if(angle > 0 || angle <= -1.5) { 3 angle = 0; 4 return false; 5 } 6 else if(event.keyCode == 38) { 7 angle -= 0.2; 8 } 9 }
为什么有一个angle呢?因为我们需要判断我们的机器人的手臂运动的角度,毕竟是手臂,转动的角度需要有一个限制对吧?但是呢,由于比较复杂(其实是懒),所以我只实现了两个部位的限制。
实现了上述的代码,我们应该就可以画出一个可以控制的机器人了。
下面是源代码:
机器人手臂源代码
转载于:https://www.cnblogs.com/zhnblog/p/6882683.html
使用three.js实现机器人手臂的运动效果相关推荐
- 简单html js 特效,Js实现简单的小球运动特效
废话不多说了,直接给大家贴js代码了,具体代码如下所示: //定义局部变量 var directX=;//定义x轴方向 var directY=;//定义y轴方向 var ballX=;//定义x轴坐 ...
- 【论文笔记】基于强化学习的机器人手臂仿人运动规划方法
文章目录 摘要 关键词 0 引言 学者研究 阶段一:采集运动数据,分析运动过程特征 阶段二:设计仿人变量.建立仿人标准和约束 阶段三:用智能算法提升仿人运动机器人性能 本文工作 1 问题描述及方法架构 ...
- 上海交大研究人员使用非侵入性脑机接口和计算机视觉引导对机器人手臂进行共享控制...
对于某些严重残疾患者来说,使用脑机接口(BCI)控制机械臂进行伸手和抓握活动是最常见的应用之一,对于基于脑电图(EEG)的非侵入性BCI而言,这具有很大的挑战性. 在该项研究中,来自上海交通大学的研究 ...
- 该项研究降低了使用大脑信号控制机器人手臂的位置误差
点击上面"脑机接口社区"关注我们 更多技术干货第一时间送达 当前脑机接口(BCI)设计用于位置控制中有两个基本限制.首先,大多数现有方案采用开环控制,因此无法跟踪位置误差,从而导致 ...
- 使用非侵入性脑机接口和计算机视觉引导对机器人手臂进行共享控制
点击上面"脑机接口社区"关注我们 更多技术干货第一时间送达 对于某些严重残疾患者来说,使用脑机接口(BCI)控制机械臂进行伸手和抓握活动是最常见的应用之一,对于基于脑电图(EEG) ...
- 建立自己的机器人手臂-组装
我写了一些有关软件开发,编码和Java相关主题的文章. 这次,我决定与您分享我所做的一个更有趣的项目. 我是Arduinos,Pis和其他用于家庭自动化的平台的粉丝,并且对电子和机械学也很感兴趣. 为 ...
- 拟人肌肉骨骼气动人工肌肉驱动的10自由度机器人手臂
Anthropomorphic musculoskeletal 10 degrees-of- freedom robot arm driven by pneumatic artificial mu ...
- MATLAB轨迹规划 发给ROS中机器人实现仿真运动
MATLAB轨迹规划 发给ROS中机器人实现仿真运动 现象如图所示: 0.matlab 与 ROS 通信: https://blog.csdn.net/qq_40569926/article/deta ...
- 机器人组装_建立自己的机器人手臂-组装
机器人组装 我写了一些有关软件开发,编码和Java相关主题的文章. 这次,我决定与您分享我做的一个更有趣的项目. 我是Arduinos,Pis和其他用于家庭自动化的平台的粉丝,并且对电子和机械学也很感 ...
最新文章
- [hdu] 5696 区间的价值 || 序列分治
- Pytorch实践中文教程(1)
- Java平台无关性——跨平台
- P5825-排列计数【EGF,NTT】
- php mysql 单例模式_PHP单例模式_PHP单例模式数据库连接类
- LeetCode 861. 翻转矩阵后的得分(贪心)
- 使用maven下载jar包,使用ant打包。yqxt项目的安装。
- Multi_thread--Linux下多线程编程互斥锁和条件变量的简单使用
- 转载—android 媒体库数据更新解决办法总结
- 使用Python2.7和火狐浏览器下载QQ空间好友相册
- Unity3D关于VR的Demo(一)
- 舌尖上的中国各地特色小吃,怀念家乡的味道!
- 苹果手机直播显示服务器,搭建流媒体服务器(iOS直播 )
- 写给女孩:二十岁之后的每一年都很重要
- Java课程设计——象棋(201521123042 姚佳希)
- Android 8.0+调用相机相册
- 【Kafka】Kafka简单总结
- arduino ssd1306屏幕打印信息(已测可用)
- 【造轮子】实现 atoi、itoa 函数
- 35岁找不到工作了吗?只能创业了吗?
热门文章
- hello python jpush api_jpush python服务器端
- python输入变量_Python如何获取用户输入
- Metasploit AFP爆破模块afp_login
- VMware虚拟机直连物理网络的两种方式
- gui的design 无界面_无蓝光不刺眼 海信阅读手机超长续航双11嗨不停_
- outlook qr码在哪里_明日方舟兑换码在哪里输入 附1200合成玉兑换码
- 用java编写汉诺塔问题_数据结构与算法之汉诺塔问题(Java递归)
- native react ssh_React Native踩坑笔记(持续更新中...)
- sklearn 相关性分析_人工智能和大数据的骨架支点—— 机器学习之相关性分析
- 脑机接口,风口还是入口?