1. 程式人生 > 其它 >ros 寫node 儲存資料到txt_從零開始搭二維鐳射SLAM 瞭解雷達資料

ros 寫node 儲存資料到txt_從零開始搭二維鐳射SLAM 瞭解雷達資料

技術標籤:ros 寫node 儲存資料到txt

終於到了寫程式碼的階段了,哈哈。

上一篇文章我們通過實驗知道了雷達資料的各種性質,但是雷達資料在程式碼裡是如何體現的呢?

本篇文章將通過新建一個ros的包來學習一下如何遍歷雷達資料,以及如何對雷達資料進行處理。

首先說明一下我使用的程式碼環境:

Ubuntu版本: 16.04.01
ROS: kinetic 版本
程式語言: C++
IDE推薦: 目前我使用的是 VS code,其如何配置會在之後的文章中講解

1 ros中鐳射雷達資料的訊息格式

通過在終端中輸入如下命令可以打印出ros中鐳射雷達資料的訊息格式

rosmsg show sensor_msgs/LaserScan

結果如下所示

std_msgs/Header header  // 資料的訊息頭  uint32 seq      // 資料的序號  time stamp      // 資料的時間戳  string frame_id    // 資料的座標系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  // 雷達資料每個點對應的強度值

2 新建ros包

知道了雷達資料的資料結構,接下來我們將通過程式碼來更深入的認識下雷達資料。

首先,通過如下命令新建個工作空間,以及一個新的包

mkdir -p ~/catkin_ws/srccd ~/catkin_ws/src/catkin_create_pkg lesson1 roscpp sensor_msgs

由於我們使用c++進行編寫,所以第一個依賴為roscpp;由於雷達資料的資料型別為sensor_msgs/LaserScan,所以第二個依賴為sensor_msgs。

2.1 src/laser_scan_node.cc

首先,在src資料夾下新建個檔案,取名為laser_scan_node.cc 。將如下程式碼複製進去,程式碼的意思已經在註釋中解釋的很清楚了。

#include #include // 宣告一個類class LaserScan{private:    ros::NodeHandle node_handle_;           // ros中的控制代碼    ros::NodeHandle private_node_;          // ros中的私有控制代碼    ros::Subscriber laser_scan_subscriber_; // 宣告一個Subscriberpublic:    LaserScan();    ~LaserScan();    void ScanCallback(const sensor_msgs::LaserScan::ConstPtr &scan_msg);};// 建構函式LaserScan::LaserScan() : private_node_("~"){    ROS_INFO_STREAM("LaserScan initial.");    // 將雷達的回撥函式與訂閱的topic進行繫結    laser_scan_subscriber_ = node_handle_.subscribe("laser_scan", 1, &LaserScan::ScanCallback, this);}LaserScan::~LaserScan(){}// 回撥函式void LaserScan::ScanCallback(const sensor_msgs::LaserScan::ConstPtr &scan_msg){    ROS_INFO_STREAM(        "seqence: " << scan_msg->header.seq <<         ", time stamp: " << scan_msg->header.stamp <<         ", frame_id: " << scan_msg->header.frame_id <<         ", angle_min: " << scan_msg->angle_min <<         ", angle_max: " << scan_msg->angle_max <<         ", angle_increment: " << scan_msg->angle_increment <<         ", time_increment: " << scan_msg->time_increment <<         ", scan_time: " << scan_msg->scan_time <<         ", range_min: " << scan_msg->range_min <<         ", range_max: " << scan_msg->range_max <<         ", range size: " << scan_msg->ranges.size() <<         ", intensities size: " << scan_msg->intensities.size());    // 第5個點的歐式座標為    double range = scan_msg->ranges[4];    double angle = scan_msg->angle_min + scan_msg->angle_increment * 4;    double x = range * cos(angle);    double y = range * sin(angle);    ROS_INFO_STREAM(        // 第5個數據點對應的極座標為:         "range = " << range << ", angle = " << angle <<         // 第5個數據點對應的歐式座標為:         ", x = " << x << ", y = " << y    );    // 通過ranges中資料的個數進行雷達資料的遍歷    // for (int i = 0; i < scan_msg->ranges.size(); i++)    // {    // }}int main(int argc, char **argv){    ros::init(argc, argv, "lesson1_laser_scan_node"); // 節點的名字    LaserScan laser_scan;    ros::spin();    // 程式執行到此處時開始進行等待,每次訂閱的訊息到來都會執行一次ScanCallback()    return 0;}

2.2 CMakeLists.txt

由於我們新增了一個.cc檔案,所以我們要在CMakeLists.txt中將這個檔案新增編譯選項,在檔案的底部新增如下內容:

# 為指定的檔案生成可執行檔案add_executable(${PROJECT_NAME}_laser_scan_node src/laser_scan_node.cc)# 為生成的可執行檔案新增依賴add_dependencies(${PROJECT_NAME}_laser_scan_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})# 為生成的可執行檔案新增庫的連結target_link_libraries(${PROJECT_NAME}_laser_scan_node  ${catkin_LIBRARIES})

2.3 package.xml

由於我們是使用的catkin_create_pkg命令生成的包,在生成包的時候它已經將package.xml配置好了,所以我們這裡不需要再去改這個檔案,只將生成的檔案內容列出來。

<?xml version="1.0"?><package format="2">  <name>lesson1name>  <version>0.0.0version>  <description>The lesson1 packagedescription>  <maintainer email="[email protected]">lxmaintainer>  <license>TODOlicense>  <buildtool_depend>catkinbuildtool_depend>  <build_depend>roscppbuild_depend>  <build_depend>sensor_msgsbuild_depend>  <build_export_depend>roscppbuild_export_depend>  <build_export_depend>sensor_msgsbuild_export_depend>  <exec_depend>roscppexec_depend>  <exec_depend>sensor_msgsexec_depend>    <export>  export>package>

2.4 編譯

修改好CMakeLists.txt檔案後,就可以進行程式碼的編譯了,具體指令如下:

cd ~/catkin_wscatkin_make

2.5 執行

不出意外的話應該沒有產生編譯錯誤.

編譯完成後會在 ~/catkin_ws/devel/lib/lesson1 資料夾下 產生了一個叫做 lesson1_laser_scan_node 的可執行檔案。

我們可以通過如下命令進行執行

首先,開一個終端,在終端中輸入 roscore再新建一個終端,在終端中輸入 cd ~/catkin_ws/devel/lib/lesson1 ./lesson1_laser_scan_node

將打印出如下的log:

[ INFO] [1606545572.752075473]: LaserScan initial.

此時是沒有其他訊息打印出來的,這是因為我們的程式碼沒有接收到鐳射雷達的資料訊息,所以我們通過如下命令進行bag的播放,bag資料下載下來後,右鍵單擊進行解壓,並放在 ~/bagfiles 資料夾下。

(本文對應的bag資料可以去我的公眾號: 從零開始搭SLAM中回覆lesson1獲取下載連結,)

再次新開一個終端,輸入如下命令cd ~/bagfilesrosbag play lesson1.bag

這時,在執行 ./lesson1_laser_scan_node 的終端視窗中應該會不停地打印出如下訊息

[ INFO] [1606545575.110606737]: seqence: 4131, time stamp: 1606455444.278184417, frame_id: front_laser_link, angle_min: -3.14159, angle_max: 3.14159, angle_increment: 0.00436332, time_increment: 7.15627e-05, scan_time: 0.102979, range_min: 0.01, range_max: 25, range size: 1440, intensities size: 1440[ INFO] [1606545575.110772238]: range = 2.6, angle = -3.12414, x = -2.5996, y = -0.0453758

2.6 結果分析

從這些資訊中我們可以看出:

  • 雷達的座標系為front_laser_link

  • 雷達資料的最小角度與最大角度分別為 -3.14159 與 3.14159,可見,這是一個水平視角為360度的雷達

  • 雷達資料的最近最遠距離分別為 0.01m 與 25m,可見,這個雷達的盲區為1cm

  • 雷達掃描一週將返回1440個數據點。

2.7 說明

2.7.1雷達資料

從雷達資料中我們可以得到雷達資料的最基本訊息,但是這些訊息不一定是真實可靠的,因為這些資料是雷達的驅動包中寫的,有些雷達廠商的程式碼不規範,在發出雷達資料時填的資訊有可能是錯誤的。如,雷達的盲區很少能有1cm這麼小的,一般都10cm以上。

2.7.2 座標轉換

還有,雷達資料中的 ranges 欄位中儲存的只有極座標系下的距離值,如果我們想知道每個資料點對應歐幾里得座標,還需要將極座標進行轉換。

轉換的方法就是 通過索引來獲取 ranges 中的值,再通過索引算出這個值對應的角度

第i個數據點的距離值為 ranges[i]
第i個數據點的角度為 angle = angle_min + angle_increment * i
所以這個點對應的x座標為 ranges[i] * cos(angle)
所以這個點對應的y座標為 ranges[i] * sin(angle)

2.7.3 雷達資料的遍歷

二維鐳射雷達資料的遍歷只有通過 ranges 欄位的個數 進行for 迴圈,並通過索引進行距離值的獲取。

2.8 launch檔案

2.8.1 launch檔案

目前,我們想要啟動這個節點需要開3個終端才能夠啟動,那有沒有更方便的方式進行啟動呢。

當然是有的,ROS中使用launch檔案來實現這個功能。

首先,先將開啟的終端全部關掉,在ubuntu中終止正在執行的東西的命令為Ctrl+C。

# 新建一個launch資料夾mkdir-p~/catkin_ws/src/lesson1/launch

在新建的launch資料夾中新建一個名為 demo.launch 的檔案,並將如下內容填進去。

<launch>        <arg name="bag_filename" default="/home/lx/bagfiles/lesson1.bag"/>        <param name="use_sim_time" value="true" />        <node name="lesson1_laser_scan_node" pkg="lesson1" type="lesson1_laser_scan_node" output="screen" />        <node name="playbag" pkg="rosbag" type="play"        args="--clock $(arg bag_filename)" />launch>

接下來只要啟動這個launch檔案,它就會幫我們啟動roscore,想要的節點,以及bag包的播放。

2.8.2 設定環境

由於我們是新建的工作空間,我們首先要執行如下命令將我們新建的包的索引在終端中註冊一下,以便終端能夠找到我們的包。

rospack profilesource ~/catkin_ws/devel/setup.bash

其中source的這條命令,是每次新開一個終端都要重新執行一次的,也可以通過如下命令將其寫入到 .bashrc 中,這樣每次新開終端就不再需要執行source命令了。

echo "source ~/catkin_ws/devel/setup.bash" >> ~/.bashrc

環境弄好了,接下來我們通過roslaunch來啟動launch檔案

roslaunch lesson1 demo.launch

如果一切順利的話我們可以得到上邊同樣的結果。

3 總結

本篇文章中,我們通過獲取鐳射雷達資料的例子,從零新建了一個ROS中的package。

我們講解了如何通過命令新建包,以及在新建包的時候的依賴如何新增。

之後,我們新建了一個.cc檔案,檔案中的內容就是按照標準ROS的格式書寫的,以後我們的程式都會按照這個模式進行程式碼的編寫。在程式中,我們知道了雷達資料的訊息內容,如何進行極座標與歐式座標的轉換,以及如何對雷達資料進行遍歷。

之後,我們修改了CMakeLists.txt檔案,併成功編譯和執行。

最後,我們講解了launch檔案以及如何設定程式碼的環境。

4 Next

接下來的文章我們不會再這麼細緻的進行環境的講解,我們將更多的關注功能的實現以及程式碼的實現。

下一篇文章我們將要介紹如何對雷達資料進行處理與過濾,如何在雷達資料中識別出人的小腿,並將人的小腿過濾掉。



其他原創文章可以戳:
從零開始搭二維鐳射SLAM --- 鐳射雷達資料效果對比從零開始搭二維鐳射SLAM --- 前言從零開始搭二維鐳射SLAM --- 序章

文章將在公眾號: 從零開始搭SLAM進行同步更新,歡迎大家關注,以在文章更新的第一時間通知您。

同時,也希望您將這個公眾號推薦給您身邊做鐳射SLAM的人們,大家共同進步。

公眾號中回覆lesson1可以獲取本文中的bag資料的下載連結
在公眾號中回覆開源地址可以獲取本文中程式碼的github地址

0df7c4dda0ed5a88d0527b49128b6c04.png