Category Archives: GPS/GNSS/RTK

芝刈りロボットへの道 ~試作版の車体製作3~

前回は芝刈りモーターを取付けて実際に芝生を刈り取れる事が確認できました。
今回は電源回りを整理していきます。

これまでの配線

自動走行ローバーに後付けで芝刈りモーターを追加したので、走行系と芝刈り系が完全に分離しており、芝刈りモーターは単にスイッチでON/OFFするだけでした。

構想

・走行系と芝刈り系のバッテリーを共通にしたいので、小型刈払い機(草かるぞう)に元々付いていた18Vバッテリー1個で両方に供給する。
・草刈るぞうバッテリーはマキタ18Vと接点共通なのでマキタバッテリーも使用可となる!
・バッテリー電圧をPixhawkから測定する為に、抵抗で分圧してAD入力端子に入れる。
・バッテリー電流検出機構も後付けできる様にしておく。
・芝刈りモーターのON/OFFをフライトコントローラーから制御する。

あとついでに・・
・方位取得精度を上げたいので外付け磁気コンパスを取付ける。
・重心が後ろ気味なので前輪(駆動輪)がスリップしがち問題への対策。

となると必要な電圧は、芝刈りモーターに18V走行モーターに12VPixhawk他に5V の計3種類となります。

でも考えたのですが走行用モーターは18VをPWMの上限67%に制限してやれば、わざわざDCDCで12Vに下げなくても良さそうですね。
但し前回使用したモータードライバーIC(TB6612FNG)は扱える最大電圧が15Vなので耐圧不足です。定格18Vバッテリーの充電直後は21V程度あるので、少なくともそれ以上の耐圧があるドライバーが必要です。 そこで秋月電子で探すとTB67H450FNGというのがあり、これだと最大定格が50Vでした。 またこのICをモジュール化したAE-TB67H450というのも売られているので、これのモジュールを使ってみましょう。

芝刈りモーターをパワーMOSでPWM制御するのも含め、前回同様のArduino NANOを使って計3個のモーターを制御するESCを作ろうと思います。

回路検討

前回のTB6612FNGとTB67H450FNGとでは制御信号の仕様が異なっています。
TB6612FNGでは動作状態を制御するIN1,IN2とは別にPWM端子があり、1CHあたりデジタル出力2本+PWM1本で制御していました(あと全体を止めるSTBYにもデジタル出力1本)。


一方TB67H450FNGではIN1,IN2に直接PWMを入力する事になります。

という事はモーター2個を制御するにはPWMが4端子必要ですね。
元々ArduinoNANOは最大6本のPWM出力が可能ですが・・・

実はモーター駆動用PWMの周波数は可聴域以上に上げていたので前回はTimer1だけ周波数を上げて、これに繋がるD9,D10端子を使っていました。
今回更に2本の端子を使うとすると、Timer0又はTimer2に接続されているD5,D6,D3,D11の内から選ぶことになりますが、Timer0はシステム内でmillis()等に使われており、Timer2はtone()等に使われているので周波数を変えてしまうと他に影響が出るのです。

こうなると周波数を変えても他に影響しないPWM端子が足りませんね。
ArduinoNANOに拘らずSTM32F103とかに変更すればいいんですけど、できれば前回のプログラムを流用したいのです。
また芝刈りモーターをパワーMOSで駆動するならゲートに3.3Vだと少し心許なく、かといってゲートドライバーを追加するのは面倒という理由もあります。

そこでArduinoNANOとTB67H450FNGの間に下の様なロジック回路を入れてみました。

ArduinoNANOから出すのはPWM値と方向信号です。TB67H450FNGではIN1,2の一方がPWMで他方がHになり、方向信号でIN1,2を切替えます。
実はこの回路で試すと大体は動作しながらも、時々モーターが回転しないことがありました。どうやら起動の瞬間に過電流検出か何かの状態に陥っている気がします。TB67H450FNG異常解除にはIN1,2両方をLにする必要があるのですが上の回路だとこれができないんですよね。

という事で苦しまぎれですが、更にANDゲートを追加しました。
起動直後はGPIOからSTBY端子をLにしておく事で異常を解除できます(と言うか過電流にならなくなる筈)。
ANDゲートは74HC08を使いたかったのですが手持ちが無かったので74LS08を使いました。実際にはこのままだと電源投入の瞬間(マイコンが動き出すまでの間)はSTBY端子がHなのでモーターが一瞬(クッと)反応しており、できればSTBY端子をプルダウンしたいところです。が、LSだと結構小さな値の抵抗になるので現状プルダウンしていません(マイコンが動き出せば直ぐにSTBY状態になるので大丈夫だとは思いますが)。

芝刈りモーター制御

芝刈りモーターは逆転する必要がないのでパワーMOS-FET 1個でPWM制御します。
実際にはほぼフル回転で使うのでPWM周波数にはこだわらずD11端子を490Hzのまま使っています。サーボ信号入力が1000~1400msは停止、1600~2000mSで回転とし、回転開始/停止時はArduinoNANOのプログラムで等加減速制御としました。

芝刈りモーターを起動するとArduinoがリブートしてしまう問題発生

RC送信機から芝刈りモーターをONにするとArduinoがリブートする現象が発生したので
ゲート抵抗を1KΩとしました。1KΩは結構大きい気がしますが当初の100Ωだと効果が無かったのです。本当は100Ωと1KΩの間の丁度良さそうなところを調べるべきでしょうけど、1KΩでもゲート容量をざっと1000pFとするとCR時間は1μSなので、PWM周波数490Hzの周期は約2mSもあり影響は少ないと思います。

という事で現状の回路図

今の所こんな回路図です。
バッテリー電圧検出は18KΩと3KΩで分圧。電流検出回路はまだ実装していません。

基板の写真・・・
最後に追加したAND回路はユニバーサル基板を空中配線しています。
左下のスペースに電流検出回路を取り付ける予定(今は太線で直結状態)。

バッテリーはAmazonで¥999で売っていたマキタ18V用コネクタ(ソケットと呼ぶのかな?)で接続します。

ついでに磁気コンパスを外付けしたい

前回走らせたとき、Missionplannerに表示される車体アイコンが走行方向に対して微妙に角度がずれた状態で直進しているのが気になりました(ドリフトしながら直進している様に見える)。  また特定の場所にくると次のウェイポイントに向かう角度が定まらず時間がかかったり止まってしまったり。。。
コンパスをキャリブレーションしてもまだ怪しい感じなので外付けコンパスを追加してなるべく地面やモーターから遠い位置に取り付けたいと思います。

使った磁気コンパスはQMC5883LというAliexpressで300円程で買った基板で、かつての定番HMC5883Lに似ているけど微妙に違うセンサーです(HMC5883Lは廃版になっているらしい)。

QMC5883LモジュールはI2CでPixhawkに接続するだけでMissiponplannerを開いたら認識されていました。 設定画面でこちらのセンサーの優先順位を上げてキャリブレーションをやり直したのですが、暫く使っていると磁気コンパスの不一致的なエラーが出てきたので内臓コンパスは止めました。

もう一つついでに全体の重心を前に移動。

これまで時々車輪が空回りしていました。たぶん重心が後ろ気味だった為に前輪(駆動輪)の荷重が小さかったのが原因と考えて、少しでも前に荷重をかける為にGPSアンテナとその支柱を前に移動しました。
外付け磁気コンパスはGPSアンテナから離したかったので車体後部に載せますが、軽くするため塩ビパイプで支柱を作りました。

これでスリップによるスタックはほぼなくなりましたね!!

という事で現状のブロック図

では実際に芝生を刈ってみます。
一気にやるのは不安なので2分割した経路をつくって・・・

航空写真とRTKで取った座標は微妙にずれていて、実際の経路は写真に対し少し右側になります。

動作前の庭の状態は1週間程前に手作業で刈っており、奥の方はまた雑草が伸び始めています(手前側は何度か試走したので刈れている)。

刈り込み前

刈り込みの様子を動画で・・・

刈り込み終了後

刈り込み後

向きを変える時にしばらく考え込むとか、刈り込みラインとラインの間に少し残るとか、まだ問題はありますが何とか動き始めました。
何より猛暑の中を自分で歩き回らなくて済むのがいいです。

この後は・・・

現時点で最低限の自動刈り込みは出来たのですが、実行する為にPC側でMissionplannerを動かしたりRC送信機でスタートを掛けたり(ヤバくなったら止める目的もあるけど)と色々手間がかかります。
安全性も含めてもうちょっと簡単に稼働できる様にしたいのですが、それにはArdupilotだけだと厳しくなってきたのでコンパニオンコンピューターを積み、そっちで色々プログラムしたいです。
と言っても経路は固定なのでROS2を動かすほどでもなく、ラズパイ上のpythonで制御するのがいいかなと思っています。
これにはRaspberry pi ZERO2Wあたりが小型で積みやすいのですが、今なぜかどこも品薄なんですよね。秋月電子では11月入荷予定となっているしAliexpressだと1万円以上の値段で売られています。 という事で手持ちにあったRaspberry pi3でやってみて、ZERO2Wが手に入る様になったらその時点で考えようと思います。

令和8年熊本地震のRTK基準局への影響

先日の7月28日に大きな地震が発生しましたが、自分や家族は無事です。
10年前(2016年)の地震では屋根の瓦が落ちて修理が必要だったり雨漏りしたりと色々ありましたが今回はそこまでの被害はありませんでした。

自宅のRTK基準アンテナの座標が気になる

なのでこうして呑気にブログを書いていられるのですが、ひとつ気になっている事があります。
それが先日稼働させたRTK基準局の座標は影響ないのか?」です。
これまた呑気な話題ですみません。

報道を見ていると断層が数10センチとか1m以上ずれたという話を聞きます。
Cmレベルの測位をするのに基準が大きくずれたら影響しそうですよね。

国土地理院の電子基準点

以前自宅のアンテナ座標を調べた時国土地理院の電子基準点のデーターを貰いましたが、地震後に公式サイトを見ると下の様に基準点成果の公表停止とのアナウンスがありました。
令和8年熊本地震に伴う基準点成果の公表停止について

そして地震後間もない頃にデーター提供サービスの地図を見ると赤色表示の基準点も見られましたが、今見ると全部緑色になっているのは復旧したって事なんでしょうかね?

電子基準点の位置は変わっていなんですかね?
熊本局のデータをダウンロードしてRINEX形式のファイルヘッダを見ると下の様な行があります。これECEFという地球重心を原点とする座標で電子基準点の位置を示しており、単位がmなので、3月のデーターと比べて最大でも1mmしか違いがない様に見えます。

今回(8月11日):
-3502497.8301 4062727.0826 3439309.0622 APPROX POSITION XYZ

前回(3月21日):
-3502497.8297 4062727.0823 3439309.0632 APPROX POSITION XYZ

※但し国土地理院サイトのQ&Aにはこういう記載があり、正確な値は日々の座標値等を見る様にと書かれているので正確にはそちらを見るべきでしょうけど。

とにかく再測定してみた。

前回同様、自宅アンテナの座標を調べてみます。
2~3時間データーを取り、電子基準点熊本局のデーターをダウンロードして座標を求めたところ下の様なプロットで、FIX時の座標を平均すると・・・
前回と比べて緯度:-22.1mm経度:+5.3mm高度:-10.6mmでした。


まあ誤差範囲でしょうし、少なくとも自分の用途では問題ないです。

芝刈りロボットへの道 ~試作版の車体製作2~

前回の続きです。
車体に芝刈り用モーターと回転刃を取付けます。

まず現状の車体はこれ。

そして先日購入した小型刈払い機「草刈るぞう」をバラしてモーターより下の部分を取付けていきます。

ニシムタで¥3999だった「草かるぞう」くん

芝刈りモーターと回転刃物の取り付け

まず「草かるぞう」の蓋を開けるとモーター部分はこんな構造になっていました。
モーターの中にファンが見えているので、ここから空気を排出するのだと思いますが、吸気はどこからするのでしょう?

モーター上下に吸い込み口らしき穴がありますが下側(刃を付ける側)はプラスチックの筐体でほぼ塞がれています。コイルはファンよりも下ですが、上側から吸うだけで冷えるのかな?

とにかく取り付け方法を考えましょう。
モーターを車体裏側に固定する為、鉄板や平棒を切ったり溶接したりしてこんなブラケットを作りました。

そしてモーターを取り付けて・・・

なお、この「草刈るぞう」には刃物としてプラスチック刃、ステンレス刃、チップソーの3種類が付いています。

刃物3種を重ねたところ

どれが最適か分からないのでまずはステンレス刃を取付けてみます。
固定用のプラスチックパーツと金属パーツで刃物を挟み込む構造です。
(※ここのネジは逆ねじになっています)

ここで車体の裏側に取り付ける為に位置を合わせてみたところ、後輪に当たることが判明。
前進している時は良いのですが、後退時はキャスターが刃物に近い側を向くので当たってしまいます。

キャスター部分だけ後ろにずらす事も考えましたが、結局本体の合板をサイズアップして新たに切り出し、全部の部品を乗せ換えました。
これで刃物も当たらなくなり搭載完了。

芝刈り用バッテリー

芝刈り機構のバッテリーは当面「草刈るぞう」付属のバッテリーパックを使用します。
また最終的にはPixHawkからの信号でモーターを制御したいですが、とりあえず適当なスイッチを載せて手動でON/OFFします。
でも丁度良さそうなスイッチが手持ちになかったんですよね。。。そこでスイッチ代わりに使ったのがこれ、サーキットブレーカー。(こんな用途につかって大丈夫なんですかね?まあ20Aも流せるはずだし。)

ところでこの草刈るぞう君のバッテリー、マキタ18Vバッテリーと接続部分が互換である事に気づきました。これは何かと都合が良いです。

左が草刈るぞう、右がマキタ(互換品)。

いよいよ芝を刈ってみる

という事で芝刈りを試せるところまで来たので早速やってみます。
庭はもう芝生というよりも雑草が伸びすぎていて結構厳しそうですけど。

やってみると・・・それなりに刈れていますね。

車体が走った軌跡がはっきり分かります。


ところがモーターの軸周りに長めの草が絡みついていました。

刃物取付用プラスチック部品を取り外すとこんなに巻き付いています。

これは何か対策が必要ですね。ある程度予想していた事ではあるのですが。

なお「草刈るぞう」のオリジナルの構造ではこういうカバーの中にモーターが入っています。このカバーと刃物取付用プラスチックとで草の侵入を防いでいる様です。

ではこれに似たカバーを作って側面をガードしてみましょう。
Fusion360でこんなのを描いて・・・

3Dプリントして取り付けました。

これでかなり良くなりましたね。

という事で・・・

草が伸び切ったところは無理ですが、一度刈り取ってしまい、伸びた分だけ頻繁に刈る様にすれば大丈夫だと見込んでいます。

あとは電源回りをマキタ系バッテリーに一本化したりPixHawkから芝刈りモーターを制御したり、そのあたりを作っていきます。
その後ですが、自動走行のパラメーター調整や、現状だと手放しで芝を刈らせるのはまだ不安があるので、諸々の安全対策をしていこうと思っています。

芝刈りロボットへの道 ~試作版の車体製作~

熊本は梅雨も明け、今年も地獄の暑さが続いております。
庭では芝生も雑草もすくすく育っており、何とか楽して芝生を刈りたいという理由でモチベーションも高まり、芝刈りロボットの製作を進めています。
(でも完成する頃には涼しくなっていそうだなぁ。来シーズンには役に立つのか?)

という事で、今回は芝刈りの前段階としてArdupilotを試作ローバーに載せて制御するテストを進めます。

全体の回路はこんな感じ。

フライトコントローラーであるPixHawkを中心に、GPS受信機モーター、ESCDroneBridgeを載せているのはこれまでの投稿に書いた通りです。
そこにRC受信機、安全スイッチを載せ、電源は3セルリポおよび以前製作したMP1584のDCDCコンバーターを載せています。

バッテリーは3セルなので定格11.1V。これをモーター電源(定格12V)とし、更にDCDCコンバーターで5Vに落としてPixHawkに供給します。
バッテリー容量は今の所600mAhですが、芝刈りモーターを回す段階になったらもっと容量の大きなものに変更する予定です。

車体(試作版)を作る

塗装合板を適当なサイズに切ったものをベースにして各部品を載せていき・・・


前輪(駆動輪)は合板を丸く切った板に3Dプリントしたハブをねじ止めしてモーターに取り付けます。合板だけだとスリップしたので(当たり前ですよね)初期の頃3Dプリンターで使っていたタイミングベルト(5mmピッチ)をボンドG17で貼り付けました。

その後、コイツも滑るので止めました。

後輪はキャスターです。もうちょい径が大きい方が良さそうですが、まずはお手軽なヤツで。。。

後輪はキャスター1個(方向変えれるやつ)

電気的なパーツはテープでベタベタ貼って搭載。
モーターの影響か、PixHawkの磁気コンパスが異常値を出したので段ボール等で高さを稼いでいます。またGPSアンテナも低いとRTKがFIXし難かったので角材の上に載せました。

走行テスト。

何度か走らせたところ、駆動輪にタイミングベルトを貼り付けた事でスリップは減ってはいるのですが、それでも時々滑って進めなくなります。
そこでクローラー(キャタピラ)の使用を考えたのですが手ごろな価格で入手できるものが見当たらないんですよね。 何とか自作できないか(蝶番をつないだりとか)考えましたがいい方法が思い浮かびません。

という事で駆動輪の径を大きくし、幅も30mmに広げた車輪を3Dプリントして幅広のタイミングベルトを貼り付けて試します。

これはFusionで描いた図

これでかなり走破性は上がりましたね!!
なんか赤色のホイールがイイ感じでもあります。電装回りはまだテープだらけですが。

改良版駆動輪を取り付けたところ

MissionPlannerでウェイポイントを設定して自動で走らせる

庭にウェイポイントを設定して走らせたところ、初期設定のままだと結構大回りになってしまいました。
そこでMissionPlannerのシミュレーションモードで走らせたところ、これでも大回りをします。
シミュレーションの段階で思った動作をしなけりゃ実車でもまず無理ですよね。

シミュレーション上で自動走行した軌跡。だいぶ大回り。

という事でここからはシミュレーションモードで確かめながらArdupilotのパラメーターを色々と変更していきます。

パラメータ調整

結果ですが、Ardupilotのパラメーターの内、関係しそうな下記を変更しました。

WP_RADIUS
これはウェイポイントにどこまで近づけば到達したと判断するかというパラメーターなので、デフォルトの3mだと芝刈り目的では遠すぎます。
折角RTKで測位しているので、ここは0.1mに変更しました。

WP_PIVOT_ANGLE
進行方向と目標方向の差が指定した角度を超えていたら車体をその場旋回させる設定らしいです。車体の構造が左右の回転差で向きを変えるskid式なのですが、これがデフォルトの0だとその場旋回してくれないそうです(少しはやっているように見えるのが謎ですが)。
5度より大きな値をセットしないと無効という事なので6度に設定。

WP_SPEED
ウェイポイントに向かう時の速度設定。
0.5m/Sに設定しました。もう少し遅くても良いかもしません。
(実はこれは前の段階で設定していた)

CRUISE_SPEEDCRUISE_THROTTLE
目標の速度とその速度を出すのに必要なスロットルの初期値。
ローバーはCROUISE_SPEEDの巡行速度を目指してまずスロットルをCRUISE_THROTTLEの値に設定し、その後CROUISE_SPEEDに合う様に微調整するそうです。
0.5m/S、50%としました。
なおウェイポイントを巡回する時はWP_SPEEDの設定値が優先だそうです。その場合のスロットルはどうやって決めるのかという疑問があるので、まずはWP_SPEEDとCRUISE_SPEEDを同じ値にしておきました。

ATC_DECEL_MAX
減速の制限値。デフォルトの0にしておけば加速の制限値と同じになるそうですが、シミュレーターでは何故か行き過ぎる現象が出たので2m/SSに変更しました(実車では改めて試す必要がありそう)。

PSC_POS_P
経路外れ時の補正強度。(多分PはPIDのP)
シミュレーター上ではかなり大きめの値にしないと最短ラインから外れ気味だったのでデフォルト0.2に対し1.0に変更しました。これも実車では再調整が必要そうですね。

他にも調整すべき箇所がありそうですが、これでだいぶマシになりました。

上記パラメーターを変更してマシになった走行の様子。。。

芝刈り機構検討

実車でのパラメーターはまだ調整中ですが、並行して芝刈り機構の搭載を検討しています。
当初はリサイクルショップで電動芝刈り機を買ってくる事を考えていましたが中々手頃なものが見つかりません。

そして行き着いたのがこれ。アルミスの「草かるぞう」という名前の小型電動刈払機です。
(最初「アルテミス」かと思ったら「アルミス」でした)
これ近所のホームセンター「ニシムタ」のセールで3999円で売ってた物です。
Amazonとかで見るよりかなり格安で、この値段ならヘタに中古の電動芝刈り機を買ったりパーツを揃えて自作するよりも安くつきそうです。

という事で、新品の「草かるぞう」を買ってきました。バラす前に少し草を刈ってみたところ結構便利です。
このまま使いたくもなりましたが結局バラしてしまいました。

中身はDCモーターが入っていて、電気的にはバッテリーとの間にスイッチがあるだけです。

次回はこれを車体に取り付けていこうと思っています。

RTK基準局を設置~その2~

前回の作業で基準局用アンテナを設置し、その座標を求めました。
今回はRasberrypiを使って自宅用NTRIPサーバーを設置します。

構想

RTK基準局はアンテナで受信した電波の遅延等を補正データーとしてローバーに送ります。
テスト的に動かすならPCを基準局にしてローバーと1対1で接続するのでもOKですが、最終的には常時稼働にしたいし、また今後台数が増えてもいい様にもしたいです。

1対1の接続イメージ

その為に下図のイメージでローカルなNTRIPサーバーを稼働させようと思います。
NTRIPサーバーにはオープンソースのNtripcasterを使用します。
受信機のデーターを受け取ってNtripcasterに投げるのはRTKLIBに含まれるSTR2STRを使います。
受信機のデーターをSTR2STRがNtripcasterに送り、NtripcasterがGCSの要求に応じてデータを配信するイメージです。

自宅内にNtripcasterを動かすイメージ。今回はこれで行く。


ところでNTRIPは基準データをネットで配信するプロトコルで、これを利用したNTRIPサーバーが各地にあり多数の善意の基準局がデーターを配信されています(こんなところや、こんなところ参照)。
本当は自分の基準データーもこれらの公開されたNTRIPサーバーに上げれば近所の人まで使えてハッピーなのですが、今回立ち上げるのは自宅に設置したRTK基準局です。 会社や学校の設備ならともかく、今のご時世に自宅の座標をセンチメートルレベルで公開してしまうのは不安があるので当面これは止めときます。

公開されたNtripサーバーを使うイメージ。本当はこうしたいところだが当面やめておく。


なお最終的に常時稼働させたいので電力が少なくて済むRaspberryPiで構築します。
RaspberryPiには、ちょっと古いですがRaspberryPi2が手持ちにあったので、str2strもNtripcasterもこの中で動作させる事にします。

という事で今回やることは次の通りです。

  1. ZED-F9P受信機の設定
  2. RaspberryPiの準備(OSインストール他)
  3. Ntripサーバー(Ntripcaster)インストール
  4. STR2STR(RTKLIBに含まれる)インストール
  5. OS起動時に自動的に立上る設定とかシャットダウンボタンの設定

ZED-F9P受信機の設定

前回アンテナ座標を得るために受信機を設定しましたが、基準局として動作させるにはRTCMデータを出力させる必要があります。
参考にしたのはトラ技2019年10月号の第1部7章で、初期状態からの変更点は・・・

  • UBX-CFG-DGNSSの中で・・・
    Differential mode3=RTK fixed: Ambiguities are fixed whenever possible.に設定。
  • UBX-RXMの全て(RAWX,SFRBX等)を停止。
  • UBX-CFG-MSGの中で以下のメッセージにUSBへの出力を有効化(Message欄に下記を選んでUSBにチェック印を入れる)。
     01-35 NAV-SAT
     F5-05 1005 (基準極座標)
     F5-4D 1077 (GPS観測データ)
     F5-57 1087 (GLONASS観測データ)
     F5-61 1097 (Galileo観測データ)
     F5-4D 1127 (BeiDou観測データ)
     F5-4D 1230 (GLONAS補正データ)
     ※QZSS(みちびき)のRTCMデーター出力は今のところF9Pでは対応していない様です。
  • UBX-CFG-NAV5の中で・・・
     Dynamic Model = 2-Stationary
     Fix Mode = 3-Auto 2D/3D
     UTC Standard = 0-Automatic
  • UBX-CFG-PRTの中で・・・
     Target = 3-USB
     Protocol in = 0+1+5-UBX+NMEA+RTCM3
     Protocol out = 0+1+5-UBX+NMEA+RTCM3
  • UBX-CFG-TMODE3の中で・・・
     Mode = 2-Fixed Mode
     Lat,Long,Altを前回求めた座標値に設定。

・・・それぞれ忘れずに設定を書き込んでおく。

RasberryPiの準備

RaspberrypiOS(現行版のTrixie、GUIは不要なのでLite版)をインストールし、sshでログインできる様に設定しました。
そして下記の基本準備を実行しておきます・・・

 $ sudo apt update
 $ sudo apt install git

Ntripcasterをインストールする。

インストールは下記コマンドで特にエラーもなく終了しました。

$ git clone https://github.com/roice/ntripcaster
$ cd ntripcaster/ntripcaster0.1.5 # この時のバージョンは0.1.5だった。
$ ./configure
$ make
$ sudo make install
$ cd /usr/local/ntripcaster/conf
$ sudo cp -p ntripcaster.conf.dist ntripcaster.conf
$ sudo cp -p sourcetable.dat.dist sourcetable.dat

次にエディタでntripcaster.confを編集します。
不明な点やたぶんローカル使用だと不要お思われる点も多いですが、下記に記載の箇所を変して動作しています。

##################################
# NtripCaster configuration file #
################################################################################

############### Server Location and Resposible Person ##########################
# Server meta info with no fuctionality.

### 下記3行を変更したがローカル使用なので多分あまり影響しない。
location BKG                     →HOIHOIDOに変更
rp_email casteradmin@ifag.de     →自分のメールアドレスに変更
server_url http://caster.ifag.de →http://192.168.1.212に変更

########################### Server Limits ######################################
# Maximum number of simultaneous connections.

### 下記3行も気分的に変更した。
max_clients 100                  → 20
max_clients_per_source 100       → 20
max_sources 40                   → 8

######################### Server passwords #####################################
# The "encoder_password" is used from the sources to log in.

### パスワードを変更しておく。
encoder_password sesam01         → XXXXXXXX

#################### Server IP/port configuration ##############################
# The server_name specifies the hostname of the server and must not be set to
# an IP-adress. It is very important that server_name resolves to the IP-adress
# the server is running at.
# For every port, the server should listen to, a new port line can be added.

### 下記1行も変更したが、これもローカルだと意味なさそう。
server_name igs.ifag.de          →rtk.hoihoido.com
#port 80
port 2101

######################## Main Server Logfile ##################################
# logfile contains information about connections, warnings, errors etc.

logdir /usr/local/ntripcaster/logs
logfile ntripcaster.log

############################ Access Control ###################################
# Here you specify which users have access to which mountpoints,
# one line per mount.
#
# Syntax: /<MOUNTPOINT>:<USER1>:<PASSWORD1>,<USER2>:<PASSWORD2>,...,<USERn>:<PASSWORDn>
#
# /<MOUNTPOINT>: name of the mountpoint. Must start with a slash.
# <USERi>: name of the user that has access to <MOUNTPOINT>.
# <PASSWORDi>: password of <USERi>.
#

### たぶん、ここが一番大事
# example:
#/mount0:user0:pass0,user1:pass1,user2:pass2
#/BUCU0:user1:password1,user2:password2 → コメントアウト
#/PADO0                                 → コメントアウト
/HOIHOIDO                              → 追加

そしてエディタでsourcetable.datも編集します。
初期設定行を全部消して下記を記載しました。こちらもローカルサーバーなので適当なところもあります(もしかするとCASやNETの行は無くてもいいのかもしれません)。
きちんとした説明は公式のドキュメントを参照してください(doc形式なのでダウンロードする方が見やすいかも)。

CAS;192.168.1.212;2101;NtripInfoCaster;HOIHOIDO;0;JPN;32.87;130.76;http://192.168.1.212
NET;LOCAL;HOIHOIDO;N;N;192.168.1.212
STR;HOIHOIDO;Koshi;RTCM3.3;1005(1),1077(1),1087(1),1097(1),1127(1),1230(1);2;GPS+GLO+GAL+BDS;;JPN;32.87;130.76;0;0;u-bloxZED-F9P;none;N;N;;

STR2STR(RTKLIB)をインストール。

RTKLIB一式をインストールしてSTR2STRが使える状態にします。
これも下記コマンドで特に問題なくインストールできました。
設定する様なファイルはありません(実行時のコマンドラインオプションに色々書く方式)。

$ git clone https://github.com/tomojitakasu/RTKLIB.git
$ cd RTKLIB/app
$ chmod 755 makeall.sh
$ ./makeall.sh

動作確認

では手動で実行してテストしてみます。まずはNtripcaster。

$ cd /usr/local/ntripcaster/bin
$ sudo ./ntripcaster

次にstr2strを実行します。
こちらはコマンドラインに動作を色々と書いています。
やっている事を一言でいうと、「シリアルポートttyACM0から230400bpsで取ってきたデーターをntripプロトコルでlocalhostのポート2101に投げる。Ntripサーバーに投げる為のパスワードはXXXXXXXXで基準局の名前はHOIHOIDO」となります。

$ cd ~/RTKLIB/appstr2str/gcc
$ ./str2str -in serial://ttyACM0:230400 -out ntrips://:XXXXXXXX@localhost:2101/HOIHOIDO

この状態でRTK基準局として補正データーを配信してくれている筈なので、Ntripプロトコルで受信できるか試してみます。

まずWindows PC上でRTKNAVIを実行し、右上の[I]ボタンを押します。
(2)Base Stationだけにチェックを入れて、TypeをNTRIP Clientに、FormatをRTCM3に設定してOpt欄の「…」を押します。
そしてラズパイサーバーのIPアドレスとポート番号(2101)を入力して一旦[GetMountp]ボタンを押すと・・・

MountpointプルダウンにNTRIPサーバー内で管理している基準局一覧が出ます。
ここでは先程作ったHOIHOIDOのみが表示されるのでこれを選びます。

OK × 2回でメイン画面に戻り、[スタートボタン]を押すと・・・

基準局で受信した結果がグラフとして出てきますね。
でもよく見るとグラフ右上の表示が「GRE」となっています。これはGPS,GLONASS,Galileoを受信していることを示しますがBeidouが無いですね。
一旦「■Stop」で止めて「Options…」ボタンを押すと下記の通りBDSにチェックが入っていませんでした。
ここにチェックを入れて・・・

再びスタートすると受信衛星数が一気に増えて賑やかになりました。
グラフ右上の表示も「GREC」となり、「C」が増えています。

という事でBeidouも受信できていますね。
なお、この中にQZSS(みちびき)が含まれていないのは上にも書いた通り、F9PのRTCM出力にQZSSを含められないからです(実は方法があったりするのでしょうか?)。

ここまで来たら目的の動作は出来ているので、あとは使いやすい様に電源を入れたら即起動する設定と、電源を切るとき用にシャットダウンボタンを追加していきます。

RaspberryPi立上げ時に起動する設定

UNIX系OSの立上り時に自動的にプログラムを実行する設定は、古くは/etc/rc.localだったり、比較的最近まで/etc/rc*.dだったりしましたが、最近はまた新しくなっていてsystemdというのが標準で、これを使えば起動が早いそうです。という事でsystemdで設定してみます。

/etc/systemd/systemの下にntripcaster.serviceという名前でファイルを作って下記の内容を書きます。

[Unit]
Description=ntripcaster
After=syslog.target

[Service]
Type=simple
WorkingDirectory= /usr/local/ntripcaster/bin
ExecStart=/usr/local/ntripcaster/bin/ntripcaster
TimeoutStopSec=5
StandardOutput=null

[Install]
WantedBy = multi-user.target

同様にstr2str.serviceという名前で以下のファイルを作ります。

[Unit]
Description=str2str
After=ntripcaster.service

[Service]
Type=simple
WorkingDirectory= /usr/local/bin
ExecStart=/usr/local/bin/str2str -in serial://ttyACM0:230400 -out ntrips://:XXXXXXXX@localhost:2101/HOIHOIDO
TimeoutStopSec=5
StandardOutput=null

[Install]
WantedBy = multi-user.target

systemdを制御する主なコマンドは下の通りなので・・・

$ sudo systemctl start <サービス名> // 手動起動
$ sudo systemctl stop <サービス名> // 手動停止
$ sudo systemctl enable <サービス名> // 自動起動設定
$ sudo systemctl disable <サービス名> // 自動起動停止
$ journalctl -u <サービス名> // ログを見る

Ntripcasterとstr2strを手動で起動してみます。

$ sudo systemctl start Ntripcaster
$ sudo systemctl start str2str

この状態で上で試したのと同様にWindows PCからNtrip経由で情報を受け取れるはずです。
問題なければ下記コマンドで一旦停止し・・・

$ sudo systemctl stop Ntripcaster
$ sudo systemctl stop str2str

立ち上げ時の自動起動を有効にすると、再起動しても立ち上がっている筈です。

$ sudo systemctl enable Ntripcaster
$ sudo systemctl enable str2str

シャットダウンボタン。

常時起動にしてもシャットダウンする事はあります。その際sshでログインしてシャットダウンするのも面倒なのでシャットダウンボタンを付けておきます(これは色々な方法がありそうですが、自分の備忘録として書いときます・・・)

まずスイッチはGPIO3とGND間にタクトスイッチを付けました。

プログラムからGPIOをコントロールするライブラリとして以前はwiringPiを使っていましたが、もう古くてサポートされない様です。で、今風なのはlibgpiodだそうなので、まずこれをインストールしておきます。

$sudo apt install libgpiod3

そして下の内容でsdbuttond.cというファイルを作成し・・・

#include <stdio.h>
//#include <wiringPi.h>
#include <gpiod.h>
#include <stdlib.h>
#include <unistd.h>

//#define DBG

#define STARTTIME 10
#define STOPTIME 3
#define SDBUTTON 3

void DoShutdown() {
#ifdef DBG
  printf("Exec shutdown!!\n");
#endif
  system ("shutdown -h now");
}

int main() {
  int starttimer=STARTTIME;
  int stoptimer=STOPTIME;
  int enablef=0;
  int val;


  // libgpiod関係の準備ここから
  const char *chip_path = "/dev/gpiochip0";
  unsigned int offset = SDBUTTON;  // GPIO**
  struct gpiod_chip *chip;
  struct gpiod_line_config *line_cfg;
  struct gpiod_request_config *req_cfg;
  struct gpiod_line_request *request;
  struct gpiod_line_settings *settings;

  // チップを開く
  chip = gpiod_chip_open(chip_path);
  if (!chip) {
    perror("gpiod_chip_open");
    return 1;
  }

  // ライン設定作成
  settings = gpiod_line_settings_new();
  gpiod_line_settings_set_direction(settings, GPIOD_LINE_DIRECTION_INPUT);
  gpiod_line_settings_set_bias(settings, GPIOD_LINE_BIAS_PULL_UP);

  // ラインコンフィグ
  line_cfg = gpiod_line_config_new();
  gpiod_line_config_add_line_settings(line_cfg, &offset, 1, settings);

  // リクエスト設定
  req_cfg = gpiod_request_config_new();
  gpiod_request_config_set_consumer(req_cfg, "button");

  // リクエスト作成
  request = gpiod_chip_request_lines(chip, req_cfg, line_cfg);
  if (!request) {
    perror("gpiod_chip_request_lines");
    return 1;
  }
  // libgpiod関係の準備ここまで


  while(1) {
    val = gpiod_line_request_get_value(request, offset);
    if (enablef==0) {
      // まだイネーブルになっていない場合。
      if (val == 1) {
        starttimer--;
      } else {
        starttimer=STARTTIME;
      }
      if ( starttimer == 0) { enablef = 1;}
    } else {
      // イネーブルになった後。
      if (val == 0) {
        stoptimer--;
      } else {
        stoptimer=STOPTIME;
      }
      if ( stoptimer == 0 ) {
        DoShutdown();
        return 0;
      }
    }
#ifdef DBG
    printf("enablef:%d starttimer:%d stotimer:%d\n",
           enablef,starttimer,stoptimer);
#endif
    sleep(1);
  }
}

各#defineの意味は下記の通りです。

  • #define STARTTIME 10
    起動後この秒数はボタンを押してもシャットダウンしない。
  • #define STOPTIME 3
    この秒数押すとシャットダウンする(但しポーリングにつき1秒のばらつき有り)
  • #define SDBUTTON 3
    ボタンを接続するGPIO番号。3にしておくと再立ち上げにも使える。


次の様にコンパイルして実行ファイルsdbuttondを/usr/local/binの下にコピーします。

$ gcc sdbuttond.c -o sdbuttond -lgpiod
$ sudo cp sdbuttond /usr/local/bin
$ sudo chmod 755 /usr/local/bin/sdbuttond

先の2件と同様に/etc/systemd/system/sdbuttond.serviceを下の内容で作って・・・

[Unit]
Description=sdbuttond
After=syslog.target

[Service]
Type=simple
WorkingDirectory= /usr/local/bin
ExecStart=/usr/local/bin/sdbuttond
TimeoutStopSec=5
StandardOutput=null

[Install]
WantedBy = multi-user.target

お試し実行してみます。

$ sudo systemctl start sdbuttond

これでGPIO3に付けたタクトスイッチを数秒押すとシャットダウンする筈・・・試すと思惑通りシャットダウンしました。
ではもう一度立上げて恒久起動にします。

$ sudo systemctl enable sdbuttond

以上で一通りのインストールが終わったので・・・

写真の様なケースをThingiverseからダウンロードしてプリントし、壁に貼り付けていますが、本当に常時稼働させる様になったら防火対策のため金属製のケースに入れたいと思います。

RTK基準局を設置

Ublox ZED-F9P受信機を入手したので前から気になっていたRTKをやってみます。
最終的には庭で自動運転車を走らせて芝生を自動的に刈り込ませるのが目標です。

RTK

RTKというのはReal time kinematicの略でGPS(GNSS)衛星からの信号を高精度に解析してセンチメートル単位の測位を可能にする技術です。
※GNSSは測位システム全般を指す言葉でその内の一つがGPSですが、ここでは全てGPSと呼ぶ事にします。

センチメートル級測位というと日本の衛星「みちびき」のCLAS等もありますが、みちびきはまだ7機の完全体制になっていないし、精度的にもRTKの方が良い(らしい)のです。

RTKではGPS受信機を2つ使います。一方はアンテナの位置が既知である基準局で、衛星からの電波を受けて、その誤差を補正データとして出力します。もう一方は実際に測定する側(今回だと自動運転車、以下ROVERと呼びます)の受信機で、自分が受信した信号を基準局のデータで補正して自己位置を推定します。

GPSの誤差は近い距離内にある受信機間だとほぼ同じになるので基準局が近くにあるとROVERの位置が精度良く測定できる事になります。

基準局は必ずしも自分で用意する必要はなく、こんなところや、こんなところを見ると日本各地に基準データーを公開して頂けているサイトがあり、近い場所にあればそちらを利用するのが手っ取り早いです。
でも折角なので今回は自宅に基準局を設置して庭で測位してみようと思います。

受信機

入手した受信機はジオセンス社のF9PX1という機種で、Ublox社のZED-F9Pモジュールを搭載した基板です。
基板のままだと不安なので写真のケースを3Dプリントしました。
このケースの3DデータはThingiverseにアップしました

アンテナ

アンテナもUblox社製のANN-MB-00です。
基準局のアンテナは一旦設置したらあまり場所を動かしたくありません。cm単位で測位するための基準点なので1cm足りとも動かしたくないのです。 なので有り合わせの鉄板とアングルを溶接してアンテナ基台を作り、屋根に固定しました。
ここは屋根の一番高い所ではないので空を遮ってしまう部分が結構あります。本当はもっと高い位置に取り付けたいのですが、まずはこれでやってみます。

基準局のアンテナ位置を調べる。

基準局はアンテナの位置(座標)が正確に分かっている必要があります。
座標を知るには色々と方法がある様ですが、一番現実的な方法として暫く受信したデーターをファイルに書き出し、これを国土地理院の電子基準点のデーターから補正する方式でやってみます。
電子基準点のデーターは無料だとリアルタイムには提供されず、一時間毎に更新されるデーターをダウンロードする事になります。よって受信データーも一旦ファイルに落としておき、後付けで解析するのです。

手順としては以下の様に実施しました。
・ZED-F9P受信機を設定する。
・暫くの間データーを受信し、結果をファイルに落とす。
電子基準点のサイトから補正データーをダウンロードする。
rtklibに含まれる後付け解析ツールrtkpostを使って測位結果を算出する。
・測位結果はある程度ばらつきがあるので、その平均値を基準局の座標とする。

ZED-F9P受信機の設定

最初にZED-F9Pを設定します。
参考にしたのは下記書籍及びサイトです・・・

・トラ技2019年10月号 F9Pの設定詳細・RTKLIB・専用基準局製作
ZED-F9Pの設定方法
高精度衛星測位 RTK-GNSSチュートリアル

u-center(u-bloxの設定ツール)を開き、まずは上記資料の中で今回の座標特定に必要な内容だけを変更しました。

デフォルト設定値から変更した内容は・・・
・BaudRateを230400に設定
・受信衛星の選択画面(Viewメニューの「Generation 9 Advenced Configuration View」)を開いたが特に変更する箇所が見当たらなかったのでデフォルトのまま。
・ViewメニューのMessage Viewを開く。
・UBX-CFG-NAV5のMin SV Elevationをとりあえず15°程度に設定。
・UBX-CFG-RXMの中でRAWX,SFRBXを出力。
・NMEAではGxGGA,GxRMC,GxGSVを残してあとは停止する。
・UBX-CFG-NMEA内で、「High precision mode」にチェックを入れる。
・現段階ではRTCM出力は不要。
・変更を書込む。

なおデーターの更新レートを標準の1秒間隔から0.2秒間隔に変更できる様な事が色々なサイトに書いてありますが、私の所では0.2秒間隔だと受信中にRAWXデータが止まってしまうという現象が発生しました。色々試すと約0.7秒より長くすると安定したので、今回は1秒間隔(デフォルト通り)で使用しました。
(当面の用途ではRAWXはアンテナ座標を調べるとき以外は出力しなくて良いので大きな問題はない筈です)。

RTK-LIB

RTKを弄るにはrtklibをインストールしておくのが定番です。
私の環境ではrtklibのサイトを開くとブラウザから「この接続ではプライバシーが保護されません」というメッセージが出ますが、気にせず先に進みます。
そしてWindows版の最新2.4.3b34(ベータ版ですが)をダウンロードしてzipを解凍します(特にインストーラー等はなく解凍するだけ)。

まずはRTK-Naviで受信してみる。

その前にF9P基板とPCをUSBケーブルで接続し、デバイスマネージャーでCOM番号を調べておきます。

そしてRTK-LIBを解凍した階層下のbinディレクトリにある、rtknavi.exeを実行するとこんな画面が開きます。

右上の「I」というボタンを押すと次のダイアログが開きます(「I」は入力設定用ボタン)。

(1)Roverにチェックを入れ、TypeはSerialに設定してCmd列の「…」ボタンを押すと下のダイアログが出るので、PortのところにCOM番号、Bitrateのところに先程設定した230400bpsをセットして「OK」を押します。

そしてFormat列をu-blox UBXに設定してOKを押すとメイン画面に戻ります。

メイン画面の下の方の「Options…」を押すと次のダイアログが開くので、GPS,GLONASS,GAlileo,QZSSにチェックをいれて「OK」をクリック。

この時点で試しに受信してみましょう。
メイン画面左下の「▶Start」を押すと受信データがグラフで表示されます。

こんな感じで受信出来ていたら「■Stop」を押して一旦止めます。

受信ログを取得する。

ではログを取得する準備をします。
メイン画面右上の「L」を押してログ設定のダイアログを開きます。
(6)Roverにチェックを入れ、TypeにFileを設定し、LogFilePathsに適当なファイル名を設定します。
(ログファイル名には日付や時刻を含めておくと後で分かりやすいです。拡張子は.ubxにしておきます。)。

OKを押してMAIN画面で「▶Start」を押すと受信が始まるので、この状態でしばらく放置します(私は半日程置きました。長い方が良い筈ですが、どれくらいが適当かは不明。なお最初は短い時間で一度試してからが良いと思います。)。

取ったデータを解析する

データを解析するには上で落としたログをRINEX形式のファイルに変換します。
これにはRTKLIBに付属のRTKCONV.exeを実行します。
先頭の「RTCM,RCV RAW or RINEX OBX ?」に先程落としたログファイル名を指定すると出力ファイル名は自動的に設定されます。

「Options…」を押して出力形式が「RINEX Ver3.03」になっている事を確認します(違っていたら設定する)。

「OK」でMAIN画面に戻って「▶Convert」を押すと変換が始まり、XXX.obs(これがRINEX形式のファイル)が出来ていればOKです。

電子基準点からデータを取る。

受信したデータを解析する為に電子基準点の補正データを取得します。
まず下記にアクセスして・・・
https://terras.gsi.go.jp/
「観測データ取得」をクリック。なにやら「同意するか?」と聞かれたら「同意」をクリック・・

するとこんな地図が現れます。
各地の電子基準点で埋め尽くされていますね。。。

地図を拡大して最も近い基準点を選択し・・・

「ダウンロード」をクリック・・・

下の様に開始日時、終了日時には上記受信ログを落とした時間帯を含む範囲を指定します。
そして衛星はGRJE、RINEXverは3.02を指定。
「任意時間のデータダウンロード」をクリック。

するとこんな画面が開くので、「観測ファイル」、「衛星軌道情報ファイル」それぞれダウンロードします。

これでxxxx.o.gz , xxxx.tar.gzの2つのファイルがダウンロード出来ている筈です。
これらを解凍してxxxx.n,xxxx,g,xxxx,q,xxxx.l等に戻します。
(*.gzやtar.gzはUNIXでは一般的な圧縮/アーカイブ形式です。解凍方法が分からない場合は検索すると沢山出てきます。なおWindowsでも10あたりからtarコマンドが入っている様です。)
なお最近発見したのですが、xxxx.o.gzは解凍しなくても、そのまま指定すればRTKPOSTが読んで貰える様です。
またxxxx.tar.gzをそのまま指定するとエラーは出るものの解凍だけしてくれる様です。
 なので一旦エラー覚悟で実行して解凍されたファイルを改めて指定すればtarコマンドを実行せずに処理できます(今イチ良く分からん動作ですが)。

※素のままのファイル名だと後で分かりにくいので時刻範囲が分かるファイル名に変更しておいた方が良いと思います。

RTKPOSTで解析する。

以上により一通りのデータが揃ったので解析します。これにより自分のアンテナ位置の正確な位置(座標)を得るのです。

ではRTKLIBに付属のrtkpost.exeを実行すると下記の画面が開きます・・・


下の方の「Options…」ボタンを押して下記ダイアログを開き、Setting1タグにて・・・
・Positioning Mode をKinematicに。
・Filter Typeは一番厳密そうなCombinedを選び・・・。
・Elevation Mask(°) を30°程度に設定し(この辺りは適当)・・・。
・GPS,GLONASS,Galileo,QZSSにチェックを入れます。


PositionsタブのBaseStationをRINEX Header Positionに設定します。
その他にも細かなパラメーターが色々あって何がベストか分からないのもありますが、このあたりでOKを押してMAIN画面に戻ります。

後は処理するファイルを下記の様に設定していきます。
・RINEX OBS: Rover欄にはRTKNaviでダウンロードしてRTKConvで変換したRINEXファイル(XXX.obs)を指定。
・RINEX OBS: Base Stationには電子基準点からダウンロードして解凍したXXX.oファイルを指定。
・RINEX NAV/CLK,SP3,FCB,IONEX,SBS/EMS or RTCM欄にはXXX.n , XXX.g , XXX.l , XXX.qを指定。
・そして一番下の欄には出力ファイル名が自動的に入っているので・・・
「▶Execute」を押すと解析が始まります。

データー期間が長いと結構時間を要します。
終わったらPlotボタンを押すとこんな感じで分布が見えるはずです。

緑の点がRTKがFixしている(Cm級測位が出来ている)箇所で、黄色の点はFixしておらず精度が悪い点です。

取れたデータからアンテナ座標を求める。

アンテナ座標を求めるにはFixしているデーターだけ使いたいのでxxx.posファイルを以下の様にちょいといじります。

まずxxx.posファイルをテキストエディタで開くと下の様になっています。
この中でQと表示された列が1である行はFIXしたポイントで、その他の2や5になっている個所は精度が悪いのポイントなので、1の行を残して取り除きます。

これはテキストエディタで作業しても良いですが、面倒なのでこのページ末に記載したrubyスクリプトで処理しました。

Q=1の行以外を消してプロットし直すと次の様になりました。


同じスクリプトに-cオプションを指定して実行するとCSVファイルが出ます。
これをExcel等の表計算ソフトで開き、latitude,lognitude,height列を全部平均した座標をアンテナの位置としました(実はExcelは値段が高く持っていないのでLibreOfficeで実行した・・・)。
ついでに標準偏差を出してみるとLatitude≒9.2E-8, Lognitude≒1.67E-7, Hight≒35.07cmとなりました。 3σをmmになおすと30.9mm, 47.0mm, 105.2mmなので、まあセンチメートル単位で測位出来ていると思います。

そして・・・

以上でアンテナ座標を決める事が出来たので引き続き以下の作業を実施していきますが、長くなったので続きは後日投稿します。

・RaspberrypiでF9P受信器に接続してRTCM3データを取得し、同時にNTRIPcasterを実行して自宅内に配信。
・もう一つのF9P受信機をPixhawkに接続してRTK測位。
・ローバーを作成して庭で走らせる。

データー処理スクリプト

#!/usr/bin/env ruby
# coding: utf-8
#
# 2025.12.07 by https://www.hoihoido.com (konchi@hoihoido.com)
#
# rtkpost.exeが吐き出す*.posファイルからFIXしている行を取り出す。
# ※FIXしたらQ列(左から5ブロック目)が1になる。
#
# 引数:
# -c CSV出力(ヘッダなし)
# -q num Q列がnumで示す値である行だけ抜き出す。
# ※Q列はFIXやFLOAT等の状態を示す。1がデフォルトでFIX状態。
#

require 'optparse'

options = {}
opt = OptionParser.new
opt.on("-c", "--CSV", "CSV output") { |v| options[:CSV] = v }
opt.on("-q VAL", "--Q", Integer, "Q num (1=FIX:default)") { |v| options[:Q] = v }

opt.parse!(ARGV)

if ( options[:Q]) then
qval=options[:Q].to_s
else
qval="1"
end

n=0
while (l=gets()) do
if (options[:CSV] and l =~ /^% +GPST +/) then
title=l.split
print "DATA,"
print title[1..-1].join(','),"\n"
end

if l =~ /^\d{4}\/\d{2}\/\d{2} +\S+ +\S+ +\S+ +\S+ +(\d) / then
if $1 == qval then
if ( options[:CSV]) then
l.gsub!(/ +/,",")
end
puts l
n+=1
end
else
if (! options[:CSV]) then
puts l
end
end

end

print(",AVE,")
print("=average(C2:C",n+1,"),")
print("=average(D2:D",n+1,"),")
print("=average(E2:E",n+1,")\n")
print(",σ,")
print("=stdev(C2:C",n+1,"),")
print("=stdev(D2:D",n+1,"),")
print("=stdev(E2:E",n+1,")\n")
print(",3σ,")
print("=3*C",n+3,",")
print("=3*D",n+3,",")
print("=3*E",n+3,"\n")
print(",3σ[mm],")
print("=40000000/360*C",n+4,"*1000,")
print("=40000000/360*D",n+4,"*1000*cos(C",n+2,"/180*pi()),")
print("=E",n+4,"*1000\n")