JRRF2026に向けたMakerChip製作記

はじめに

今年もJapan RepRap Festivalの季節がやってきました。JRRF2026は5月30日〜31日に東京流通センターで開催されます。出展はしませんが、土曜日(1日目)に一般参加で遊びに行く予定です。

こういう3Dプリンターのイベントに行くなら、ぜひ用意しておきたいのがMakerChipです。直径40mm、厚さ3〜3.5mmの小さなチップを、Maker同士が名刺代わりに交換する文化があります。別に義務ではないのですが、持っていると会場で出会った人と交換できます。そして交換したチップを眺めていると、多色印刷を駆使していたり、造形にとんでもないこだわりが詰まっていたり、その人の技術力やセンスが凝縮されていて面白いのです。


昨年の振り返りと課題

昨年のJRRF2025にも参加し、オリジナルのMakerChipを製作しました。

去年のMakerChip。白一色のマットPLAで製作した

当時は手持ちのフィラメントが白のマットPLAしかなかったため、白一色で凹凸や光沢を工夫するしかありませんでした。

去年のチップを横から見たところ。薄い土台が半透明になっている

これはこれで良い勉強になりましたが、会場で他の参加者が多色印刷を駆使しているのを見て、今年は色や素材を増やした本格的なものを作ろうと決めていました。


あれから一年

あれから手持ちのフィラメントの種類も増え、0.2mmノズルの扱いにも慣れてきました。今年は「多色」「ねじ式ギミック」などを盛り込んだ、パーツ組み立て式のMakerChipを作ります。

すべて分解すると以下のようになります。

MakerChipを完全に分解した状態。各パーツが並んでいる

200個近い量産を予定しているため、一体型で印刷するとどこか一箇所で印刷ミスが発生しただけで全てがゴミになってしまいます。パーツを細かく分割し、最後に組み立てる方式にすることで、歩留まりの改善をねらいました。


基本バリエーション

基本となるモデルは、表側のリングに白マットPLA(文字は銀シルク)、裏側のベースに黒マットPLA(文字は白マット)を組み合わせた仕様です。

0.2mmと0.4mmノズルの使い分けによる速度と精度の両立

細かい文字を綺麗に出すためには0.2mmノズルが必要ですが、すべてのパーツを0.2mmノズルで印刷すると膨大な時間がかかります。そこで、文字とベースの印刷を分離してノズルを使い分ける工夫をしました。

まず、0.2mmノズルに白(裏面)や銀(表面)のフィラメントをセットし、細密なテキストレイヤー(高さ0.12mm)のみをビルドプレートに印刷します。

0.2mmノズルで印刷したテキスト部分。

0.2mmノズルで印刷したテキスト部分。9枚がビルドプレート上に並んでいる

テキスト部分のプレビュー。

テキストレイヤーの印刷が終わったら、ノズルを通常の0.4mmに変更し、上からベースの黒(または白)を被せるように重ねて印刷します。

黒色ベースを印刷中。テキスト部分の上から被せるように印刷している

底面パターンはオクタグラムスパイラルにして表情をつけています。

文字以外の肉厚なベース部分を0.4mmノズルで高速に埋めることで、文字の精密さを維持したまま、印刷時間を大幅に短縮しました。

一時停止による品質チェック

テキストレイヤーを印刷する際、2層目に入る前に一時停止コマンドをスライサーに挿入しています。 1層目が綺麗にベッドに定着しているかを確認し、問題なければその場で印刷を終了(1層でも十分な厚みがあるため)、かすれがある場合は一時停止を解除して2層目を重ねることで、文字の品質を均一に保てるようにしました。

表面リングの印刷

表面のリングパーツも同様の手順で印刷します。まず0.2mmノズルで銀色のシルクPLAを用いて微細な文字を印刷します。

銀色シルクPLAで印刷したリングのテキスト部分

銀色シルクPLAで印刷したリングのテキスト部分

その後、0.4mmノズルと白色マットPLAに切り替えてリング本体を重ねて印刷します。

白色PLAで印刷したリング本体

ビルドプレートから取り外したカバーパーツ。

ねじ込みによる組み立て

裏面パーツと表面リングが刷り上がったら、ねじ込んで組み立てます。

ねじ込み完了した状態を横から見た写真

設計段階でクリアランスを調整したため、接着剤なしできれいに噛み合い、簡単に外れることはありません。

ねじ式のメイカーチップは、以下のようなBlender Pythonスクリプトで生成できます。

import bpy
import math
import bmesh

def create_heavy_thread_manifold_coin_case():
    for name in ["CoinCase_Base", "CoinCase_Lid"]:
        if name in bpy.data.objects:
            bpy.data.objects.remove(bpy.data.objects[name], do_unlink=True)
        if name + "_Mesh" in bpy.data.meshes:
            bpy.data.meshes.remove(bpy.data.meshes[name + "_Mesh"])

    N = 128
    Z_TOP = 3.5
    Z_FLOOR = 0.6
    Z_NECK = 2.9
    Z_LID_CAVITY = 3.0

    R_OUTER = 20.0
    R_INNER = 16.0

    R_BASE_ROOT = 17.2
    THREAD_DEPTH = 0.65
    THREAD_H_WIDTH = 0.65
    CLEARANCE = 0.15

    R_LID_ROOT = R_BASE_ROOT + THREAD_DEPTH + CLEARANCE
    
    LEAD = 3.0
    PITCH = LEAD / 2
    Z_START = 0.8

    def clip(z, z_min, z_max):
        return max(z_min, min(z_max, z))

    def get_screw_params(t):
        zc1 = Z_START + LEAD * t
        if 0.05 <= t <= 0.45:
            tl = (t - 0.05) / 0.40
            f = (tl / 0.2) if tl < 0.2 else (((1.0 - tl) / 0.2) if tl > 0.8 else 1.0)
            d1 = THREAD_DEPTH * f
        else:
            d1 = 0.0

        zc2 = zc1 - PITCH
        if 0.55 <= t <= 0.95:
            tl = (t - 0.55) / 0.40
            f = (tl / 0.2) if tl < 0.2 else (((1.0 - tl) / 0.2) if tl > 0.8 else 1.0)
            d2 = THREAD_DEPTH * f
        else:
            d2 = 0.0

        return zc1, d1, zc2, d2

    def get_base_profile(t):
        zc1, d1, zc2, d2 = get_screw_params(t)

        z2_b = clip(zc2 - THREAD_H_WIDTH, Z_FLOOR, Z_NECK)
        z2_m = clip(zc2, Z_FLOOR, Z_NECK)
        z2_t = clip(zc2 + THREAD_H_WIDTH, Z_FLOOR, Z_NECK)

        z1_b = clip(zc1 - THREAD_H_WIDTH, Z_FLOOR, Z_NECK)
        z1_m = clip(zc1, Z_FLOOR, Z_NECK)
        z1_t = clip(zc1 + THREAD_H_WIDTH, Z_FLOOR, Z_NECK)

        return [
            (0.0, 0.0), (R_OUTER, 0.0), (R_OUTER, Z_FLOOR), (R_BASE_ROOT, Z_FLOOR),
            (R_BASE_ROOT, z2_b), (R_BASE_ROOT + d2, z2_m), (R_BASE_ROOT, z2_t),
            (R_BASE_ROOT, z1_b), (R_BASE_ROOT + d1, z1_m), (R_BASE_ROOT, z1_t),
            (R_BASE_ROOT, Z_NECK), (R_INNER, Z_NECK), (R_INNER, Z_FLOOR), (0.0, Z_FLOOR)
        ]

    def get_lid_profile(t):
        zc1, d1, zc2, d2 = get_screw_params(t)

        z2_b = clip(zc2 - THREAD_H_WIDTH, Z_FLOOR, Z_LID_CAVITY)
        z2_m = clip(zc2, Z_FLOOR, Z_LID_CAVITY)
        z2_t = clip(zc2 + THREAD_H_WIDTH, Z_FLOOR, Z_LID_CAVITY)

        z1_b = clip(zc1 - THREAD_H_WIDTH, Z_FLOOR, Z_LID_CAVITY)
        z1_m = clip(zc1, Z_FLOOR, Z_LID_CAVITY)
        z1_t = clip(zc1 + THREAD_H_WIDTH, Z_FLOOR, Z_LID_CAVITY)

        return [
            (0.0, Z_TOP), (R_OUTER, Z_TOP), (R_OUTER, Z_FLOOR), (R_LID_ROOT, Z_FLOOR),
            (R_LID_ROOT, z2_b), (R_LID_ROOT - d2, z2_m), (R_LID_ROOT, z2_t),
            (R_LID_ROOT, z1_b), (R_LID_ROOT - d1, z1_m), (R_LID_ROOT, z1_t),
            (R_LID_ROOT, Z_LID_CAVITY), (0.0, Z_LID_CAVITY)
        ]

    def build_mesh(name, profile_func):
        verts = []
        faces = []
        num_u = len(profile_func(0.0))

        for i in range(N):
            t = i / N
            angle = 2 * math.pi * t
            prof = profile_func(t)
            for r, z in prof:
                verts.append((r * math.cos(angle), r * math.sin(angle), z))

        for i in range(N):
            next_i = (i + 1) % N
            for j in range(num_u - 1):
                v0 = i * num_u + j
                v1 = i * num_u + (j + 1)
                v2 = next_i * num_u + (j + 1)
                v3 = next_i * num_u + j
                faces.append((v0, v1, v2, v3))

        mesh = bpy.data.meshes.new(name + "_Mesh")
        mesh.from_pydata(verts, [], faces)
        mesh.update()

        bm = bmesh.new()
        bm.from_mesh(mesh)
        bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.001)
        bmesh.ops.recalc_face_normals(bm, faces=bm.faces)
        bm.to_mesh(mesh)
        bm.free()

        obj = bpy.data.objects.new(name, mesh)
        bpy.context.collection.objects.link(obj)
        return obj

    base_obj = build_mesh("CoinCase_Base", get_base_profile)
    lid_obj = build_mesh("CoinCase_Lid", get_lid_profile)

    base_obj.location.x = -22.0
    lid_obj.location.x = 22.0
    lid_obj.rotation_euler[1] = math.pi
    lid_obj.location.z = Z_TOP

create_heavy_thread_manifold_coin_case()

STLをダウンロード


モンブラン(HueForge)

私は食べ物の3Dモデルを作るのが好きで、いくつかストックがあります。その中からモンブランを選んだ理由は、HueForgeで再現しやすい色味だったからです。モンブラン自体は全体的に茶色い見た目をしていますが、HueForgeでは複数の色のフィラメントを薄く重ねることでグラデーションを作り出します。黒、赤、黄、白の4色を重ね合わせていくと、その中間でモンブランの茶色っぽい色合いが自然に再現できます。

今回は底から黒、赤、金、白の4色を順に積み重ねています。黄色の代わりに金属光沢のあるゴールドのシルクPLAを使用することで、光が反射した際の質感を向上させました。

HueForgeのプレビュー画面。右が元画像、左がシミュレーション

モンブランの光沢部分にはあらかじめ小さな穴を開けておき、キュービックジルコニアをゼリー状の瞬間接着剤で接着して輝きのアクセントにしています。

キュービックジルコニアが光を反射してきらりと輝いている

セメダイン3000ゴールド

1層目の剥がれ対策(Blenderでの底面調整)

HueForgeで設定した積層ピッチは0.04mmと非常に薄いため、最初の1層目がベッドにうまく定着せず、印刷開始直後に剥がれて失敗するケースが多発しました。

これを解決するため、Blender側でモデルの底面を物理的に0.08mm厚くモデリングし直しました。 スライサーの設定で「1層目のみ0.12mm厚、2層目以降は0.04mm」として扱うことで、ベッドへの定着力が飛躍的に安定し、1バッチ25枚をノーミスで高速(1枚あたり12分)で印刷できるようになりました。

スライサーのアップデート

AMS無しでの多色積層には手動でのフィラメント交換が必要ですが、最近のBambu Studioのアップデートによって手動でのフィラメント切り替えと一時停止の挙動が非常にスムーズになりました。多色印刷ユニット(AMS)を持たない環境でも、手動交換の手間が大幅に軽減され、量産のハードルが下がったのは嬉しいです。


透明PETGバージョン

200個近い数を一人で量産する過程で、同じものばかり作り続けると飽きてしまうため、別素材のバリエーションとして透明のPETGフィラメントを使用したシェルパーツも製作しました。

Blenderのカーブモディファイアを使用し、シェルの側面に沿って「kame404」という文字テクスチャを凹凸で巻いています。

プラスチック染色

印刷した透明PETGパーツを、市販のプラスチック染色剤「プラ染太郎(ミントグリーン)」の溶液に入れ、5〜10分ほど浸けて染色しました。

浸ける時間によって淡いミントから深みのあるグリーンまで、個体ごとに少しずつ異なる色味になります。 特に淡く染まったものは、正面から見るとほぼ透明に見えますが、斜めから見たときにエッジ部分が厚いガラス板の断面のように緑色に光るため、非常に綺麗な仕上がりになりました。


NFCとQR

ねじ式構造を分解すると、内部にブログのURLを仕込めるスペースがあります。

NFCシールの不足とQRコードの検討

初期のロットでは、内部にNFC(NTAG213)シールを貼り付け、スマホをかざすだけでブログにアクセスできるようにしていました。

底面の内側にNFCシールを貼り付けた

底面の内側にNFCシールを貼り付けた

しかし、量産途中でNFCシールの在庫が切れてしまいました。代わりとしてQRコードの配置を検討しました。3DプリンターでQRコードを直接印刷する手法も考えられますが、このサイズで細密なQRコードを描画しようとすると潰れやすく、歩留まりが悪くなります。

レーザープリンターによるQRシール

そこで、Pythonスクリプトを用いて一括生成したQRコードを、100円ショップのラベルシールに印刷して貼り付ける手法を採用しました。

レーザープリンターを使用すれば、極小サイズでもドットが非常にシャープに印刷されます。これを裁断機で細かくカットし、手作業で内側に貼り付けました。

貼り付ける手間は発生しますが、3DプリンターでQRコードの印刷エラーと戦うよりも圧倒的に早く、この記事のURLへと確実に繋がるQRを仕込むことができました。


おわりに

AMS無しのA1 miniで200個近くのチップを生産するプロジェクトでしたが、パーツを分割した組み立て式設計にしたことで、印刷トラブルの影響を最小限に抑えられました。量産中の気まぐれから生まれた染色PETGや、急遽作成した高精細QRシールなど、試行錯誤も含めてバリエーションの豊かなチップ群になりました。

JRRF2026の会場でお会いできましたら、ぜひこれらのチップを交換させてください。

メイカーチップコレクションでゲットを押していただけると嬉しいです👍
もしこの記事を気に入っていただけましたら、SNSなどでシェアしていただけると励みになります!!