标签归档:可视化

ROS激光传感器可视化

问题描述

实验平台:TIAGo

编程语言:Python

TIAGo所使用的激光传感器在移动基座的底部,具体的描述信息如下

Laser range-finder

Located at the front of the base. This sensor measures distances in a horizontal plane. It is a valuable asset for navigation and mapping. Bad measurements can be caused by reflective or transparent surfaces.

为了获取激光数据,我们必须订阅/scan 消息,消息返回的类型为sensor_msgs/LaserScan ,该类型的文档可以访问http://docs.ros.org/indigo/api/sensor_msgs/html/msg/LaserScan.html查看,这里为了方便,我直接摘抄这个结构体的描述文档到了下面

std_msgs/Header header
float32 angle_min
float32 angle_max
float32 angle_increment
float32 time_increment
float32 scan_time
float32 range_min
float32 range_max
float32[] ranges
float32[] intensities

其中ranges是一个浮点数组,每个数组成员代表着激光传感器返回的距离,这个距离有可能是inf,因为激光传感器可能会因为没有探测到物体而返回无限远。这个我们需要在后面留意一下。intensities也是一个浮点数组,用来保存对应ranges上的强度。

所有代码如下:

#!/usr/bin/env python

import cv2
import numpy as np
import time
import rospy
from sensor_msgs.msg import LaserScan
import os

def scan_callback(data):
    distances = data.ranges
    angle_min = data.angle_min
    angle_increment = data.angle_increment

    pixels_relative_center = []
    for index in range(len(distances)):
        distance = distances[index]
        if distance == float('inf'):
            continue
        angle = angle_min + angle_increment * index
        x = np.cos(angle) * distance
        y = np.sin(angle) * distance

        pixel_x = int(round(x * 30))
        pixel_y = int(round(y * 30))
        pixels_relative_center.append((pixel_x, pixel_y))

    pixels_relative_center = list(set(pixels_relative_center))

    # create rgb image
    rgb_image = np.zeros((801, 801, 3), np.uint8)
    rgb_image.fill(255)

    pixels_relative_origin = []
    for (pixel_x, pixel_y) in pixels_relative_center:
        col = 400 + pixel_x
        row = 400 - pixel_y
        pixels_relative_origin.append((row, col))

    # draw the point
    for (row, col) in pixels_relative_origin:
        cv2.circle(rgb_image, (col, row), 2, (0, 0, 0), -1)

    # draw the point of robot
    cv2.circle(rgb_image, (400, 400), 4, (0, 255, 0), -1)

    cv2.imshow("rgb", rgb_image)
    cv2.waitKey(30)


def shutdown():
    cv2.destroyAllWindows()
    # noinspection PyProtectedMember
    os._exit(0)


rospy.on_shutdown(shutdown)
rospy.init_node('laser_image', anonymous=True)
rospy.Subscriber("/scan", LaserScan, scan_callback, queue_size=5)
while True:
    time.sleep(1)

可以持续移动机器人,将会发现数据图随着机器人的变化而变化。

代码讲解

使用循环,每次步进angle_increment 的角度,然后使用了三角函数公式来计算出激光数据点相对于机器人中心点(准确说应该是/base_laser_link,但是这里为了简便就说是中心点了)的坐标x和y。

x = np.cos(angle) * distance
y = np.sin(angle) * distance

为了能够将x,y坐标转换为像素的个数(方便显示),我将他们整体扩大了30倍。实际情况中如果有map,应该结合map的分辨率来计算这个值

pixel_x = int(round(x * 30))
pixel_y = int(round(y * 30))

然后,我们新建了一个801×801的空白画布。这个画布大小是我自己定义的,足够容纳我实验环境下激光传感器返回的所有点。pixels_relative_center 数组保存的是相对于机器人中心点,激光数据点的像素坐标。在后面的循环中,我们将这些数据点转换为相对于画布原点的像素坐标,并将他们放在pixels_relative_origin 数组中。这里需要注意的是,opencv画布坐标轴的位置和方向是不一样的。400就是我们的画布中心点,所以转换方式如下

col = 400 + pixel_x
row = 400 - pixel_y

最后,我们将这些点画出来即可

cv2.circle(rgb_image, (col, row), 2, (0, 0, 0), -1)