5自由度并联机械臂实现搬运功能

1. 功能说明

      本文示例将实现R306样机5自由度并联机械臂搬运牛奶到指定地点的功能。该机械臂由1个5自由度并联机械臂和1个单轴丝杠平台构成,机械臂通过并联的方式同时控制同一个端点的运动。其驱动系统采用大扭矩舵机和小扭矩舵机、执行末端为夹持机构。

 

2. 并联机械臂机械系统

      并联机械臂与串联机械臂每个独立的关节模块组成不同,并联机械臂是通过连杆组将多个驱动同时共同控制机械臂末端,如下图(四连杆并联机械臂)所示,可以分解为三个部分:

       第一个部分是下图中蓝色的四连杆,舵机y作为驱动,舵机x固定不动,这时会形成一个FBCD四连杆结构,FB为机架,FD为驱动杆;

      第二个部分是下图中红色的四连杆,舵机x作为驱动,舵机y固定不动,这时会形成一个ABCD四连杆,其中AD作为机架,AB作为驱动杆,BC作为传动杆,CD作为随动杆;  

      第三部分为DGHI平行四连杆,这个部分没有驱动,主要作用是保证执行端HI保持一个方向;

      综上可知CD杆为第一部分四连杆和第二部分四连杆共同控制的杆件。

 3. 并联机械臂运动学算法

      当机械臂完成动作时,需通过舵机对L1和L2了两根连杆进行控制和调节,2个舵机的角度分别为θ1和θ2。为了方便分析,将机械臂简化进行分析。

       图中L1=AB,L2=BC,L3=CD,L4=DA,L5=AF,L6=DF,其中假设舵机x和舵机y的0°和180°的极限位置为AF杆方向,θ1和θ2分别为舵机x和舵机y的转动角度,其中L1、L2、L3、L5、L6都为已知尺寸(可用尺子量出)。

      在这个模型中,并联机械臂的运动算法就是CD杆的运动轨迹,如果要求出CD杆的运动轨迹,实际是求出CD杆与DF杆的夹角∠CDF。

      从图中可知:∠CDF=∠CDA+∠ADF;

      ∠CDA位于四连杆ABCD中,通过欧拉公式可以推导;∠ADF位于三角形ADF中,可以通过三角形余弦定理推导;

      求解∠CDA,假设∠CAD=α:

根据欧拉公式展开得:

      从上面的公式中可知我们还需要求出L4和∠BAD;

      L4位于三角形ADF中,可通过三角函数求解得出。在三角形ADF中需要知道两条相邻边长和该相邻边的夹角,其中L6和L5为已知量,所以:

       其中∠DFA=180°-θ2;

      ∠BAD=180°-θ1-∠DAF;

        同理可得:

         最终可解得:

         计算机械臂的端点I的运动轨迹,可建立舵机x和舵机y转动中心连线为x轴的平面坐标系,如下图所示:

         如上图所示,对D点和I点(x,y)作平面直角坐标系的投影,根据前面计算出的D点角度位置计算I点的运动坐标,最终可获得I(f(θ1,θ2),f(θ1,θ2))的关系。

4. 电子硬件

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

主控板

Basra主控板(兼容Arduino Uno)

扩展板

Bigfish2.1扩展板

电池7.4V锂电池

电路连接:舵机接线:由上至下分别连接在Bigfish扩展板的D4、D7、D3、D8、D12接口。

5. 功能实现

上位机:Controller 1.0

下位机编程环境:Arduino 1.8.19

① 将参考例程(get_milk_new2.ino)下载到主控板:

/*------------------------------------------------------------------------------------

  版权说明:Copyright 2023 Robottime(Beijing) Technology Co., Ltd. All Rights Reserved.

           Distributed under MIT license.See file LICENSE for detail or copy at

           https://opensource.org/licenses/MIT

           by 机器谱 2023-04-07 https://www.robotway.com/

  ------------------------------*/

#include <Servo.h>


#define servo_speed 120   //servo_speed

#define action_delay 1000 //action_delay


Servo myServo[5];

int servo_port[5]={4, 7, 3, 8, 12};   //servo_pin

float value_init[5]={115, 60, 156, 35, 75};   //servo_value_inital


int num = 10;   

int servo_num = sizeof(servo_port)/sizeof(servo_port[0]); //servo_pin length

boolean debug = false;   //if(true) debug


void setup() {

  Serial.begin(9600);  

  for(int i=0;i<servo_num;i++)

    ServoGo(i,value_init[i]);  

  delay(action_delay);

}


void loop() {

      servo_move(115, 105, 160, 110, 20);

      servo_move(140, 60, 160, 35, 75);

      get_milk();

      putMilk_near();

     

      servo_move(115, 105, 160, 110, 20);

      servo_move(140, 60, 160, 35, 75);

      get_milk();

      putMilk_far();

}


void ServoStart(int which)

{

  if(!myServo[which].attached())myServo[which].attach(servo_port[which]);

  pinMode(servo_port[which], OUTPUT);

}


void ServoStop(int which)

{

  myServo[which].detach();

  digitalWrite(servo_port[which],LOW);

}


void ServoGo(int which , int where)

{

  if(where!=200)

  {

    if(where==201) ServoStop(which);

    else

    {

      ServoStart(which);

      myServo[which].write(where);

    }

  }  

  ServoStart(which);

  myServo[which].write(where);

}


void get_milk()

{

  servo_move(115, 105, 160, 110, 20);   //down

  servo_move(160, 95, 160, 95, 40);    //get

  servo_move(160, 60, 160, 35, 75);    //up  

  delay(action_delay);  

  servo_move(160, 60, 25, 35, 75); //turn  

}



void putMilk_near()

{

  //Serial.println("near");

  servo_move(160, 60, 25, 60, 65); //down

  servo_move(140, 60, 25, 60, 65); //put

  servo_move(115, 60, 160, 35, 75);//reset

}


void putMilk_far()

{

  //Serial.println("far");

  servo_move(160, 100, 22, 56, 54); //down

  servo_move(140, 103, 22, 56, 54); //put

  servo_move(115, 60, 160, 35, 75); //reset

}



void servo_move(float value0, float value1, float value2, float value3, float value4)

{

  //Serial.println("begin");  

  float value_arguments[] = {value0, value1, value2, value3, value4};

  float value_delta[servo_num];  

  for(int i=0;i<servo_num;i++)

  {

    value_delta[i] = (value_arguments[i] - value_init[i]) / num;

  }  

  for(int i=0;i<num;i++)

  {

    for(int k=0;k<servo_num;k++)

    {

      value_init[k] = value_delta[k] == 0 ? value_arguments[k] : value_init[k] + value_delta[k];

    }

   

    for(int j=0;j<servo_num;j++)

    {

      ServoGo(j,value_init[j]);

    }

    delay(servo_speed);

  }

  delay(action_delay);  

}

② 双击打开Controller 1.0.exe。将波特率与串口设置好,同时留下4、7、3、8、12几个舵机串口,调试舵机角度。具体操作步骤可参考【U002】如何驱动模拟舵机-Controller 1.0b软件的使用

 ③ 将调试好的舵机角度写到对应例程(get_milk_new2.ino)中的位置;这里以几个角度为例,大家可尝试自己更改角度使其动作看起来更流畅。

6. 资料内容

①搬运-例程源代码

②搬运-样机3D文件

③Controller1.0b资料包

资料内容详见:5自由度并联机械臂-搬运

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个示例代码,用于实现一个6自由度并联机器人的运动学模型。其中,我们使用了Python语言和SymPy库来简化数学计算。 ```python import sympy as sp # 定义符号变量 q1, q2, q3, q4, q5, q6 = sp.symbols('q1 q2 q3 q4 q5 q6') # 定义链接长度 l1, l2, l3, l4, l5, l6 = sp.symbols('l1 l2 l3 l4 l5 l6') # 由于机器人是并联的,每个执行器都有两个连接点,因此我们定义两个末端点 # 分别对应于每个执行器的末端点 p1 = sp.Matrix([0, 0, 0]) p2 = sp.Matrix([0, 0, l1]) p3 = sp.Matrix([0, l2, l1]) p4 = sp.Matrix([0, l3, l1]) p5 = sp.Matrix([l4, l3, l1]) p6 = sp.Matrix([l5, l3, l1]) # 定义末端执行器的位置 # 通过将每个执行器的末端点相加来计算末端执行器的位置 T = sp.Matrix([0, 0, l1+l2+l3+l4+l5+l6]) R = sp.Matrix([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) EE = sp.Matrix.hstack(R, T) EE = sp.Matrix.vstack(EE, sp.Matrix([0, 0, 0, 1])) # 定义每个关节的旋转矩阵 Rz1 = sp.Matrix([[sp.cos(q1), -sp.sin(q1), 0, 0], [sp.sin(q1), sp.cos(q1), 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) Ry2 = sp.Matrix([[sp.cos(q2), 0, sp.sin(q2), 0], [0, 1, 0, 0], [-sp.sin(q2), 0, sp.cos(q2), 0], [0, 0, 0, 1]]) Ry3 = sp.Matrix([[sp.cos(q3), 0, sp.sin(q3), 0], [0, 1, 0, 0], [-sp.sin(q3), 0, sp.cos(q3), 0], [0, 0, 0, 1]]) Rx4 = sp.Matrix([[1, 0, 0, 0], [0, sp.cos(q4), -sp.sin(q4), 0], [0, sp.sin(q4), sp.cos(q4), 0], [0, 0, 0, 1]]) Ry5 = sp.Matrix([[sp.cos(q5), 0, sp.sin(q5), 0], [0, 1, 0, 0], [-sp.sin(q5), 0, sp.cos(q5), 0], [0, 0, 0, 1]]) Rx6 = sp.Matrix([[1, 0, 0, 0], [0, sp.cos(q6), -sp.sin(q6), 0], [0, sp.sin(q6), sp.cos(q6), 0], [0, 0, 0, 1]]) # 定义每个链接的起始位置和末端位置 p2_1 = p2 - p1 p3_2 = p3 - p2 p4_3 = p4 - p3 p5_4 = p5 - p4 p6_5 = p6 - p5 # 通过链接的长度和旋转矩阵计算每个链接的位置 T1 = Rz1 * p2_1 T2 = T1 * Ry2 * p3_2 T3 = T2 * Ry3 * p4_3 T4 = T3 * Rx4 * p5_4 T5 = T4 * Ry5 * p6_5 # 计算每个链接的末端位置 P1 = p1 P2 = P1 + T1 P3 = P2 + T2 P4 = P3 + T3 P5 = P4 + T4 P6 = P5 + T5 # 将末端执行器的位置表示为上述链接的末端位置之和 EE = P6 + EE[0:3, 3] # 输出结果 print(sp.simplify(EE)) ``` 这个代码可以计算出机器人末端执行器的位置,只需提供每个关节的旋转角度和每个链接的长度即可。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值