Arduino Uno R3とCAN-BUS Shieldを使って、DDT M15モータをテストしたときのコード
DDT M15[1]は、Switch Scienceで輸入されている中国製の安いけど比較的よくできたモーターです。
いいところは置いておいて、問題点だけ列挙しておきます。
・マニュアルがなんか変
・電源電圧の範囲が狭い。24Vが規定電圧で、下限が22V以上で、動作する上限の記述がなし。瞬間最大定格は63V。18V-22Vに落ちても即停止とはならないけれど、アラームが付く。つまり、かなり安定した電源を用意しなければならない。
・初代からバージョンが進んでいるのに、速度制御が怪しいらしい。
・タイヤの交換できなさそう。タイヤの向きの変更方法も不明。
Switch Scienceの方がサンプルコードを公開してくださっているのですが、面倒くさいことにGo言語なので、Arduino用にちょっと書き直してみます。
高トルクモーターDDT-M15の使い方 #DirectDriveTech — スイッチサイエンス
使用機材は、Arduino Uno R3とSeed Studio CAN-BUS Shild v2です。
また電源は、OWON SPE6103(60V/10A)です。最大出力電流がモーターの最大負荷時の電流未満なので、最大負荷テストはできません。
テスト用の土台は、ダイソーで買ってきた木材とアルミのは材で製作しました。
// demo: CAN-BUS Shield, send data // loovee@seeed.cc #include <SPI.h> #define CAN_2515 // #define CAN_2518FD // Set SPI CS Pin according to your hardware #if defined(SEEED_WIO_TERMINAL) && defined(CAN_2518FD) // For Wio Terminal w/ MCP2518FD RPi Hat: // Channel 0 SPI_CS Pin: BCM 8 // Channel 1 SPI_CS Pin: BCM 7 // Interupt Pin: BCM25 const int SPI_CS_PIN = BCM8; const int CAN_INT_PIN = BCM25; #else // For Arduino MCP2515 Hat: // the cs pin of the version after v1.1 is default to D9 // v0.9b and v1.0 is default D10 const int SPI_CS_PIN = 9; const int CAN_INT_PIN = 2; #endif #ifdef CAN_2518FD #include "mcp2518fd_can.h" mcp2518fd CAN(SPI_CS_PIN); // Set CS pin #endif #ifdef CAN_2515 #include "mcp2515_can.h" mcp2515_can CAN(SPI_CS_PIN); // Set CS pin #endif byte canTx(int id, unsigned char stmp[8], int delay_ms = 10 ) { // send data: id = 0x00, standrad frame, data len = 8, stmp: data buf byte ret = CAN.MCP_CAN::sendMsgBuf(id, 0, 8, stmp); delay(delay_ms); // send data per 10ms SERIAL_PORT_MONITOR.println("CAN BUS sendMsgBuf ok!"); return ret; } byte canRx(byte& len,unsigned long &id, unsigned char *buf) { byte ret = 0; if (CAN_MSGAVAIL == CAN.checkReceive()) { // check if data coming ret = CAN.readMsgBuf(&len, buf); // read data, len: data length, buf: data buf unsigned long canId = CAN.getCanId(); SERIAL_PORT_MONITOR.println("-----------------------------"); SERIAL_PORT_MONITOR.print("get data from ID: 0x"); SERIAL_PORT_MONITOR.print(canId, HEX); SERIAL_PORT_MONITOR.print("\t"); for (int i = 0; i < len; i++) { // print the data SERIAL_PORT_MONITOR.print(buf[i]); SERIAL_PORT_MONITOR.print("\t"); } SERIAL_PORT_MONITOR.println(); } } void setup() { SERIAL_PORT_MONITOR.begin(115200); while(!Serial){}; while (CAN_OK != CAN.begin(CAN_1000KBPS)) { // init can bus : baudrate = 500k SERIAL_PORT_MONITOR.println("CAN init fail, retry..."); delay(100); } SERIAL_PORT_MONITOR.println("CAN init ok!"); byte len = 0; unsigned long id = 0; unsigned char recv_buf[8]; // send data: id = 0x00, standrad frame, data len = 8, stmp: data buf unsigned char motor_can_terminal_resistor_switch_setting[8] = {0, 0, 0, 0, 0, 0, 0, 0}; canTx(0x109, motor_can_terminal_resistor_switch_setting); canRx(len, id, recv_buf); unsigned char set_status_report[8] = {0x80, 0, 0, 0, 0, 0, 0, 0}; canTx(0x106, set_status_report); canRx(len, id, recv_buf); unsigned char setting_mode_and_feedback[8] = {0, 0, 0, 0, 0, 0, 0, 0}; canTx(0x105, setting_mode_and_feedback); canRx(len, id, recv_buf); } void loop() { byte len; unsigned long id; unsigned char recv_buf[8] = {0, 0, 0, 0, 0, 0, 0, 0}; unsigned char query_operation[8] = {0x01, 0x01, 0x02, 0x04, 0x55, 0, 0, 0}; canTx(0x107, query_operation); canRx(len, id, recv_buf); unsigned char sending_in_openloop[8] = {0x10, 0x01, 0, 0, 0, 0, 0, 0}; canTx(0x32, sending_in_openloop); canRx(len, id, recv_buf); } // END FILE
ボリュームで回転速度を調整できるように修正。中央が0なことに注意。
void loop() { byte len; unsigned long id; unsigned char recv_buf[8] = {0, 0, 0, 0, 0, 0, 0, 0}; int i = analogRead( PIN_ANALOG_INPUT ); float RANGE_MAX = 32767.f; float RANGE_MIN = -32767.f; float f = (float)i / 1023.f * (RANGE_MAX-RANGE_MIN) + RANGE_MIN; Serial.println( f ); unsigned char query_operation[8] = {0x01, 0x01, 0x02, 0x04, 0x55, 0, 0, 0}; canTx(0x107, query_operation); canRx(len, id, recv_buf); unsigned char data0 = ((int)f >> 8) & 0xff; unsigned char data1 = ((int)f) & 0xff; unsigned char sending_in_openloop[8] = {data0, data1, 0, 0, 0, 0, 0, 0}; canTx(0x32, sending_in_openloop); canRx(len, id, recv_buf); }
[1] DDT M1502D_233 タイヤ付きダイレクトドライブモーター 24V/9.6Nm/115rpm — スイッチサイエンス
メモ:お手軽にWebRTCでVideoChatをしたいので、Janus-gatewayをAWS EC2やRaspberry Pi4上に立てたときのメモ
(まだ書きかけ)
今、ネットワーク越しに動画データを送りたいと考えたときに、どうしてもWEBRTCがその候補に挙がってきます。WEBRTCのライブラリやサービスは山のようにあるのですが、進歩が速いことを理由に行き届かないことが多くて、どれもこれも中途半端で問題だらけです。
まずWEBRTCが何なのかを把握するために、次の4つぐらいの区別が付くようにしとく必要が有ります。
・WEBRTC(P2P): 1対1で繋ぐとき。(もともとの設計。簡単。)
・WEBRTC SFUサーバー: サーバーを中心の媒介として複数間をつなぐとき
(・WEBRTC MCUサーバー): サーバーを中心の媒介として処理をさせつつ複数間をつなぐとき。使う機会はあまりなさそう。
・WEBRTCクライアント: ユーザーがWEBRTCを使うときのインターフェース
インターネットに繋いでNAT越えをするときには、次も知っておく必要が有ります。WEBRTCが難しいとか言われるのは、むしろこっちな気がする。
・STUNサーバー: インターネットから自分クライアントのネットワークがどのように見えているかを教えてくれるもの
・TURNサーバー: NAT越しのデータをやり取りするときに中継してくれるもの。サービスをお金を払って買うのでなければ、基本的にcoturnとかでPublicIPのサーバーを自分で用意する必要がある。
逆に言うとインターネットに繋ぐ必要が無ければ、こんなの要らないんです。SoftetherでVPNを繋げても構わないとかいう環境だったら、そうしちゃった方が早いし安い。
今やりたいことは自分の管理下にあるローカルネットワークで動画を転送したいだけです。なので、GstreamerでもFFMpegでも良いっちゃ良いのですが、なんだかトラブルだらけであまり直に触りたくありません。OBSが遅延無く配信してくれればそれでも十分なのですが、遅延が大きいのが解決できません。遅延が少ないことを前提にするとWEBRTCに乗っかるのが早い、となります。
お金がたっぷりあれば、時雨堂さんのSora Cloudが抜群に良いと思います。簡単に圧倒的な高画質を実現できるし、NAT越えに困ることも無いでしょう。HololensやUnity用でクライアントを作るためのSDKも用意されていて、他には類似の良いものがあまり見つけられません。(結局止めてたらしい。)
でも、固定費を年間60万円とか120万円とかそれ以上払える場合に限りますし、ビジネス展開することが前提のサービスです。また、海外との接続は対象外になっています。あとクライアントのためのSDKを作ってすぐ止めてしまう(ROSとか。。。Momoも音声デバイス選択とか放置されているし)ので、開発コストを掛けるリスクが取れません。
ちょっとWEBRTCを試してみたいとか、ビジネスとしてサービスを提供するのでなければ、オープンソースで開発されているWEBRTCサーバーが選択肢に入ってきます。
・Mediasoup
・Janus-gateway
が有名どころです。たいてい有料のものも含めて、どのサービスもクライアントは自分で作れ、というスタンスなのですが、開発コストを掛けたくないので、良いサンプルがあるかどうかがとても重要になってきます。
Githubに落ちているクライアントをつまみ食い程度に試した印象だと、Mediasoupは2020年以前のクライアントサンプルがことごとくまともに動かない感じがします。WEBインターフェースのgetUserMediaがhttps必須になった後のもので、使いやすい感じのものが見つけられませんでした。(Electronで動いて欲しかった、けど、分からなかった。)
Janus-Gatewayは、自身が用意しているWEBサンプルが充実しているのと、中国人の方が作りかけているQtとC++を使ったインターフェースが動くには動いてくれました。中途半端でもC++で書いてあってくれれば、あとはこっちで何とかなるので大変に有難い。ただ残念ながらGithubに上がっているJanus-Gateway向けの他のクライアントの例は多くは有りませんでした。
ということで、個人レベルでWEBRTC SFUを使うなら、Janus-gatewayの公式サンプルが良さそうです。
メモ:Janus-gatewayを立てて、推奨画質やビットレートの目標値を設定するのに編集が必要だったファイルはこちら。
nano /var/www/html/janus.js
sudo nano /opt/janus/etc/janus/janus.jcfg
sudo nano /opt/janus/etc/janus/janus.plugin.videoroom.jcfg
リンク
[0] https://github.com/meetecho/janus-gateway
[1] https://www.mikan-tech.net/entry/2020/05/02/173000
[2] https://www.mikan-tech.net/entry/2020/05/02/173000
[3] https://qiita.com/mksamba/items/601ae5b738b16b2b81d3
[4] https://github.com/meetecho/janus-gateway/issues/1354
[5] https://qiita.com/mksamba/items/601ae5b738b16b2b81d3
[6] https://towardsaws.com/setting-up-janus-webrtc-on-aws-a8aa8914b0c6
[7] https://qiita.com/satosisotas/items/d154b168772982a74b8d
[8] https://stackoverflow.com/questions/60261648/ice-failed-for-component-1-in-stream
[9] https://qiita.com/akase244/items/09d0898e51cf1b0faad7
AWS
[/] https://qiita.com/kanegoon/items/4bcdf5184cf1752eb44f
メモ:"qt.qpa.plugin: Could not find the Qt platform plugin "windows" in "" error" がWindows環境vcpkgでopenCVをインストールしたときに出てきてしまった場合の対処法
オムロンの環境センサ2JCIE-BU01の公式サンプルにちょっと足して、シリアルナンバーを取得できるようにする
オムロンの環境センサ2JCIE-BU01は1万円ぐらいの価格で、USBドングルぐらいの大きさで、一杯データの取れる優秀な奴です。Bluetoothも内蔵していますので、有線でも無線でも使えます。
問題点があるとすれば、名前が覚えにくいことぐらいですね。ずっと使っていますが、いまだにうろ覚えです。
pythonの公式サンプルコードが公開されていて、自前のプログラムからすぐに呼び出して使うことができます[1]。
ただ複数台使おうとすると、このサンプルだとシリアル番号を取ることができないので、個体特定が面倒くさいです。ちょっと書き足してシリアルを取ればいいだけなので、やってしまいましょう。
最初からシリアルも取れるサンプルプログラムを公開されている方もいらっしゃる[2]ので、そちらを使われてもいいかもしれません。
データの書式は公式ユーザーマニュアルの70-73ページあたりのcommon frame formatとpayload frame formatに書かれています。(マニュアルの最初に書いてほしい・・・)
https://components.omron.com/jp-ja/ds_related_pdf/CDSC-016.pdf
前略 def now_utc_str(): """ Get now utc. """ return datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S") def print_info(info): """ print device information. """ model_number = info[7:17].decode() serial_number = info[17:27].decode() firmware_revision = info[27:32].decode() hardware_revision = info[32:37].decode() manufacture_name = info[37:42].decode() print("") print("Model number:" + model_number) print("Serial Number:" + serial_number) print("Firmware Revision:" + firmware_revision) print("Hardware Revision:" + hardware_revision) print("Manufacturer Name:" + manufacture_name) if __name__ == '__main__': # Serial. ser = serial.Serial("COM3", 115200, serial.EIGHTBITS, serial.PARITY_NONE) # Get Device Information 0x180A # common frame format is Header(0x52, 0x42: 2bytes) + Length(Payload-CRC-16: 2bytes) + Payload(n bytes) +CRC-16(2bytes) by little endian # payload frame format is Command(0x01:Read, 0x02:Write, 1byte) + Address(2bytes) + Data(n bytes) command = bytearray([0x52, 0x42, 0x05, 0x00, 0x01, 0x0a, 0x18]) command = command + calc_crc(command, len(command)) tmp = ser.write(command) time.sleep(0.1) info = ser.read(ser.inWaiting()) # common frame format is Header(0x52, 0x42: 2bytes) + Length(Payload-CRC-16: 2bytes) + Payload(n bytes) +CRC-16(2bytes) by little endian # payload frame format is Command(0x01:Read, 0x02:Write, 1byte) + Address(2bytes) + Data(n bytes) print_info(info) time.sleep(1) try: # LED On. Color of Green. 後略
[1] omron-devhub/2jciebu-usb-raspberrypi
github.com
[2] nobrin/omron-2jcie-bu01
github.com
今日の本文
Whisperで僕の英語をどうしてもうまく認識してくれないので、良いマイクを買いました。でも結果は変わりませんでした。ソニーのマイクさんごめんなさい。
追記
3台つなげて、抜いてもさしても簡単には落ちないようにして、ZMQでOSC形式のMessageをひたすら送り続けるやつ。
掘り起こし手直し版 ofxZmqとofxTurboJpegで画像配信と受信
(編集中)
ofApp.h
#pragma once #include "ofMain.h" #include "ofxTurboJpeg.h" class ofxZmqSubscriber; class ofxZmqPublisher; class ofApp : public ofBaseApp{ public: void setup(); void update(); void draw(); void keyPressed(int key); void keyReleased(int key); void mouseMoved(int x, int y ); void mouseDragged(int x, int y, int button); void mousePressed(int x, int y, int button); void mouseReleased(int x, int y, int button); void mouseEntered(int x, int y); void mouseExited(int x, int y); void windowResized(int w, int h); void dragEvent(ofDragInfo dragInfo); void gotMessage(ofMessage msg); ofxZmqSubscriber* subscriber; ofxZmqPublisher* publisher; ofVideoGrabber cap; ofxTurboJpeg turbo; ofImage frame; ofBuffer send_buf; int quality; ofImage image; ofBuffer recv_buf; protected: ofFbo fbo; ofPixels pix; bool flag_display; ofVec2f src_size, dst_size; };
ofApp.cpp
#include "ofApp.h" #include "ofxZmq.h" #pragma comment(lib, "C:\\vcpkg\\installed\\x64-windows\\lib\\libzmq-mt-4_3_4.lib") //-------------------------------------------------------------- void ofApp::setup(){ ofSetFrameRate(60); cap.setVerbose(true); cap.setDeviceID(2); flag_display = true; src_size = ofVec2f(/*3840, 1920*/1920,1080); dst_size = src_size; // ofVec2f(2560, 1440); if (cap.initGrabber(src_size.x, src_size.y)) { //frame.allocate(cap.getWidth(), cap.getHeight(), OF_IMAGE_COLOR); quality = 95; // start server publisher = new ofxZmqPublisher(); publisher->setHighWaterMark(1); publisher->bind("tcp://*:9999"); } // start client subscriber = new ofxZmqSubscriber(); subscriber->setHighWaterMark(1); // subscriber->connect("tcp://localhost:9999"); fbo.allocate(dst_size.x, dst_size.y, GL_RGB, 4); } //-------------------------------------------------------------- void ofApp::update(){ while (subscriber->hasWaitingMessage()) { subscriber->getNextMessage(recv_buf); turbo.load(image, recv_buf); cout << "received data: " << recv_buf.size() << endl; } cap.update(); if (cap.isFrameNew()) { if (src_size != dst_size) { // TODO: using FBO for image size adjustment fbo.begin(); cap.draw(0, 0, fbo.getWidth(), fbo.getHeight()); fbo.end(); fbo.readToPixels(pix); turbo.save(send_buf, pix, quality); } else { turbo.save(send_buf, cap.getPixelsRef(), quality); } publisher->send(send_buf); } } //-------------------------------------------------------------- void ofApp::draw(){ if (!flag_display) return; if (src_size != dst_size) { // resized fbo ofPushMatrix(); ofScale(0.5); fbo.draw(0, 0, fbo.getWidth(), fbo.getHeight()); ofPopMatrix(); //image.draw(cap.getWidth(), 0); } else { cap.draw(0, 0); //image.draw(cap.getWidth(), 0); } } //-------------------------------------------------------------- void ofApp::keyPressed(int key) { if (key == OF_KEY_RETURN) { flag_display = !flag_display; std::cout << "display " << flag_display << std::endl; } if (key == OF_KEY_UP) { quality = (quality < 100) ? quality + 1 : quality; std::cout << "quality " << quality << std::endl; } if (key == OF_KEY_DOWN) { quality = (quality > 0) ? quality - 1 : quality; std::cout << "quality " << quality << std::endl; } } //-------------------------------------------------------------- void ofApp::keyReleased(int key){ } //-------------------------------------------------------------- void ofApp::mouseMoved(int x, int y ){ } //-------------------------------------------------------------- void ofApp::mouseDragged(int x, int y, int button){ } //-------------------------------------------------------------- void ofApp::mousePressed(int x, int y, int button){ } //-------------------------------------------------------------- void ofApp::mouseReleased(int x, int y, int button){ } //-------------------------------------------------------------- void ofApp::mouseEntered(int x, int y){ } //-------------------------------------------------------------- void ofApp::mouseExited(int x, int y){ } //-------------------------------------------------------------- void ofApp::windowResized(int w, int h){ } //-------------------------------------------------------------- void ofApp::gotMessage(ofMessage msg){ } //-------------------------------------------------------------- void ofApp::dragEvent(ofDragInfo dragInfo){ }
買う予定の本2022年7月版
諸事忙しすぎて、本を積むことすらできていません。すでに世間からおいて行かれている気がするので、本を積むことで功徳も積んで、少しでも普通に近づきたいと思います。
でも、何冊かもう山に埋もれているような気も。。。
コンピュータビジョン関係
CAD Modeling関係
実写合成のためのBlender3DCG制作ワークフロー
実写合成のための Blender 3DCG制作ワークフロー | Taka Tachibana |本 | 通販 | Amazon
リアルタイムグラフィックスの数学
リアルタイムグラフィックスの数学 ― GLSLではじめるシェーダプログラミング | 巴山 竜来 |本 | 通販 | Amazon
作例で学ぶ Substance 3D Designerの教科書
作例で学ぶ Substance 3D Designerの教科書 | もんしょ, 黒澤 徹太郎, mino, 佐藤 英一 |本 | 通販 | Amazon
ロボット関係
人に優しいロボットのデザイン 「なんもしない」の心の科学
ROS2とPythonで作って学ぶAIロボット入門
ROS2とPythonで作って学ぶAIロボット入門 (KS理工学専門書) | 出村 公成, 萩原 良信, 升谷 保博, タン ジェフリー トゥ チュアン |本 | 通販 | Amazon
Unityではじめる ROS・人工知能 ロボットプログラミング実践入門
Unityではじめる ROS・人工知能 ロボットプログラミング実践入門 | 布留川 英一, 佐藤 英一 |本 | 通販 | Amazon
ロボット工学者が考える「嫌なロボット」の作り方―ヒューマンエージェントインタラクションの思想
ロボット工学者が考える「嫌なロボット」の作り方──ヒューマンエージェントインタラクションの思想 | 松井 哲也 | Kindle本 | Kindleストア | Amazon
LiDARを用いた高度自己位置推定システム - 移動ロボットのための自己位置推定の高性能化とその実装例
LiDARを用いた高度自己位置推定システム - 移動ロボットのための自己位置推定の高性能化とその実装例 - | 赤井 直紀 |本 | 通販 | Amazon
機械学習
ディープラーニングを支える技術
ディープラーニングを支える技術 ——「正解」を導くメカニズム[技術基礎] Tech × Books plus | 岡野原 大輔 | コンピュータ・IT | Kindleストア | Amazon
Scientific Visualization: Python + Matplotlib
機械学習を解釈する技術〜予測力と説明力を両立する実践テクニック
物理数学ノート 新装合本版
The Mathematics of Artificial Intelligence
arxiv.org/abs/2203.08890
データビジュアライゼーションの基礎 ―明確で、魅力的で、説得力のあるデータの見せ方・伝え方
データビジュアライゼーションの基礎 ―明確で、魅力的で、説得力のあるデータの見せ方・伝え方 | Claus O. Wilke, 小林 儀匡, 瀬戸山 雅人 |本 | 通販 | Amazon
オーディオ関係
主にFinalのブログから。もう、洋書とか探すの大変なのでFinalで売って欲しい。。
オーディオトランスデューサ工学― マイクロホン、スピーカ、イヤホンの基本と現代技術 ―
オーディオトランスデューサ工学―マイクロホン、スピーカ、イヤホンの基本と現代技術 (音響テクノロジーシリーズ) | 大賀 寿郎, 日本音響学会 |本 | 通販 | Amazon
Audio Processing – Learning from experience(邦訳「オーディオ信号処理 – 経験からの学習」)
JEITA RC-8140C ヘッドホン及びイヤホン
マイクロホン・スピーカ談義
図解入門 よくわかる 最新 音響の基本と仕組み
図解入門よくわかる最新音響の基本と仕組み[第2版] (How‐nual Visual Guide Book) | 岩宮 眞一郎 |本 | 通販 | Amazon
Headphone technology - Hear-through, bone conduction, and noise canceling
AES E-Library » Headphone Technology: Hear-Through, Bone Conduction, and Noise Canceling
Sound System Engineering, fourth edition
DigiFi 小岩井ことりと楽しむオーディオの世界 (別冊ステレオサウンド)
DigiFi 小岩井ことりと楽しむオーディオの世界 (別冊ステレオサウンド) | 小岩井ことり |本 | 通販 | Amazon