Up 歩行脳NNストアのプログラム 作成: 2026-08-25
更新: 2026-10-04

4足歩行ロボット > 自律型> コンピュータ > プログラミング > 歩行脳NNストア > プログラム

    ##################################### # Python の ROS2ライブラリ import rclpy # <ユーザ定義のデータ型を使う>用 from rclpy.node import Node # データ型をimport # publisher のデータ型 from my_robot_msgs.msg import BrainCmd # subscriber のデータ型 # from my_robot_msgs.msg import BrainCmd from my_robot_msgs.msg import PositionStatus from my_robot_msgs.msg import EarthStatus from my_robot_msgs.msg import VisualStatus # <チャイルドプロセスで #  NN に入力し,そして出力を受ける>用 import subprocess import json # <電流値の確率分布から max値を取り出す>用 import numpy as np ############### # I2C import smbus # get_clock 用 import time ##################################### # ストアをノード Brain, brain に設定 class Brain(Node):   ###############################   # コントラクタ   # (クラスがもつ属性の定義・初期化)   def __init__(self):    # ノート名 : brain    super().__init__('brain')    ##################    # NN に対する<入力する・出力を受ける>を    # child process として実行    # NNプロセス起動    self.nn =    subprocess.Popen(    ['/usr/bin/brain_nn'],    stdin=subprocess.PIPE,    stdout=subprocess.PIPE,    text=True,    bufsize=1    )    ##################    # subscriber    # subscriber データを保持する変数    # None に初期設定    self.brain = None    self.position = None    self.earth = None    self.visual = None    # subscriber の設定    self.create_subscriber(    BrainCmd,    '/brain/cmd',    self.brain_callback,    10    )    self.create_subscriber(    PositionStatus,    '/position/status',    self.position_callback,    10    )    self.create_subscriber(    EarthStatus,    '/earth/status',    self.earth_callback,    10    )    self.create_subscriber(    VisualStatus,    '/vision/status',    self.visual_callback,    10    )    ##################    # publisher の設定    # publish するデータは,BrainCmd    # publisher の設定    self.publisher =    # 形式 : (データ型, topic, バッファ)    self.create_publisher(    BrainCmd,    '/brain/cmd',    10    )    # self.publisher をこのように設定すると,    # self.publisher.publish(msg) で    # msg のpublish となる    # (こういう仕組みになっている)    ##################    # I2C    # MCU の I2C 設定が既にされている    # その内容を書く    # 回路 I2C1 を使用    self.bus = smbus.SMBus(1)    # slave の motor_store_buffer は    #  アドレスが 0x10    self.slave_address = 0x10    ##################    # タイマー    #    # brain_command    # (NN の入力の生成から publish までの処理)    # に timer を付ける    # タイマーは 「新製品出たよ」 コール用    # NN の処理速度は,約100Hz (0.01秒周期)    # そこで 「0.01秒」 を publish の周期とする       self.timer =    self.create_timer(    0.01,    self.brain_command    )      ####################################   # サブルーチン   #      #   callback   #      brain_callback   #      position_callback   #      earth_callback   #      visual_callback   #   brain_nn   #   current_to_i2c_data   #   brain_command   ####################################      ##########################   # callback   #   publisher の call に対し,callback する   #   → 新製品が届く   ##########################   def brain_callback(self, msg):    self.brain = msg   def position_callback(self, msg):    self.position = msg   def earth_callback(self, msg):    self.earth = msg   def visual_callback(self, msg):    self.visual = msg   ##########################   # brain_nn   #   NN に入力し,出力を受ける(プロセス通信ラッパ)   #   motor_command(self) から呼び出される   ############################   def brain_nn(self, brain_input):    # 入力を JSON にして送る    input_json = json.dumps(brain_input)    self.nn.stdin.write(input_json + "\n")    self.nn.stdin.flush()    # NN の出力を受け取る    output_json = self.nn.stdout.readline()    nn_output = json.loads(output_json)    return nn_output   ##########################   # current_to_i2c_data   #   fload32[12] の current を   #   2byte が 12個の配列 data に変換   ############################   def current_to_i2c_data(current):    # A → mA    current_mA = (    current * 1000    ).astype(np.int16)    data = []    for value in current_mA:    high = (value >> 8) & 0xFF    low = value & 0xFF    data.append(high)    data.append(low)    return data   ############################   # brain_command   #   (1) NN 入力生成   #   (2) NN 呼び出し → 入力→ 出力   #   (3) 出力を OUT 用に成形   #   (4) publish   #   (5) モータストアへ I2C 送信   ############################   def brain_command(self):    ##################    # (1) 歩行脳NN の入力を生成    ##################    # IN庫の入荷棚が全部埋まっていなければスキップ    if None in (    self.brain,    self.position,    self.earth,    self.visual    ):    return    # 歩行脳NN の入力は,    # 4つの subscriber のデータをつないたもの    brain_input = {    "brain": self.brain.__dict__    "position": self.position.__dict__,    "visual": self.visual.__dict__,    "earth": self.earth.__dict__,    }    ##################    # (2) NN 呼び出し → 入力→ 出力    ##################    nn_output = self.brain_nn(brain_input)    ##################    # (3) 出力を OUT 用に成形    ##################    #  出力はモータ12個の電流値の確率分布    #  それぞれに対し最大確率の電流値を択る    current =    np.argmax(    nn_output,    axis=1    )    .astype(np.float32)    ##################    # (4) publish    ##################    # BrainCmd メッセージを生成    msg = BrainCmd()    # BrainCmd のデータ型は,電流値 だけ    # この変数を current とする。    # ROS2 は Python list を要求する    msg.current = current.tolist()    # publish    self.publisher.publish(msg)    ##################    # (5) モータストアへ I2C 送信    ##################    # byte列へ変換    i2c_data = current_to_i2c_data( current.)    # motor_store_buffer に書き込む    self.bus.write_i2c_block_data(    self.slave_address,    0x00,    i2c_data    ) ##################################### ##################################### ################## #  main ################## # main は,つぎの定型にするのが,Python 流 # (「***クラスの物を spin させる」) def main(args=None):   rclpy.init(args=args)   # オブジェクト化   node = Brain()   rclpy.spin(node)   node.destroy_node()   rclpy.shutdown() ################## # プログラムの起動 ################## if __name__ == '__main__':   main()