原创 机器人制作方案 | Delta并联机械臂搬运磁铁物料功能的实现

2023-6-27 17:39 663 8 8 分类: 机器人/ AI 文集: Delta机械臂

1. 功能说明

本文示例将利用Delta并联机械臂实现不同点定点搬运磁铁物料的效果。


2. 结构说明

Delta并联机械臂,其驱动系统采用精度较高的42步进电机;传动系统为丝杠和万向球节;执行末端为搭载电磁铁的圆盘支架。


3. Delta机械臂运动学算法

这里给大家介绍一种Delta并联机械臂的运动轨迹解法,通过控制电机的转动参数,最终求解出电磁铁圆盘支架的运动轨迹规律,样机采用R037b

该机械臂由3个丝杠平台构成,通过并联的方式同时控制同一个端点的运动;三个丝杠位于一个正三角形边线的中心位置,连杆采用球头万向节连杆结构。

① 首先我们建立一个空间直角坐标系,该直角坐标系以三个丝杠平台在俯视图方向投影的内切圆心为原点,x轴与tower1和tower3之间的连线平行,y轴过tower2,其中z=0的平面设置在三个限位开关所在平面。


② 建立坐标系之后,我们可以得出3个限位开关Z轴投影的坐标为A=(-msin(60°),mcos(60°),0);B=(0,m,0);C=(msin(60°),mcos(60°),0);其中m为在xy投影面上正三角形的内切圆心到B点的距离。


③确定各限位开关的位置(即确定各丝杠平台上滑块的初始位置),丝杠平台的运动可简化为如下:【其中N点为滑块初始位置,Q点为端点初始位置,P为Q点在丝杠平台上Z轴的投影;N1,P1,Q1点为丝杠平台运动后的位,T点为某一固定点,假设为delta机械臂上端点在Z向可以运动的最大值在丝杠平台Z向的投影点】


④逆运动学是根据Q1点的位置确定NN1的距离。     

在图中有几个可以通过测量得到已知值,分别是连杆长度NQ/N1Q1、NT的距离、终点Q1点的坐标;假设我们输入的量是终点Q1的坐标(X1,Y1,Z1);这里需要注意的是Z1坐标为负值,为了方便理解在后面的推导中我们都对Z1取绝对值。     

我们需要计算的是NN1的距离:


其中Q1的Z坐标与P1的Z坐标一致,所以NP1为已知量为Q1的Z坐标值Z1,即可以将上面的公式改为:

这里我们只需要计算出N1P1的值即可:

其中NIQ1为连杆长度,可通过测量得知,所以这里面需要我们计算出Q1P1。     

⑤求出Q1P1:【该长度我们可以通过两点坐标距离公式得出,借助俯视图投影进行计算】


为方便计算Q1P1,图中我们将N,N1,P,P1,T点都投影到Z为0的点,则Q1(X1,Y1,0)。

根据点坐标公式可得:

综上所述:

注意前面我们对Z1取了一次绝对值,实际Z1为负值,

所以最终推导公式为:


这样我们就求出了NN1(丝杠移动距离)与Q1(执行端运动的终点)坐标的关系。

4. 功能实现

4.1 电子硬件

在这个示例中,我们采用了以下硬件,请大家参考:


4.2 电路连接说明

① 硬件连接-电子元件

各轴步进电机与SH-ST步进电机扩展板的接线顺序如下(从上至下):
X:红蓝黑绿

Y:红蓝黑绿

Z:黑绿红蓝

② 硬件连接-限位传感器

各个轴的限位传感器(触碰)与Bigfish扩展板的接线如下:
X:A0

Y:A3

Z:A4

③ 电磁铁连在Bigfish扩展板的D5、D6接口上。

4.3 编写程序

编程环境:Arduino 1.8.19

Delta机械臂有两种运动方式:第一种是自动运行搬运;第二种是用电脑发送指令,然后再根据指令运动。     

这里仅列出Delta机械臂自动运行搬运(Delta.ino)的程序:【其它的程序源码可下载文末资料获取】

  1. /*------------------------------------------------------------------------------------
  2. 版权说明:Copyright 2023 Robottime(Beijing) Technology Co., Ltd. All Rights Reserved.
  3. Distributed under MIT license.See file LICENSE for detail or copy at
  4. https://opensource.org/licenses/MIT
  5. by 机器谱 2023-02-08 https://www.robotway.com/
  6. ------------------------------------------------------------------------------------*/
  7. #include "Arduino.h"
  8. #include <AccelStepper.h>
  9. #include <MultiStepper.h>
  10. #include "Configuration.h"
  11. AccelStepper stepper_x(1, 2, 5); //tower1
  12. AccelStepper stepper_y(1, 3, 6); //tower2
  13. AccelStepper stepper_z(1, 4, 7); //tower3
  14. //AccelStepper stepper_a(1, 12, 13);
  15. MultiStepper steppers;
  16. float delta[NUM_STEPPER];
  17. float cartesian[NUM_AXIS] = {0.0, 0.0, 0.0}; //当前坐标
  18. float destination[NUM_AXIS]; //目标坐标
  19. boolean dataComplete = false;
  20. float down = -111;
  21. float up = -105;
  22. /*********************************************Main******************************************/
  23. void setup() {
  24. Serial.begin(9600);
  25. pinMode(EN, OUTPUT);
  26. steppers.addStepper(stepper_x);
  27. steppers.addStepper(stepper_y);
  28. steppers.addStepper(stepper_z);
  29. stepperSet(1600, 400.0);
  30. stepperReset();
  31. delay(1000);
  32. Get_command(0, 0, down);
  33. Process_command();
  34. delay(1000);
  35. }
  36. void loop() {
  37. float r = 25;
  38. float x1 = 0.0;
  39. float y1 = 0.0;
  40. Get_command(25, 0, down);
  41. Process_command();
  42. delay(1000);
  43. for(int i=0;i<2;i++){
  44. for(float i=0.0;i<=360;i+=10){
  45. x1 = r * cos(i / 180 * 3.141592);
  46. y1 = r * sin(i / 180 * 3.141592);
  47. Get_command(x1, y1, down);
  48. Process_command();
  49. }
  50. }
  51. delay(1000);
  52. for(int j=0;j<2;j++){
  53. for(float i=360.0;i>=0;i-=10){
  54. x1 = r * cos(i / 180 * 3.141592);
  55. y1 = r * sin(i / 180 * 3.141592);
  56. Get_command(x1, y1, down);
  57. Process_command();
  58. }
  59. }
  60. delay(1000);
  61. Get_command(0, 0, down);
  62. Process_command();
  63. test();
  64. delay(1000);
  65. stepperReset();
  66. delay(1000);
  67. }
  68. /***************************************Get_commond*******************************************/
  69. void Get_command(float _dx, float _dy, float _dz){
  70. destination[0] = _dx;
  71. destination[1] = _dy;
  72. destination[2] = _dz;
  73. if(destination[0] == 0 && destination[1] == 0 && destination[2] == 0){
  74. stepperReset();
  75. }
  76. else
  77. {
  78. dataComplete = true;
  79. }
  80. if(serial_notes){
  81. Serial.print("destinationPosition: ");
  82. Serial.print(destination[0]);
  83. Serial.print(" ");
  84. Serial.print(destination[1]);
  85. Serial.print(" ");
  86. Serial.println(destination[2]);
  87. }
  88. }
  89. /***************************************Process_command***************************************/
  90. void Process_command(){
  91. if(dataComplete){
  92. digitalWrite(EN, LOW);
  93. if(cartesian[0] == destination[0] && cartesian[1] == destination[1] && cartesian[2] == destination[2]){
  94. return;
  95. }
  96. else
  97. {
  98. Line_DDA(destination[0], destination[1], destination[2]);
  99. }
  100. }
  101. dataComplete = false;
  102. digitalWrite(EN, HIGH);
  103. }
  104. /************************************************** DDA ************************************************/
  105. void Line_DDA(float x1, float y1, float z1)
  106. {
  107. float x0, y0, z0; // 当前坐标点
  108. float cx, cy; // x、y 方向上的增量
  109. x0 = cartesian[0];y0 = cartesian[1];z0 = cartesian[2];
  110. int steps = abs(x1 - x0) > abs(y1 - y0) ? abs(x1 - x0) : abs(y1 - y0);
  111. cx = (float)(x1 - x0) / steps;
  112. cy = (float)(y1 - y0) / steps;
  113. for(int i = 0; i <= steps; i++)
  114. {
  115. cartesian[0] = x0 - cartesian[0];
  116. cartesian[1] = y0 - cartesian[1];
  117. cartesian[2] = z1 - cartesian[2];
  118. calculate_delta(cartesian);
  119. stepperSet(1350.0, 50.0);
  120. stepperMove(delta[0], delta[1], delta[2]);
  121. cartesian[0] = x0;
  122. cartesian[1] = y0;
  123. cartesian[2] = z1;
  124. x0 += cx;
  125. y0 += cy;
  126. if(serial_notes){
  127. Serial.print("currentPosition: ");
  128. for(int i=0;i<3;i++){
  129. Serial.print(cartesian[i]);
  130. Serial.print(" ");
  131. }
  132. Serial.println();
  133. Serial.println("-------------------------------------------------");
  134. }
  135. }
  136. }
  137. /***************************************calculateDelta****************************************/
  138. void calculate_delta(float cartesian[3])
  139. {
  140. if(cartesian[0] == 0 && cartesian[1] == 0 && cartesian[2] == 0){
  141. delta[0] = 0; delta[1] =0; delta[2] = 0;
  142. }
  143. else
  144. {
  145. delta[TOWER_1] = sqrt(delta_diagonal_rod_2
  146. - sq(delta_tower1_x-cartesian[X_AXIS])
  147. - sq(delta_tower1_y-cartesian[Y_AXIS])
  148. ) + cartesian[Z_AXIS];
  149. delta[TOWER_2] = sqrt(delta_diagonal_rod_2
  150. - sq(delta_tower2_x-cartesian[X_AXIS])
  151. - sq(delta_tower2_y-cartesian[Y_AXIS])
  152. ) + cartesian[Z_AXIS];
  153. delta[TOWER_3] = sqrt(delta_diagonal_rod_2
  154. - sq(delta_tower3_x-cartesian[X_AXIS])
  155. - sq(delta_tower3_y-cartesian[Y_AXIS])
  156. ) + cartesian[Z_AXIS];
  157. for(int i=0;i<3;i++){
  158. delta[i] = ((delta[i] - 111.96) * stepsPerRevolution / LEAD);
  159. }
  160. }
  161. if(serial_notes){
  162. Serial.print("cartesian x="); Serial.print(cartesian[X_AXIS]);
  163. Serial.print(" y="); Serial.print(cartesian[Y_AXIS]);
  164. Serial.print(" z="); Serial.println(cartesian[Z_AXIS]);
  165. Serial.print("delta tower1="); Serial.print(delta[TOWER_1]);
  166. Serial.print(" tower2="); Serial.print(delta[TOWER_2]);
  167. Serial.print(" tower3="); Serial.println(delta[TOWER_3]);
  168. }
  169. }
  170. /****************************************stepperMove******************************************/
  171. void stepperMove(long _x, long _y, long _z){
  172. long positions[3];
  173. positions[0] = _x; //steps < 0, 向下运动 ; steps > 0, 向上运动
  174. positions[1] = _y;
  175. positions[2] = _z;
  176. steppers.moveTo(positions);
  177. steppers.runSpeedToPosition();
  178. stepper_x.setCurrentPosition(0);
  179. stepper_y.setCurrentPosition(0);
  180. stepper_z.setCurrentPosition(0);
  181. }
  182. /****************************************stepperSet******************************************/
  183. void stepperSet(float _v, float _a){
  184. stepper_x.setMaxSpeed(_v); //MaxSpeed: 650
  185. stepper_x.setAcceleration(_a);
  186. stepper_y.setMaxSpeed(_v);
  187. stepper_y.setAcceleration(_a);
  188. stepper_z.setMaxSpeed(_v);
  189. stepper_z.setAcceleration(_a);
  190. }
  191. /****************************************stepperReset******************************************/
  192. void stepperReset(){
  193. digitalWrite(EN, LOW);
  194. if(cartesian[2] != 0){
  195. Get_command(0, 0, cartesian[2]);
  196. Process_command();
  197. digitalWrite(EN, LOW);
  198. }
  199. while(digitalRead(SENSOR_TOWER1) && digitalRead(SENSOR_TOWER2) && digitalRead(SENSOR_TOWER3)){
  200. stepperMove(10, 10, 10);
  201. }
  202. stepperSet(1200.0, 100.0);
  203. stepperMove(-400, 0, 0);
  204. while(digitalRead(SENSOR_TOWER1)){
  205. stepperMove(10, 0, 0);
  206. }
  207. stepperMove(0, -400, 0);
  208. while(digitalRead(SENSOR_TOWER2)){
  209. stepperMove(0, 10, 0);
  210. }
  211. stepperMove(0, 0, -400);
  212. while(digitalRead(SENSOR_TOWER3)){
  213. stepperMove(0, 0, 10);
  214. }
  215. for(int i=0;i<3;i++){
  216. cartesian[i] = 0.0;
  217. }
  218. if(serial_notes) Serial.println("resetComplete!");
  219. digitalWrite(EN, HIGH);
  220. }
  221. /*************************************************** electromagnet *************************************************************/
  222. void putUp(){
  223. digitalWrite(9, HIGH);
  224. digitalWrite(10, LOW);
  225. }
  226. void putDown(){
  227. digitalWrite(9, LOW);
  228. digitalWrite(10, LOW);
  229. }
  230. /************************************************** test ******************************************************************/
  231. void test(){
  232. Get_command(0, 0, down);
  233. Process_command();
  234. delay(500);
  235. putUp();
  236. Get_command(0, 0, up);
  237. Process_command();
  238. Get_command(25, 0, up);
  239. Process_command();
  240. Get_command(25, 0, down);
  241. Process_command();
  242. putDown();
  243. delay(500);
  244. putDown();
  245. putUp();
  246. Get_command(25, 0, up);
  247. Process_command();
  248. Get_command(0, 25, up);
  249. Process_command();
  250. Get_command(0, 25, down);
  251. Process_command();
  252. putDown();
  253. delay(500);
  254. putDown();
  255. putUp();
  256. Get_command(0, 25, up);
  257. Process_command();
  258. Get_command(-25, 0, up);
  259. Process_command();
  260. Get_command(-25, 0, down);
  261. Process_command();
  262. putDown();
  263. delay(500);
  264. putUp();
  265. Get_command(-25, 0, up);
  266. Process_command();
  267. Get_command(0, -25, up);
  268. Process_command();
  269. Get_command(0, -25, down);
  270. Process_command();
  271. putDown();
  272. delay(500);
  273. putUp();
  274. Get_command(0, -25, up);
  275. Process_command();
  276. Get_command(25, 0, up);
  277. Process_command();
  278. Get_command(25, 0, down);
  279. Process_command();
  280. putDown();
  281. delay(500);
  282. putUp();
  283. Get_command(25, 0, up);
  284. Process_command();
  285. Get_command(0, 0, up);
  286. Process_command();
  287. Get_command(0, 0, down);
  288. Process_command();
  289. delay(500);
  290. putDown();
  291. }



5. 扩展

    下图是另一种外观的Delta机械臂(R037c),控制原理完全一样。



Delta并联机械臂的程序源代码及样机3D文件详见【R037】Delta并联机械臂-电磁铁搬运 https://www.robotway.com/h-col-194.html

作者: 机器谱, 来源:面包板社区

链接: https://mbb.eet-china.com/blog/uid-me-4052389.html

版权声明:本文为博主原创,未经本人允许,禁止转载!

PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
EE直播间
更多
我要评论
0
8
关闭 站长推荐上一条 /3 下一条