机器人精确移动包

move_near

之前有写过, 将ROS官方的move_basic包改写成了python形式, 同时将它写成了一个完整的action接口

最近测试时发现了问题, odom的数据波动可能会导致机器人陷入正反馈从而一直移动

具体表现为:

  1. 机器人移动精度设置为0.005 m

  2. 机器人在移动到接近0.005的位置, odom发生微小的跳变

    本来distRemaining应该是从 1 降到 0.5, 降到 0.006, 然后小于0.005, 机器人停住, 但是里程计波动, 使得distRemaining变成-0.006, 此时机器人还要继续后退, 就会导致distRemaining持续增大, 机器人无法停止

修改

​ 将计算机器人移动距离的distRemaining修改为累加制, 通过odom的逐差来减小odom的累进误差

结果

​ 机器人移动精度可以达到0.0005 m, 甚至还能降, 但是已经超出了需求, 如果odom更好, 应该能达到更好的效果

调用

shell 复制代码
# 填充需要前往的位置, 在本例中使用的是base_link, 让机器人相对自身运动
$ rostopic pub /move_near/goal move_base_msgs/MoveBaseActionGoal 

注意事项

​ 在机器人移动过程中没有避障! 没有避障! 这不是move_base的接口! 不会调用costmap, 无避障操作!

python 复制代码
#! /usr/bin/env python3

import 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, MoveBaseGoal

import math
import tf2_ros
from tf.transformations import euler_from_quaternion, quaternion_from_euler

class 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 = 0
        self.prevAngleRemaining = 0

    def imu_cb(self, msg):
        self.now_imu = msg

    def odom_cb(self, msg):
        self.now_odom = msg

    def normalizeAngle(self,angle):
        if angle < -math.pi:
            angle += 2* math.pi
        if angle > math.pi:
            angle -= 2*math.pi
        return angle

    def rad2deg(self,rad):
      return rad * 180 / math.pi

    def sign(self,n):
      if n < 0:
        return -1
      else:
        return 1

    def 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_yaw

    def rotate(self,yaw):
      rospy.loginfo("Requested rotation: {} degrees".format(self.rad2deg(yaw)))
      r = rospy.Rate(20)
      initial_yaw = self.getCurrentYaw()
      done = False

      while(not done and not rospy.is_shutdown()):
        rotated_yaw = self.getCurrentYaw() - initial_yaw
        angleRemaining = yaw - rotated_yaw
        angleRemaining = 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 = -speed
        else:
          vel.angular.z = speed
        if (abs(angleRemaining) < self.angularTolerance):
          vel.angular.z = 0
          done = True
          r.sleep()
          rotated_yaw = self.getCurrentYaw() - initial_yaw
          angleRemaining = yaw - rotated_yaw
          rospy.loginfo("Rotate finished! error: {} degrees".format(self.rad2deg(angleRemaining)))
        self.cmd_pub.publish(vel)
        r.sleep()

      return True

    def moveLinear(self,dist):
      done = False
      r = rospy.Rate(20)
      initial_odom = self.now_odom
      distRemaining = dist
      while(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_odom
        rospy.logdebug("travelledDist: {}".format(travelledDist))
        # for speed direction judgement
        if dist <= 0:
          distRemaining += travelledDist
          dist += travelledDist
        else:
          distRemaining -= travelledDist
          dist -= travelledDist
        rospy.logdebug("distRemaining: {}".format(distRemaining))
        vel = Twist()
        speed = max(self.minLinearVelocity, min(self.maxLinearVelocity, 2.5* abs(distRemaining)))

        if abs(distRemaining) < self.linearTolerance:
          speed = 0
          done = True
          rospy.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 = -speed

        vel.linear.x = speed

        try:
          self.cmd_pub.publish(vel)
        except Exception as e:
          rospy.logerr("Error while publishing: {}".format(e))
        r.sleep()

      return True


    def execute_cb(self, goal):
        success = True
        behind = False
        self.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 robot
        if face2goalYaw > math.pi/2 or face2goalYaw < -math.pi/2:
            behind = True
            face2goalYaw = self.normalizeAngle(face2goalYaw + math.pi)
            # face2goalYaw = self.normalizeAngle(face2goalYaw)

        # face to goal
        if self.rotate(face2goalYaw):
          pass
        else:
          rospy.logwarn("Trun to goal failed!")

        # Move to goal
        dist2goal = math.hypot(goal.target_pose.pose.position.x, goal.target_pose.pose.position.y)
        
        # if the goal is behind the robot, move backward
        if behind:
          dist2goal = -dist2goal
        else:
          dist2goal = dist2goal

        if self.moveLinear(dist2goal):
          pass
        else:
          success = False
          rospy.logwarn("Move to goal failed!")

        # Turn to  goal yaw
        relative_yaw = self.goalPose['yaw'] - face2goalYaw
        relative_yaw = self.normalizeAngle(relative_yaw)

        if self.rotate(relative_yaw):
          pass
        else:
          success = False
          rospy.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()
相关推荐
机器人猎头David9 小时前
VLA、世界模型、强化学习,在机器人里分别解决什么问题?
人工智能·机器人·机器人猎头·机器人猎头公司
竞赛考级题库10 小时前
202606 青少年等级考试机器人理论真题一级 建议答题时长:60min(含答案与分析)
机器人
TMT星球12 小时前
大晓发布全球首个可仿真人–场景交互重建框架HSImul3R
人工智能·科技·机器人
hey you~12 小时前
语音机器人可以给老客户做定期关怀回访吗?运营方案参考
机器人·老客户关怀·客户回访·客户关系维护·客户分层
FPC工厂——皇榜科技12 小时前
机器人灵巧手线路板:从“握得住”到“摸得准”:一只机械手的柔性神经密码
网络·人工智能·科技·机器人·pcb工艺
workflower16 小时前
智能体-企业行政问答助手
人工智能·机器学习·机器人·云计算·无人机
tianxuanjg19 小时前
工业/协作机器人研发采购指南:如何适配新品迭代的CNC加工合作
人工智能·经验分享·机器人·无人机·材质
GlobalInfo20 小时前
2026-2032年全球固定相机式工业机器人视觉系统行业产量、产值及主要企业格局分析报告
数码相机·机器人
拓维运动科技20 小时前
机器人性能测试平台:从室外场地到实验室标准化测评的技术路径
机器人·六自由度运动模拟平台·南京拓维运动科技·非标定制机器人动态测试平台·机器人性能测试平台·模拟机器人性能工况
HyperAI超神经20 小时前
机器人运动误差降低90.4%,英伟达/哈佛等提出Hydra-0,用Action Flow统一世界建模与控制
人工智能·深度学习·机器人·英伟达·世界模型