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

news2024/11/16 23:38:06

全方位移动机器人 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 参数调教的问题

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C++ std::max_element std::min_element std::minmax_element】

一 、std::max_element 寻找范围 [first, last) 中的最大元素。 (1) 用 operator< 比较元素。 (3) 用给定的二元比较函数 comp 比较元素。 (2),(4) 同 (1,3) &#xff0c;但按照 policy 执行。这些重载仅若 std::is_execution_policy_v<std::decay_t > (C20 前)std:…

Alter database open fails with ORA-00600 kcratr_nab_less_than_odr

Alter database open fails with ORA-00600 kcratr_nab_less_than_odr (Doc ID 1296264.1)​编辑To Bottom APPLIES TO: Oracle Database - Enterprise Edition - Version 11.2.0.1 to 11.2.0.1 [Release 11.2] Oracle Database - Enterprise Edition - Version 12.1.0.1 to …

【精彩回顾】 用sCrypt在Bitcoin上构建智能合约(1)

2023年3月24日&#xff0c;sCrypt在英国Exeter大学举办了关于智能合约的大学讲学。sCrypt首席执行官刘晓晖做了题为“用sCrypt在Bitcoin上构建智能合约”的演讲&#xff0c;并与到场的老师、学生进行了深入交流、互动。这次课程着重讲解了 BSV 智能合约的基础概念&#xff0c;以…

Java学习笔记(七)——面向对象编程(中级)

一、IDEA &#xff08;一&#xff09;常用的快捷键 &#xff08;二&#xff09;模版/自定义模版 二、包 &#xff08;一&#xff09;包的命名 &#xff08;二&#xff09;常用的包 &#xff08;三&#xff09;如何引入&#xff08;导入&#xff09;包 &#xff08;四&am…

挑战字节软件测试岗,原来这么轻松...

当前就业环境&#xff0c;裁员、失业消息满天飞&#xff0c;好像有一份工作就不错了&#xff0c;更别说高薪了。其实这只是一方面&#xff0c;而另一方面&#xff0c;各大企业依然求贤若渴&#xff0c;高技术人才依然紧缺&#xff0c;只要你技术过硬&#xff0c;拿个年薪50w不是…

算法萌新闯力扣:同构字符串

力扣题&#xff1a;同构字符串 开篇 对于字符串相关的题目&#xff0c;哈希表经常会使用到&#xff0c;这道题更是如此&#xff0c;还用到了两个哈希表。拿下它&#xff0c;你对字符串题目的理解就会更上一层楼。 题目链接:205.同构字符串 题目描述 代码思路 看完题目后&a…

《Linux从练气到飞升》No.28 Linux中的线程同步

&#x1f57a;作者&#xff1a; 主页 我的专栏C语言从0到1探秘C数据结构从0到1探秘Linux菜鸟刷题集 &#x1f618;欢迎关注&#xff1a;&#x1f44d;点赞&#x1f64c;收藏✍️留言 &#x1f3c7;码字不易&#xff0c;你的&#x1f44d;点赞&#x1f64c;收藏❤️关注对我真的…

mfc140u.dll丢失怎么修复?4种亲测有效的方法分享

在计算机使用过程中&#xff0c;我们可能会遇到各种各样的问题&#xff0c;其中之一就是某些重要的dll文件丢失。DLL文件是动态链接库文件&#xff0c;它们包含了许多程序运行所需的函数和资源。当这些文件丢失或损坏时&#xff0c;可能会导致程序无法正常运行。本文将详细介绍…

【Python】operator模块

Python中operator模块提供了一套与 Python 的内置运算符对应的高效率函数。 不仅对应内置运算符&#xff0c;还可以获取方法。可优化涉及回调函数的运算性能&#xff0c;比lambda、Python函数的开销小、速度快。 import operator[x for x in dir(operator) if not x.startswi…

ucrtbase.dll缺失的解决方法,全面分析ucrtbase.dll丢失是什么原因

ucrtbase.dll是Windows操作系统中的一个动态链接库文件&#xff0c;它包含了许多常用的函数和资源&#xff0c;用于支持应用程序的正常运行。当ucrtbase.dll丢失时&#xff0c;可能会导致以下问题&#xff1a; 1. 程序无法启动&#xff1a;当应用程序需要调用ucrtbase.dll中的…

局域网监控软件如何防止数据泄密

局域网监控软件在防止数据泄密方面扮演着重要的角色。以下是一些电脑监控软件可以采取的措施&#xff1a; 1、审计聊天内容&#xff1a;一些电脑监控软件可以审计通过聊天工具外发的所有内容&#xff0c;包括文字、图片、文件和视频等。这可以帮助企业及时发现和防止敏感数据的…

骨传导蓝牙耳机哪款好?这五款骨传导耳机闭眼入都不会错!

随着科技的发展&#xff0c;数码产品更新换代的速度也是越来越快&#xff0c;如今无线蓝牙耳机已经占据主流&#xff0c;特别是运动爱好者&#xff0c;很多人都会为自己挑选一款好用的运动耳机&#xff0c;而骨传导耳机异军突起&#xff0c;凭借听歌不入耳、佩戴舒适稳固等特性…

《Linux从练气到飞升》No.27 Linux中的线程互斥

&#x1f57a;作者&#xff1a; 主页 我的专栏C语言从0到1探秘C数据结构从0到1探秘Linux菜鸟刷题集 &#x1f618;欢迎关注&#xff1a;&#x1f44d;点赞&#x1f64c;收藏✍️留言 &#x1f3c7;码字不易&#xff0c;你的&#x1f44d;点赞&#x1f64c;收藏❤️关注对我真的…

springBoot 入门一 :创建springBoot项目

创建springBoot项目 配置maven 项目报错处理

MyBatis配置与映射文件深度解析

文章目录 MyBatis配置文件解析配置文件的组成部分配置数据源和数据库连接信息MyBatis的属性和类型别名 MyBatis映射文件详解映射文件的作用编写简单的映射文件resultMap和resultType的区别 结语 &#x1f388;个人主页&#xff1a;程序员 小侯 &#x1f390;CSDN新晋作者 &…

unity UGUI无限循环滚动居中

最近在做一个ui循环滚动的功能&#xff0c;网上找了半天脚本感觉都和我实际需求不太符合&#xff0c;自己花费一些时间完成了这个功能记录一下。下面开始正题 &#xff0c;我是采用unity自带组件Scroll View来完成&#xff0c;首先设置Scroll View如下图 面板层级结构如下 然…

zookeeper的安装部署

目录 简介 Zookeeper架构设计及原理 1.Zookeeper定义 2.Zookeeper的特点 3.Zookeeper的基本架构 4.Zookeeper的工作原理 5.Zookeeper的数据模型 &#xff08;1&#xff09;临时节点 &#xff08;2&#xff09;顺序节点 &#xff08;3&#xff09;观察机制 Zookeeper集…

ICC2/innovus merge gds

我正在「拾陆楼」和朋友们讨论有趣的话题&#xff0c;你⼀起来吧&#xff1f; 拾陆楼知识星球入口 calibre merge gds的方法示例参考往期文章: Calibre Merge GDS ICC2: write_gds -merge_files "std.gds sram.gds io.gds ip.gds ... ..." innovus: streamout -…

Postman接口Mock Servier服务器

近期在复习Postman的基础知识&#xff0c;在小破站上跟着百里老师系统复习了一遍&#xff0c;也做了一些笔记&#xff0c;希望可以给大家一点点启发。 应用场景&#xff1a;后端的接口还没有开发完成&#xff0c;前端的业务需要调用后端的接口&#xff0c;可以使用mock模拟。 一…

开发板上网详细教程

开发板上网详细教程 PC端操作开发板操作 写在前面 今天想配置开发板的boa服务器&#xff0c;需要下载sudo apt-get install bison flex&#xff0c;但是一直报错&#xff0c;就蒙蔽了&#xff0c;后来想想真不应该啊&#xff0c;电脑和开发板通信没问题&#xff0c;但也只是如此…