全方位移动机器人 Stanley 轨迹跟踪 Gazebo 仿真

全方位移动机器人 Stanley 轨迹跟踪 Gazebo 仿真

本来打算今天出去跑一下 GPS,但是下雨,作罢

添加参考轨迹信息

以下三个功能包不需要修改:

  • mrobot:在 Rviz 和 Gazebo 中仿真机器人
  • cmd_to_mrobot:运动学解算,控制仿真机器人运动
  • mrobot_states_update:发布机器人状态信息

pure-pursuit 轨迹跟踪中只包含参考轨迹的 X、Y 信息,不包括参考航向角信息,而 stanley 轨迹跟踪需要参考航向角信息,因此修改 double_lane.cpp

主要有两种思路:

1、自定义参考轨迹消息,包括 ref_x,ref_y, ref_phi

float64[] ref_x
float64[] ref_y
float64[] ref_phi

但是参考轨迹要在 Rviz 中显示的话还是需要发布标准格式的消息,即 nav_msgs::Path

因此 double_lane 节点需要发布两个话题,一个给 stanley 控制器,一个给 Rviz,有点冗余


2、将参考航向角信息转化为 nav_msgs::Path 消息下的四元数格式

航向角相当于欧拉角中的偏航角(yaw),因此需要编写欧拉角到四元数的转换函数

array<float, 4> calEulerToQuaternion(const float roll, const float pitch, const float yaw) {
    array<float, 4> calQuaternion = {0.0f, 0.0f, 0.0f, 1.0f};  // 初始化四元数

    // 使用Eigen库来进行四元数计算
    Eigen::Quaternionf quat;
    quat = Eigen::AngleAxisf(roll, Eigen::Vector3f::UnitX()) *
           Eigen::AngleAxisf(pitch, Eigen::Vector3f::UnitY()) *
           Eigen::AngleAxisf(yaw, Eigen::Vector3f::UnitZ());

    calQuaternion[0] = quat.x();
    calQuaternion[1] = quat.y();
    calQuaternion[2] = quat.z();
    calQuaternion[3] = quat.w();

    return calQuaternion;
}

这里做了一个小 check,用calEulerToQuaternioncalQuaternionToEuler两个函数验证了转化的正确性

stanley 控制器实现

整体框架和 pure-pursuit 控制器很像,控制算法依然在 poseCallback 回调函数中实现

在参考路径回调函数 pointCallback 中添加参考航向角信息

void pointCallback(const nav_msgs::Path &msg)
{
    // 避免参考点重复赋值
    if (pointNum != 0)
    {
        return;
    }

    // geometry_msgs/PoseStamped[] poses
    pointNum = msg.poses.size();

    // 提前开辟内存
    r_x_.resize(pointNum);
    r_y_.resize(pointNum);
    r_phi_.resize(pointNum);
    for (int i = 0; i < pointNum; i++)
    {
        r_x_[i] = msg.poses[i].pose.position.x;
        r_y_[i] = msg.poses[i].pose.position.y;
        array<float, 3> rpy = calQuaternionToEuler(msg.poses[i].pose.orientation.x, msg.poses[i].pose.orientation.y,
                                                   msg.poses[i].pose.orientation.z, msg.poses[i].pose.orientation.w);
        r_phi_[i] = rpy[2];
        // cout << r_x_[i] << "\t" << r_y_[i] << "\t" << r_phi_[i] << endl;
    }
}

C++ 中 stanley 算法的实现和 MATLAB 中很像

%==============================================================
% Calculate outputs
%==============================================================
function sys = mdlOutputs(t,x,u)
    global U; %store chi_tilde=[vel-vel_ref; delta - delta_ref]
    global cx;
    global cy;
    global phi_ref;
    pi = 3.1415926;
    
    tic
    fprintf('Update start, t=%6.3f\n',t);
    x = u(1);
    y = u(2);
    yaw_angle =u(3)*pi/180;%CarSim输出的Yaw angle为角度,角度转换为弧度
    v = u(4) / 3.6;

    gain_k = 5;
    L = 2.7;                                % [m] wheel base of vehicle
    
    N =  length(cx);
    Distance = zeros(N,1);
    x = x + L * cos(yaw_angle);
    y = y + L * sin(yaw_angle);
    for i = 1:N
        Distance(i) =  sqrt((cx(i)-x)^2 + (cy(i)-y)^2);
    end   
    %ind是最近点索引
    [value, location]= min(Distance);
    ind = location;
    %error是横向误差,根据实际位置与参考点的y值判断
    if y < cy(ind)
        error = value;
    else
        error = -value;
    end
    
    alpha = phi_ref(ind) - yaw_angle;
    
    if v < 1
       v = 1; 
    end
    theta = atan(gain_k * error/v);    
    delta = alpha + theta;
%     delta = delta * 180 / pi;
    U = delta;
    sys= U; % vel, steering, x, y
    toc
% End of mdlOutputs.
// 计算发送给模型车的转角
void poseCallback(const geometry_msgs::PoseStamped &currentWaypoint)
{
    // 获取当前位姿
    auto currentPositionX = currentWaypoint.pose.position.x;
    auto currentPositionY = currentWaypoint.pose.position.y;
    auto currentPositionZ = 0.0;
    auto currentQuaternionX = currentWaypoint.pose.orientation.x;
    auto currentQuaternionY = currentWaypoint.pose.orientation.y;
    auto currentQuaternionZ = currentWaypoint.pose.orientation.z;
    auto currentQuaternionW = currentWaypoint.pose.orientation.w;
    std::array<float, 3> calRPY =
        calQuaternionToEuler(currentQuaternionX, currentQuaternionY,
                             currentQuaternionZ, currentQuaternionW);
    float currentYaw = calRPY[2];
    cout << currentPositionX << "\t" << currentPositionY << "\t" << currentYaw << endl;

    // 计算前轴中心位置
    float front_x = currentPositionX + Ld * cos(currentYaw);
    float front_y = currentPositionY + Ld * sin(currentYaw);
    // 计算最近点距离及索引
    float cur_distance = 0.0;
    float min_distance = 1e9;
    for (int i = 0; i < pointNum; ++i)
    {
        cur_distance = sqrt(pow(r_x_[i] - front_x, 2) + pow(r_y_[i] - front_y, 2));
        if (cur_distance < min_distance)
        {
            min_distance = cur_distance;
            targetIndex = i;
        }
    }

    // 到达终点后结束控制,当两次以终点为目标点时结束
    if (targetIndex == pointNum - 1)
    {
        ++final_cnt;
        if (final_cnt >= 2)
        {
            final_cnt = 2;
        }
    }

    // 计算横向误差,根据实际位置与参考点Y值判断
    float lateral_error = 0.0;
    if (currentPositionY < r_y_[targetIndex])
    {
        lateral_error = min_distance;
    }
    else
    {
        lateral_error = -min_distance;
    }

    // 计算航向偏差角和横向偏差角
    float alpha = r_phi_[targetIndex] - currentYaw;
    float theta = atan(Gain_K * lateral_error / currentVelocity);

    // cout << "targetIndex: " << targetIndex << endl;
    // cout << "ref_phi: " << r_phi_[targetIndex] << "\tcurrentYaw: " << currentYaw << endl;
    // cout << "currentVelocity: " << currentVelocity << "\tlateral_error: " << lateral_error << endl;
    // cout << "alpha: " << alpha << "\ttheta: " << theta << endl;
    // cout << "---------" << endl;

    // 发布小车运动指令及运动轨迹
    geometry_msgs::Twist vel_msg;
    vel_msg.linear.z = 1.0;
    if (targetIndex == pointNum - 1 && final_cnt >= 2)
    {
        vel_msg.linear.x = 0;
        vel_msg.angular.z = 0;
        stanley_ctrl.publish(vel_msg);
    }
    else
    {
        float delta = alpha + theta;
        vel_msg.linear.x = 0.5;
        vel_msg.angular.z = delta;
        stanley_ctrl.publish(vel_msg);
        // 发布小车运动轨迹
        geometry_msgs::PoseStamped this_pose_stamped;
        this_pose_stamped.header.frame_id = "world";
        this_pose_stamped.header.stamp = ros::Time::now();
        this_pose_stamped.pose.position.x = currentPositionX;
        this_pose_stamped.pose.position.y = currentPositionY;
        this_pose_stamped.pose.orientation.x = currentQuaternionX;
        this_pose_stamped.pose.orientation.y = currentQuaternionY;
        this_pose_stamped.pose.orientation.z = currentQuaternionZ;
        this_pose_stamped.pose.orientation.w = currentQuaternionW;
        path.poses.push_back(this_pose_stamped);
    }
    actual_path.publish(path);
}

💡 添加对终点的处理,当第二次以终点为目标点时结束控制

跟踪效果分析

纵向速度 0.5m/s,增益参数 Gain_K = 2.0 时在开始跟踪后片刻立即跑飞

在这里插入图片描述

增益参数调整为 1.0 后勉强可以跟踪上参考轨迹

在这里插入图片描述

但横向跟踪误差和横摆角误差都很大,最大横向跟踪误差 0.4694m,最大横摆角误差 0.2868 rad,都是比较大的误差

💡 感觉这里误差均值的意义不大,因为正误差与负误差会抵消,还需结合论文判断

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

根据Stanley 轨迹跟踪算法研究(2)中 CarSim+Simulink 联合仿真结果可知:相同增益参数 Gain_K,速度越低越不稳定,前轮转角会存在越多的抖动;相同速度下,增益参数越大越不稳定,前轮转角会存在越多的抖动

考虑移动机器人纵向速度仅为 0.5m/s,速度较低,因此增益参数Gain_K应取小值,同时依然存在 Gazebo 关节控制器 PID 参数调教的问题

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/148131.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

冯诺依曼体系和操作系统简单介绍

冯诺依曼体系和操作系统简单介绍 冯诺依曼体系 输入设备&#xff1a;键盘&#xff0c;话筒&#xff0c;摄像头&#xff0c;usb&#xff0c;鼠标&#xff0c;磁盘/ssd&#xff0c;网卡等等输出设备&#xff1a;显示器&#xff0c;喇叭&#xff0c;打印机&#xff0c;磁盘&#…

Path Aggregation Network for Instance Segmentation(2018.9)

文章目录 Abstract1. IntroductionOur FindingsOur Contributions 3. Framework3.1. Bottom-up Path AugmentationMotivationAugmented Bottom-up Structure 3.2. Adaptive Feature PoolingMotivationAdaptive Feature Pooling Structure 3.3. Fully-connected FusionMask Pred…

如何从 iCloud 恢复永久删除的照片?答案在这里!

在数字时代&#xff0c;丢失珍贵的照片可能会令人痛苦。然而&#xff0c;了解如何从 iCloud 恢复永久删除的照片可以带来一线希望。无论是意外删除还是技术故障&#xff0c;本指南都提供了 2023 年的最新方法来找回您的珍贵记忆。发现分步解决方案并轻松重新访问您的照片库。不…

Linux Ubuntu系统中添加磁盘

在学习与训练linux系统的磁盘概念、文件系统等&#xff0c;需要增加磁盘、扩展现有磁盘容量等&#xff0c;对于如何添加新的磁盘&#xff0c;我们在“Linux centos系统中添加磁盘”中对centos7/8版本中如何添加、查看、删除等&#xff0c;作了介绍&#xff0c;而对Ubuntu版本中…

css技巧分享(优惠券缺角样式实现)

主要知识点&#xff1a;radial-gradient radial-gradient() CSS 函数创建一个图像&#xff0c;该图像由从原点辐射的两种或多种颜色之间的渐进过渡组成。它的形状可以是圆形或椭圆形。函数的结果是 数据类型的对象。这是一种特别的 。 .coupon{width: 190rpx;height: 194rpx;b…

腾讯滑块验证

不在同一起跑线&#xff0c;力所能及尽力就好。 之前的文章里介绍腾讯系列点选类型的验证&#xff0c;然后的话也是有时间去看了无感验证跟这个滑块验证&#xff0c;就放在一起来说说吧&#xff0c;之前的文章在这&#xff1a;TX验证码_逆向学习之旅的博客-CSDN博客 这个tdc_pa…

《使用EasyExcel在Excel中增加序号列的方法》

《使用EasyExcel在Excel中增加序号列的方法》 1、简介2、正文3、核心代码4、使用方法5、效果 1、简介 在处理Excel文件时&#xff0c;有时候需要为表格增加序号列。本文介绍了如何使用Java代码实现在Excel中增加序号列的功能&#xff0c;并提供了一个示例代码。 2、正文 在处理…

ping: www.baidu.com: Name or service not known解决办法

解决服务器无法ping通外网问题 1、问题描述&#xff1a; 配置了网卡信息&#xff0c;发现还是无法访问外网&#xff0c;并报ping: www.baidu.com: Name or service not known信息 2、问题原因&#xff1a; 这就是外网没开通好 3、解决方法&#xff1a; 修改网卡文件&#xff…

在qt的设计师界面没有QVTKOpenGLWidget这个类,只有QOpenGLWidget,那么我们如何得到QVTKOpenGLWidget呢?

文章目录 前言不过,时过境迁,QVTKOpenGLWidget用的越来越少,官方推荐使用qvtkopengnativewidget代替QVTKOpenGLWidget 前言 在qt的设计师界面没有QVTKOpenGLWidget这个类,只有QOpenGLWidget,我们要使用QVTKOpenGLWidget,那么我们如何得到QVTKOpenGLWidget呢? 不过,时过境迁,Q…

Vue中的watch的使用

先看下Vue运行机制图 那么我们思考一件事&#xff0c;vue是通过watcher监听数据的变化然后给发布-订阅&#xff0c;这样实现了dom的渲染&#xff0c;那么我们思考一件事&#xff0c;我们往往需要知道一个数据的变化然后给页面相应的渲染&#xff0c;那么我们工作中在组件中的数…

【nlp】2.3 LSTM模型

LSTM模型 1 LSTM介绍2 LSTM的内部结构图2.1 LSTM结构分析2.2 Bi-LSTM介绍2.3 使用Pytorch构建LSTM模型2.4 LSTM优缺点1 LSTM介绍 LSTM(Long Short-Term Memory)也称长短时记忆结构, 它是传统RNN的变体,与经典RNN相比能够有效捕捉长序列之间的语义关联,缓解梯度消失或爆炸…

Windows10下Docker安装Mysql5.7

文章目录 Windows10下Docker安装Mysql5.7环境说明打开命令工具搜索镜像拉取镜像查看所有镜像启动镜像查看容器查看所有容器查看运行中容器 进入容器进入容器命令输入账号命令输入密码 添加mysql的远程账号创建一个数据库 Windows10下Docker安装Mysql5.7 环境说明 docker&…

几款数据备份软件调研与使用

目的 为确保企业数据安全、避免被非法入侵、数据勒索、破坏业务连续性、及时对重要数据、业务数据、程序、进行备份做到有备无患。遇到突发事件可使用备份数据快速恢复。保障系统正常运行 Filezilla工具介绍&#xff1a; FileZilla是一个免费开源的FTP软件&#xff0c;分为客户…

超级账本区块链Fabric2.4.4版本搭建过程(完整过程)

前提环境:乌班图20.04环境 安装所需要的工具 先配置一下代理源为阿里云代理&#xff1a; sudo apt-get update 更新源 sudo apt-get install ssh 安装远程客户端 sudo apt-get install curl 安装命令行工具 sudo apt-get install git 安装git sudo apt-get install gcc 安装…

BUUCTF easyre 1

BUUCTF:https://buuoj.cn/challenges 题目描述&#xff1a; 下载附件&#xff0c;解压得到一个.exe文件。 密文&#xff1a; 解题思路&#xff1a; 1、使用IDA pro打开exe文件&#xff0c;在反汇编窗口&#xff08;IDA View-A&#xff09;&#xff0c;直接找到flag。 也可以…

KODExplorer中ace.js代码编辑器中自定义PHP提示片段

目录 KODExplorerace.js参考 KODExplorer 这是搭建云盘工具&#xff0c;该工具可以作为在线开发工具使用&#xff0c;其中使用了ace.js作为编辑器&#xff0c;这里主要讲解ace.js编辑器中如何自定义代码提示下载旧版本&#xff0c;再升级到新版本&#xff0c;直接下载新版本没…

解决 requests-2.17.3 依赖 chardet 库版本不匹配的问题

问题背景 在使用 requests-2.17.3 版本时&#xff0c;我遇到了一个异常&#xff1a;“Requests dependency ‘chardet’ must be version > 3.0.2, < 3.1.0”。我尝试运行了以下命令来修复问题&#xff0c;但仍然无法解决&#xff1a; pip install -U chardet > 3.0…

【Maven】基础快速入门

文章目录 1、Maven概述1.1、Maven是什么1.2、Maven的作用 2、下载安装Maven2.1、新版下载2.2、旧版下载2.3、安装2.4、配置环境变量2.5、配置阿里云镜像2.6、配置本地仓库 3、Maven基础概念3.1、坐标 4、Maven依赖管理4.1、依赖配置与依赖传递4.1.1、依赖传递冲突4.1.2、可选依…

CH12_处理继承关系

函数上移&#xff08;Pull Up Method&#xff09; 反向重构&#xff1a;函数下移&#xff08;Push Down Method&#xff09; class Employee {/*...*/} class Salesman extends Employee {get name() {/*...*/} } class Engineer extends Employee {get name() {/*...*/} }cla…

软件性能测试学习笔记(LoadRunner):从零开始

文章目录 概述LoadRunner的使用创建编辑脚本&#xff08;Virtual User Generator&#xff09;集合点思考时间事务检查点关联参数化 运行负载测试&#xff08;Controller&#xff09; 性能测试报告场景设置表格测试指标记录表 其他的杂谈内容 概述 软件的性能测试与软件的功能测…