前提・実現したいこと
初めて質問させていただきます。
現在、Slamtec社のRPLidar A3(https://www.slamtec.com/en/Lidar/A3)を用いた、マッピングシステムを、RaspberryPi3 B+ にて作成しております。
SDKが公開されているので、その中のultra_simpleというアプリケーションでテストを行い、C++には不慣れながらも、出力データに日付と時刻を付与するところまではできました。
出力結果は以下の通りです。
2020-02-14 16:41:06.3,0.12,02084.00 2020-02-14 16:41:06.3,0.34,02088.00 2020-02-14 16:41:06.3,0.56,02084.00 (中略) 2020-02-14 16:41:06.3,1.48,02084.00 2020-02-14 16:41:06.3,1.72,02084.00
データは「日時,theta,dist」という要素で、Lidarが取得した点群までの角度(theat)と距離(dist)を表しています。
そのままでは、点群を閲覧するソフト等で閲覧することができない(*1)ため、これらのデータから、X座標とY座標に変換する必要があります。
*1:これはもしかしたら私が探しきれていないだけかもしれません。もしご存知の方がおられたらご教示いただけますと幸いです。
また、Lidarはクローラーに搭載して使用しているのですが、従来通りの水平ではなく、垂直に設置しています。
閉鎖空間を進みながら、その内壁を点群で取得していくことが目的です。
そのため、実際には進んだ距離も必要となりますが、こちらはクローラーにエンコーダーを搭載して、取得しています。
発生している問題
現在、最後の、XY座標への変換の工程で、行き詰っております。
当初、こちらのサイトを参考に変換を行ったのですが、うまくいかず、
三角関数部分を少し変更して、以下のように変更しました。
[参考ソース]
Python
1#!/usr/bin/python3 2 3import re 4import math 5 6file = "test.dat" # Lidarログ出力ファイル 7 8f = open(file, 'r') 9line = f.readline() 10 11while line: 12 line = line.strip() 13 m = re.search("theta: ([0-9.]+) Dist: ([0-9.]+) Q: ([0-9]+)", line) 14 s = float(m.group(1)) 15 c = float(m.group(2)) 16 r = s * math.pi /180 17 a = c * math.sin(r) 18 b = (-1.0) * c * math.cos(r) 19 print(str(a) + " " + str(b) + " " + "1.0") 20 line = f.readline() 21 22f.close()
[修正後ソース]
Python
1#!/usr/bin/python3 2 3import math 4 5file = "lidarlog2.txt" # Lidarログ出力ファイル 6 7f = open(file, 'r') 8line = f.readline() 9ignore_line = 1 10 11for line in f: 12 if ignore_line > 9: # headerを無視 13 line = line.strip().rstrip('\n') 14 line_data = line.split(",") 15 s = float(line_data[1]) 16 c = float(line_data[2]) 17 print(s) 18 a = c * math.cos(s) # X座標 19 b = c * math.sin(s) # Y座標 20 print(line_data[0] + " " + str(a) + " " + str(b)) 21 line = f.readline() 22 else: 23 ignore_line = ignore_line + 1 24 25f.close() 26
いずれの場合でも、リダイレクト出力したファイルを点群閲覧ソフトで読み込んだところ、以下のようになりました。
上記画像は、クローラーを停止させた状態で、数秒間、Lidarを回して取得した点群を、変換したものです。
正しくは、赤で囲った部分が、左の天井から壁、床に当たり、全体では長方形になるはずのところ、
左に行けば行くほど曲がってしまいます。
計算がまずいのか、そもそもの考え方が間違っているのか、自分では思い至らないため、先達の皆様の知見をお借りいただければと、
望む次第です。
試したこと
はっきり言って、まったくなす術がないのですが、補足にも書いております通り、幸いにしてPythonで点群取得を実装されている方が
おられたので、そのコードから該当しそうな部分を参考に、ソースを変更したりもしましたが、今度は点群が全て同心円上に並んでしまい、
まともな形状にすらなりませんでした。
補足情報(FW/ツールのバージョンなど)
最初、こちらのソースを利用していたのですが、
C++のほうが取得点群数が倍以上も多いため、このような回りくどいことを行っています。
最後に
何をどう書いていいかもよくわからないまま、思いつくことを書いてまいりましたが、ほかに必要な情報がありましたら、ご指摘いただければ幸いです。
よろしくお願いいたします。
修正(2020/2/27 16:30)
修正後のソースで、角度をラジアンに変換して、三角関数の引数のする箇所を間違っていました。
ただ、この点に関して、ラジアンに変換せずにthetaを直接使用するように改変したかといいうと、
- Lidarの仕様書に、thetaが角度として図示されており、
- 参考ソースで変換した際に点群が前述の画像の通り、歪んでいたため
です。
最初に使用した修正後ソースは以下の通りです。
[修正後ソース]
Python
1#!/usr/bin/python3 2 3import math 4 5file = "lidarlog2.txt" # Lidarログ出力ファイル 6 7f = open(file, 'r') 8line = f.readline() 9ignore_line = 1 10 11for line in f: 12 if ignore_line > 9: # headerを無視 13 line = line.strip().rstrip('\n') 14 line_data = line.split(",") 15 s = float(line_data[1]) 16 c = float(line_data[2]) 17 print(s) 18 r = s * math.pi /180 19 a = c * math.cos(r) # X座標 20 b = c * math.sin(r) # Y座標 21 print(line_data[0] + " " + str(a) + " " + str(b)) 22 line = f.readline() 23 else: 24 ignore_line = ignore_line + 1 25 26f.close() 27
修正と追加(2020/2/28 11:05)
Githubに専用のリポジトリを作って、Pythonのソースと部屋の中で取得した点群データをアップしました。
ソースも一部修正をしています。
具体的には、if ブロック内の以下の点です。
- 無視するheader部分の行数が間違っていたので、
ignore_line > 9
を、ignore_line > 8
に変更 line = f.readline()
を削除
修正:解決編(2020/2/28 14:55)
yuki23様のご指摘にあった、出力されている角度が360を超えている問題を解決すべく、元々のC++のソースと、自身が改変したC++を見比べ、可能な限り元の状態を維持しつつ、必要なデータを加えていくように修正を行いました。
その結果、360度以上のデータが出力されることはなくなりました。
そのままの形では、依然θと距離しか出力されてませんので、上述の修正後ソースをさらに修正し、Ⅹ座標とY座標に変換したところ、以下のように、部屋の形がきちんと描画されました。
元のソース、修正しておかしくなっていたソース、最終的に正常に動いたソースは以下の通りです。
[オリジナルC++ソース(抜粋)]
C++
1 while (1) { 2 rplidar_response_measurement_node_hq_t nodes[8192]; 3 size_t count = _countof(nodes); 4 5 op_result = drv->grabScanDataHq(nodes, count); 6 if (IS_OK(op_result)) { 7 drv->ascendScanData(nodes, count); 8 for (int pos = 0; pos < (int)count ; ++pos) { 9 printf("%s theta: %03.2f Dist: %08.2f Q: %d \n", 10 (nodes[pos].flag & RPLIDAR_RESP_MEASUREMENT_SYNCBIT) ?"S ":" ", 11 (nodes[pos].angle_z_q14 * 90.f / (1 << 14)), 12 nodes[pos].dist_mm_q2/4.0f, 13 nodes[pos].quality); 14 } 15 } 16 17 if (ctrl_c_pressed){ 18 break; 19 } 20 } 21
[修正失敗版C++ソース(抜粋)]
C++
1 while (1) { 2 rplidar_response_measurement_node_t nodes[8192]; 3 size_t count = _countof(nodes); 4 5 op_result = drv->grabScanData(nodes, count); 6 7 if (IS_OK(op_result)) { 8 drv->ascendScanData(nodes, count); 9 for (int pos = 0; pos < (int)count ; ++pos) { 10 clock_gettime(CLOCK_REALTIME, &ts); 11 std::strftime(buf, sizeof buf, "%F %T", std::localtime(&ts.tv_sec)); 12 13 millisec = ts.tv_nsec / 100000000; 14 theta = (nodes[pos].angle_q6_checkbit >> RPLIDAR_RESP_MEASUREMENT_ANGLE_SHIFT)/64.0f; 15 dist = nodes[pos].distance_q2/4.0f; 16 17 outfile << buf 18 << ":" 19 << millisec 20 << "," 21 << std::to_string(theta) 22 << "," 23 << std::to_string(dist) 24 << "\n"; 25 26 printf("%s.%ld,%03.2f,%08.2f\n", 27 buf, 28 millisec, 29 (nodes[pos].angle_q6_checkbit >> RPLIDAR_RESP_MEASUREMENT_ANGLE_SHIFT)/64.0f, 30 nodes[pos].distance_q2/4.0f); 31 } 32 } 33 34 if (ctrl_c_pressed){ 35 break; 36 } 37 } 38
[最終版C++ソース(抜粋)]
C++
1 while (1) { 2 rplidar_response_measurement_node_hq_t nodes[8192]; 3 size_t count = _countof(nodes); 4 5 op_result = drv->grabScanDataHq(nodes, count); 6 if (IS_OK(op_result)) { 7 drv->ascendScanData(nodes, count); 8 for (int pos = 0; pos < (int)count ; ++pos) { 9 clock_gettime(CLOCK_REALTIME, &ts); 10 std::strftime(buf, sizeof buf, "%F %T", std::localtime(&ts.tv_sec)); 11 millisec = ts.tv_nsec / 100000000; 12 printf("%s.%ld,%03.2f,%08.2f\n", 13 buf, 14 millsec, 15 (nodes[pos].angle_z_q14 * 90.f / (1 << 14)), 16 nodes[pos].dist_mm_q2/4.0f); 17 } 18 } 19 20 if (ctrl_c_pressed){ 21 break; 22 } 23 } 24
whileブロックに入ってすぐの、rplidar_response_measurement_node_hq_t nodes[8192];
というところで、hqがついたりつかなかったりしていますが、
修正失敗版を最初にmakeした時、「hqありは使えない、ないのを使え」みたいなエラーをはかれたため、修正し、その後発生するエラーに対応していったら、結果的に上述のようになった次第です。
今回、改めてSDKを落とすと、バージョンが上がっており、それが原因か、単に以前が自分のミスだったのかは不明ですが、するっとmakeできてしまい、動くに至りました。
もっと細かく、何がどうしてエラーだったか、ご報告するのが本来の在り方だとは承知しておりますが、いかんせん、この問題とはこの1年、何度か中断しつつ付き合ってきたため、最初の頃の記憶があいまいで、説明のしようがありません。
ご理解、ご容赦の程よろしくお願いいたします。
謝辞
最後に、皆様のご助言のおかげで、無事にきれいな点群(と言っても16000点前後/秒程度ですが、これはLidarの限界です)の取得に成功しましたこと、心より感謝いたします。
ずっと一人で作業をしていたら、動いているからと、元のソースを見直すことを頑なに拒んでいたかもしれません。
こちらで、私などよりもはるかに知見のある皆様の意見に、素直に耳を傾けることができ、解決することができました。
本当にありがとうございました。
回答3件
あなたの回答
tips
プレビュー