当前位置: 首页 > article >正文

亚博microros小车-原生ubuntu支持系列:13 激光雷达避障

一 背景知识

小车发了了数据包含激光雷达数据,类型是sensor_msgs/msg/LaserScan

bohu@bohu-TM1701:~$ ros2 node info /YB_Car_Node
/YB_Car_Node
  Subscribers:
    /beep: std_msgs/msg/UInt16
    /cmd_vel: geometry_msgs/msg/Twist
    /servo_s1: std_msgs/msg/Int32
    /servo_s2: std_msgs/msg/Int32
  Publishers:
    /battery: std_msgs/msg/UInt16
    /imu: sensor_msgs/msg/Imu
    /odom_raw: nav_msgs/msg/Odometry
    /scan: sensor_msgs/msg/LaserScan
  Service Servers:

  Service Clients:

  Action Servers:

  Action Clients

激光雷达数据

sensor_msgs/msg/LaserScan 格式

scan_data 是从激光雷达(LiDAR)获取的激光扫描数据,通常使用 sensor_msgs/LaserScan 消息类型表示。该消息包含以下字段:

    header:
        std_msgs/Header 类型,包含消息的元数据,如时间戳和坐标系 ID。

    angle_min:
        float32 类型。表示扫描的最小角度,通常以弧度为单位。

    angle_max:
        float32 类型。表示扫描的最大角度,通常以弧度为单位。

    angle_increment:
        float32 类型。每个测量点之间的角度增量,即相邻点之间的角度差,通常以弧度为单位。

    time_increment:
        float32 类型。相邻测量点之间的时间增量,单位是秒。

    scan_time:
        float32 类型。完成一次扫描的总时间,单位是秒。

    range_min:
        float32 类型。激光雷达可检测到的最小距离,通常以米为单位。该值用于过滤无效数据。

    range_max:
        float32 类型。激光雷达可检测到的最大距离,通常以米为单位。该值用于过滤无效数据。

    ranges:
        float32[] 数组。存储每个扫描点的距离值,通常以米为单位。数组中的每个元素代表从激光雷达到障碍物的距离。

    intensities:
    float32[] 数组。存储每个扫描点的强度值,表示反射回来的信号强度。这个字段并不是所有激光雷达都支持或使用的。

小车的贴一下实际获取数据如下

header:
  stamp:
    sec: 1737861564
    nanosec: 600000000
  frame_id: laser_frame
angle_min: -3.1415927410125732
angle_max: 3.1415927410125732
angle_increment: 0.01745329238474369
time_increment: 0.0
scan_time: 0.0
range_min: 0.11999999731779099
range_max: 8.0
ranges:
- 1.0069999694824219
- 1.0269999504089355
- 1.0880000591278076
- 1.1549999713897705
- 1.2380000352859497
- 1.3289999961853027
- 1.50600004196167
- 0.9179999828338623
- 0.9210000038146973
- 1.4880000352859497
- 1.4830000400543213
- 1.4769999980926514
- 1.468999981880188
- 1.4670000076293945
- 1.3170000314712524
- 1.4570000171661377
- 1.4509999752044678
- 1.4459999799728394
- 1.440999984741211
- 1.4390000104904175
- 1.437000036239624
- 1.434000015258789
- 1.4329999685287476
- 1.4329999685287476
- 1.4329999685287476
- 1.4329999685287476
- 1.1959999799728394
- 1.1779999732971191
- 1.1759999990463257
- 1.1720000505447388
- 0.12200000137090683
- 0.13300000131130219
- 0.13300000131130219
- 0.0
- 0.03099999949336052
- 1.4450000524520874
- 1.4479999542236328
- 1.4520000219345093
- 0.847000002861023
- 0.8130000233650208
- 0.7940000295639038
- 0.7979999780654907
- 0.8029999732971191
- 0.8050000071525574
- 0.8050000071525574
- 0.8090000152587891
- 0.8240000009536743
- 1.5520000457763672
- 1.562999963760376
- 1.5729999542236328
- 1.593999981880188
- 1.6030000448226929
- 1.6130000352859497
- 1.6349999904632568
- 1.6349999904632568
- 1.6579999923706055
- 1.6790000200271606
- 1.690999984741211
- 1.7170000076293945
- 0.6190000176429749
- 0.6159999966621399
- 0.6330000162124634
- 0.6779999732971191
- 0.9139999747276306
- 0.890999972820282
- 0.8830000162124634
- 0.8709999918937683
- 0.8410000205039978
- 0.6729999780654907
- 0.6650000214576721
- 0.6510000228881836
- 0.6460000276565552
- 0.6389999985694885
- 0.6190000176429749
- 0.6119999885559082
- 0.6039999723434448
- 0.5929999947547913
- 0.5870000123977661
- 0.5809999704360962
- 0.5699999928474426
- 0.5649999976158142
- 0.5600000023841858
- 0.550000011920929
- 0.5460000038146973
- 0.5410000085830688
- 0.5239999890327454
- 0.5230000019073486
- '...'
intensities:
- 154.0
- 154.0
- 155.0
- 87.0
- 78.0
- 78.0
- 124.0
- 177.0
- 166.0
- 170.0
- 171.0
- 175.0
- 175.0
- 170.0

 

二 激光雷达避障

小车连接上代理,运行程序,小车上的雷达扫描设定范围内是否有障碍物,有障碍物则会根据障碍物的位置,自动调整速度,使其自身避开障碍物。通过动态参数调节器可以调整雷达检测的范围和避障检测的距离等参数。

src/yahboomcar_laser/yahboomcar_laser/目录下新建文件laser_Avoidance.py

代码如下:

#ros lib
import rclpy
from rclpy.node import Node
from geometry_msgs.msg import Twist
from sensor_msgs.msg import LaserScan #激光雷达消息类型
import os
import sys

#commom lib
import math
import numpy as np
import time
from time import sleep
from yahboomcar_laser.common import *
print ("improt done")
RAD2DEG = 180 / math.pi #弧度转角度

class laserAvoid(Node):
    def __init__(self,name):
        super().__init__(name)
        #create a sub  订阅激光雷达数据的主题 /scan
        self.sub_laser = self.create_subscription(LaserScan,"/scan",self.registerScan,1)
        self.sub_JoyState = self.create_subscription(Bool,'/JoyState', self.JoyStateCallback,1)#订阅手柄控制状态的主题 
        #create a pub 发布机器人控制指令的主题
        self.pub_vel = self.create_publisher(Twist,'/cmd_vel',1)
        
        
        #declareparam 声明参数
        self.declare_parameter("linear",0.3)#线速度
        self.linear = self.get_parameter('linear').get_parameter_value().double_value
        self.declare_parameter("angular",1.0)#角速度
        self.angular = self.get_parameter('angular').get_parameter_value().double_value
        self.declare_parameter("LaserAngle",45.0)#激光雷达角度
        self.LaserAngle = self.get_parameter('LaserAngle').get_parameter_value().double_value
        self.declare_parameter("ResponseDist",0.55)#响应距离
        self.ResponseDist = self.get_parameter('ResponseDist').get_parameter_value().double_value
        self.declare_parameter("Switch",False)#开关
        self.Switch = self.get_parameter('Switch').get_parameter_value().bool_value
        
        self.Right_warning = 0#存储各方向障碍物检测结果
        self.Left_warning = 0
        self.front_warning = 0
        self.Joy_active = False #手柄控制是否激活
        self.Moving = False
        self.ros_ctrl = SinglePID()#前面common的PID控制器
        
        self.timer = self.create_timer(0.01,self.on_timer)#定时器,每0.01秒调用on_timer()
        
    def on_timer(self):
        self.Switch = self.get_parameter('Switch').get_parameter_value().bool_value
        self.angular = self.get_parameter('angular').get_parameter_value().double_value
        self.linear = self.get_parameter('linear').get_parameter_value().double_value
        self.LaserAngle = self.get_parameter('LaserAngle').get_parameter_value().double_value
        self.ResponseDist = self.get_parameter('ResponseDist').get_parameter_value().double_value

    #手柄回调函数
    def JoyStateCallback(self, msg):
        if not isinstance(msg, Bool): return
        self.Joy_active = msg.data
    #退出时发的指令    
    def exit_pro(self):
        cmd1 = "ros2 topic pub --once /cmd_vel geometry_msgs/msg/Twist "
        cmd2 = '''"{linear: {x: 0.0, y: 0.0, z: 0.0}, angular: {x: 0.0, y: 0.0, z: 0.0}}"'''
        cmd = cmd1 +cmd2
        os.system(cmd)
    #激光雷达回调函数 :处理订阅到的雷达数据   
    def registerScan(self, scan_data):
        if not isinstance(scan_data, LaserScan): return
        ranges = np.array(scan_data.ranges)#数据转换
        self.Right_warning = 0
        self.Left_warning = 0
        self.front_warning = 0
        
        for i in range(len(ranges)):
            angle = (scan_data.angle_min + scan_data.angle_increment * i) * RAD2DEG
            if angle > 180: angle = angle - 360
            #60 means that the range of radar detection is set to 120 degrees
            #根据设定的雷达检测的角度和障碍物检测距离判断前、左、右是否有障碍物存在
            if 20 < angle < self.LaserAngle:
                if ranges[i] < self.ResponseDist*1.5:
                    self.Left_warning += 1
            if -self.LaserAngle < angle < -20:
                if ranges[i] < self.ResponseDist*1.5:
                    self.Right_warning += 1
            if abs(angle) <= 20:
                 if ranges[i] <= self.ResponseDist*1.5: 
                        self.front_warning += 1
        if self.Joy_active or self.Switch == True: #手柄或者开关打开,则停止移动
            if self.Moving == True:
                self.pub_vel.publish(Twist())
                self.Moving = not self.Moving
            return
        self.Moving = True
        twist = Twist()
        #根据检测到障碍物,发布小车的指令让小车避开障碍物
        if self.front_warning > 10 and self.Left_warning > 10 and self.Right_warning > 10:
            print ('1, there are obstacles in the left and right, turn right')
            twist.linear.x = self.linear
            twist.angular.z = -self.angular
            self.pub_vel.publish(twist)
            sleep(0.2)
        
        elif self.front_warning > 10 and self.Left_warning <= 10 and self.Right_warning > 10:
            print ('2, there is an obstacle in the middle right, turn left')
            twist.linear.x = self.linear
            twist.angular.z = self.angular
            self.pub_vel.publish(twist)
            sleep(0.2)
            if self.Left_warning > 10 and self.Right_warning <= 10:
                twist.linear.x = self.linear
                twist.angular.z = -self.angular
                self.pub_vel.publish(twist)
                sleep(0.5)
        elif self.front_warning > 10 and self.Left_warning > 10 and self.Right_warning <= 10:
            print ('4. There is an obstacle in the middle left, turn right')
            twist.linear.x = self.linear
            twist.angular.z = -self.angular
            #self.pub_vel.publish(twist)
            sleep(0.2)
            if self.Left_warning <= 10 and self.Right_warning > 10:
                twist.linear.x = self.linear
                twist.angular.z = self.angular
                self.pub_vel.publish(twist)
                sleep(0.5)
        elif self.front_warning > 10 and self.Left_warning < 10 and self.Right_warning < 10:

            print ('6, there is an obstacle in the middle, turn left')
            twist.linear.x = self.linear
            twist.angular.z = self.angular
            self.pub_vel.publish(twist)
            sleep(0.2)
        elif self.front_warning < 10 and self.Left_warning > 10 and self.Right_warning > 10:
            print ('7. There are obstacles on the left and right, turn right')
            twist.linear.x = self.linear
            twist.angular.z = -self.angular
            self.pub_vel.publish(twist)
            sleep(0.4)
        elif self.front_warning < 10 and self.Left_warning > 10 and self.Right_warning <= 10:
            print ('8, there is an obstacle on the left, turn right')
            twist.linear.x = self.linear
            twist.angular.z = -self.angular
            self.pub_vel.publish(twist)
            sleep(0.2)
        elif self.front_warning < 10 and self.Left_warning <= 10 and self.Right_warning > 10:
            print ('9, there is an obstacle on the right, turn left')
            twist.linear.x = self.linear
            twist.angular.z = self.angular
            self.pub_vel.publish(twist)
            sleep(0.2)
        elif self.front_warning <= 10 and self.Left_warning <= 10 and self.Right_warning <= 10:
            print ('10, no obstacles, go forward')
            twist.linear.x = self.linear
            twist.angular.z = 0.0
            self.pub_vel.publish(twist)
        #self.pub_vel.publish(twist)

def main():
    rclpy.init()
    laser_avoid = laserAvoid("laser_Avoidance")
    print ("start it")
    try:
        rclpy.spin(laser_avoid)
    except KeyboardInterrupt:
        pass
    finally:
        laser_avoid.exit_pro()
        laser_avoid.destroy_node()
        rclpy.shutdown()
    

我加了注释,主要逻辑是收到激光雷达数据后回调处理registerScan,通过发布/cmd_vel 来控制小车避障。

小车运行:

ros2 run yahboomcar_laser laser_Avoidance

6, there is an obstacle in the middle, turn left
6, there is an obstacle in the middle, turn left
6, there is an obstacle in the middle, turn left
6, there is an obstacle in the middle, turn left
6, there is an obstacle in the middle, turn left
2, there is an obstacle in the middle right, turn left
9, there is an obstacle on the right, turn left
10, no obstacles, go forward
10, no obstacles, go forward
10, no obstacles, go forward
10, no obstacles, go forward
10, no obstacles, go forward

控制参数调整

ros2 run rqt_reconfigure rqt_reconfigure

以上的参数说明如下:

  • linera:线速度大小

  • angular:角速度大小

  • LaserAngle:雷达检测角度

  • ResponseDist:障碍物检测距离,当检测的物体在该范围内,则认为是障碍物

  • Switch:玩法开关

我测试了下,大一些障碍物能识别到,垃圾桶、鞋盒子这种。椅子腿细一些的不太行会撞上去。

太矮的也不行。能从房间自主识别到门口出去。这算是小车第一次不是根据键盘控制自主运行,虽然没一会就回遇到障碍物困住了。避障的逻辑处理的相对简单些。

从节点图来看,小车的激光雷达发布的数据被程序节点laser_Avoidance接受,通过/cmd_vel 控制小车运动避障。

至此。


http://www.kler.cn/a/520300.html

相关文章:

  • 自签证书的dockerfile中from命令无法拉取镜像而docker的pull命令能拉取镜像
  • WebForms 表单
  • Qt监控系统辅屏预览/可以同时打开4个屏幕预览/支持5x64通道预览/onvif和rtsp接入/性能好
  • 计算机网络之应用层
  • 案例研究丨浪潮云洲通过DataEase推进多维度数据可视化建设
  • Stable Diffusion 3.5 介绍
  • 基于OSAL的嵌入式裸机事件驱动框架——软件定时器osal_timer
  • 自由学习记录(32)
  • [VSCode] vscode下载安装及安装中文插件详解(附下载链接)
  • HBase-2.5.10 伪分布式环境搭建【Mac】
  • linux ——waitpid介绍及示例
  • Office2021下载与安装保姆级教程【Office Tool Plus】
  • 蓝桥杯c/c++需要掌握的基础语法总结
  • AWS Wavelength
  • LeetCode:2412. 完成所有交易的初始最少钱数(贪心 java)
  • Spring MVC 中的 DispatcherServlet:工作流程与应用场景解析
  • FreeRTOS实时操作系统学习小结
  • kubernetes 核心技术-Service
  • 【SQL注入】DVWA靶场SQL注入实践
  • 在线可编辑Excel
  • 【超详细】C#事件
  • MyBatis最佳实践:提升数据库交互效率的秘密武器
  • 聊一聊 CSS 样式的导入方式
  • 使用Ollama部署deepseek大模型
  • unity制作动画的技巧相关注意点
  • 证券投资和量化交易的概率和预测的准确性