差动驱动机器人轨迹-CoCube

news2024/12/23 0:12:53

轨迹博客:

玫瑰线轨迹如何规划?(desmos+ROS2+turtlesim+……)

ROS1云课→23turtlesim绘制小结(数学和编程)


如上所涉及的机器人假定模型都是差动驱动机器人。

许多移动机器人使用一种称为差动驱动的驱动机构。它由安装在公共轴上的两个驱动轮组成,每个轮可以独立地向前或向后驱动。

图1:差动驱动运动学-Dudek和Jenkin《移动机器人的计算原理》

机器人旋转的点被称为ICC -瞬时曲率中心

通过改变两个轮子的速度,可以改变机器人的轨迹。

这类驱动器有三个有趣的例子:

  1. 直线运动,左右轮速度一样
  2. 绕轮轴的中点旋转,也就是原地旋转,左右轮速度大小一样,方向相反。
  3. 绕某一轮为中心旋转,左轮或右轮只有一轮有速度

注意,差动驱动机器人不能沿着轴的方向移动——这是一个奇点

曲线上存在不可导、不连续、根本没有定义的点,这些点就叫做奇点。基本上来说求导就可以瑕点是函数趋于无穷的点;奇点是函数未定的点。比如间断点,无定义点。奇点包含瑕点。

机器人系统数学建模(现代控制理论1)

图2 差动机器人数学建模

差速驱动车辆对每个车轮速度的微小变化非常敏感。轮子之间相对速度的差异会影响机器人的轨迹。它们对地平面的微小变化也非常敏感,可能需要额外的轮子(脚轮)来支撑。

凹凸不平的地面会影响轨迹精度。

差动驱动机器人的正向运动学

在图1中,假设机器人在某个位置(x,y),朝向与X轴成θ角的方向。假设机器人的中心位于轮轴的中点。通过操纵控制参数Vl、Vr,可以使机器人移动到不同的位置和方向。(注:Vl,Vr)为车轮沿地面的速度)。

图3:差动机器人的正向运动学 

移动机器人的逆运动学

如何控制机器人达到给定的位置(x,y,θ)——这就是所谓的逆运动学问题。

不幸的是,差动驱动机器人在建立其位置时符合所谓的非完整约束。例如,机器人不能沿着它的轴横向移动。类似的非完整约束是汽车只能转动前轮。它不能直接侧向移动,因为平行泊车(侧方位停车)需要更复杂的转向操作。因此,不能简单地指定一个任意的机器人姿态(x,y,θ)并找到控制机器人所需要的速度。

这激活了机器人沿直线移动,然后原地旋转一圈,然后再次直线移动的策略,作为差动驱动机器人的导航策略。

直线运动轨迹:

圆周运动轨迹: 

未闭合

 闭合后,但控制为开环,也就是没有反馈控制。

正方形运动轨迹(非连续控制): 

 可参考官方示例代码:

#include <rclcpp/rclcpp.hpp>
#include <turtlesim/msg/pose.hpp>
#include <geometry_msgs/msg/twist.hpp>
#include <std_srvs/srv/empty.hpp>
#include <math.h>

turtlesim::msg::Pose::SharedPtr g_pose;
turtlesim::msg::Pose g_goal;

enum State
{
  FORWARD,
  STOP_FORWARD,
  TURN,
  STOP_TURN,
};

State g_state = FORWARD;
State g_last_state = FORWARD;
bool g_first_goal_set = false;

#define PI 3.141592

void poseCallback(const turtlesim::msg::Pose::SharedPtr pose)
{
  g_pose = pose;
}

bool hasReachedGoal()
{
  return fabsf(g_pose->x - g_goal.x) < 0.01 && fabsf(g_pose->y - g_goal.y) < 0.01 && fabsf(g_pose->theta - g_goal.theta) < 0.002;
}

bool hasStopped()
{
  return g_pose->angular_velocity < 0.0001 && g_pose->linear_velocity < 0.0001;
}

void printGoal()
{
  RCLCPP_INFO(rclcpp::get_logger("draw_square"), "New goal [%f %f, %f]", g_goal.x, g_goal.y, g_goal.theta);
}

void commandTurtle(rclcpp::Publisher<geometry_msgs::msg::Twist>::SharedPtr twist_pub, float linear, float angular)
{
  geometry_msgs::msg::Twist twist;
  twist.linear.x = linear;
  twist.angular.z = angular;
  twist_pub->publish(twist);
}

void stopForward(rclcpp::Publisher<geometry_msgs::msg::Twist>::SharedPtr twist_pub)
{
  if (hasStopped())
  {
    RCLCPP_INFO(rclcpp::get_logger("draw_square"), "Reached goal");
    g_state = TURN;
    g_goal.x = g_pose->x;
    g_goal.y = g_pose->y;
    g_goal.theta = fmod(g_pose->theta + static_cast<float>(PI) / 2.0f, 2.0f * static_cast<float>(PI));
    // wrap g_goal.theta to [-pi, pi)
    if (g_goal.theta >= static_cast<float>(PI)) g_goal.theta -= 2.0f * static_cast<float>(PI);
    printGoal();
  }
  else
  {
    commandTurtle(twist_pub, 0, 0);
  }
}

void stopTurn(rclcpp::Publisher<geometry_msgs::msg::Twist>::SharedPtr twist_pub)
{
  if (hasStopped())
  {
    RCLCPP_INFO(rclcpp::get_logger("draw_square"), "Reached goal");
    g_state = FORWARD;
    g_goal.x = cos(g_pose->theta) * 4 + g_pose->x;
    g_goal.y = sin(g_pose->theta) * 4 + g_pose->y;
    g_goal.theta = g_pose->theta;
    printGoal();
  }
  else
  {
    commandTurtle(twist_pub, 0, 0);
  }
}


void forward(rclcpp::Publisher<geometry_msgs::msg::Twist>::SharedPtr twist_pub)
{
  if (hasReachedGoal())
  {
    g_state = STOP_FORWARD;
    commandTurtle(twist_pub, 0, 0);
  }
  else
  {
    commandTurtle(twist_pub, 1.0f, 0);
  }
}

void turn(rclcpp::Publisher<geometry_msgs::msg::Twist>::SharedPtr twist_pub)
{
  if (hasReachedGoal())
  {
    g_state = STOP_TURN;
    commandTurtle(twist_pub, 0, 0);
  }
  else
  {
    commandTurtle(twist_pub, 0, 0.1f);
  }
}

void timerCallback(rclcpp::Publisher<geometry_msgs::msg::Twist>::SharedPtr twist_pub)
{
  if (!g_pose)
  {
    return;
  }

  if (!g_first_goal_set)
  {
    g_first_goal_set = true;
    g_state = FORWARD;
    g_goal.x = cos(g_pose->theta) * 4 + g_pose->x;
    g_goal.y = sin(g_pose->theta) * 4 + g_pose->y;
    g_goal.theta = g_pose->theta;
    printGoal();
  }

  if (g_state == FORWARD)
  {
    forward(twist_pub);
  }
  else if (g_state == STOP_FORWARD)
  {
    stopForward(twist_pub);
  }
  else if (g_state == TURN)
  {
    turn(twist_pub);
  }
  else if (g_state == STOP_TURN)
  {
    stopTurn(twist_pub);
  }
}

int main(int argc, char** argv)
{
  rclcpp::init(argc, argv);
  auto nh = rclcpp::Node::make_shared("draw_square");
  auto pose_sub = nh->create_subscription<turtlesim::msg::Pose>("turtle1/pose", 1, std::bind(poseCallback, std::placeholders::_1));
  auto twist_pub = nh->create_publisher<geometry_msgs::msg::Twist>("turtle1/cmd_vel", 1);
  auto reset = nh->create_client<std_srvs::srv::Empty>("reset");
  auto timer = nh->create_wall_timer(std::chrono::milliseconds(16), [twist_pub](){timerCallback(twist_pub);});

  auto empty = std::make_shared<std_srvs::srv::Empty::Request>();
  reset->async_send_request(empty);

  rclcpp::spin(nh);
}

开环控制,重复精度不高,效果如下:

 

思考题:

如何绘制如下曲线,选择一款绘制即可。

 


-Fin-


 

 

 

 

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

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

相关文章

vue项目图片裁剪上传——vue-cropper的使用,裁剪后上传头像

vue项目图片裁剪上传——vue-cropper的使用&#xff0c;裁剪后上传头像 npm地址&#xff1a;https://www.npmjs.com/package/vue-cropper github地址&#xff1a;https://github.com/xyxiao001/vue-cropper 在线demo&#xff1a;http://github.xyxiao.cn/vue-cropper/exampl…

学习分享:如何进行全局变量的学习

​对于很多朋友&#xff0c;尤其是刚接触全局变量的朋友而言&#xff0c;全局变量的学习对他们来说不是一件容易的事情。关于这方面的学习&#xff0c;很多朋友不太理解它的用法及分析方法&#xff0c;所以会比较乱&#xff0c;难以掌握。 什么是axure全局变量&#xff1f;全局…

【STM32】详解PWM的概念和原理

PWM的概念和原理一、PWM是什么&#xff1f;二、如何实现&#xff1f;三、STM32中的PWM四、使用库函数配置PWM将LED0设置为呼吸灯一、PWM是什么&#xff1f; PWM&#xff08;Pulse width modulation&#xff09;脉冲宽度调制。PWM是通过编程控制输出方波的频率和占空比&#xf…

oracle自启动的p***并行进程过多导致的process进程超限问题

某项目现场反馈无任何业务连接&#xff0c;查询v$process仍有500多个进程&#xff1b; 查询会话连接&#xff0c;也只有十几个会话&#xff1b; select b.MACHINE, b.PROGRAM,b.USERNAME, count(*) from v$process a, v$session b where a.ADDR b.PADDR and b.USERNAME is…

RCNN学习笔记-ResNeXt

论文地址&#xff1a;https://arxiv.org/pdf/1611.05431.pdf Abstract 我们提出了一种简单、高度模块化的图像分类网络体系结构。我们的网络是通过重复一个构建块来构建的&#xff0c;该构建块聚合了一组具有相同拓扑的变换。我们的简单设计产生了一个只有几个超参数可设置的…

JavaScript -- 数组常用方法及示例代码总结

文章目录数组的方法1 Array.isArray()2 at()3 concat()4 indexOf()5 lastIndexOf()6 join()7 slice()8 push()9 pop()10 unshift()11 shift()12 splice()13 reverse()14 sort()15 forEach()16 filter()17 map()18 reduce()数组的方法 Array参考文档&#xff1a;https://develo…

AIoT通用组件服务攻略之设备“收纳”好帮手——分组管理

天翼物联网平台&#xff08;AIoT&#xff09;通用组件服务提供设备分组管理功能&#xff0c;既可将单个产品下的部分设备划成一个分组&#xff0c;也可将多个产品下的设备划成一个统一分组&#xff0c;主要用于对设备进行归类管理&#xff0c;便于对设备进行批量操作。 对设备…

Java中高级核心知识全面解析——类加载过程

一个非数组类的加载阶段&#xff08;加载阶段获取类的二进制字节流的动作&#xff09;是可控性最强的阶段&#xff0c;这一步我们可以去完成还可以自定义类加载器去控制字节流的获取方式&#xff08;重写一个类加载器的 loadClass() 方法&#xff09;。数组类型不通过类加载器创…

适用于嵌入式单片机的差分升级通用库+详细教程

1. 什么是差分/增量升级&#xff1f; 借用网上的介绍&#xff1a;适合嵌入式的差分升级又叫增量升级&#xff0c;顾名思义就是通过差分算法将源版本与目标版本之间差异的部分提取出来制作成差分包&#xff0c;然后在设备通过还原算法将差异部分在源版本上进行还原从而升级成目…

Jekyll如何自定义摘要

最近搭建博客网站的时候遇到一个问题&#xff1a;博客的摘要包含了内容的格式&#xff0c;比如下面这张图。 标题的样式显示在摘要中&#xff0c;这可太奇怪了。我在查找文档之后没有想到合适的解决方案&#xff0c;于是乎就去 Jekyll 的项目下面提了个 Issue 问了一下。 在…

js原生实现步骤条

实现思路: 1.定義一個流程數組和一个步骤状态 2.遍历这个流程数组&#xff0c;如果步骤状态大于流程&#xff0c;checked&#xff1d;true&#xff0c; 3.页面输出遍历的流程数组&#xff0c;checked的div点亮 最终效果 <!DOCTYPE html> <html> <title>js原…

音视频开发:直播推流技术指南

一、推流架构 推流SDK客户端的模块主要有三个&#xff0c;推流采集端、队列控制模块、推流端。其中每个模块的主要流程如下&#xff0c;本文的主要目的就是拆分推流流程。 1.1 采集端 视频采集&#xff1a;通过Camera采集视频。 音频采集&#xff1a;通过麦克风采集音频。 …

HTML做一个节日页面【六一儿童节】纯HTML代码

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

「动态规划学习心得」正则表达式匹配

正则表达式匹配 给你一个字符串 s 和一个字符规律 p&#xff0c;请你来实现一个支持 ‘.’ 和 ‘*’ 的正则表达式匹配。 ‘.’ 匹配任意单个字符‘*’ 匹配零个或多个前面的那一个元素 所谓匹配&#xff0c;是要涵盖 整个 字符串 s的&#xff0c;而不是部分字符串。 输入&…

客户管理系统中的常用术语都有哪些 (下)

CRM客户管理系统概念问世的二十多年来&#xff0c;曾帮助过无数企业打造优质的客户关系&#xff0c;带来显著的业绩增长。为了让您有更好的理解&#xff0c;小编把CRM常用术语进行了汇总&#xff0c;希望能够帮助到正在了解CRM的您。 销售方法及营销理念 LTC (Leads To Cash)…

超细节的javaWeb知识点总结

文章目录Servlet系统架构C/S架构B/S架构C/S和B/S结构的系统&#xff0c;哪个好&#xff0c;哪个不好&#xff1f;JavaEE是什么&#xff1f;B/S结构的系统通信原理&#xff08;没有涉及到Java小程序&#xff09;WEB系统的访问过程关于域名&#xff1a;IP地址是啥&#xff1f;端口…

搜索与图论-树与图的广度优先遍历

文章目录一、树与图的广度优先遍历1. 构建2. 遍历3. 具体实现详见例题——图中点的层次二、树与图的广度优先遍历例题——图中点的层次具体实现&#xff08;一&#xff09;1. 样例演示2. 实现思路3. 代码注解4. 实现代码具体实现&#xff08;二&#xff09;1. 代码注解2. 实现代…

JupyterLab | 这几款插件推荐给天天使用JupyterLab的你!~

1写在前面 最近用了用JupyterLab&#xff0c;总体来说体验还是不错的&#xff0c;代码写完就是一篇完整的Paper了&#xff0c;非常给力。&#x1f973; 不过单纯使用JupyterLab可能还是有一些不尽人意的地方&#xff0c;这些问题基本都可以通过添加插件来弥补&#xff0c;今天就…

内存可见性问题

目录 1.什么是内存可见性问题 2.内存可见性问题是怎么发生的 3.解决方法&#xff1a;volatile 4.volatile使用的注意事项 5.内存可见性问题的延伸 缓存&#xff08;cache&#xff09; 1.什么是内存可见性问题 首先来看一段代码 class Counter{public int flag 0; } pu…

docker部署redis集群 删除节点(缩容)

上篇博文完成了redis集群的搭建&#xff1a;点这里 以及redis集群的添加节点 即扩容&#xff1a;点这里 本篇博文写一下怎样在redis集群中删除节点&#xff08;还是在之前博文的基础上&#xff09;&#xff0c;博文中的111.111.111.111均换成实际IP使用 删除从节点 我这里想…