机器人精确移动包
move_near
之前有写过, 将ROS官方的move_basic包改写成了python形式, 同时将它写成了一个完整的action接口
最近测试时发现了问题, odom的数据波动可能会导致机器人陷入正反馈从而一直移动
具体表现为:
-
机器人移动精度设置为0.005 [m]
-
机器人在移动到接近0.005的位置, odom发生微小的跳变
本来distRemaining应该是从 1 降到 0.5, 降到 0.006, 然后小于0.005, 机器人停住, 但是里程计波动, 使得distRemaining变成-0.006, 此时机器人还要继续后退, 就会导致distRemaining持续增大, 机器人无法停止
修改
将计算机器人移动距离的distRemaining修改为累加制, 通过odom的逐差来减小odom的累进误差
结果
机器人移动精度可以达到0.0005 [m], 甚至还能降, 但是已经超出了需求, 如果odom更好, 应该能达到更好的效果
调用
# 填充需要前往的位置, 在本例中使用的是base_link, 让机器人相对自身运动
$ rostopic pub /move_near/goal move_base_msgs/MoveBaseActionGoal
注意事项
在机器人移动过程中没有避障! 没有避障! 这不是move_base的接口! 不会调用costmap, 无避障操作!
#! /usr/bin/env python3import rospy
import actionlib
from sensor_msgs.msg import Imu
from nav_msgs.msg import Odometry
from geometry_msgs.msg import Pose, Twist, PoseStamped
from move_base_msgs.msg import MoveBaseAction, MoveBaseGoalimport math
import tf2_ros
from tf.transformations import euler_from_quaternion, quaternion_from_eulerclass MoveNear(object):def __init__(self, name):self.now_imu = Imu()self.now_odom = Odometry()self.current_goal = PoseStamped()rospy.Subscriber("/imu", Imu, self.imu_cb)rospy.Subscriber("/odom", Odometry, self.odom_cb)self.cmd_pub = rospy.Publisher('/cmd_vel', Twist, queue_size=5)self.current_goal_pub = rospy.Publisher('current_goal', PoseStamped, queue_size=1)self.minAngularVelocity = rospy.get_param("~min_angular_velocity",0.02)self.maxAngularVelocity = rospy.get_param("~max_angular_velocity",0.2)self.angularAcceleration = rospy.get_param("~angular_acceleration",0.2)self.angularTolerance = rospy.get_param("~angularTolerance",0.01)self.minSpeedDistance = rospy.get_param("~minSpeedDistance", 0.03)self.minLinearVelocity = rospy.get_param("~min_linear_velocity",0.01)self.maxLinearVelocity = rospy.get_param("~max_linear_velocity",0.2)self.linearAcceleration = rospy.get_param("~linear_acceleration",0.2)self.linearTolerance = rospy.get_param("~linearTolerance",0.0005)self.minSpeedDistance = rospy.get_param("~minSpeedDistance",0.05)self._action_name = "move_near"self._as = actionlib.SimpleActionServer(self._action_name, MoveBaseAction, execute_cb=self.execute_cb, auto_start = False)self._as.start()self.initialPose = {'x':0.0, 'y':0.0, 'yaw':0.0}self.goalPose = {'x':0.0, 'y':0.0, 'yaw':0.0}self.oscillation = 0self.prevAngleRemaining = 0def imu_cb(self, msg):self.now_imu = msgdef odom_cb(self, msg):self.now_odom = msgdef normalizeAngle(self,angle):if angle < -math.pi:angle += 2* math.piif angle > math.pi:angle -= 2*math.pireturn angledef rad2deg(self,rad):return rad * 180 / math.pidef sign(self,n):if n < 0:return -1else:return 1def getCurrentYaw(self):orientation_list = [self.now_imu.orientation.x,self.now_imu.orientation.y,self.now_imu.orientation.z,self.now_imu.orientation.w](_,_,current_yaw) = euler_from_quaternion(orientation_list)return current_yawdef rotate(self,yaw):rospy.loginfo("Requested rotation: {} degrees".format(self.rad2deg(yaw)))r = rospy.Rate(20)initial_yaw = self.getCurrentYaw()done = Falsewhile(not done and not rospy.is_shutdown()):rotated_yaw = self.getCurrentYaw() - initial_yawangleRemaining = yaw - rotated_yawangleRemaining = self.normalizeAngle(angleRemaining)rospy.logdebug("angleRemaining: {} degrees".format(self.rad2deg(angleRemaining)))vel = Twist()speed = max(self.minAngularVelocity,min(self.maxAngularVelocity,math.sqrt(max(2.0 * self.angularAcceleration *(abs(angleRemaining) - self.angularTolerance),0))))if angleRemaining < 0:vel.angular.z = -speedelse:vel.angular.z = speedif (abs(angleRemaining) < self.angularTolerance):vel.angular.z = 0done = Truer.sleep()rotated_yaw = self.getCurrentYaw() - initial_yawangleRemaining = yaw - rotated_yawrospy.loginfo("Rotate finished! error: {} degrees".format(self.rad2deg(angleRemaining)))self.cmd_pub.publish(vel)r.sleep()return Truedef moveLinear(self,dist):done = Falser = rospy.Rate(20)initial_odom = self.now_odomdistRemaining = distwhile(not done and not rospy.is_shutdown()):travelledDist = math.hypot(self.now_odom.pose.pose.position.x - initial_odom.pose.pose.position.x,self.now_odom.pose.pose.position.y - initial_odom.pose.pose.position.y)# 保持了之前的命名, 在这里更新odom的值initial_odom = self.now_odomrospy.logdebug("travelledDist: {}".format(travelledDist))# for speed direction judgementif dist <= 0:distRemaining += travelledDistdist += travelledDistelse:distRemaining -= travelledDistdist -= travelledDistrospy.logdebug("distRemaining: {}".format(distRemaining))vel = Twist()speed = max(self.minLinearVelocity, min(self.maxLinearVelocity, 2.5* abs(distRemaining)))if abs(distRemaining) < self.linearTolerance:speed = 0done = Truerospy.loginfo("Linear movement finished! error: {} meters".format(distRemaining))rospy.loginfo("finished, breaking!")break# 在即将到达目的地时用最小速度跑, 提高精度if abs(distRemaining) < self.minSpeedDistance:rospy.loginfo_once("disRemaining is less than minSpeedDistance, slow down!")speed = self.minLinearVelocity# 这里可以控制机器人即使移动超过了距离, 则将速度反向# 避免之前移动越界导致的正反馈, 避免越走离目的地越远的行为if distRemaining < 0 :speed = -speedvel.linear.x = speedtry:self.cmd_pub.publish(vel)except Exception as e:rospy.logerr("Error while publishing: {}".format(e))r.sleep()return Truedef execute_cb(self, goal):success = Truebehind = Falseself.current_goal_pub.publish(goal.target_pose)orientation_list = [goal.target_pose.pose.orientation.x,goal.target_pose.pose.orientation.y,goal.target_pose.pose.orientation.z,goal.target_pose.pose.orientation.w](_,_,self.goalPose['yaw']) = euler_from_quaternion (orientation_list)face2goalYaw = math.atan2(goal.target_pose.pose.position.y, goal.target_pose.pose.position.x)# Check if the goal is behind the robotif face2goalYaw > math.pi/2 or face2goalYaw < -math.pi/2:behind = Trueface2goalYaw = self.normalizeAngle(face2goalYaw + math.pi)# face2goalYaw = self.normalizeAngle(face2goalYaw)# face to goalif self.rotate(face2goalYaw):passelse:rospy.logwarn("Trun to goal failed!")# Move to goaldist2goal = math.hypot(goal.target_pose.pose.position.x, goal.target_pose.pose.position.y)# if the goal is behind the robot, move backwardif behind:dist2goal = -dist2goalelse:dist2goal = dist2goalif self.moveLinear(dist2goal):passelse:success = Falserospy.logwarn("Move to goal failed!")# Turn to goal yawrelative_yaw = self.goalPose['yaw'] - face2goalYawrelative_yaw = self.normalizeAngle(relative_yaw)if self.rotate(relative_yaw):passelse:success = Falserospy.loginfo("Trun to goal failed!")if success:result = PoseStamped()rospy.loginfo('%s: Succeeded' % self._action_name)self._as.set_succeeded(result)else:rospy.logerr("CHECK MOVE_NEAR!!!!")if __name__ == '__main__':rospy.init_node('move_near')server = MoveNear(rospy.get_name())rospy.spin()
相关文章:
机器人精确移动包
move_near 之前有写过, 将ROS官方的move_basic包改写成了python形式, 同时将它写成了一个完整的action接口 最近测试时发现了问题, odom的数据波动可能会导致机器人陷入正反馈从而一直移动 具体表现为: 机器人移动精度设置为0.005 [m] 机器人在移动到接近0.005的位置, odom…...
强化学习环境报错解决
问题:nameerror: name ‘glpushmatrix‘ is not defined 解决:更换pyglet包的版本。pyglet2.0a4会报这个错误,把版本换成pyglet1.5.27即可。 问题:安装了gym和ale-py但是还是找不到模型,报错ModuleNotFoundError: No…...
Ganache本地测试网如何在远程环境中进行访问和操作
文章目录 前言1. 安装Ganache2. 安装cpolar3. 创建公网地址4. 公网访问连接5. 固定公网地址 前言 Ganache 是DApp的测试网络,提供图形化界面,log日志等;智能合约部署时需要连接测试网络。 Ganache 是一个运行在本地测试的网络,通过结合cpol…...
Kotlin中的函数分类(顶层、成员、局部、递归等)
在 Kotlin 中,函数可以按照不同的方式进行分类。在本篇博客中,我们将介绍以下几种常见的函数分类,并提供示例代码进行演示。 顶层函数: 顶层函数是指定义在文件中的函数,不依赖于任何类或对象。它们可以在文件的任何…...
字符串排序程序
字符串排序程序,对一个字符串中的数值进行从小到大的排序 例如排序前给定的字符串为" 20 78 9 -7 88 36 29" 排序后: -7 9 20 29 36 78 88 要求使用包装类对数值类型的字符串转换成整型进行排序。 public class StringSort {public static vo…...
功率放大器在材料测试中的应用有哪些
功率放大器在材料测试中有广泛的应用,尤其在材料的物理、电子和热学性质等方面的研究中起到了重要的作用。下面Aigtek安泰将详细介绍功率放大器在材料测试中的一些主要应用。 电学特性测试:功率放大器用于材料的电学特性测试,如电导率、介电常…...
汽车屏类产品(一):流媒体后视镜Camera Monitoring System (CMS)
前言: CMS,有叫电子侧视镜,虚拟倒车镜,电子倒车镜, 电子取代镜等,ISO 国际标准组织称其为摄像头监控系统。电子后视镜由“摄像头+屏幕”组成,汽车外后视镜经历了光学镜面从平面镜到曲面镜的迭代进步,CMS也实现从商用车到乘用车的过渡。显示模式为外部摄像头采集图像,…...
三元组(C++ 实现矩阵快速转置)
三元组稀疏矩阵是一种高效存储稀疏矩阵的方法。它通过记录矩阵中非零元素的行、列和值来表示一个稀疏矩阵。我们在三元组里存储的是每个元素的行、列以及值。 题目: 任意输入一个稀疏矩阵M,用三元组顺序表压缩存储该稀疏矩阵M,然后求其转置矩…...
Apriori(关联规则挖掘算法)
关联规则分析 事务库 上表所示的购物篮数据即是一个事务库,该事务库记录的是用户行为的数据。 事务 上表事务库中的每一条记录被称为一笔事务。在购物篮事务中,每一次购物行为即为一笔事务,例如第一行数据“用户1购买商品A,B,C”即为一条事…...
new Object()到底占用几个字节
Java内存模型 对象内存中可以分为三块区域:对象头(Header),实例数据(Instance Data)和对齐填充(Padding),以64位操作系统为例(未开启指针压缩的情况)Java对象布局 如下图所示: 其中对象头中的Mark Word中的详细信息在文章synchr…...
瞬态抑制二极管TVS的工作原理?|深圳比创达电子EMC(上)
TVS二极管具有响应速度快、漏电流小、钳位电压稳以及无寿命衰减的特性,从小到信号线静电防护,大到电力系统抗雷击浪涌,TVS都发挥着至关重要的作用。本章对瞬态抑制二极管TVS工作机理展开分析,供产品选型参考。接下来就跟着深圳比创…...
Nginx 同一端口 同时支持http与https 协议
文章目录 需求分析 需求 通过 nginx ,让同一端口 同时支持http与https 协议 分析 通过使用 Nginx,可以实现同一端口同时支持 HTTP 和 HTTPS 协议。下面是一种可能的配置方式: 配置 HTTP 服务 在 Nginx 配置文件中,添加以下配置…...
【Express】文件上传管理 multer 中间件
Multer是Node.js中用于处理文件上传的中间件。它可以帮助你处理文件上传的相关逻辑,如接收和保存上传的文件、限制文件大小、设置文件类型限制等。只能用于处理 multipart/form-data 类型的表单数据,它主要用于上传文件。 下面是使用Multer中间件的基本…...
性能监控软件是什么?有哪些优势?
在现代科技驱动的世界中,计算机系统的性能对于企业和个人用户都至关重要。性能监控软件是一种不可或缺的工具,可以帮助我们实时跟踪、分析和优化系统的性能。本文将介绍性能监控软件的概念、其重要性以及如何选择和使用这些工具来提高系统效率。 一、性能…...
分布式事务及CAP和BASE顶底
一、分布式事务 单体应用肯定就不存在分布式事务了,只有在分布式微服务系统中,各个服务之间通过RPC调用后,每个微服务有自己和数据库的连接,各个微服务的回滚不影响其他的微服务事务,这几必须使用分布式事务来解决分布…...
Django REST Framework完整教程-认证与权限-JWT的使用
文章目录 1.认证(Authentication)与权限(Permission)1.1.视图添加权限1.2.登录验证1.3.常用DRF自带权限类1.4.自定义权限类1.5.全局权限1.6.函数视图权限 2.认证详解2.1.认证方案2.2.如何使用TokenAuthentication? 3.JSON Web Token(JWT)认证3.1.工作原理3.2.安装3.…...
领域内容第18名
恭喜入榜...
[1024]程序员节 一晃6年过去了
加入开发者大军,一晃已是6年有余,从最初的Andoird开发如火如荼,到现在的秋风萧瑟,宛如被秋风吹得只剩躯干的树木,等待来年的焕发新芽。 我本不是一个科班出身的开发者,但是为了生活,说白了为了钱…...
数据结构 | 构造哈夫曼树
template<class T> void Heap<T>::PercolateUp() //为了向上调整为堆,我们需要比较当前节点和其父节点的值,如果父节点的值比当前节点大,则交换它们的值。 { int p size - 1, c (p - 1) / 2;//c表示当前节点的父节点࿰…...
实验室烧杯可以用超声波清洗机吗
实验室烧杯可以用超声波清洗机吗?答案是可以的!超声波清洗机不仅可以清洗实验烧杯,还可以用于清洗实验室中的试管、培养皿、移液管、载玻片、容量瓶、锥形瓶等各类实验器皿。在实验中,如果烧杯清洁不到位,会使得实验数…...
KubeSphere 容器平台高可用:环境搭建与可视化操作指南
Linux_k8s篇 欢迎来到Linux的世界,看笔记好好学多敲多打,每个人都是大神! 题目:KubeSphere 容器平台高可用:环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...
Java - Mysql数据类型对应
Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...
什么是库存周转?如何用进销存系统提高库存周转率?
你可能听说过这样一句话: “利润不是赚出来的,是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业,很多企业看着销售不错,账上却没钱、利润也不见了,一翻库存才发现: 一堆卖不动的旧货…...
什么是EULA和DPA
文章目录 EULA(End User License Agreement)DPA(Data Protection Agreement)一、定义与背景二、核心内容三、法律效力与责任四、实际应用与意义 EULA(End User License Agreement) 定义: EULA即…...
WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)
一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解,适合用作学习或写简历项目背景说明。 🧠 一、概念简介:Solidity 合约开发 Solidity 是一种专门为 以太坊(Ethereum)平台编写智能合约的高级编…...
自然语言处理——Transformer
自然语言处理——Transformer 自注意力机制多头注意力机制Transformer 虽然循环神经网络可以对具有序列特性的数据非常有效,它能挖掘数据中的时序信息以及语义信息,但是它有一个很大的缺陷——很难并行化。 我们可以考虑用CNN来替代RNN,但是…...
关于 WASM:1. WASM 基础原理
一、WASM 简介 1.1 WebAssembly 是什么? WebAssembly(WASM) 是一种能在现代浏览器中高效运行的二进制指令格式,它不是传统的编程语言,而是一种 低级字节码格式,可由高级语言(如 C、C、Rust&am…...
大学生职业发展与就业创业指导教学评价
这里是引用 作为软工2203/2204班的学生,我们非常感谢您在《大学生职业发展与就业创业指导》课程中的悉心教导。这门课程对我们即将面临实习和就业的工科学生来说至关重要,而您认真负责的教学态度,让课程的每一部分都充满了实用价值。 尤其让我…...
【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)
1.获取 authorizationCode: 2.利用 authorizationCode 获取 accessToken:文档中心 3.获取手机:文档中心 4.获取昵称头像:文档中心 首先创建 request 若要获取手机号,scope必填 phone,permissions 必填 …...
MySQL账号权限管理指南:安全创建账户与精细授权技巧
在MySQL数据库管理中,合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号? 最小权限原则…...
