ROS-基于简单势场算法编写的线型编队领航

本文介绍了基于ROS的线型编队控制项目,利用激光雷达实现跟随避障。通过分析最近目标点,设置引力和斥力策略调整小车速度和角速度。代码中包含数据捕获、位置计算和速度发布等关键部分,适用于ROS初学者。在实际应用中,需考虑如何优化行驶平稳性、消除转弯时的混乱以及选择合适的控制参数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 前言

        大创项目也快结束了,记录一下,免得以后都不记得自己还做过ROS了,有关领航者编队控制的事情,有时间再更新吧......

        前段时间,在古月居学习了一个利用雷达结点跟随最近目标点的程序。与此同时,项目基于领航者-跟随者的编队控制避障项目也遇到了瓶颈,正在尝试仿真,因此在琢磨之余以古月的代码为底板写了一套线型编队跟随,效果还可以,比较稳定(不受网络质量、ROS通信的影响),最重要的是非常简单!虽然存在很明显的缺陷,但非常适合入门ROS的一个小程序。先贴出之前看到的那个文章:

        ROS下空环境中用激光雷达做跟随

        翻阅本文章的内容,首先需要学习ROS相关概念,对python有一定的基础。且本文不再对机器人的激光雷达进行介绍说明。这里附一个介绍激光雷达数据的,没看要先看,不然可能会看不懂激光数据处理那部分的代码:

        激光雷达数据分析

控制原理

        在一个线型编队中,为了保持编队阵型,一辆车与它前后车辆的相对关系可以有如下图的三种情况(这里拿相对复杂的中间车说明,头车与尾车更简单一些):

  • 当前车离自己远了,会给小车一个向前的引力,吸引小车前进,保持距离。线速度与到前车的距离distance有关,角速度与到前车的y轴距离有关(这里需要用到TF的相关知识)
  • 当前车离自己太近了,为了避免碰撞,会给小车一个向后的斥力,排斥小车迫使它先后退。它的线速度、角速度关系与第一种情况相同。
  • 当后车离自己太近了,为了避免碰撞,会给小车一个向前的斥力,排斥小车迫使它向前走。它的线速度、与第一种情况相同,但角速度与第一种情况相反。(右手比划比划就明白了)

         根据这个思路,我们就可以写出一套有关小车运动控制的程序。在小车上,距离的判断用激光雷达扫描完成,而引力与斥力则体现在小车的运动速度、角速度上。

应用与解析

使用方法

        我的实验方案是一台阿克曼头车后面配合两台差速,下面给出的是中间车的代码,头车启用底盘+键盘结点引领全队,跟车启用底盘+雷达+此代码即可。(有关雷达驱动怎么写,里程计驱动怎么写就不细说了,网上资源很丰富)

#!/usr/bin/env python
#-*-coding:UTF-8 -*-

import rospy
from geometry_msgs.msg import Twist
from rospy.exceptions import ROSInterruptException
from sensor_msgs.msg import LaserScan
import math


#capture lidar data
def capturer():
    ldata=rospy.wait_for_message("/scan",LaserScan,timeout=None)         # catch lidar datas
    return ldata


#publish the cmd to the car
def cmd_pub(data):
    p_gain_vel = 1.15
    p_gain_ang = 2.5
    vel_msg = Twist()

    pub = rospy.Publisher('/cmd_vel', Twist,queue_size=10)
    x_pillar,y_pillar = position_cal(data)
    print(x_pillar,y_pillar)
#repel      
    if x_pillar<0.325 and x_pillar > 0:
        vel_msg.linear.x = 0.01/x_pillar
        vel_msg.angular.z = (y_pillar * p_gain_ang)
# atraction
    elif x_pillar < -0.30 and x_pillar > -0.35:
        if x_pillar <=-0.30 and x_pillar > -0.31:
            vel_msg.linear.x = -(x_pillar *p_gain_vel * 0.1)
            vel_msg.angular.z = -(y_pillar * p_gain_ang)
        elif x_pillar <= -0.31 and x_pillar > -0.32:
            vel_msg.linear.x = -(x_pillar *p_gain_vel * 0.2)
            vel_msg.angular.z = -(y_pillar * p_gain_ang)
        elif x_pillar <= -0.32 and x_pillar > -0.33:
            vel_msg.linear.x = -(x_pillar *p_gain_vel * 0.3)
            vel_msg.angular.z = -(y_pillar * p_gain_ang)
        elif x_pillar <= -0.33 and x_pillar > -0.34:
            vel_msg.linear.x = -(x_pillar *p_gain_vel * 0.4)
            vel_msg.angular.z = -(y_pillar * p_gain_ang)
        elif x_pillar <= -0.34 and x_pillar > -0.35:
            vel_msg.linear.x = -(x_pillar * p_gain_vel*0.5)
            vel_msg.angular.z = -(y_pillar * p_gain_ang)
#backward_repel
    elif x_pillar < 0 and x_pillar > -0.2:
        vel_msg.linear.x = 0.01/x_pillar
        vel_msg.angular.z = (y_pillar * p_gain_ang)
    else:
         vel_msg.linear.x = 0
         vel_msg.angular.z = 0
    print(vel_msg.linear.x,vel_msg.angular.z)

    pub.publish(vel_msg)


#calculate the position of the car & item
def position_cal(data):
    smallest_distance = 200
    arr_size =int(math.floor((data.angle_max-data.angle_min)/data.angle_increment))
    for i in range(arr_size):
        if data.ranges[i] < smallest_distance:
            smallest_distance = data.ranges[i]
            alpha_pillar = (data.angle_min + (i * data.angle_increment))
    x = smallest_distance * math.cos(alpha_pillar)
    y = smallest_distance * math.sin(alpha_pillar)
    return x,y 


def main():
    rospy.init_node('follower')
    rate = rospy.Rate(10)
    while not rospy.is_shutdown():
        lidar_data=capturer()
        cmd_pub(lidar_data)
        rate.sleep


if __name__ == "__main__":
    try:
        main()
    except rospy.ROSInterruptException:
        print('Error or Interuption happen')
        pass

模块引入        

        首先引入需要的一些模块,这里用到了rospy(个人不会cpp,只能改成python的),发布速度信息的Twist,激光雷达数据的Laserscan,还有一些数学公式math。

import rospy
from geometry_msgs.msg import Twist
from rospy.exceptions import ROSInterruptException
from sensor_msgs.msg import LaserScan
import math

雷达结点

        定义一个函数capturer用来接收并返回雷达的相关数据。wait_for_message是一个对于稳定输出源很好用的接收函数,它不需要回调函数,是一个直接赋值的功能。至于为什么好用这里不做详细赘述,感兴趣的小伙伴们可以自行查询。

#capture lidar data
def capturer():
    ldata=rospy.wait_for_message("/scan",LaserScan,timeout=None)         # catch lidar datas
    return ldata

 位置计算

        根据得到的激光雷达数据,实时的计算此时距离机器人的最近点,推算出它的距离,进而推算出x和y坐标。(可以观察x,y的取值,并思考为啥——它是以目标点为原点建立的tf)这里要用到激光雷达数据的相关知识。

#calculate the position of the car & item
def position_cal(data):
    smallest_distance = 200
    arr_size =int(math.floor((data.angle_max-data.angle_min)/data.angle_increment))
    for i in range(arr_size):
        if data.ranges[i] < smallest_distance:
            smallest_distance = data.ranges[i]
            alpha_pillar = (data.angle_min + (i * data.angle_increment))
    x = smallest_distance * math.cos(alpha_pillar)
    y = smallest_distance * math.sin(alpha_pillar)
    return x,y

 引力斥力函数(速度发布器)

        说好听点,叫引力斥力函数,其实就是一个速度的发布器。根据对x,y(理论上应该是distance和θ)判定来给机器人赋予不同的值,来调制小车的位姿以及姿态。都是大量的ifelse语句,逐个分析即可,趋势与前面原理所述一致。

#publish the cmd to the car
def cmd_pub(data):
    p_gain_vel = 1.15
    p_gain_ang = 2.5
    vel_msg = Twist()

    pub = rospy.Publisher('/cmd_vel', Twist,queue_size=10)
    x_pillar,y_pillar = position_cal(data)
    print(x_pillar,y_pillar)
#repel      
    if x_pillar<0.325 and x_pillar > 0:
        vel_msg.linear.x = 0.01/x_pillar
        vel_msg.angular.z = (y_pillar * p_gain_ang)
# atraction
    elif x_pillar < -0.30 and x_pillar > -0.35:
        if x_pillar <=-0.30 and x_pillar > -0.31:
            vel_msg.linear.x = -(x_pillar *p_gain_vel * 0.1)
            vel_msg.angular.z = -(y_pillar * p_gain_ang)
        elif x_pillar <= -0.31 and x_pillar > -0.32:
            vel_msg.linear.x = -(x_pillar *p_gain_vel * 0.2)
            vel_msg.angular.z = -(y_pillar * p_gain_ang)
        elif x_pillar <= -0.32 and x_pillar > -0.33:
            vel_msg.linear.x = -(x_pillar *p_gain_vel * 0.3)
            vel_msg.angular.z = -(y_pillar * p_gain_ang)
        elif x_pillar <= -0.33 and x_pillar > -0.34:
            vel_msg.linear.x = -(x_pillar *p_gain_vel * 0.4)
            vel_msg.angular.z = -(y_pillar * p_gain_ang)
        elif x_pillar <= -0.34 and x_pillar > -0.35:
            vel_msg.linear.x = -(x_pillar * p_gain_vel*0.5)
            vel_msg.angular.z = -(y_pillar * p_gain_ang)
#backward_repel
    elif x_pillar < 0 and x_pillar > -0.2:
        vel_msg.linear.x = 0.01/x_pillar
        vel_msg.angular.z = (y_pillar * p_gain_ang)
    else:
         vel_msg.linear.x = 0
         vel_msg.angular.z = 0
    print(vel_msg.linear.x,vel_msg.angular.z)

    pub.publish(vel_msg)

主函数

        最后,大功告成,在主函数里创建结点,定义发布频率,调用前面的函数,就可以愉快的调参数了。

def main():
    rospy.init_node('follower')
    rate = rospy.Rate(10)
    while not rospy.is_shutdown():
        lidar_data=capturer()
        cmd_pub(lidar_data)
        rate.sleep

效果如下:

编队领航


 改进的思路

        整体代码在大致框架写完后需要考虑几个问题:

        1.如何保证中间车辆的平稳行驶,也就是避免小车由于距离的变化进而在车队中间像弹簧一样的跳动?

                设置合理的速度系数,一个是为了让它的速度变化更加贴合编队运动,一个也是为了让中间车更倾向于被某一种力引领。也就是说引力和斥力的设置范围应该不同,且有一个力更占主动性。

        2.如何尽可能消除在编队转弯时,由于雷达判定所带来的编队混乱问题?

                可以限定y的扫描范围,减小因为拐弯带来的错误点。

        3.如何选取合适的引力、斥力范围,以及线速度、角速度系数?

                线速度、角速度过大,会造成机器人的摇摆、抽搐。线速度、角速度过小,机器人不能及时跟随前车的方向变化。

一个没有做的想法

        对于这个编队,虽然很好玩,但它毕竟就直接建立在雷达数据之上。太简单了,因此也没法再做更多的事情。拿y进行角速度的判断实际上是不合理的,只能说趋势上是一致的,但做不到精准的控制。

        一种想法是根据相对变化趋势来进行机器人的调制,也就是通过毕竟上一个点与当前点的变化趋势来进行算法编写。

        另一个想法就是在算法中拿到机器人的位姿(odom)然后利用提供的机器人状态信息来增强编队控制的准确性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hamooddd

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值