精华内容
下载资源
问答
  • 冗余机械臂运动学建模 基于ROS的仿真平台搭建 高维空间RRT规划及动态规划 控制器设计及仿真实验
  •   ➤ 01整体控制框架说明 ...底层设备发送时,设备地址位表示为指令来源地址(默认为0xff) 指令确认(cmdcfm) 底层设备接收到指令并处理后,赋值该字节用于反馈指令执行情况。参考"types.h"中E_R

     

    01整体控制框架说明


    ▲ 整体控制框架说明

    ▲ 整体控制框架说明

     

    02硬件接口


    2.1综合通信接口

    • 串行通信232逻辑电平
    • 波特率: 115200bps
    • 数据位:8
    • 停止位:1
    • 奇偶校验:无

     

    03通信协议


    • 设备地址
      上层设备发送时,设备地址位表示为指令目标地址(默认为0xff)
      底层设备发送时,设备地址位表示为指令来源地址(默认为0xff)

    • 指令确认(cmdcfm)
      底层设备接收到指令并处理后,赋值该字节用于反馈指令执行情况。参考"types.h"中E_RtnTydef定义。自定义机械臂错误取值范围为0x30~0x3F;

    • 校验(Check)方式:和校验
      Check = cmd + cmdcfm + Len_1+Len_2 +data ;

     

    04指令定义


    4.1根据机械臂控制需求,设计底层指令如下:

    4.2 指令说明

    4.2.1 底层控制指令
    (1) 获取当前位置 PS_PR_LOW_GET_CURRENT_POSITION
    返回参数1:uint16_t heightValue; //高度值
    返回参数2:uint16_t armAngleValue; //大臂角度
    返回参数3: uint16_t foreArmAngle; //小臂角度
    返回参数4: uint16_t clawWidth; //抓手值
    说明:
    即时返回当前四个自由度的绝对值。
    (2) 高度控制 PS_PR_LOW_SET_HEIGHT
    参数1:uint16_t heightValue; //高度值
    参数2:uint8_t heightRunSpeed; //运行速度
    说明:
    设置即运行。
    (3) 大臂角度控制 PS_PR_LOW_SET_HEIGHT
    参数1:uint16_t armAngleValue; //大臂角度
    参数2:uint8_t armAngleRunSpeed; //运行速度
    说明:
    设置即运行。
    (4) 小臂角度控制PS_PR_LOW_SET_HEIGHT
    参数1: uint16_t foreArmAngle; //小臂角度
    参数2: uint8_t foreArmAngletRunSpeed; //运行速度
    说明:
    设置即运行。

    (5) 抓手宽度控制PS_PR_LOW_SET_HEIGHT
    参数1: uint16_t clawWidth; // 抓手值
    参数2: uint8_t clawWidthRunDir; // 抓手运行方向 1 :松开 2:抓取
    参数3: uint8_t clawWidthRunSpeed; //运行速度
    说明:
    设置即运行。
    (6) 设定综合位置 PS_PR_SET_LOW_LEVEL_PARAMS
    参数1: robot_arm_low_ArmAngle_t robot_arm_low_ArmAngle; // 大臂参数
    参数2: robot_arm_low_foreArmAngle_t robot_arm_low_foreArmAngle; // 小臂参数
    说明:
    设定大臂+小臂的综合运行参数, 要求大臂小臂同时运行,达到目标参数。设置即运行。
    (7) 获取综合位置 PS_PR_GET_LOW_LEVEL_PARAMS
    参数1: robot_arm_low_ArmAngle_t robot_arm_low_ArmAngle; // 大臂参数
    参数2: robot_arm_low_foreArmAngle_t robot_arm_low_foreArmAngle; // 小臂参数
    说明:
    获取当前已设定的大臂+小臂的综合运行参数。

    4.2.2 底层指令序列管理指令
    (1) 获取当前序列状态 PS_PR_GET_LOW_ORDER_LISTS_STATUS
    参数1: uint8_t RunStatus:2; //机械臂运行状态 0 :停止 1:运行 2:暂停 3:序列运行完成
    参数2: uint8_t OrderTransStatus:2; //子指令传输状态 0 :初始化 1:正在传输 2:导入完成
    参数3: uint16_t orderTotalNum; //当前子指令总数
    参数4: uint16_t currentOrderNum; //当前执行子指令序号
    说明:
    获取当前机械臂序列状态。该状态参数描述机械臂任意时刻的运动状态,可持续补充。
    (2) 开始序列传输 PS_PR_SET_LOW_ORDER_LISTS_START
    参数1:uint16_t orderTotalNum; //子指令总数
    说明:
    开启子指令的导入过程,发送将要传输的子指令总数。此指令后,机械臂进入指令序列导入状态,标记OrderTransStatus为1,持续接收子指令,并存储。
    (3) 终止序列传输 PS_PR_SET_LOW_ORDER_LISTS_END
    无参数
    说明:
    关闭子指令的导入过程,标记OrderTransStatus为2,表示导入完成。要求返回机械臂序列状态。

    (4) 设定子动作 PS_PR_SET_LOW_ORDER_LIST
    参数1: uint16_t orderNum; //子指令编号(1~65535)
    参数2: uint8_t orderCmd; //子指令 0x81~0x85 (PS_PR_LOW_SET_HEIGHT ~ PS_PR_SET_LOW_LEVEL_PARAMS)
    参数3: uint8_t orderParamsLength; //指令参数长度. 说明:
    说明:
    设定子动作指令,多个子动作构成指令序列。
    (5) 根据指令编号获取子动作 PS_PR_GET_LOW_ORDER_LIST
    参数1:robot_arm_low_order_list_t.orderNum(2B)说明:
    获取指定指令编号的子指令。
    (6) 指令序列开始执行 PS_PR_RUN_LOW_ORDER_LISTS
    无参数
    说明:
    开始执行当前指令序列执行。设置RunStatus为1。
    执行完成后标记RunStatus为3,表示当前序列执行完成。

    (7) 指令序列停止 PS_PR_STOP_LOW_ORDER_LISTS
    无参数
    说明:
    停止执行当前指令序列执行。设置RunStatus为0。

    (8) 指令序列暂停 PS_PR_PAUSE_LOW_ORDER_LISTS
    无参数
    说明:
    暂停执行当前指令序列。设置RunStatus为2。

    (9) 指令序列恢复执行 PS_PR_RESUME_LOW_ORDER_LISTS
    无参数
    说明:
    恢复执行当前指令序列。设置RunStatus为1。

     

    05指令示例


    5.1 直接设定底层四自由度参数

    0)获取当前四个自由度绝对位置(具体数值范围可后续约定)
    5A FF 80 00 00 00 80
    返回示例:
    返回当前绝对位置 高度值1000, 大臂角度2000,小臂角度500 抓手值为200
    5A FF 80 00 08 00 E8 03 D0 07 F4 01 C8 00 07

    1)设置 高度为2000,速度为50(具体数值范围可后续约定)
    5A FF 81 00 03 00 D0 07 32 8D
    返回示例:
    收到指令并执行
    5A FF 81 01 03 00 D0 07 32 8E
    执行完成
    5A FF 81 02 03 00 D0 07 32 8F
    执行错误
    5A FF 81 03 03 00 D0 07 32 90

    2)设置 综合位置 大臂角度2000,速度为200 小臂角度500 速度20(具体数值范围可后续约定)
    5A FF 85 00 06 00 D0 07 C8 F4 01 14 33
    返回示例:
    收到指令并执行
    5A FF 85 01 06 00 D0 07 C8 F4 01 14 34
    执行完成
    5A FF 85 02 06 00 D0 07 C8 F4 01 14 35
    执行错误
    5A FF 85 03 06 00 D0 07 C8 F4 01 14 36

    5.2 设定底层指令序列
    1)获取当前序列状态
    5A FF 91 00 00 00 91
    返回示例:
    机械臂正在运行、子指令未传输、当前指令总数为20、当前执行指令为15。
    5A FF 91 00 05 00 01 14 00 0F 00 BA

    2)开始序列传输 子动作个数为20
    5A FF 92 00 02 00 14 00 A8
    返回示例:
    收到指令并执行
    5A FF 92 00 00 00 92

    3)设置子动作指令 动作序号为15 ,综合位置 大臂角度2000,速度为200 小臂角度500 速度20
    5A FF 94 00 0A 00 0F 00 85 06 D0 07 C8 F4 01 14 E0
    返回示例:
    收到指令并执行
    5A FF 94 00 00 00 94

    4)设置开始执行指令序列
    5A FF A1 00 00 00 A1
    返回示例:
    收到指令并执行
    5A FF A1 00 00 00 A1

     

    06控制流程说明


    6.1 指令分层控制逻辑

    6.2 指令序列控制流程

     

    07一些约定


    展开全文
  • 基于ROS的移动操作机械臂底层规划及运动仿真[D].哈尔滨工业大学,2015. 0.摘要 钱伟学长的论文在我学习轨迹规划初期就开始阅读,在学习过程中,也对于很多疑惑的问题,找寻到了答案,在我学习ROS系统结构,插值,RRT...

    [1]钱伟. 基于ROS的移动操作机械臂底层规划及运动仿真[D].哈尔滨工业大学,2015.

    0.摘要

    钱伟学长的论文在我学习轨迹规划初期就开始阅读,在学习过程中,也对于很多疑惑的问题,找寻到了答案,在我学习ROS系统结构,插值,RRT算法,过程中都有非常大的帮助!对于论文中的很多内容,我还是不能完全体会,借此机会,梳理学长的论文内容,也分享给大家。

    在这里插入图片描述

    在这里插入图片描述
    本篇分享我们主要讨论第2、3、5章,因为RRT我觉得太深奥了,让我再往后拖一拖再学!

    2.第二章 冗余机械臂运动学建模分析

    本章通过对冗余机械臂的构型进行运动学分析,利用D-H参数法建立了机械臂的连杆坐标系,解决了正向运动学的问题。逆向运动学的研究首先给出了S-E-W
    以及手臂三角形平面的概念,将机械臂的冗余自由度变换为手臂三角形平面的转动角度,实现了机械臂的降维,从而求出末端位姿对应的当前关节角度与手臂平面转动角度的关系。笛卡尔空间插值运动包含了常见的直线插补、圆弧插补以及冗余机械臂特有的自运动插补运动,通过离线计算的方式记录关节角度随时间的变化关系,证明了本章逆运动学求解方法的准确性和有效性。

    2.1综述
    简单来说就是对一个机械臂:建模→求DH参数表→正运动学→逆运动学→直线和圆弧插值
    只不过学长做的是冗余机械臂(七自由度),所以在运动学求解过程中有很多问题需要进行个性化的解决。
    我研究的是六自由度机械臂,所以就不考虑冗余的问题
    “建模→求DH参数表→正运动学→逆运动学”这部分问题我在之前的专栏博客中对六自由度机械臂进行了详细书写,也给出了C++代码
    今天主要给出直线和圆弧插值的部分
    2.2直线插值
    在这里插入图片描述
    学长给出的方法是:位置对x,y,z直接插值,姿态对欧拉角插值, 最核心的公式是2-15
    不难看出这里边△S没有被定义,我现在也不太知道他的具体算法
    在这里插入图片描述
    但是这段给我了一个很好的提醒,因为我自己用C++编写插值,都是没有时间戳的,都是直接把两个P1,P2直接直线或者三次插值,没有考虑速度加速度限制,包括实际我们进行GAZEBO下的动力学仿真或者机械臂仿真,都是要考虑实际驱动的问题。
    2.3圆弧轨迹插值

    在这里插入图片描述
    其他的公式我就不截图,可以去读学长的论文
    圆弧轨迹插值的核心就是,输入是圆弧上两个点P1,P2和圆心C,然后算出从P1到P2的圆心角theta1到thata2,直接对theta进行插值

    3.基于ROS的平台仿真

    3.1ROS的基本组成
    在这里插入图片描述
    这段话,我每次读都会有不同的感受,推荐去拜读古月居的书籍、视频等等
    3.2GAZEBO
    你如果打开Gazebo的任何一个简介,都会告诉你,他是一个动力学仿真工具,而RVIZ只是一个显示工具,你用RVIZ显示出来的东西和matlab的机器人工具包是一样的,所以真正想为了机械臂的应用做仿真,还是得用Gazebo
    以下是我个人对Gazebo的肤浅的个人现阶段理解:
    就是你真的想做Gazebo的仿真,就要自己写C++或者Python的代码,这就是我觉得钱伟学长最厉害的地方,网上有很多RVIZ和gazebo的联合使用,我的理解,那还是一个不含动力学(比如摩擦,力矩,重力因素)的正逆运动学计算,他就是把RVIZ显示的数据再输入给Gazebo而已!
    还有那个GAzebo真的是属实有点迷惑了,我的使用体验就是啥都不能调试(我学得太差了,我还是得重新学!我2月份会写出Gazebo和Rviz界面的学习笔记)
    说回来,在Gazebo下结合做控制,你要不就用moveit,要不就用ros_control,是gazebo的接口,但是我现在用moveit就要(moveit+Rviz+Gazebo)我觉得他不是动力学仿真,用ros_contorl,我写出来的控制器,就不是很稳定

    一瓶子不满,半瓶子晃荡就是我!

    4.第五章 控制器设计及仿真实验

    4.1控制框图
    在这里插入图片描述
    这很自动化了,要是添加视觉伺服,这个框架会更复杂
    4.2两种消息实现方式

    在这里插入图片描述
    这里学长还给出了自己定义的消息列表
    在这里插入图片描述
    实际上是比gazebo+rviz+moveit简洁了很多的
    就是自己从头搭的框架,这件事,说复杂也没那么复杂,但是工作量很大,对ROS的系统架构熟悉程度要求也高!

    展开全文
  • 我们执行命令,运行aubo机械臂(以仿真环境为例): roslaunchaubo_i10_moveit_configmoveit_planning_execution.launch roslaunchaubo_demoMoveGroupInterface_To_Kinetic.launch 执行命令rosnodelist,发现系统...

     

    我们执行命令,运行aubo机械臂(以仿真环境为例):

    roslaunch aubo_i10_moveit_config  moveit_planning_execution.launch

    roslaunch aubo_demo MoveGroupInterface_To_Kinetic.launch

     

    执行命令    rosnode list    ,发现系统中活跃的节点有:

    /MoveGroupInterface_To_Kinetic

    /aubo_driver

    /aubo_joint_trajectory_action

    /aubo_robot_simulator

    /move_group

    /robot_state_publisher

    /rosout

    /rviz_jupj_virtual_machine_24743_2290800773247990345

    这些节点中跟我们密切相关的是/MoveGroupInterface_To_Kinetic 、/aubo_driver 、/aubo_joint_trajectory_action 。

    一、/MoveGroupInterface_To_Kinetic     

    aubo_robot/aubo_demo/src/MoveGroupInterface_To_Kinetic.cpp中创建了该节点。该节点调用MoveIt! API接口,进行路径规并将规划到的信息通过move_group节点发布出去。该节点根据用户的需要实现相应的功能。也就是说,在实际项目中,这就是你创建的节点(当然根据项目复杂程度的不同,可以有多个类似节点)。

    我们打开这个cpp文件,可以看到aubo官方在这个文件中,使用代码设置aubo机械臂在世界坐标系中的位置和姿态。我们在实际项目中,可以参考aubo官方提供的例程,实现我们需要的功能(控制机械臂到达真实世界中的指定位置和呈现指定姿态,这个指定的位置和姿态一般需要外界来提供,如3D相机)。

    二、/aubo_driver

    该节点往下连接真实的机械臂控制器,设置机械臂的运动参数,发送MoveIt!为机械臂计算出来的路径(每个joint的关节角值) ,获取机械臂的状态,并将这些参数向上发送给move_group(MoveIt!)、rouout、aubo_robot_simulator、robot_state_punlisher、rviz等需要获取该消息的节点。aubo_driver节点是离机械臂控制器最近的节点,直接与机械臂控制器进行命令和数据的交互。

    我们打开aubo_driver/src/driver_node.cpp文件,该文件里面只有一个main函数:

    using namespace aubo_driver;

    int main(int argc, char **argv)

    {

        ros::init(argc, argv, "aubo_driver");//注册aubo_driver节点

        ros::NodeHandle n;   

    /*从参数服务器获取机械臂关节轴的数量*/

        int num = 0;

        ros::param::get("/aubo_driver/external_axis_number", num);

        int N = 3;

        while(num == 0 && N < 0)

        {

          sleep(1);

          ros::param::get("/aubo_driver/external_axis_number", num);

          N--;

        }

    /*以关节轴的数量为参数,实例化robot_driver*/

        AuboDriver robot_driver(num);

        robot_driver.run();

    /*使用6个线程来处理该节点的业务*/

        ros::AsyncSpinner spinner(6);

        spinner.start();

    /*设定发送路径信息的频率*/

        ros::Rate loop_rate(robot_driver.UPDATE_RATE_);

        while(ros::ok())

        {

            robot_driver.updateControlStatus();

            loop_rate.sleep();

            ros::spinOnce();

        }

        ROS_WARN("Exiting robot_driver");

        return(0);

    }

     

    我们来看一看    AuboDriver robot_driver(num)做了什么。

    进入 AuboDriver的构造函数,该构造函数中最重要的就是发布和订阅了一些topic,实现了一些server。

        /** publish messages **/

        joint_states_pub_ = nh_.advertise<sensor_msgs::JointState>("joint_states", 300);

        joint_feedback_pub_ = nh_.advertise<control_msgs::FollowJointTrajectoryFeedback>("feedback_states", 100);

        joint_target_pub_ = nh_.advertise<std_msgs::Float32MultiArray>("/aubo_driver/real_pose", 50);

        robot_status_pub_ = nh_.advertise<industrial_msgs::RobotStatus>("robot_status", 100);

        io_pub_ = nh_.advertise<aubo_msgs::IOState>("/aubo_driver/io_states", 10);

        rib_pub_ = nh_.advertise<std_msgs::Int32MultiArray>("/aubo_driver/rib_status", 100);

        cancle_trajectory_pub_ = nh_.advertise<std_msgs::UInt8>("aubo_driver/cancel_trajectory",100);

        io_srv_ = nh_.advertiseService("/aubo_driver/set_io",&AuboDriver::setIO, this);

        ik_srv_ = nh_.advertiseService("/aubo_driver/get_ik",&AuboDriver::getIK, this);

        fk_srv_ = nh_.advertiseService("/aubo_driver/get_fk",&AuboDriver::getFK, this);

     

        /** subscribe topics **/

        trajectory_execution_subs_ = nh_.subscribe("trajectory_execution_event", 10, &AuboDriver::trajectoryExecutionCallback,this);

        robot_control_subs_ = nh_.subscribe("robot_control", 10, &AuboDriver::robotControlCallback,this);

        moveit_controller_subs_ = nh_.subscribe("moveItController_cmd", 2000, &AuboDriver::moveItPosCallback,this);

        teach_subs_ = nh_.subscribe("teach_cmd", 10, &AuboDriver::teachCallback,this);

        moveAPI_subs_ = nh_.subscribe("moveAPI_cmd", 10, &AuboDriver::AuboAPICallback, this);

        controller_switch_sub_ = nh_.subscribe("/aubo_driver/controller_switch", 10, &AuboDriver::controllerSwitchCallback, this);

     

    发布的topic中最重要的是joint_states、feedback_states、aubo_driver/real_pose、aubo_driver/rib_status、aubo_driver/cancel_trajectory;

    创建的节点中最重要的是trajectory_execution_event、 moveItController_cmd。

    创建的服务中最重要的是/aubo_driver/set_io。

    以上这些topic和server是必须的,其他的topic和server是非必须的,实际上,除了上面的topic和server, AuboDriver构造函数中其他的topic和server,大部分是为了你自己实现一些功能做的一些预留,譬如你自己进行运动学和逆运动学计算,而不是使用MoveIt!,或者是根据需要在两者之间切换。切换的方法是通过发布一条topic信息:

    rostopic pub -1 aubo_driver/controller_switch std_msgs/Int32 -- 0   //使用你自己的算法

    rostopic pub -1 aubo_driver/controller_switch std_msgs/Int32 -- 1   //使用MoveIt!

    你在终端中发布上条消息,那么跟/aubo_driver/controller_switch关联的函数AuboDriver::controllerSwitchCallback将处理这种切换。

    aubo_driver的主要功能是:

    1、连接机械臂控制器

    AuboDriver::connectToRobotController()函数根据sim和robot_ip的值,可以连接真实的机械臂控制器,或者连接仿真器。具体连接方式我们在前面已经说过了。

    2、发布和订阅节点、创建服务。

    这些节点和服务实现了aubo机械臂的操作和状态反馈。

    1)每1/50秒向机械臂控制器查询一次机械臂的状态,并通过joint_states话题发布出去;joint_state话题是非常重要的一个话题,很多节点需要订阅这个话题来获取机械臂的状态,如ros定义的节点robot_state_publisher,该节点订阅joint_state,根据tf tree,进行坐标系的抓换和计算;

    2)如果机械臂在执行操作,每1/50秒查询机械的执行状态feedback,并通过feedback_states话题发布出去;经过其他节点的转发后mvoe_group通过action client获取最终获取该信息;

    3)通过订阅话题moveItController_cmd获取MoveIt!发布的机械臂路径信息;

    4)每MAXALLOWEDDELAY * robot_driver.UPDATE_RATE_ = 50 * 0.002 = 0.1s查询是否有路径信息(关节角数据),如果有,则通过函数robotServiceSetRobotPosData2Canbus()向机械臂控制发送MoveIt!计算得到的路径信息;

    5)获取、发布机械臂IO信息;

    6)仿真相关功能;

    三、/aubo_joint_trajectory_action

    MoveIt!实现了一个action client,通过该client将路径信息发送出去。我们要想获取该信息,需要实现一个action server。 aubo_joint_trajectory_action节点中就实现了这个action server。

    每个节点都是从该节点的main函数开始的,我们来看下aubo_joint_trajectory_action节点的main函数:

    using industrial_robot_client::joint_trajectory_action::JointTrajectoryAction;

    int main(int argc, char** argv)

    {

      // initialize node

      ros::init(argc, argv, "aubo_joint_follow_action"); //创建节点

     

      std::string robot_name, controller_name;

      ros::param::get("/robot_name", robot_name);//从参数服务器获取机械臂名字,该名字将用于创建action server

      while(robot_name == "")

      {

        sleep(1);

        ROS_INFO("Waiting for the robot description to start up!");

      }

      if(robot_name == "aubo_i5")

          controller_name = "aubo_i5_controller/follow_joint_trajectory";

      else if(robot_name == "aubo_i3")

              controller_name = "aubo_i3_controller/follow_joint_trajectory";

      else if(robot_name == "aubo_i7")

              controller_name = "aubo_i7_controller/follow_joint_trajectory";

      else if(robot_name == "aubo_i10")

              controller_name = "aubo_i10_controller/follow_joint_trajectory";

      else if(robot_name == "aubo_i5l")

              controller_name = "aubo_i5l_controller/follow_joint_trajectory";

     

      JointTrajectoryAction action(controller_name);

      action.run();

     

      return 0;

    }

    main函数实现的功能主要是:

    1、创建节点aubo_joint_follow_action,该节点在CMakeList.txt文件中被重命名为aubo_joint_trajectory_action。

    2、从参数服务器获取机械臂名字,该名字将用于创建action server 。

    3、实例化类JointTrajectoryAction的对象action

    4、执行类函数JointTrajectoryAction::run();

     

    main函数比较简单。我们再看下类 JointTrajectoryAction的构造函数,在类的构造函数中创建了跟MoveIt! action client通信的action server。

    JointTrajectoryAction::JointTrajectoryAction(std::string controller_name) :

        action_server_(node_, controller_name, boost::bind(&JointTrajectoryAction::goalCB, this, _1),

                       boost::bind(&JointTrajectoryAction::cancelCB, this, _1), false), has_active_goal_(false),

                           controller_alive_(false), has_moved_once_(false)

    以aubo_10机械臂为例,该action server的名字是 aubo_i10_controller/follow_joint_trajectory。

    MoveIt! action client与aubo_joint_trajectory_action节点的action server之间通过以下几个话题进行通信:

    1)/aubo_i10_controller/follow_joint_trajectory/goal 

    MoveIt!发布目标位置的路径信息,aubo_joint_trajectory_action节点获取到该信息后,发布到话题joint_path_command,最终aubo_driver获取到该信息发送给机械臂控制器;

    2)/aubo_i10_controller/follow_joint_trajectory/cancel  

    MoveIt!发送终止机械臂运行命令,处理逻辑如上;

    3)/aubo_i10_controller/follow_joint_trajectory/feedback

    aubo_joint_trajectory_action节点将从aubo_driver发送的话题feedback_states获取到aubo机械臂的反馈信息发布给MoveIt!(move_group)。因为feedback_states是周期性发布的,所以/aubo_i10_controller/follow_joint_trajectory/feedback也会被周期性的发布;

    4)/aubo_i10_controller/follow_joint_trajectory/result

    aubo_joint_trajectory_action节点将本次规划执行的结果发送给MoveIt!。

    5)/aubo_i10_controller/follow_joint_trajectory/status

    aubo_joint_trajectory_action节点将机械臂执行状况的当前状态发送给MoveIt!。

     

    /MoveGroupInterface_To_Kinetic   、/aubo_driver和/aubo_joint_trajectory_action这三个节点是完成MoveIt!操作aubo机械臂必须要有的节点。

    /MoveGroupInterface_To_Kinetic  是实现上层业务的节点,是应用开发人员必须要写的文件。

    /aubo_driver节点实现了与真实aubo机械臂控制器数据通信的功能;

    /aubo_joint_trajectory_action是获取MoveIt!路径信息,将MoveIt!与/aubo_driver进行连接;

    以上三个节点是必须要有的,否则无法控制真实的机械臂。

    如果哪天你想控制其他的机械臂,或者是自己制作一个机械臂,要使用MoveIt!进行路径规划,那么你需要编写具有类似于以上三个节点功能的代码。

    aubo官方在这三个几点之外,还提供了一个中间处理节点/aubo_robot_simulator。该节点做了一部分中间处理,理论上不是必须的。你只要在以上三个节点中稍作修改,那么/aubo_robot_simulator这个节点是可以去掉的。

    aubo的官方在提供的源码包中,因为要兼容仿真/显示以及自己编写规划算法这样的功能,代码中有一些是为了实现这些功能而添加进去的,可以根据项目的需要,添加或删除相应的代码。

     

     

     

     

     

     

     

     

     

    展开全文
  • 底层的电机控制已经基本完成,还需要解决的最后一个问题就是根据机械臂的运动,将机械臂的位姿状态信息发回到上位机的ROS,让RVIZ中的机械臂和现实中的机械臂保持一致。 目前没有反馈的信息发回到上位机,所以每当...

    底层的电机控制已经基本完成,还需要解决的最后一个问题就是根据机械臂的运动,将机械臂的位姿状态信息发回到上位机的ROS,让RVIZ中的机械臂和现实中的机械臂保持一致。

    目前没有反馈的信息发回到上位机,所以每当点击 Update ,然后Plan and Execute之后,机械臂都是从初始位置重新规划,而不是接着上一次运动到的位置作为起点规划。

    在修改move_group配置文件的时候,做过如下修改,意思就是使用真实机械臂发布的关节信息 /joint_states 更新机械臂位姿,而不是使用虚拟的机械臂。

      <!-- We do not have a robot connected, so publish fake joint states -->
      <node name="joint_state_publisher" pkg="joint_state_publisher" type="joint_state_publisher">
        <param name="/use_gui" value="$(arg use_gui)"/>
        <!--rosparam param="/source_list">[/move_group/fake_controller_joint_states]</rosparam -->
        <rosparam param="/source_list">[/joint_states]</rosparam >
      </node>
    

    运行程序之后 通过 rostopic echo 命令查看信息如下:

    $ rostopic echo /joint_states
    ---
    header:
      seq: 1333
      stamp:
        secs: 1573198571
        nsecs: 845865011
      frame_id: ''
    name: ['Link1', 'Link2', 'Link3', 'Link4', 'Link5']
    position: [0.0, 0.0, 0.0, 0.0, 0.0]
    velocity: []
    effort: []
    ---
    header:
      seq: 1334
      stamp:
        secs: 1573198571
        nsecs: 945868015
      frame_id: ''
    name: ['Link1', 'Link2', 'Link3', 'Link4', 'Link5']
    position: [0.0, 0.0, 0.0, 0.0, 0.0]
    velocity: []
    effort: []
    ---

    可以看到各个关节的数据流,很明显就能看出每个关节的position一直是0,这就解释了为什么每次RVIZ中的规划都是从0开始。所以只要将机械臂各个关节运动的position信息用 /joint_states 消息发布即可。

    既然要发布各个关节的位姿信息,那么我们首先需要在机械臂上安装编码器,用编码器来检测机械臂位姿的变化。为了方便测试,先对一个关节安装编码器,然后将编码器的AB信号线连接至beaglebone,编码器的具体使用方法参考文章https://blog.csdn.net/qq_34935373/article/details/88735024

    然后需要修改之前的客户端和服务器的代码,客户端代码需要增加一个编码器数据采集线程,并将数据通过套接字发送至客户端,而客户端需要增加一个数据接收线程,用来处理发送过来的编码器数据,并转换成关节的位姿信息,通过/joint_states这个话题在ROS上发布。

    以下只呈现了主要修改部分,还有一些小细节没有列出,具体参考后面的完整详细代码!

    对于服务器代码redwall_arm_client.cpp 增加一个设备树加载的函数:

    /* 加载 PRU 设备树函数 */
    int write_tree(void)
    {
        int fd = open(pwm_path,O_WRONLY);
        if(fd < 0){
            printf("打开slots失败\n");
        }
        write(fd,"am33xx_pwm",10);
        write(fd,"EBB-PRU-Example",15);
        write(fd,"bone_eqep1",10);
        close(fd);
    
        return 0;
    }

    增加一个编码器处理线程:

    /* 发送的结构体 p3*/
    struct pos_data
    {
        char lumbar_pos[10];
        int big_arm_pos;        // 因为现在就一个板子和编码器测试,
        int small_arm_pos;      // 所以只有第一个lumbar关节数是真宗的
        int wrist_pos;
        int hand_pos;
    };
    
    /* 编码器数据子线程 */
    void *send_pos(void *socketCon)
    {
        while(1)
        {
            /* 获取编码器数据 */
            int _socketCon = *((int *)socketCon);
            while(p_lumbar.size()!=0)
            {
                FILE *stream = fopen(speed_encoder"position","r+");
                fgets(p3.lumbar_pos, 10, stream);
                // 先用仅有的一个编码器数据模拟一下!!
                p3.big_arm_pos = atoi(p3.lumbar_pos)/2;
                p3.small_arm_pos = atoi(p3.lumbar_pos)/3;
                p3.wrist_pos = atoi(p3.lumbar_pos)/4;
                p3.hand_pos = atoi(p3.lumbar_pos)/5;
                fclose(stream);
                bzero(writebuf, sizeof(p3));           // 清空缓冲区
                memcpy(writebuf,&p3,sizeof(p3));       // 复制到缓冲区
                int sendMsg_len = write(_socketCon,writebuf, sizeof(p3));
                if (sendMsg_len > 0) {
                    //ROS_INFO("Send Msg_len %d successful!",sendMsg_len);
                } else {
                    printf("向%s:%d发送失败\n");
                }
            }
            usleep(1000);
        }
    }

    接下来修改redwall_arm_server.cpp代码:

    /* 发布joint_states数据 */
    #include <sensor_msgs/JointState.h>
    #include <tf/transform_broadcaster.h>
    
    /* 发送的结构体 p3*/
    struct pos_data
    {
        char lumbar_pos[10];
        int big_arm_pos;
        int small_arm_pos;
        int wrist_pos;
        int hand_pos;
    };
    
    /*SOCKET服务器端接受数据子线程*/
    void *fun_thrReceiveHandler(void *socketInfo)
    {
        int buffer_length;
        _MySocketInfo _socketInfo = *((_MySocketInfo *)socketInfo);
        // 定义一个joint_states消息发布节点
        ros::NodeHandle nj;
        ros::Publisher joint_pub = nj.advertise<sensor_msgs::JointState>("/joint_states", 1);
        while(1)
        {
            bzero(recvbuf,sizeof(p4));   //对recvbuf清零
            buffer_length = read(_socketInfo.socketCon,recvbuf,sizeof(p4)); // 返回读取的数据长度
            if(buffer_length == 0){
                printf("%s:%d 客户端在创建孙子线程的过程中意外关闭了\n",_socketInfo.ipaddr,_socketInfo.port);
                conClientCount--;
                break;
            }else if(buffer_length < 0){
                printf("接受客户端数据失败\n");
                break;
            }
            /* 处理数据 */
            recvbuf[buffer_length] = '\0';
            memcpy(&p4,recvbuf,sizeof(recvbuf));
            int lumbar_encode = atoi(p4.lumbar_pos);
            
            // 同样,先用模拟数据测试一下
            int big_arm_encode = p4.big_arm_pos;
            int small_arm_encode = p4.small_arm_pos;
            int wrist_encode = p4.wrist_pos;
            int hand_encode = p4.hand_pos;
    
            /******************* 发布消息 ***************/
            sensor_msgs::JointState joint_state;
            joint_state.header.stamp = ros::Time::now();
            joint_state.name.resize(5);
            joint_state.position.resize(5);
            joint_state.name[0] ="Link1";
            joint_state.position[0] = lumbar_encode*2*3.14/2500;      // 编码器使用的是2500线
            joint_state.name[1] ="Link2";
            joint_state.position[1] = big_arm_encode*2*3.14/2500;
            joint_state.name[2] ="Link3";
            joint_state.position[2] = small_arm_encode*2*3.14/2500;
            joint_state.name[3] ="Link4";
            joint_state.position[3] = wrist_encode*2*3.14/2500;
            joint_state.name[4] ="Link5";
            joint_state.position[4] = hand_encode*2*3.14/2500;
            joint_pub.publish(joint_state);
        }
        printf("接受数据线程结束了\n");
        return NULL;
    }
    

    程序修改完毕,运行之后再次通过 rostopic echo 命令查看信息如下:

    $ rostopic echo /joint_states
    ---
    header:
      seq: 25208
      stamp:
        secs: 1573298912
        nsecs: 974132974
      frame_id: ''
    name: ['Link1', 'Link2', 'Link3', 'Link4', 'Link5']
    position: [-0.3768, -0.1884, -0.1256, -0.092944, -0.07536]
    velocity: []
    effort: []
    ---
    header:
      seq: 25224
      stamp:
        secs: 1573298912
        nsecs: 974790147
      frame_id: ''
    name: ['Link1', 'Link2', 'Link3', 'Link4', 'Link5']
    position: [-0.3768, -0.1884, -0.1256, -0.092944, -0.07536]
    velocity: []
    effort: []
    ---

    可以看到现在的/joint_states是有数据的,和编码器的数据是对应的。观察RVIZ中的机械臂,现在每次规划机械臂的初始位置都是根据编码器反馈的数据得到的新位置。

    有一些需要注意的地方就是两者的发送和接受频率要一致,如果不一致,RViz中的模型就会出现幻影,不能实现同步.如一下错误:

    Invalid Trajectory: start point deviates from current robot state more than 0.01
    joint 'arm_elbow_joint': expected: 0, current: 1.84494

     

     

     

     

    展开全文
  • 上一篇文章中,我们简要介绍了ROS中action的基本知识,为了控制真实机械臂,我们已经修改了moveit配置文件,使得moveti启动后具备一个control_msgs::FollowJointTrajectoryAction类型的action客户端,接下来,我们...
  • 文章目录协作机械臂够用了吗?怎样才算自适应机械臂?适应操作对象位置的不确定性适应复杂外部环境的干扰适应类似任务的快速迁移什么让自适应成为可能?力控技术用位置来控制力 ——用关节电流控制力 ——关节力矩...
  • 【LeArm】动手实践机械臂(一)

    千次阅读 热门讨论 2020-10-03 12:30:59
    导语:由于工业机器人不开源,无法二次开发,不能让它按照我们想要的轨迹运动,于是突发奇想,想买个小的开源机械臂能够对底层代码进行了解,也能动手实践一下,在此记录LeArm的学习历程。 LeArm智能机械臂简介   ...
  • 首先,对机械臂进行运动学建模,包括正运动学方程和逆运动学方 程。使用 C++ 完成运动学算法的编译工作。 其次,研究轨迹规划方法,并针对机械臂三种运动模式,分别设计 轨迹规划算法。通过三种运动模式的结合使用...
  • 底层控制器、上位机语言、通讯协议简述整体方案,结合上位机程序设计,从控制流程、控制语言、各轴运动控制、调试及数据监控等方面阐述五轴机械臂控制方案,以用于双炉焦炭反应性测定仪的焦炭反应性试验。五轴机械臂...
  • 对于利用moveit控制真实的机械臂的流程一、首先假设你的底层的硬件控制是完成了的二、保证你的moveit_setup_assistant已经做好三、更改内容四、解决follow_joint_trajectory的server端的问题五、重点来了 ...
  • 这是我在做项目之初做的一个简单的底层数据接收的代码,有数据解析,基于VS2010开发的,希望对初学者有一定帮助。
  • 此处对于机械臂的操作是实例类MoveGroupCommander(是一个接口类),其父类是move_group,基本的关系大概就是这样的 类的继承关系如下: moveit的操作,ros的底层是用c++实现的,但是也有python的接口方便调用,...
  • 开发ROS 程序包控制机械臂

    千次阅读 2016-11-29 19:52:20
    ROS(Robot Operation System)是一个机器人软件平台,提供一些标准操作系统服务,例如硬件抽象,底层设备控制,常用功能实现,进程间消息以及数据包管理。ROS是基于一种图状架构,从而不同节点的进程能接受,发布,...
  • 机械臂底层通信协议说明(V0.1.3).docx 相应文件存储在: D:\zhuoqing\DesignCenter\XQWF\2021\机械臂控制需求文档   ▌02 机械臂当前需求以及后续开发需求 版本:V 0.2.0编辑:吕俊飞时间:2021年1月25日
  • 机械臂可正常执行 假设: 添加路径点函数:add_wayPoint() 按轨迹移动函数:trackMove() 改造驱动: 使用ROS封装驱动 加入action服务端代码 在CMakeLists.exe和package.xml中添加相关以来 find_package(catkin...
  • 三维可视化系统的建立依赖于三维图形平台, 如 OpenGL、VTK、OGRE、OSG等, 传统的方法多采用OpenGL进行底层编程,即对其特有的函数进行定量操作, 需要开发人员熟悉相关函数, 从而造成了开发难度大、 周期长等问题...
  • 调试准备工作 (1)安装pycharm-community ...(1)调试过程中,电脑与机械臂需要在同一网络中。 (2)需要将numpy等工具包,安装好。 (3)底层语句在robot.py和urrobot.py两个文件中。 import urx
  • stm32+pca9685控制舵机机械臂

    万次阅读 2017-08-24 21:34:06
    1.硬件PCA9685是一款基于IIC总线通信的12位...该模块由于主要活跃在Aruino周边,所以在使用Arduino开发其底层驱动库是十分完善的,但对于单片机开发人员就不太友好了,需要自行根据用户手册在单片机上编写底层的驱动。
  •   ▌01 底层串口控制命令 机械臂底层通信协议说明
  • ROS与底层串口通讯有两种方式: 底层作为ROS节点:https://blog.csdn.net/Kalenee/article/details/80644896 采用传统的串口通讯方式进行通讯:https://blog.csdn.net/Kalenee/article/details/82422196 硬件: ...
  • 三维可视化系统的建立依赖于三维图形平台, 如 OpenGL、VTK、OGRE、OSG等, 传统的方法多采用OpenGL进行底层编程,即对其特有的函数进行定量操作, 需要开发人员熟悉相关函数, 从而造成了开发难度大、 周期长等问题...
  • ▶从底层至上层的全面革新,全方位一体式的前沿机器人技术,拥有强大自适应性 ▶由资深机器人及AI专家开发的独有力觉控制及AI技术 2019年4月2日,汉诺威,德国 —— 在德国的2019汉诺威工业展上,AI机器人公司...
  • 在说磁盘和内存之前先说一下磁盘,顾名思义磁盘,像盘子一样,磁盘上有许多磁道,一圈套一圈,磁盘还有机械臂,通过机械的变换位置,来获取不同磁道上的数据。每圈磁道进行等长的切割,切割成的每块叫做基本存储单元...

空空如也

空空如也

1 2 3
收藏数 52
精华内容 20
关键字:

机械臂底层