基于Moveit的三维圆弧路径规划

        基于 MoveIt 的三维圆弧路径规划是利用 MoveIt 这个运动规划框架来实现机器人在三维空间中沿着圆弧路径进行规划和执行的过程。通常情况下,路径规划是在机器人自由空间中进行的,目标是找到一条连续的轨迹,将机器人从起始点移动到目标点,同时满足运动学约束、碰撞检测和路径平滑性等要求。

        三维圆弧路径规划的目标是生成一条近似于圆弧的路径,使得机器人能够以最优的方式在三维空间中移动。这种路径通常用于需要精确控制末端执行器轨迹的应用,如机械加工、焊接等。

前期回顾

        接上篇文章基于moveit的机械臂路径规划接口函数,上期编写了在关节空间和笛卡尔空间的运动函数,包括(move_j,move_p,move_l,_move_c等),但是当时的move_c函数,只能通过给定一个半径,再基于机械臂当前位置画一个二维空间下的圆周,代码如下。

bool move_c(double radius)
{
    geometry_msgs::Pose start_pose = arm.getCurrentPose().pose;
    std::vector<geometry_msgs::Pose> waypoints;
    waypoints.push_back(start_pose);

    //在x,y平面内生成圆周路点位姿
    double a=  start_pose.position.x;
    double b = start_pose.position.y;

    for(double th=0.0; th<6.28; th=th+0.01)
    {
        start_pose.position.x = a + (radius - radius * cos(th));
        start_pose.position.y = b + radius * sin(th);
        waypoints.push_back(start_pose);
    }
    // //在y,z平面内生成圆周路点位姿
    // double a=  start_pose.position.y;
    // double b = start_pose.position.z;
    // for(double th=0.0; th<6.28; th=th+0.01)
    // {
    //     start_pose.position.z = b + (radius - radius * cos(th));
    //     start_pose.position.y = a + radius * sin(th);
    //     waypoints.push_back(start_pose);
    // }
    // //在x,z平面内生成圆周路点位姿
    // double a=  start_pose.position.x;
    // double b = start_pose.position.z;
    // for(double th=0.0; th<6.28; th=th+0.01)
    // {
    //     start_pose.position.z = b + radius * sin(th);
    //     start_pose.position.x = a + (radius - radius * cos(th));
    //     waypoints.push_back(start_pose);
    // }

    // 设置末端的规划时间
    arm.setPlanningTime(10.0);

    moveit_msgs::RobotTrajectory trajectory;
    const double jump_threshold = 0.0;
    const double eef_step = 0.01;
    double fraction = 0.0;
    int maxtries = 100;   
    int attempts = 0;     

    while (fraction < 1.0 && attempts < maxtries)
    {
        fraction = arm->computeCartesianPath(waypoints, eef_step, jump_threshold, trajectory);
        attempts++;
    }

    if (fraction == 1)
    {
        ROS_INFO("Move_C Path computed successfully. Moving the arm.");

        
        moveit::planning_interface::MoveGroupInterface::Plan plan;
        plan.trajectory_ = trajectory;

        arm.execute(plan);
        sleep(1);
        arm.setPlanningTime(5.0);
        return true;
    }
    else
    {
        ROS_INFO("Move_C Path planning failed with only %0.6f success after %d attempts.", fraction, maxtries);
        return false;
    }

}

         规划结果如下图所示,可以看出这种规划生成的路径有几个缺点:1、只能通过给定的半径画一个圆周;2、只能在两个维度的平面进行规划。

74955a3acd774b728ff73bd4d40395c1.png

三维圆弧路径规划

        三维圆弧路径规划要求我们通过给定的目标点和中间点以及机械臂当前的初始位置,确定一条圆弧,然后根据圆弧计算出路径点,实现机械臂的圆弧运动。

        这里我们需要解决几个问题:

        1、怎么通过给定的三个三维点确定圆心;

        2、怎么确定圆弧对应圆的半径(这一步,只要得到圆心就很容易算出);

        3、怎么确定圆弧的圆心角。 

#怎么确定确定圆心 ?      

7e8a55b0c2244bdca926805729cf3c7c.png

 #怎么确定确定半径 ?

e3f454f7b49e4b24b06b800ea84b5468.png

 #怎么确定圆心角b8cb4bed5a824a548ebd59443111a777.png

 #怎么根据圆弧确定路点

ec285d28a1aa4ebf894e3dce993039dd.png

#完整代码

bool move_c(const geometry_msgs::Pose& mid_pose, const geometry_msgs::Pose& end_pose) 
{
    geometry_msgs::Pose start_pose = arm.getCurrentPose().pose;

    // 将geometry_msgs::Pose转换为Eigen::Vector3d
    Eigen::Vector3d p1(start_pose.position.x, start_pose.position.y, start_pose.position.z);
    Eigen::Vector3d p2(mid_pose.position.x, mid_pose.position.y, mid_pose.position.z);
    Eigen::Vector3d p3(end_pose.position.x, end_pose.position.y, end_pose.position.z);

    // 计算两个向量
    Eigen::Vector3d v1 = p2 - p1;
    Eigen::Vector3d v2 = p3 - p2;

    // 计算v1和v2的叉积,得到法向量
    Eigen::Vector3d normal = v1.cross(v2);

    // 检查法向量是否为零向量(即三点共线)
    if (normal.isZero()) {
        // 三点共线,不能确s定一个圆
        ROS_INFO("三点共线,无法确定圆");
        return false;
    }

    // 归一化法向量
    normal.normalize();

    // 建立平面方程 ax + by + cz + d = 0, 其中 [a, b, c] 是法向量, d 是常数
    double d = -(normal.dot(p1)); // 使用点p1来确定d

    // 设置方程组 A*x = b 来解圆心
    // 圆心满足 |C-P1| = |C-P2| = |C-P3|, 其中C是圆心, P1, P2, P3是给定的点
    // 这可以转化为以下线性方程组:
    // 2(x2-x1)xc + 2(y2-y1)yc + 2(z2-z1)zc = x2^2 + y2^2 + z2^2 - x1^2 - y1^2 - z1^2
    // 2(x3-x2)xc + 2(y3-y2)yc + 2(z3-z2)zc = x3^2 + y3^2 + z3^2 - x2^2 - y2^2 - z2^2
    // 加上平面方程 ax + by + cz + d = 0, 我们有三个方程来解三个未知数 xc, yc, zc

    Eigen::Matrix3d A;
    Eigen::Vector3d b;

    A(0, 0) = 2 * (p2.x() - p1.x());
    A(0, 1) = 2 * (p2.y() - p1.y());
    A(0, 2) = 2 * (p2.z() - p1.z());
    b(0) = p2.squaredNorm() - p1.squaredNorm();

    A(1, 0) = 2 * (p3.x() - p2.x());
    A(1, 1) = 2 * (p3.y() - p2.y());
    A(1, 2) = 2 * (p3.z() - p2.z());
    b(1) = p3.squaredNorm() - p2.squaredNorm();

    A(2, 0) = normal.x();
    A(2, 1) = normal.y();
    A(2, 2) = normal.z();
    b(2) = -d;

    // 解方程组
    Eigen::Vector3d circle_center = A.colPivHouseholderQr().solve(b);
    
    // 计算半径
    double radius = (p1 - circle_center).norm();//用p2和p3也行

    // 计算圆心角
    double angle = 2 * acos(v1.dot(v2) / (v1.norm() * v2.norm()));

    ROS_INFO("circle_center:(%f,%f,%f)",circle_center[0],circle_center[1],circle_center[2]);
    ROS_INFO("radius = %f, angle = %f", radius, angle);

    // 设置圆弧路点位姿
    std::vector<geometry_msgs::Pose> waypoints;

    // for(double th=0.0; th<angle; th=th+0.01)
    // {
    //     geometry_msgs::Pose temp_pose;
    //     temp_pose.position.x = circle_center[0] + radius * cos(th);
    //     temp_pose.position.y = circle_center[1] + radius * sin(th);
    //     temp_pose.position.z = start_pose.position.z;// 保持z坐标不变,在xy平面内
    //     temp_pose.orientation = start_pose.orientation;// 假设所有点的姿态相同
    //     waypoints.push_back(temp_pose);
    // }

    // 设置圆弧的采样点数
    int num_points = 100;
    double delta_angle = angle / num_points;

    // 根据参数t生成圆弧路径
    for (int i = 0; i <= num_points; ++i)
    {
        double t = delta_angle * i;

        geometry_msgs::Pose temp_pose;
        temp_pose.position.x = circle_center[0] + radius * cos(t);
        temp_pose.position.y = circle_center[1] + radius * sin(t);

        // 根据给定的三个点的高度来插值计算z坐标
        double t_normalized = static_cast<double>(num_points - i) / num_points;
        temp_pose.position.z = p1.z() + t_normalized * (p3.z() - p1.z()); // 在z方向上插值变化
    
        // 假设所有点的姿态相同,你也可以修改这里来设置不同的姿态
        temp_pose.orientation = start_pose.orientation; 

        waypoints.push_back(temp_pose);
    }

    //waypoints逆序
    reverse(waypoints.begin(), waypoints.end());
    
    // 设置终端的规划时间
    arm.setPlanningTime(10.0);

    moveit_msgs::RobotTrajectory trajectory;
    const double jump_threshold = 0.0;
    const double eef_step = 0.01;
    double fraction = 0.0;
    int maxtries = 100;   
    int attempts = 0;     

    while (fraction < 1.0 && attempts < maxtries)
    {
        fraction = arm->computeCartesianPath(waypoints, eef_step, jump_threshold, trajectory);
        attempts++;
    }

    if (fraction == 1)
    {
        ROS_INFO("Move_C Path computed successfully. Moving the arm.");

        
        moveit::planning_interface::MoveGroupInterface::Plan plan;
        plan.trajectory_ = trajectory;

        arm.execute(plan);
        sleep(1);
        arm.setPlanningTime(5.0);
        return true;
    }
    else
    {
        ROS_INFO("Move_C Path planning failed with only %0.6f success after %d attempts.", fraction, maxtries);
        return false;
    }

}

       规划结果如下图所示。可以看出它是在三维空间下生成的一段圆弧轨迹。

a5400cbe30dc4bc88293acb92326b62f.png

 

 

 

 

 

 

  • 31
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
以下是使用Python实现的ROS Noetic中MoveIt的笛卡尔路径规划函数示例: ```python import rospy import moveit_commander import moveit_msgs.msg from geometry_msgs.msg import Pose, Point, Quaternion def cartesian_path_planning(): # 初始化MoveIt moveit_commander.roscpp_initialize(sys.argv) rospy.init_node('cartesian_path_planning', anonymous=True) robot = moveit_commander.RobotCommander() scene = moveit_commander.PlanningSceneInterface() group_name = "manipulator" move_group = moveit_commander.MoveGroupCommander(group_name) # 设置笛卡尔路径规划的目标点 waypoints = [] wpose = move_group.get_current_pose().pose wpose.position.x += 0.1 wpose.position.y += 0.1 waypoints.append(copy.deepcopy(wpose)) wpose.position.z += 0.1 waypoints.append(copy.deepcopy(wpose)) wpose.position.y -= 0.1 waypoints.append(copy.deepcopy(wpose)) # 设置笛卡尔路径规划的约束条件 (plan, fraction) = move_group.compute_cartesian_path( waypoints, # waypoint poses 0.01, # eef_step 0.0) # jump_threshold # 执行笛卡尔路径规划 move_group.execute(plan, wait=True) move_group.stop() move_group.clear_pose_targets() # 关闭MoveIt moveit_commander.roscpp_shutdown() moveit_commander.os._exit(0) ``` 在这个函数中,我们首先初始化MoveIt和ROS节点。接下来,我们设置了三个目标点,并在每个目标点之间设置了一些笛卡尔轨迹。然后,我们调用`compute_cartesian_path()`函数计算笛卡尔路径。最后,我们执行笛卡尔路径规划并关闭MoveIt和ROS节点。 请注意,这只是一个示例函数,你需要根据你的实际情况来修改代码。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值