Skip to content

RerunでWMX3のログを漏れなく可視化する

This content is not available in your language yet.

RerunでWMX3のログを可視化する」の続きです。

前回は位置情報をWindowsアプリのループの速度で取得していました。 そのため、取得頻度にばらつきがあったり、そもそも早くても数ミリ秒レベルでしか取得できないため、実際には不連続なデータになっていました。

今回は、WMX3の軸の位置をサイクルレベルで連続したデータとしてまとめて取得することで、この問題を解消します。 コードは以下です。

実行するには、wmx3-rerun-memlogcargo runします。 すると、Rerunビューワが起動し、軸0の指令位置をロギングします。 ログの頻度は約500ミリ秒ごとですが、もれなく連続してロギングできます。

コード説明

C側の実装

メモリログ機能を使うため、以下の関数を新しく追加しました。

./src/ffi/wmx.cpp
int start_memlog(int axis);
int stop_memlog();
int get_memlog(double pos[1000], long long cycle_counter[1000], size_t* pCount);

実装の詳細は省略します。WMX3 User Manualにメモリログの使い方は記載されています。

Rust側の実装

main.rsで、メモリログを使用し、一括取得した位置データをRerunで可視化します。

./src/main.rs
use std::{thread::sleep, time::Duration};
// lib.rsでビルドしたFFIコードを参照
use wmx3_rerun::*;
use std::time::Instant;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let ret = unsafe { open_wmx() };
println!("open_wmx = {}", ret);
// rerunビューワをスポーン。
let rec = rerun::RecordingStreamBuilder::new("wmx3-rerun").spawn()?;
// あらかじめ波形を登録しておく。
rec.log_static(
"axis0/cmdpos0",
&rerun::SeriesLines::new()
.with_colors([[255, 0, 0]])
.with_names(["cmdpos0"])
.with_widths([2.0]),
)?;
let ret = unsafe { start_memlog(0) };
println!("start_memlog = {}", ret);
// 10秒間メモリログで指令位置を取得してプロットに適用する。横軸はサイクルカウンタとする。
let start = Instant::now();
while start.elapsed() < Duration::from_secs(10) {
// 一括で1000サイクル分とってくる。実際に取得したサイズはcountで得る。
const N: usize = 1000;
let mut pos: [f64; N] = [0.0; N];
let mut cycle_counter: [i64; N] = [0; N];
let mut count: usize = 0;
let ret = unsafe { get_memlog(pos.as_mut_ptr(), cycle_counter.as_mut_ptr(), &mut count) };
println!("get_memlog = {} count {}", ret, count);
for i in 0..count {
rec.set_time_sequence("cycle", cycle_counter[i]);
rec.log("axis0/cmdpos0", &rerun::Scalars::single(pos[i]))?;
}
// 直近1000サイクル取得するので、周期が1msなら1000ms分になる。
// 500msならsleepしても漏れなくとれる。
sleep(Duration::from_millis(500));
}
let ret = unsafe { stop_memlog() };
println!("stop_memlog = {}", ret);
let ret = unsafe { close_wmx() };
println!("close_wmx = {}", ret);
Ok(())
}

メモリログの開始と終了を追記します。

let ret = unsafe { start_memlog(0) };
println!("start_memlog = {}", ret);
// 省略
let ret = unsafe { stop_memlog() };
println!("stop_memlog = {}", ret);

最大1000サイクルのデータを一括取得します。 C側にRustのメモリを渡す必要があるので、as_mut_ptr()というメソッドを利用します。

// 一括で1000サイクル分とってくる。実際に取得したサイズはcountで得る。
const N: usize = 1000;
let mut pos: [f64; N] = [0.0; N];
let mut cycle_counter: [i64; N] = [0; N];
let mut count: usize = 0;
let ret = unsafe { get_memlog(pos.as_mut_ptr(), cycle_counter.as_mut_ptr(), &mut count) };
println!("get_memlog = {} count {}", ret, count);

いよいよプロットへの追加です。 count個のデータを取得したので、その回数だけループを回します。

まず、サイクルカウンタを横軸に適用します。set_time_sequenceというメソッドに変わったことに注意してください。 位置データを取得した際に、一緒にサイクルカウンタというデータも取得しました。 サイクルカウンタはWMX3内のサイクル番号を表していて、通信開始から1サイクルごとに+1されるカウンタです。

次に、そのサイクルでの位置をプロットします。

for i in 0..count {
rec.set_time_sequence("cycle", cycle_counter[i]);
rec.log("axis0/cmdpos0", &rerun::Scalars::single(pos[i]))?;
}

これは500ms待機するコードです。 メモリログ機能により、最大で1000サイクル、つまり周期1msで1000ms分のデータをもれなく取れるため、 500ms待機しても連続してデータロギングができることがわかります。

// 直近1000サイクル取得するので、周期が1msなら1000ms分になる。
// 500msならsleepしても漏れなくとれる。
sleep(Duration::from_millis(500));

実行結果です。予めWOSで往復運転させています。 サイズが2MBと大きいので、リンクにしています。見てもいいよという方はリンクをクリックしてご覧ください。

アニメーション画像を見る。