• 車種別
  • パーツ
  • 整備手帳
  • ブログ
  • みんカラ+

ToshiTechのブログ一覧

2021年12月28日 イイね!

Fusion360用3/3Dマウスの製作(1号機)

Fusion360用3/3Dマウスの製作(1号機)

・ネットでたまたま目にした"3Dマウス"という言葉が気になって調べてみたら、
https://www.3dconnexion-japan.com/%E8%A3%BD%E5%93%81%E7%B4%B9%E4%BB%8B/
世の中には3D-CAD等でモデルの回転やズームを行える左手用のデバイスがあるという事がわかりました。直感的な操作ができて良さげですが、お試しで使うにはちょっと手が出せない価格ではあります。
alt

・そこで"3Dマウス 自作"で検索するとこんな方もいらっしゃる。↓すごい!
https://shk-maker.hatenablog.jp/entry/2019/10/12/154328

・ここで改めて考えてみるとFusion360でのモデルの操作方法は、
 ズーム:マウスホイールを回転
 平行移動:マウスホイールを押したままマウスを移動
 回転:シフトキーとマウスホイールを押したままマウスを移動
なので、左手が本当に必要となるのは回転だけです。そこでジョイスティックを1つだけ使って回転に特化した(というか回転しかできない)左手用のデバイスが作れるのでは、と考えました。本物の1/3しか機能がないので名付けて"3/3Dマウス"です。
・入手したジョイスティックには押しボタンスイッチが付いているので、これを短く押すと回転の中心(ピボット)を設定、長押しするとモデルを画面全体にフィットする機能を割り当てました。
alt

●使用した部品
・ジョイスティック Amazonで\150/個
https://www.amazon.co.jp/gp/product/B082M4V5S9
・マイコンボード Digispark micro-Bコネクタ版 \435/個(以前は\250くらいでしたが)
https://www.amazon.co.jp/KKHMF-Digispark-Kickstarter-Attiny85-Arduino/dp/B082M58JR2
・アナログ入力2chとデジタル入力1ch、それとHIDに対応したUSBインターフェースがあるものということでDigisparkのmicro-Bタイプを選択しました。
alt

・ケースはダイソーのクリーム入れ \100
・15kΩの抵抗1本とUSBケーブルは手持ちの物
総額で\700未満ですね。

●回路説明
・DigisparkのP0はデジタル入出力として使えます。今回はスイッチ入力に割り当てます。
・P1はボード上にLEDが付けられているので無改造ではデジタル出力としてしか使えません。これは動作確認用にそのまま使うことにします。
・P2はアナログ入力として使えます。X軸のアナログ入力に割り当てます。
・P3, P4はUSB用に割り当てられているので今回のようにユーザープログラムがUSBを利用するときには使えません。
・P5はリセット端子を兼ねているのですが、電圧をLに落とさなければアナログ入力として使えます。Y軸のアナログ入力に割り当てます。
・ただしそのままだとジョイスティックを倒したときにP5の電圧がL電位まで落ちてリセットがかかってしまうので、ジョイスティックを改造してポテンショメーターの1番端子とGND間に15kΩの抵抗を直列に挿入します。これでP5の電圧はジョイスティックを最大に動かしても3~5Vの範囲に収まります。(ATtiny85のスペックだとリセットピンのLレベル電圧Maxは0.2Vccなので5V使用時に1.0Vとなり、それに比べたら十分余裕があります。)
・Digisparkで使われているATtiny85のヒューズと呼ばれている内部のフラッシュ領域を焼き直すとリセット機能を簡単に無効にできるのですが、今回はDigispark側を無改造で使うことにしてハードウェア側で対応しています。
alt
●ジョイスティックの改造
・Y軸用のポテンショのGNDに落ちているピンの周囲のパターンをカットし、ピンとGNDとの間に15kΩの抵抗を入れます。パターンを観察するためレジストを剥いでしまったので最後にマニキュアを塗っておきました。
alt
●ケースに組み込み
・ケースに穴をあけて基板をグルーガンで固定しました。ジョイスティックには6mmの足を付けてDigisparkとは2階建て構造です。LEDが隠れてしまうので透明の樹脂棒で導光を試みましたがあまりうまくいきませんでした。
alt
●ソフトウェア説明
・Digisparkにはキーボードライブラリ<DigiKeyboard.h>とマウスライブラリ<DigiMouse.h>が用意されているのですが、両者を同時に組み込むことができませんでした。
・検索してマウスとキーを同時にエミュレートできるAdafruit-Trinket-USBライブラリを見つけました。↓
https://github.com/adafruit/Adafruit-Trinket-USB
これのTrinketHidComboフォルダをArduinoIDEを展開したフォルダの下のlibrariesフォルダにコピーしておきます。
・ジョイスティックの中点がきっちり抵抗値の50%になるとは限らないのと、Y軸側はそもそも中点が50%ではないので最初にキャリブレーションデータの取得を行います。//#define DEBUGのコメントを外してビルドするとマウスのあるウインドウにX,YのA/D値とそれらを疑似マウスの移動量に変換した値が次々に表示されるので、X,YそれぞれのA/D最小/中点/最大の値をメモします(エディタ等で空白のファイルを開いてその上にマウスカーソルを置いた後で書き込みを開始する必要があります)。メモした値でソースを修正し、再度ビルドして疑似マウス移動量が設定どおりに変化することを確認します。
・#define DEBUGを再度コメントアウトしてビルドして焼きこめば完成です。
・アップしたソースは1号機と2号機それぞれをビルドできるようになっています。1号機が今回の物で2号機はスライド式のジョイスティックを使った小型タイプなのですが、そちらはまた別途。
・マウスをエミュレートしているだけなので、従来のマウスとシフトキーによる回転操作をした時と同様にマウスカーソルも移動します。//#define CURSORRECOVERのコメントを外してビルドすると一定移動距離ごとにカーソルを戻す機能が組み込まれるのですが、どうも元の位置にきっちり戻らないのと、マウスカーソルがちらちらするのと、従来の操作方とのカーソル位置の違いに違和感があるのと、たまにピボット点が変わってしまうという問題があり今は使っていません。
・スイッチをクリックすると、シフトを押しながらホイールを押すコードだけを送っています。これでカーソル位置にピボットが設定されます。
・Fusion360側の機能としてホイールをダブルクリックするとモデルを画面全体にフィットする動きになります。これにより短時間に2回ジョイスティックを動かすとダブルクリックと判定されて予期せずフィットになってしまうことがありました。そこでジョイスティックやスイッチを戻した時に、カーソル位置を少しだけ"行って来い"させてダブルクリック判定に入らないようにしています。
・そのためスイッチのダブルクリックによる画面フィットは不可能になったので、長押ししたらF6キーを送ることで画面フィットを実現しています。

●使ってみて
・確かに直感的にモデルを回転することができる気がします。
・回転しながらのズームはできません。これはマウスによる従来の操作でもできないので当然ではありますが。
・ジョイスチックを倒した方向にマウスを動かすとモデルを急峻に回転させることができます。
・常時キーボードの前に置いておくのは邪魔くさいし、使うたびに引っ張り出すというのも面倒です→2号機に続く

★以下は備忘録です
●Digispark用開発環境の構築
・こちらを参考に↓
http://digistump.com/wiki/digispark/tutorials/connecting
IDE1.6.5がお勧めのようなので、こちらの↓Previous Releasesから1.6.5のZIP版を落としてフォルダごと解凍。
https://www.arduino.cc/en/software
・普段使っているArduinoIDEとは別環境にしたかったので、展開したarduino.exeと同じ階層にportableという名前のフォルダを追加。
・arduino.exeのショートカットをデスクトップに作成し、Digispark専用のIDEはそこから起動。
・IDEを起動したら環境設定でボードマネージャのURLとして以下を追加。
http://digistump.com/package_digistump_index.json
・ツールのボードマネージャでDigistump AVR Board by Digistumpをインストール。
(私の場合バージョン1.6.7じゃないとその後うまくビルドできませんでした)
・この際ドライバーのインストールで一番上の1つがエラーになったので、↓ここの
https://github.com/digistump/DigistumpArduino
toolsフォルダの下のwindows用ドライバを解凍してインストールしたら全部組み込めた。(このドライバが必須かどうかはわからないですが)
・IDEのツールからボードを"Digispark (Default - 16.5mhz)"を選択。
・ビルド&書き込みボタンを押してPlug in device now...(will timeout in 60 seconds)と表示されたらDigisparkを接続。(抜き差しが面倒なので私はUSB延長ケーブルの途中にスイッチを入れて+5Vラインをオンオフできるようにしています)

[↓ソースはここから]
/*****************************************************************************
*
*   JoystickMouse.ino -- Joystickでマウスをエミュレート[Digispark]
*
*   Adafruit-Trinket-USBのTrinketHidComboフォルダをlibrariesフォルダ下にコピー
*   https://github.com/adafruit/Adafruit-Trinket-USB
*
*   JoystickのSW→P0
*   JoystickのX軸→P2(AD1)
*   JoystickのY軸→P5(AD0)
*    PB5はリセット入力兼用なのでY軸のポテンショのGND側に15kΩを
*    直列に挿入して電圧がLレベルにならないようにしている。
*
*   rev1.0  2021/12/22  initial revision by Toshi
*
*****************************************************************************/
#include <TrinketHidCombo.h>    // マウス&キーボードライブラリ

//#define DEBUG         // A/D読み取り値出力時にはコメントを外してビルド
//#define CURSORRECOVER // カーソル位置を戻すならコメントを外す
//#define _2X           // 2号機ならコメントを外す

#define SW PB0  // スイッチポート
#define LED PB1 // LEDポート

#define LED_ON digitalWrite(LED, 1);
#define LED_OFF digitalWrite(LED, 0);

#ifndef _2X // 1号機(ポテンショメータータイプのJoystick))
 #define ADMAXX 1023    // X軸の最大A/D実測値
 #define ADCENTERX 516  // X軸のセンターA/D実測値
 #define ADMINX 0       // X軸の最小A/D実測値
 #define ADMAXY 1023    // Y軸の最大A/D実測値
 #define ADCENTERY 835  // Y軸のセンターA/D実測値
 #define ADMINY 667     // Y軸の最小A/D実測値+2
 #define MAXMOUSE 8     // マウス最大移動量
 #define DEADZONE 0     // ジョイスティックの不感帯(マウス移動量)
#else       // 2号機(スライドタイプのJoystick)
 #define ADMAXX 1023    // X軸の最大A/D実測値
 #define ADCENTERX 476  // X軸のセンターA/D実測値
 #define ADMINX 0       // X軸の最小A/D実測値
 #define ADMAXY 1023    // Y軸の最大A/D実測値
 #define ADCENTERY 828  // Y軸のセンターA/D実測値
 #define ADMINY 677     // Y軸の最小A/D実測値+2
 #define MAXMOUSE 6     // マウス最大移動量
 #define DEADZONE 1     // ジョイスティックの不感帯(マウス移動量)
#endif  // _2X

#define LOOPTIME 20     // ループごとの待ち時間
#define MINMOVE 15      // ホイールダブルクリック判定回避の移動量
#define RESETMOVE 50    // マウスの位置を戻すカウント
#define RECOVERGAIN 54  // 行きに対する戻り側カウント量[%]

int AdData0, AdData1;   // A/D読み取り値
char MoveX, MoveY;      // マウス移動量
int TotalX, TotalY;     // マウス総移動量
bool fPush;             // シフトキー押されているかどうか

void setup()
{
    pinMode(SW, INPUT_PULLUP);
    pinMode(LED, OUTPUT);
    TrinketHidCombo.begin();    // USBデバイスエンジン起動
}
void loop()
{
    static bool fonshort, fonlong;
    static int buf0, buf1;

    // A/D読み取り(0~1023)
    AdData0 = AdRead(0, &buf0); // PB5はAD0(Y軸)
    AdData1 = AdRead(1, &buf1); // PB2はAD1(X軸)

    // A/D読み取り値からマウス移動量への変換
    MoveY = Ad2Mouse(AdData0, ADMINY, ADCENTERY, ADMAXY);
    MoveX = Ad2Mouse(AdData1, ADMINX, ADCENTERX, ADMAXX);

    // SWの短押し/長押しを判定
    JudgeButton(digitalRead(SW) == 0, &fonshort, &fonlong);

#ifdef DEBUG
    DebugPrint();   // A/D読み取り値とマウス移動量をカーソル位置に出力
    delay(500);
#else

    /** 画面Fit **/
    if (fonlong)    // 長押し?
    {
        // F6キーを押す
        TrinketHidCombo.pressKey(0, KEYCODE_F6);
        TrinketHidCombo.pressKey(0, 0);
    }

    /** カーソル位置にピボットを設定 **/
    else if (fonshort)  // 短押し?
    {
        // シフトキーを押す
        TrinketHidCombo.pressKey(KEYCODE_MOD_LEFT_SHIFT,
                                    KEYCODE_LEFT_SHIFT);
        fPush = true;   // シフトキー状態オンを記憶
        // ホイールボタンをオン
        TrinketHidCombo.mouseMove(0, 0, MOUSEBTN_MIDDLE_MASK);
        // ホイールボタンをオフ
        TrinketHidCombo.mouseMove(0, 0, 0);
    }

    /** オービット **/
    else if (MoveX != 0 || MoveY != 0)  // スティックが倒されている?
    {
        if (!fPush) // まだ押されていないなら
        {
LED_ON
            // シフトキーを押す
            TrinketHidCombo.pressKey(KEYCODE_MOD_LEFT_SHIFT,
                                            KEYCODE_LEFT_SHIFT);
            fPush = true;   // シフトキー状態オンを記憶
        }
        if (fPush)  // シフトキーが押されているなら
        {
            // ホイールを押しながらマウスを移動させる
            TrinketHidCombo.mouseMove(MoveX, MoveY, MOUSEBTN_MIDDLE_MASK);
            TotalX += MoveX;    // 移動量の累積
            TotalY += MoveY;
            // トータル移動量が規定に達した?
            if (abs(TotalX) >= RESETMOVE || abs(TotalY) >= RESETMOVE)
            {
                // カーソル位置を戻す
                RecoverMouse(&TotalX, &TotalY);
            }
        }
    }

    /** スティック&ボタン操作なし **/
    else
    {
        if (fPush)  // シフトキーが押されていたら
        {
LED_OFF
            TrinketHidCombo.mouseMove(0, 0, 0); // ホイールボタンをオフ
            // 短時間で2回来た時ダブルクリックと判定されるのを回避する
            TrinketHidCombo.mouseMove(MINMOVE, 0, 0);   // 少し動かして
            TrinketHidCombo.poll();
            TrinketHidCombo.mouseMove(-MINMOVE, 0, 0);  // 元に戻す
            TrinketHidCombo.pressKey(0, 0);     // シフトキーをオフ
            fPush = false;                      // シフトキー状態オフを記憶
            // カーソル位置を戻す
            RecoverMouse(&TotalX, &TotalY);
        }
    }
    delay(LOOPTIME);        // 少し待つ
    TrinketHidCombo.poll(); // USBに何かする必要があるかどうかを確認
#endif  // DEBUG
}
// デバッグビルド時にA/D読み取り値とマウス移動値を出力
void DebugPrint()
{
    TrinketHidCombo.print(AdData1); // X軸A/D値
    TrinketHidCombo.print(",");
    TrinketHidCombo.print(AdData0); // Y軸A/D値
    TrinketHidCombo.print("  ");
    TrinketHidCombo.print((int)MoveX);  // X軸移動量
    TrinketHidCombo.print(",");
    TrinketHidCombo.println((int)MoveY);// Y軸移動量
}
/*----------------------------------------------------------------------------
    カーソル位置を戻す
    書式 void RecoverMouse(int* totalx, int* totaly)

    int* totalx;    移動した量x
    int* totaly;    移動した量y
----------------------------------------------------------------------------*/
void RecoverMouse(int* totalx, int* totaly)
{
#ifdef CURSORRECOVER    // カーソル位置を戻すモードなら
    int x, y;

    TrinketHidCombo.mouseMove(0, 0, 0); // ホイールボタンをオフ

    // mouseMoveの引数はsigned char型なので範囲内で繰り返す
    while (!(*totalx == 0 && *totaly == 0))
    {
        x = min(*totalx, 127);
        x = max(x, -127);       // ライブラリ側で-127までのようなので
        y = min(*totaly, 127);
        y = max(y, -127);
        TrinketHidCombo.poll();
        // マウスカーソルを移動
        // (移動したカウントと戻り量が異なるのでRECOVERGAINは要調整)
        TrinketHidCombo.mouseMove(-x * RECOVERGAIN / 100,
                                  -y * RECOVERGAIN / 100, 0);
        *totalx -= x;
        *totaly -= y;
    }
#else
    *totalx = *totaly = 0;  // 移動量の累積をクリア
#endif // CURSORRECOVER
}
/*----------------------------------------------------------------------------
    A/D読み取りと平均
    書式 ret = AdRead(int ch, int* buf)

    int ret;        A/D値(0~1023)
    int ch;         A/Dチャンネル
    int* buf;       sumバッファ
----------------------------------------------------------------------------*/
#define AVENUM 10   // 平均数。最大で31
int AdRead(int ch, int* buf)
{
    *buf = 0;       // 合計をクリア
    for (int i = 0; i < AVENUM; i++)
    {
        *buf += analogRead(ch); // A/Dデータを累積
    }
    return (*buf + AVENUM / 2) / AVENUM;    // 四捨五入
}
/*----------------------------------------------------------------------------
    A/D読み取り値からマウス移動量への変換
    書式 ret = Ad2Mouse(int data, int dmin, int dcenter, int dmax)

    char ret;       マウス移動量
    int data;       A/Dデータ
    int dmin;       A/D最小値
    int dcenter;    A/Dセンター値
    int dmax;       A/D最大値
----------------------------------------------------------------------------*/
char Ad2Mouse(int data, int dmin, int dcenter, int dmax)
{
    char x, ret = 0;

    if (data >= dcenter)    // センターよりプラス側
    {
        x = (char)map(data, dcenter, dmax, 0, MAXMOUSE + DEADZONE);
    }
    else    // センターよりマイナス側
    {
        x = (char)map(data, dcenter, dmin, 0, -MAXMOUSE - DEADZONE);
    }
    // 不感帯処理
    if (abs(x) > DEADZONE)  // 不感帯より値が大きいなら
    {
        ret = (char)(sign(x) * (abs(x) - DEADZONE));    // 不感帯分を引く
    }
    return ret;
}
/*----------------------------------------------------------------------------
    ボタンの短押し/長押し判定
    書式 void JudgeButton(bool flag, bool* fonshort, bool* fonlong)

    bool flag;      スイッチのon/off状態
    bool* fonshort; 短押し判定
    bool* fonlong;  長押し判定
----------------------------------------------------------------------------*/
#define ONTIMELONG 20   // 長押し判定時間
#define ONTIMESHORT 2   // 短押し判定時間
void JudgeButton(bool flag, bool* fonshort, bool* fonlong)
{
    static int ontime, offtime = 30000;
    static bool flong;

    *fonshort = *fonlong = false;       // いったんクリア

    AddOnOffTime(flag, &ontime, &offtime);  // オンオフ時間の累積

    // オン時間が長押し時間に達した
    if (ontime == ONTIMELONG)
    {
        *fonlong = true;    // 長押しと判定
        flong = true;
    }
    // オフ時間が短押し時間に達して長押しではない
    if (offtime == ONTIMESHORT && !flong)
    {
        *fonshort = true;   // 短押しと判定
    }
    // 長押し後にオフ時間が短押し時間+1に達した
    if (flong && offtime > ONTIMESHORT)
    {
        flong = false;      // 長押し状態解除
    }
}
/*----------------------------------------------------------------------------
    符号を返す
    書式 ret = sign(int x)

    int ret;    負なら-1, さもなければ1
    int x;      データ
----------------------------------------------------------------------------*/
int sign(int x)
{
    if (x < 0) return -1;
    return 1;
}
/*----------------------------------------------------------------------------
    フラグのオン/オフ時間の累積 
    書式 void AddOnOffTime(bool flag, int* ontime, int* offtime)

    bool flag;      フラグ
    int* ontime;    オン時間
    int* offtime;   オフ時間
----------------------------------------------------------------------------*/
#define TIMEMAX 30000
void AddOnOffTime(bool flag, int* ontime, int* offtime)
{
    if (flag)                           /* オンしてるなら */
    {
        *offtime = 0;                   /* オフ時間を0に */
        if (*ontime < TIMEMAX)          /* 規定に達するまでタイマ++ */
        {
            (*ontime)++;
        }
    }
    else                                /* オフなら */
    {
        *ontime = 0;                    /* オン時間を0に */
        if (*offtime < TIMEMAX)         /* 規定に達するまでタイマ++ */
        {
            (*offtime)++;
        }
    }
}
/*** end of JoystickMouse.ino ***/
[↑ここまで]

Posted at 2021/12/28 15:31:52 | コメント(0) | トラックバック(0) | Fusion360 | パソコン/インターネット
2021年12月26日 イイね!

USBを給電用に分岐

USBを給電用に分岐
中華ナビでUSB接続のワンセグチューナーとブースターのために、以前2ポートハブを使いましたが、ブースター側へは+5Vを給電するだけなのでハブの必要はありません。セリアでPCのUSBポートからチャージするためのタコ足ソケットが売っていたのでこれを改造することにしました。
alt

蓋を開けるとこんな感じでコネクタとチップ抵抗が載っていて、入力のD+/D-端子は結線されていません。
alt


alt

今回はR1~R4のチップ抵抗を外して入出力のD+/D-を直結しました。もう片方の給電側はどうせその先が結線されていないので抵抗はそのままにしてありますが、もちろん外しても構いません。
alt

alt

ケースに戻して完成です。
alt

このセリアの商品、よく見るとUSBのマークの枝が逆になってます。チャージ専用なのでデーター転送には使えませんよ、と言いたいのかな?
Posted at 2021/12/26 11:23:31 | コメント(0) | トラックバック(0) | 中華ナビ | パソコン/インターネット
2021年10月02日 イイね!

中華ナビでワンセグ受信2 TVブースターの効果検証

中華ナビでワンセグ受信2 TVブースターの効果検証
AliExpressに発注していたTV用ブースターが1か月もせずに届きました。
https://ja.aliexpress.com/item/4001297100315.html
alt
前回ラジオ用のブースターを入れてそれなりに効果が出たので満足はしていたのですが、せっかくなので比較してみることにしました。
https://minkara.carview.co.jp/userid/3336538/blog/45438118/

●接続の準備
①このブースターのアンテナ入出力はF型接栓です。前回購入したGT13用変換ケーブルが1本余っているのでそれを改造することにしました。この変換ケーブル自体の同軸は細いのでそのままではF型プラグを取り付けられません。手元のF型プラグに以前使って余っていたダイソーの3C2V同軸ケーブルを介して半田付けしました。(このダイソーTVケーブル、以前同軸だけ使いたかったので先っぽのF型プラグ部を捨ててしまってました。捨てなきゃそのまま使えたのですが、、、)
②ブースターとチューナーの接続は、以前購入したWindows用のワンセグチューナーに付属していたF型-MCX変換ケーブルをそのまま流用しました。
③このブースターはUSBの5V給電です。電流は実測したら26mAしか流れませんでしたので、以前100均(たぶんセリア)で購入してあった2ポートUSBハブでチューナーと同時に給電することにしました。チューナーはハブ経由でも問題なく動作しました。
alt
●ブースターの実力
ナビのTV受信アプリPadTVでチャンネルをスキャンした時に、何局ヒットするかを比べることで相対的な感度比較を行いました。
【ケース1ブースターなしで車のアンテナをワンセグチューナーに直結した場合
→測定不能。電波状態は日によって違うので、この日はチャンネルがヒットしませんでした。(このアプリ、スキャンしてヒットしないと結果も表示されすに前の画面に戻ってしまいます。)
ケース2前回取り付けたラジオ用ブースターを使った場合
→36チャンネル
ケース3今回のTVブースターを使った場合
→46チャンネル

ということで、このTV用ブースターは効果抜群という結果となりました。
各接続部が車の振動で緩まないようにアセテートテープで固定してから実車に搭載しました。ラジオ用のブースターの方はいったん下ろしてケーブルを元に戻し、本来のラジオ側に入れて使うことにしました。(ブースターが2段入っていることになるので強電界地域でどうなるかというのはありますが。)

Posted at 2021/10/02 10:48:24 | コメント(0) | トラックバック(0) | 中華ナビ | クルマ
2021年09月11日 イイね!

車内で使える磁気コンパス

車内で使える磁気コンパス
ナビの開始時に『北に進みます』とか言われて、「で、私は今どっちを向いてるの?」ということがわりとあります(メーカーのNAVIだとオフする前の方位を覚えてくれていたような気がしますが)。そんなときはアナログな手法ですが磁気コンパスがあると便利です。
・ルームミラーの片隅に取り付けられる小型の物を買ってみましたが、全然使い物になりませんでした。車のボディは工場での製造過程でプレスされたり電磁石で吊るされたりで残留磁気を帯びていることは知っていましたが、私の車は地磁気よりも格段に大きかったようで方位磁針がほとんど動きません。

●残留磁気をキャンセルできるタイプの磁気コンパス
・そこで車体の磁場をキャンセルできるタイプの磁気コンパスを購入しました。Amazonで購入時の価格は\700ちょいでした。そのショップは先ほど見たら\1,000以上アップしていたのでURLは書きませんが、AliExpressだと$6くらいで売ってます。
・アルミの小さな羽子板みたいな調整用のツールが付属しているのが特徴です。
alt
●取り付け
・吸盤の付いた取り付けプレートは、使うと車の振動で本体が激しく上下にブレてしまうので取り外しました。
・助手席エアバッグが展開しても大丈夫そうな位置に、厚みの異なるゲルテープや両面テープを何種類か使って水平を出して取り付けました。
alt
●調整
①車を北に向けて、ゲージがNを指すように右側の調整ノブを回します。
②車を東に向けて、ゲージがEを指すように正面の調整ノブを回します。
これを何回か繰り返します。
・調整ノブを回してからゲージが安定するまでにタイムラグがあるので、少し回してはじーっと待つ、という調子で行います。
・ノブ端面の溝が水平で補正ゼロ、垂直になるまで回すと補正MAXです。MAXでも補正できないようなら本体を違う場所に設置する必要がありますね。
・調整は非常にデリケートなので最終段階では指先にほんの少し力を入れる程度にとどめる必要があります。
・真横に他の車が止まっていると多少なりとも影響されるので要注意です。
・車の振動でずれるかもしれないので、調整ツールは常に搭載しておくことにします。

●結果
・車内でもきちんと車の向きがわかるようになりました(^_^)/

●磁場補正部の中身は?
・とても気に入ったので耐久性が不明なこともあり予備としてもう一つ購入したのですが、中を見てみたいという思いがフツフツと、、、とうとう誘惑に負けてしまいました。
・熱で溶かして4か所カシメてあるので、その部分の出っ張りを削るとゲージ部と磁場補正部を上下に分離できました。
・磁場補正部は、直交する2本のシャフトにそれぞれ1組の小型の補正用磁石が付けられていて、シャフトの回転とともに磁石も回転する構造でした。
alt
alt

●磁場補正の原理
・それぞれのシャフトを回転させると、ゲージ用磁石が置かれた平面に縦&横それぞれの方向に加わる補正用磁気の大きさと向きが変わるしかけになっていることがわかりました。車体が旋回したとしても残留磁気と補正用磁気の角度の関係は変わらないので常に相殺されるというわけです。納得。
alt
●ゲージ用磁石は固定されていない!
・磁石を使って極性の確認をしたりしているうちに、補正量ゼロにもかかわらず磁石を当てた時のゲージがそっぽを向くようになってしまいました。
alt
・方位表示部は半円球のドーム状のゲージの中に方位検出用のフェライトと思われる磁石が付いているのですが、この磁石はゲージとは緩く止められているだけのようで、強い磁界を一気にかけるとゲージより先に「ヒュン」と回ってしまうことがわかりました。
alt
・ゲージ部は液体で満たされ密閉されているので直接は触れません。ネオジウム磁石のN極を一気に近づけたり、近場で左右にクィクィッと細かく動かしたりしてゲージのNが正面に来るように調整することができました。やれやれ。通常の使用では磁石がずれたりしないと信じたいです。
alt
●磁気偏角の補正ができるということか?
・方位磁石の向きと実際の真北では日本では5~10degずれ(磁気偏角)があるそうで、真北を正面にした時に東京では方位磁針は左回りに6degずれるとのことです。ということはこの磁気コンパスの1目盛は5degなので、正面に磁石を近づけた時に1目盛ちょい右が正面に来るようにしておけば、ゲージのNが真北を示す、、、ということでいいのかな?
・磁気偏角は世界各地で異なるそうなので、この磁気コンパスはグローバル仕様という事なのかもしれません。

●単に製造上のばらつきのような
・ならば実車に搭載した方のコンパスも同じように微調整しようとネオジウム磁石を近づけてみたのですが、こちらはどうやってもきっちりNが正面のままでずらすことができません。どちらが製品として正しい出来なのかは謎ですが、もし購入時にそもそも正しい方位を示さない物に当たっても強力な磁石を使えば修正できますよ、というお話です。

Posted at 2021/09/11 10:16:02 | コメント(0) | トラックバック(0) | クルマ
2021年09月06日 イイね!

中華ナビのバックガイド線をステア連動に

中華ナビのバックガイド線をステア連動に
Owniceもそうでしたが、NaviFly UIS7862もCAN-BOXを接続することができるようになっています。またこのナビにはそれ用のコネクタも同梱されており、そこからTx, Rxのリード線が出ています。
alt
・OwniceにはCAN-BOXも付属していたので、Fit3でステア連動が動作することは確認しましたが結局使わずじまいでした。(エアコンやドアなど不要な表示がされるのと、そもそもバックガイド線の表示アプリの出来がいまいちだった)
・ちなみにCAN-BOXのメーカーはSimpleSoftです。
alt
・今はOwniceを車から降ろし机上でいじれる状態になったので、それを使ってCAN-BOXとナビ間ののプロトコルを解析し、自作のCAN-BOXでステア舵角だけをナビに送ることで、NaviFlyでバックガイド線のステアリング連動ができるはずだと考えました。

●まずは実車でB-CAN解析
・CAN通信ボードMCP2515とArduino NANOを接続し、簡単なプログラムを焼いて実車のB-CAN信号をログしてEXCELでIDでソートして解析を行いました。
ビットレート:125kbps
ステア舵角信号ID:0x12F97150(拡張フォーマット)
DLC:3
data:D0=?, D1=ステア舵角(MSB), D2=ステア舵角(LSB) モトローラフォーマット
1LSB:0.1deg
方向:右回転が正(すなわちステアを右に90deg回すとデータは900)
インターバル:100ms

●CAN-BOX / NAVI間通信解析
・部屋に戻って先ほどのArudinoを使い、今度はダミーのステア舵角データをB-CANに送信するプログラムを作ってCAN-BOXとOwnice間の通信をモニタしました。
電圧レベル:0-3V
・以前amazonで\800で購入したこのロジアナ(今は\1000以上するみたいですが)でプロトコル解析までできてしまいます。
alt
ビットレート:38400bps
非同期シリアル(async)8bit,1stop,パリティ無し
・それぞれがフレームを送信した後でACK(0xff)を受信するハンドシェークを互いに行っています。
・通電後はネゴシエーションを行いCAN-BOXのソフトバージョンやその他の情報をやり取りしています。
・その後0.2秒ごとにナビはCAN-BOXにステア舵角データのリクエストを送信し、CAN-BOXはそれに対する応答としてステア舵角を返しています。
★CAN-BOX→NAVIへのステア舵角の送信フレーム
D0=0x2e, D1=0x29, D2=2, D3=ステア舵角(LSB), D4=ステア舵角(MSB), D5=チェックサム
・D0はSTXで全てのフレームの始まりは常に0x2eです。
・D1はファンクションIDで、0x29がステア舵角を示します。
・D2はデータのバイト数でステア舵角の場合は2です。
・D3,D4がステア舵角で、並びはCANと逆のインテルフォーマットです。
・D5はD1~D4の合計をビット反転した下8bitです。
・ネゴシエーション部分をコピーするのはめんどうだなぁと思い、ものは試しでArduinoでダミーのステア舵角の送信フレームだけを100msでOwniceに送ったら、それだけでバックガイド線が曲がることがわかりました。ハンドシェイクもネゴシエーションもやらなくてもエラーにはならないようです。(当然システム情報のCANバージョンは表示されませんが。)
・同じことを実車のNaviFlyで試してバックガイド線が曲がることを確認しました。
・NaviFlayのコネクタから出ている線のTx/RxというラベルはCAN-BOXから見たもので、NAVIに送信する信号をTxに接続する必要があるので要注意です。
・「工場設定」「モデル選択」でCAN-BOXのメーカーで「XP」を選択します。XPというのがSimpleSoftの別名のようです。
alt
・「工場設定」「車のカメラ方式を使用する」で「バック連動ガイドライン」をオンにしないとガイド線はそもそも表示されません。
・その下の「バックガイド線を表示」はオフにしておかないとステア連動になりません。なんか逆みたいに感じますが。
・その下の「バックトラック反転」をオンにすると操舵方向とガイド線の曲がる方向が逆になります。
alt
●結果
・自作のCAN-BOXを使ってフル転舵した時の表示です。
alt
●回路
・ハードはArduino NANOとCANボードMCP2515と3端子レギュレータを接続しただけです。ただしCAN-BOXの送信レベルは3Vなので抵抗分圧で5→3Vに変換しています。
・電源は外付けの3端子レギュレータで5Vを作っています。Arduinoにはもともと3端子レギュレータが搭載されているのですが、私の使った中国製のArduinoにはFS1117という中華なチップが使われていて、これの絶対最大定格が+12.0Vなので車の電圧(14V台になることがある)には耐えられないはずです。電流は40mAも流れないので78L05で十分かと思います。少し暖かくはなりますが。本物の(?)Arduinoでは耐圧の高い3端子レギュレータが載っているのかもしれませんが、その場合はVin端子に直結できますね。
・バックの時だけ機能すればいいので、電源はリアカメラへの給電の+12Vと同じところからもらうようにします。
・ということは普段はこのユニットは電源オフのままCANバスにぶら下がっていることになりますが、CANトランシーバー用のICはそのような使われ方でも他の通信に影響しないように作られているので問題ありません。
alt
●製作
・ダイソーのプラケースにArduinoNANOとMCP2515をクッションテープと両面テープでガタつかないように収めました。空中配線の部分はグルーで固定しています。
alt
alt
●ソフトウェア
・B-CANからステア舵角を受信したら補正してNAVIに送信するだけの機能です。
・ステア舵角は1/4に補正しています。そのまま送信するとラインが曲がりすぎるので。ネゴシエーションしていないのでそこで舵角の分解能とかを送っているのかもしれません。あるいは車種とモデルイヤーを選べばいいのかもしれませんが詰めていません。
・今回は実車でざっくり合わせただけで全然厳密な予測進路ではないです。バックガイド線の表示設定との兼ね合いで微調整をした方が良いと思います。
・CANのステア舵角の受信間隔が100msなので受信2回に対して送信1回としています。
・ビルドするためには次に置いたソースを例えばMyCanBox.inoという名前で作成したら同じ名前のMyCanBoxフォルダを作って入れてください。
・CAN制御のソフトはhttps://github.com/coryjfowler/MCP_CAN_libから取得しています。私はライブラリとして組み込むのが好きではないのでzipからmcp_can.cpp, mcp_can.h, mcp_can_dfs.hを取り出してソースと同じローカルフォルダに置いています。IDEにライブラリとして組み込んでそちらを使う場合は#include文の"mcp_can.h"を<mcp_can.h>に変更してください。その場合はローカルへのコピーは不要です。
[↓ソースはここから]
/*****************************************************************************
*
*   MyCanBox.ino -- B-CANのステア舵角をNAVIに送信(SimpleSoft[XP]プロトコル)
*
*   rev1.0  2021/09/06  initial revision by Toshi
*
*****************************************************************************/
#include <string.h>
#include <SoftwareSerial.h>
#include <SPI.h>
#include "mcp_can.h"    // https://github.com/coryjfowler/MCP_CAN_lib

#define CAN_CS 10   // MCP2515ボードのCSが接続されているピン番号
#define CANID_STEER_ANGLE 0x12F97150    // Fit3ステア舵角のCAN ID

MCP_CAN CAN0(CAN_CS);           // CANオブジェクト
SoftwareSerial Serial2(8, 9);   // ソフトシリアル(Rx, Tx)
unsigned char TxBuf[6];         // 送信データバッファ

void setup()
{
    Serial.begin(115200);   // デバッグ用
    Serial2.begin(38400);   // NAVIへの送信はソフトシリアルを利用

    TxBuf[0] = 0x2e;    // STX
    TxBuf[1] = 0x29;    // ステア舵角を示すコード
    TxBuf[2] = 0x02;    // データ長

    CAN0.begin(MCP_STDEXT, CAN_125KBPS, MCP_8MHZ);// CAN初期設定
    CAN0.init_Mask(0, 1, 0x1fffffff);       // マスクビット設定 0だとDon'tCare
    CAN0.init_Mask(1, 1, 0x1fffffff);       // マスクレジスタは2つある
    CAN0.init_Filt(0, 1, CANID_STEER_ANGLE);// このIDだけ受け取る
    CAN0.setMode(MCP_NORMAL);               // CAN開始
}

void loop()
{
    unsigned long id;
    unsigned char dlc, rxbuf[8];
    short angle;
    static short i;

    if (CAN0.checkReceive() == CAN_MSGAVAIL)// CAN受信したら
    {
        CAN0.readMsgBuf(&id, &dlc, rxbuf);  // データを読み出す
        id &= CAN_EXTENDED_ID;              // 拡張IDなので上位ビットを落とす
        if (id == CANID_STEER_ANGLE)        // 念のためIDをチェック
        {
            if (i)  // 2回に1回(200ms)
            {
                // そのままだとラインが曲がりすぎるので補正する
                angle = (rxbuf[1] << 8 | rxbuf[2]) / 4; // 補正したステア舵角
                Serial.println(angle);          // デバッグ用
                TxBuf[3] = angle & 0xff;        // 補正ステア舵角LSB
                TxBuf[4] = (angle >> 8) & 0xff; // 補正ステア舵角MSB
                // チェックサム(TxBuf1~4の合計のビット反転した物)をセット
                TxBuf[5] = ~(TxBuf[1] + TxBuf[2] + TxBuf[3] + TxBuf[4]);
                Serial2.write(TxBuf, 6);// バイナリ―データを送信
            }
            i ^= 1; // 0→1, 1→0
        }
    }
}
/*** end of MyCanBox.ino ***/
[↑ここまで]

Posted at 2021/09/06 16:04:28 | コメント(0) | トラックバック(1) | 中華ナビ | クルマ

プロフィール

ToshiTechです。よろしくお願いします。 なにかを作るのが好きです。優先順位としては  ①世の中にないから作る  ②世の中にはあるけど値段が高いので...
みんカラ新規会員登録

ユーザー内検索

<< 2025/9 >>

 123456
78910111213
14151617181920
21222324252627
282930    

愛車一覧

ホンダ フィット(RS) ホンダ フィット(RS)
ホンダ フィット3(RS)に乗っています。

過去のブログ

2025年
01月02月03月04月05月06月
07月08月09月10月11月12月
2024年
01月02月03月04月05月06月
07月08月09月10月11月12月
2023年
01月02月03月04月05月06月
07月08月09月10月11月12月
2022年
01月02月03月04月05月06月
07月08月09月10月11月12月
2021年
01月02月03月04月05月06月
07月08月09月10月11月12月
ヘルプ利用規約サイトマップ
© LY Corporation