Jetson NanoでFPVラジコンを作る(9)
前回記事を書いたあとステアリングモーターと駆動モーターを制御するライブラリを書いた。
RCCar::steer(float)
とRCCar::go(float)
を使ってこんな感じで制御できる。
両関数とも引数の値域は0.0〜1.0である。
int main(int argc, const char *argv[]) { RCCar car; //swing the steering for(float s = 0.f; s <= 3.f; s+=0.05f) { car.steer(std::sin(2*M_PI*s)); std::this_thread::sleep_for(std::chrono::milliseconds(50)); } //stop and go for(float s = 0.f; s <= 2.f; s+=0.01f) { auto speed = 0.2/*max speed cap*/ * ((-std::cos(2*M_PI*s)+1)/2); car.go(speed); std::this_thread::sleep_for(std::chrono::milliseconds(100)); } return 0; }
今回はゲームパッドの入力を受け取るためのライブラリを書く。
現在使用しているゲームパッドは以下。
GitHubに第七回で使用したevtest
のソースコードがあった*1ので、
以下のLinux Kernelのドキュメントを参照しながらコードを読んだ。
do_capture()
という関数がボタンなどの入力を受け付ける処理のメイン部分となっている。
ざっくり要約すると、例えば/dev/input/event0
などのデバイスファイルを
if ((fd = open(filename, O_RDONLY)) < 0) { //エラー処理 }
した後、
struct input_event ev[64]; int i, rd; fd_set rdfs; FD_ZERO(&rdfs); FD_SET(fd, &rdfs); while (!stop) { select(fd + 1, &rdfs, NULL, NULL, NULL); //... rd = read(fd, ev, sizeof(ev)); for (i = 0; i < rd / sizeof(struct input_event); i++) { //struct input_eventの処理 } }
のような感じでイベント(struct input_event
)を受け取る。
ここをみると、struct input_event
はごくシンプルな構造体になっている。
struct input_event { struct timeval time; unsigned short type; unsigned short code; unsigned int value; };
timeは入力時刻で今回はあまり気にしないとして、イベント種別を表すtypeはここ、具体的なイベント名を表すcodeと入力値を表すvalueはtype毎に意味が決められており、ここに説明がある。
これもざっくり要約してみる。
ON/OFFの二値をとるキーやボタンはtypeがEV_KEY
となり、この場合codeは例えばKEY_A
(キーボードのAというキーの場合)のようになる。valueはキー押し下げ時に1、キーのリリース時に0が設定される。
アナログコントローラーやトリガーのような多値をとるセンサーの場合はtypeがEV_ABS
*2となり、この場合も具体的なセンサー名がcodeに設定される。valueはセンサー毎に値域(0〜1023や0〜65535など)が決まっている*3。
では、ことゲームパッドに関してどのボタンが使えるのかというのがここに書いてある。 図が分かりやすかったので以下に引用する。
____________________________ __ / [__ZL__] [__ZR__] \ | / [__ TL __] [__ TR __] \ | Front Triggers __/________________________________\__ __| / _ \ | / /\ __ (N) \ | / || __ |MO| __ _ _ \ | Main Pad | <===DP===> |SE| |ST| (W) -|- (E) | | \ || ___ ___ _ / | /\ \/ / \ / \ (S) /\ __| / \________ | LS | ____ | RS | ________/ \ | | / \ \___/ / \ \___/ / \ | | Control Sticks | / \_____/ \_____/ \ | __| | / \ | \_____/ \_____/ |________|______| |______|___________| D-Pad Left Right Action Pad Stick Stick |_____________| Menu Pad
アナログスティックのcodeは左側はABS_X
(横軸)、ABS_Y
(縦軸)、右側はABS_RX
(横軸)、ABS_RY
(縦軸)である。
また、トリガーについてもドキュメントには記載があるが、今使っているゲームパッドだとevtest
の結果を見る限りドキュメントとは違い、codeは左側、右側それぞれABS_Z
、ABS_RZ
となっている。
上記を踏まえ、簡単なC++のライブラリを作りここにpushした。
使い方はこんな感じ。
下記を実行すると、ゲームパッドを認識したとき、ABS_X
とABS_RZ
を操作したとき、ゲームパッドを認識しなくなったときにメッセージが出力される。
#include "lunchjet_user_input_device.h" #include <iostream> #include <linux/input.h> #include <linux/input-event-codes.h> class Listener : public UserInputDevice::EventListener { void on_connect() { std::cout << "connect" << std::endl; } void on_receive(const struct input_event &event) override { std::cout << "event type:" << event.type << " code:" << event.code << " value:" << event.value << std::endl; } void on_close() { std::cout << "close" << std::endl; } }; int main(int argc, const char *argv[]) { //シグナル周りの設定 UserInputDevice device("/dev/input/event3", stop_user_input_device_thread); Listener listener; device.add_listener({{EV_ABS, ABS_X}, {EV_ABS, ABS_RZ}}, listener); if(device.listen() == -1){ std::cout << "listen() failed" << std::endl; exit(1); } pause(); std::cout << "exit main()" << std::endl; return 0; }
これでラジコンの制御とゲームパッドの入力がそれぞれ実装できたので、次回はいよいよ両者を結合して動作確認をする。
(追記)次回 : Jetson NanoでFPVラジコンを作る(10) - 自由課題
前回までの記事
Jetson NanoでFPVラジコンを作る(1) - 自由課題
Jetson NanoでFPVラジコンを作る(2) - 自由課題
Jetson NanoでFPVラジコンを作る(3) - 自由課題
Jetson Nanoを使ってFPVラジコンを作る(4) - 自由課題
Jetson Nanoを使ってFPVラジコンを作る(5) - 自由課題
Jetson Nanoを使ってFPVラジコンを作る(6) - 自由課題
Jetson Nanoを使ってFPVラジコンを作る(7) - 自由課題
Jetson NanoでFPVラジコンを作る(8) - 自由課題