def test_pad(cfg): from parts import get_js_controller controller = get_js_controller(cfg) V = dk.vehicle.Vehicle() V.add(controller, outputs=['angle', 'throttle', 'lift_throttle', 'mode', 'recording'], threaded=True) class Prt: def run(self, angle, throttle, lift_throttle, mode, recording): print('angle:{}, throttle:{}, lift:{}, mode:{}, rec:{}'.format( str(angle), str(throttle), str(lift_throttle), str(mode), str(recording))) V.add(Prt(), inputs=['angle', 'throttle', 'lift_throttle', 'mode', 'recording']) from parts import ForkliftMotorDriver motor_driver = ForkliftMotorDriver(debug=False) V.add(motor_driver, inputs=['throttle', 'angle', 'lift_throttle'], outputs=[ 'left_motor_vref', 'left_motor_in1', 'left_motor_in2', 'right_motor_vref', 'right_motor_in1', 'right_motor_in2', 'lift_motor_vref', 'lift_motor_in1', 'lift_motor_in2' ]) class Prt2: def run(self, left_verf, left_in1, left_in2, right_verf, right_in1, right_in2, lift_verf, lift_in1, lift_in2): print('ForkliftMD left verf:{}, in1:{}, in2:{}'.format( str(left_verf), str(left_in1), str(left_in2))) print('ForkliftMD right verf:{}, in1:{}, in2:{}'.format( str(right_verf), str(right_in1), str(right_in2))) print('ForkliftMD lift verf:{}, in1:{}, in2:{}'.format( str(lift_verf), str(lift_in1), str(lift_in2))) V.add(Prt2(), inputs=[ 'left_motor_vref', 'left_motor_in1', 'left_motor_in2', 'right_motor_vref', 'right_motor_in1', 'right_motor_in2', 'lift_motor_vref', 'lift_motor_in1', 'lift_motor_in2' ]) import pigpio pgio = pigpio.pi() from parts import PIGPIO_OUT, PIGPIO_PWM V.add(PIGPIO_OUT(pin=cfg.LEFT_MOTOR_IN1_GPIO, pgio=pgio), inputs=['left_motor_in1']) V.add(PIGPIO_OUT(pin=cfg.LEFT_MOTOR_IN2_GPIO, pgio=pgio), inputs=['left_motor_in2']) V.add(PIGPIO_PWM(pin=cfg.LEFT_MOTOR_PWM_GPIO, pgio=pgio, freq=cfg.PWM_FREQ, range=cfg.PWM_RANGE, threshold=cfg.PWM_INPUT_THRESHOLD), inputs=['left_motor_vref']) V.add(PIGPIO_OUT(pin=cfg.RIGHT_MOTOR_IN1_GPIO, pgio=pgio), inputs=['right_motor_in1']) V.add(PIGPIO_OUT(pin=cfg.RIGHT_MOTOR_IN2_GPIO, pgio=pgio), inputs=['right_motor_in2']) V.add(PIGPIO_PWM(pin=cfg.RIGHT_MOTOR_PWM_GPIO, pgio=pgio, freq=cfg.PWM_FREQ, range=cfg.PWM_RANGE, threshold=cfg.PWM_INPUT_THRESHOLD), inputs=['right_motor_vref']) V.add(PIGPIO_OUT(pin=cfg.LIFT_MOTOR_IN1_GPIO, pgio=pgio), inputs=['lift_motor_in1']) V.add(PIGPIO_OUT(pin=cfg.LIFT_MOTOR_IN2_GPIO, pgio=pgio), inputs=['lift_motor_in2']) V.add(PIGPIO_PWM(pin=cfg.LIFT_MOTOR_PWM_GPIO, pgio=pgio, freq=cfg.PWM_FREQ, range=cfg.PWM_RANGE, threshold=cfg.PWM_INPUT_THRESHOLD), inputs=['lift_motor_vref']) try: V.start(rate_hz=20, max_loop_count=10000) except KeyboardInterrupt: print('exit') finally: pgio.stop()
def drive(cfg, model_path=None, use_joystick=False, model_type=None, camera_type='single', meta=[] ): ''' Construct a working robotic vehicle from many parts. Each part runs as a job in the Vehicle loop, calling either it's run or run_threaded method depending on the constructor flag `threaded`. All parts are updated one after another at the framerate given in cfg.DRIVE_LOOP_HZ assuming each part finishes processing in a timely manner. Parts may have named outputs and inputs. The framework handles passing named outputs to parts requesting the same named input. ''' if cfg.DONKEY_GYM: #the simulator will use cuda and then we usually run out of resources #if we also try to use cuda. so disable for donkey_gym. os.environ["CUDA_VISIBLE_DEVICES"]="-1" if model_type is None: if cfg.TRAIN_LOCALIZER: model_type = "localizer" elif cfg.TRAIN_BEHAVIORS: model_type = "behavior" else: model_type = cfg.DEFAULT_MODEL_TYPE #Initialize car V = dk.vehicle.Vehicle() if camera_type == "stereo": if cfg.CAMERA_TYPE == "WEBCAM": from donkeycar.parts.camera import Webcam camA = Webcam(image_w=cfg.IMAGE_W, image_h=cfg.IMAGE_H, image_d=cfg.IMAGE_DEPTH, iCam = 0) camB = Webcam(image_w=cfg.IMAGE_W, image_h=cfg.IMAGE_H, image_d=cfg.IMAGE_DEPTH, iCam = 1) elif cfg.CAMERA_TYPE == "CVCAM": from donkeycar.parts.cv import CvCam camA = CvCam(image_w=cfg.IMAGE_W, image_h=cfg.IMAGE_H, image_d=cfg.IMAGE_DEPTH, iCam = 0) camB = CvCam(image_w=cfg.IMAGE_W, image_h=cfg.IMAGE_H, image_d=cfg.IMAGE_DEPTH, iCam = 1) else: raise(Exception("Unsupported camera type: %s" % cfg.CAMERA_TYPE)) V.add(camA, outputs=['cam/image_array_a'], threaded=True) V.add(camB, outputs=['cam/image_array_b'], threaded=True) from donkeycar.parts.image import StereoPair V.add(StereoPair(), inputs=['cam/image_array_a', 'cam/image_array_b'], outputs=['cam/image_array']) else: inputs = [] threaded = True print("cfg.CAMERA_TYPE", cfg.CAMERA_TYPE) if cfg.DONKEY_GYM: from donkeycar.parts.dgym import DonkeyGymEnv cam = DonkeyGymEnv(cfg.DONKEY_SIM_PATH, env_name=cfg.DONKEY_GYM_ENV_NAME) threaded = True inputs = ['angle', 'throttle'] elif cfg.CAMERA_TYPE == "PICAM": from donkeycar.parts.camera import PiCamera cam = PiCamera(image_w=cfg.IMAGE_W, image_h=cfg.IMAGE_H, image_d=cfg.IMAGE_DEPTH) elif cfg.CAMERA_TYPE == "WEBCAM": from donkeycar.parts.camera import Webcam cam = Webcam(image_w=cfg.IMAGE_W, image_h=cfg.IMAGE_H, image_d=cfg.IMAGE_DEPTH) elif cfg.CAMERA_TYPE == "CVCAM": from donkeycar.parts.cv import CvCam cam = CvCam(image_w=cfg.IMAGE_W, image_h=cfg.IMAGE_H, image_d=cfg.IMAGE_DEPTH) elif cfg.CAMERA_TYPE == "CSIC": from donkeycar.parts.camera import CSICamera cam = CSICamera(image_w=cfg.IMAGE_W, image_h=cfg.IMAGE_H, image_d=cfg.IMAGE_DEPTH, framerate=cfg.CAMERA_FRAMERATE) elif cfg.CAMERA_TYPE == "V4L": from donkeycar.parts.camera import V4LCamera cam = V4LCamera(image_w=cfg.IMAGE_W, image_h=cfg.IMAGE_H, image_d=cfg.IMAGE_DEPTH, framerate=cfg.CAMERA_FRAMERATE) elif cfg.CAMERA_TYPE == "MOCK": from donkeycar.parts.camera import MockCamera cam = MockCamera(image_w=cfg.IMAGE_W, image_h=cfg.IMAGE_H, image_d=cfg.IMAGE_DEPTH) else: raise(Exception("Unkown camera type: %s" % cfg.CAMERA_TYPE)) V.add(cam, inputs=inputs, outputs=['cam/image_array'], threaded=threaded) if use_joystick or cfg.USE_JOYSTICK_AS_DEFAULT: #modify max_throttle closer to 1.0 to have more power #modify steering_scale lower than 1.0 to have less responsive steering #from donkeycar.parts.controller import get_js_controller from parts import get_js_controller ctr = get_js_controller(cfg) if cfg.USE_NETWORKED_JS: from donkeycar.parts.controller import JoyStickSub netwkJs = JoyStickSub(cfg.NETWORK_JS_SERVER_IP) V.add(netwkJs, threaded=True) ctr.js = netwkJs else: #This web controller will create a web server that is capable #of managing steering, throttle, and modes, and more. ctr = LocalWebController() V.add(ctr, inputs=['cam/image_array'], outputs=['user/angle', 'user/throttle', 'user/mode', 'recording'], threaded=True) #this throttle filter will allow one tap back for esc reverse th_filter = ThrottleFilter() V.add(th_filter, inputs=['user/throttle'], outputs=['user/throttle']) #See if we should even run the pilot module. #This is only needed because the part run_condition only accepts boolean class PilotCondition: def run(self, mode): if mode == 'user': return False else: return True V.add(PilotCondition(), inputs=['user/mode'], outputs=['run_pilot']) class LedConditionLogic: def __init__(self, cfg): self.cfg = cfg def run(self, mode, recording, recording_alert, behavior_state, model_file_changed, track_loc): #returns a blink rate. 0 for off. -1 for on. positive for rate. if track_loc is not None: led.set_rgb(*self.cfg.LOC_COLORS[track_loc]) return -1 if model_file_changed: led.set_rgb(self.cfg.MODEL_RELOADED_LED_R, self.cfg.MODEL_RELOADED_LED_G, self.cfg.MODEL_RELOADED_LED_B) return 0.1 else: led.set_rgb(self.cfg.LED_R, self.cfg.LED_G, self.cfg.LED_B) if recording_alert: led.set_rgb(*recording_alert) return self.cfg.REC_COUNT_ALERT_BLINK_RATE else: led.set_rgb(self.cfg.LED_R, self.cfg.LED_G, self.cfg.LED_B) if behavior_state is not None and model_type == 'behavior': r, g, b = self.cfg.BEHAVIOR_LED_COLORS[behavior_state] led.set_rgb(r, g, b) return -1 #solid on if recording: return -1 #solid on elif mode == 'user': return 1 elif mode == 'local_angle': return 0.5 elif mode == 'local': return 0.1 return 0 if cfg.HAVE_RGB_LED and not cfg.DONKEY_GYM: from donkeycar.parts.led_status import RGB_LED led = RGB_LED(cfg.LED_PIN_R, cfg.LED_PIN_G, cfg.LED_PIN_B, cfg.LED_INVERT) led.set_rgb(cfg.LED_R, cfg.LED_G, cfg.LED_B) V.add(LedConditionLogic(cfg), inputs=['user/mode', 'recording', "records/alert", 'behavior/state', 'modelfile/modified', "pilot/loc"], outputs=['led/blink_rate']) V.add(led, inputs=['led/blink_rate']) def get_record_alert_color(num_records): col = (0, 0, 0) for count, color in cfg.RECORD_ALERT_COLOR_ARR: if num_records >= count: col = color return col class RecordTracker: def __init__(self): self.last_num_rec_print = 0 self.dur_alert = 0 self.force_alert = 0 def run(self, num_records): if num_records is None: return 0 if self.last_num_rec_print != num_records or self.force_alert: self.last_num_rec_print = num_records if num_records % 10 == 0: print("recorded", num_records, "records") if num_records % cfg.REC_COUNT_ALERT == 0 or self.force_alert: self.dur_alert = num_records // cfg.REC_COUNT_ALERT * cfg.REC_COUNT_ALERT_CYC self.force_alert = 0 if self.dur_alert > 0: self.dur_alert -= 1 if self.dur_alert != 0: return get_record_alert_color(num_records) return 0 rec_tracker_part = RecordTracker() V.add(rec_tracker_part, inputs=["tub/num_records"], outputs=['records/alert']) if cfg.AUTO_RECORD_ON_THROTTLE and isinstance(ctr, JoystickController): #then we are not using the circle button. hijack that to force a record count indication def show_record_acount_status(): rec_tracker_part.last_num_rec_print = 0 rec_tracker_part.force_alert = 1 ctr.set_button_down_trigger('circle', show_record_acount_status) #Sombrero if cfg.HAVE_SOMBRERO: from donkeycar.parts.sombrero import Sombrero s = Sombrero() #IMU if cfg.HAVE_IMU: from donkeycar.parts.imu import Mpu6050 imu = Mpu6050() V.add(imu, outputs=['imu/acl_x', 'imu/acl_y', 'imu/acl_z', 'imu/gyr_x', 'imu/gyr_y', 'imu/gyr_z'], threaded=True) #Behavioral state if cfg.TRAIN_BEHAVIORS: bh = BehaviorPart(cfg.BEHAVIOR_LIST) V.add(bh, outputs=['behavior/state', 'behavior/label', "behavior/one_hot_state_array"]) try: ctr.set_button_down_trigger('L1', bh.increment_state) except: pass inputs = ['cam/image_array', "behavior/one_hot_state_array"] #IMU elif model_type == "imu": assert(cfg.HAVE_IMU) #Run the pilot if the mode is not user. inputs=['cam/image_array', 'imu/acl_x', 'imu/acl_y', 'imu/acl_z', 'imu/gyr_x', 'imu/gyr_y', 'imu/gyr_z'] else: inputs=['cam/image_array'] def load_model(kl, model_path): start = time.time() try: print('loading model', model_path) kl.load(model_path) print('finished loading in %s sec.' % (str(time.time() - start)) ) except Exception as e: print(e) print('ERR>> problems loading model', model_path) def load_weights(kl, weights_path): start = time.time() try: print('loading model weights', weights_path) kl.model.load_weights(weights_path) print('finished loading in %s sec.' % (str(time.time() - start)) ) except Exception as e: print(e) print('ERR>> problems loading weights', weights_path) def load_model_json(kl, json_fnm): start = time.time() print('loading model json', json_fnm) from tensorflow.python import keras try: with open(json_fnm, 'r') as handle: contents = handle.read() kl.model = keras.models.model_from_json(contents) print('finished loading json in %s sec.' % (str(time.time() - start)) ) except Exception as e: print(e) print("ERR>> problems loading model json", json_fnm) if model_path: #When we have a model, first create an appropriate Keras part kl = dk.utils.get_model_by_type(model_type, cfg) model_reload_cb = None if '.h5' in model_path: #when we have a .h5 extension #load everything from the model file load_model(kl, model_path) def reload_model(filename): load_model(kl, filename) model_reload_cb = reload_model elif '.json' in model_path: #when we have a .json extension #load the model from there and look for a matching #.wts file with just weights load_model_json(kl, model_path) weights_path = model_path.replace('.json', '.weights') load_weights(kl, weights_path) def reload_weights(filename): weights_path = filename.replace('.json', '.weights') load_weights(kl, weights_path) model_reload_cb = reload_weights else: print("ERR>> Unknown extension type on model file!!") return #this part will signal visual LED, if connected V.add(FileWatcher(model_path, verbose=True), outputs=['modelfile/modified']) #these parts will reload the model file, but only when ai is running so we don't interrupt user driving V.add(FileWatcher(model_path), outputs=['modelfile/dirty'], run_condition="ai_running") V.add(DelayedTrigger(100), inputs=['modelfile/dirty'], outputs=['modelfile/reload'], run_condition="ai_running") V.add(TriggeredCallback(model_path, model_reload_cb), inputs=["modelfile/reload"], run_condition="ai_running") outputs=['pilot/angle', 'pilot/throttle'] if cfg.TRAIN_LOCALIZER: outputs.append("pilot/loc") V.add(kl, inputs=inputs, outputs=outputs, run_condition='run_pilot') #Choose what inputs should change the car. class DriveMode: def run(self, mode, user_angle, user_throttle, pilot_angle, pilot_throttle): if mode == 'user': return user_angle, user_throttle elif mode == 'local_angle': return pilot_angle, user_throttle else: return pilot_angle, pilot_throttle * cfg.AI_THROTTLE_MULT V.add(DriveMode(), inputs=['user/mode', 'user/angle', 'user/throttle', 'pilot/angle', 'pilot/throttle'], outputs=['angle', 'throttle']) #to give the car a boost when starting ai mode in a race. aiLauncher = AiLaunch(cfg.AI_LAUNCH_DURATION, cfg.AI_LAUNCH_THROTTLE, cfg.AI_LAUNCH_KEEP_ENABLED) V.add(aiLauncher, inputs=['user/mode', 'throttle'], outputs=['throttle']) if isinstance(ctr, JoystickController): ctr.set_button_down_trigger(cfg.AI_LAUNCH_ENABLE_BUTTON, aiLauncher.enable_ai_launch) class AiRunCondition: ''' A bool part to let us know when ai is running. ''' def run(self, mode): if mode == "user": return False return True V.add(AiRunCondition(), inputs=['user/mode'], outputs=['ai_running']) #Ai Recording class AiRecordingCondition: ''' return True when ai mode, otherwize respect user mode recording flag ''' def run(self, mode, recording): if mode == 'user': return recording return True if cfg.RECORD_DURING_AI: V.add(AiRecordingCondition(), inputs=['user/mode', 'recording'], outputs=['recording']) #Drive train setup if cfg.DONKEY_GYM: pass elif cfg.DRIVE_TRAIN_TYPE == "SERVO_ESC": from donkeycar.parts.actuator import PCA9685, PWMSteering, PWMThrottle steering_controller = PCA9685(cfg.STEERING_CHANNEL, cfg.PCA9685_I2C_ADDR, busnum=cfg.PCA9685_I2C_BUSNUM) steering = PWMSteering(controller=steering_controller, left_pulse=cfg.STEERING_LEFT_PWM, right_pulse=cfg.STEERING_RIGHT_PWM) throttle_controller = PCA9685(cfg.THROTTLE_CHANNEL, cfg.PCA9685_I2C_ADDR, busnum=cfg.PCA9685_I2C_BUSNUM) throttle = PWMThrottle(controller=throttle_controller, max_pulse=cfg.THROTTLE_FORWARD_PWM, zero_pulse=cfg.THROTTLE_STOPPED_PWM, min_pulse=cfg.THROTTLE_REVERSE_PWM) V.add(steering, inputs=['angle']) V.add(throttle, inputs=['throttle']) elif cfg.DRIVE_TRAIN_TYPE == "DC_STEER_THROTTLE": from donkeycar.parts.actuator import Mini_HBridge_DC_Motor_PWM steering = Mini_HBridge_DC_Motor_PWM(cfg.HBRIDGE_PIN_LEFT, cfg.HBRIDGE_PIN_RIGHT) throttle = Mini_HBridge_DC_Motor_PWM(cfg.HBRIDGE_PIN_FWD, cfg.HBRIDGE_PIN_BWD) V.add(steering, inputs=['angle']) V.add(throttle, inputs=['throttle']) elif cfg.DRIVE_TRAIN_TYPE == "DC_TWO_WHEEL": from donkeycar.parts.actuator import TwoWheelSteeringThrottle, Mini_HBridge_DC_Motor_PWM left_motor = Mini_HBridge_DC_Motor_PWM(cfg.HBRIDGE_PIN_LEFT_FWD, cfg.HBRIDGE_PIN_LEFT_BWD) right_motor = Mini_HBridge_DC_Motor_PWM(cfg.HBRIDGE_PIN_RIGHT_FWD, cfg.HBRIDGE_PIN_RIGHT_BWD) two_wheel_control = TwoWheelSteeringThrottle() V.add(two_wheel_control, inputs=['throttle', 'angle'], outputs=['left_motor_speed', 'right_motor_speed']) V.add(left_motor, inputs=['left_motor_speed']) V.add(right_motor, inputs=['right_motor_speed']) elif cfg.DRIVE_TRAIN_TYPE == "SERVO_HBRIDGE_PWM": from donkeycar.parts.actuator import ServoBlaster, PWMSteering steering_controller = ServoBlaster(cfg.STEERING_CHANNEL) #really pin #PWM pulse values should be in the range of 100 to 200 assert(cfg.STEERING_LEFT_PWM <= 200) assert(cfg.STEERING_RIGHT_PWM <= 200) steering = PWMSteering(controller=steering_controller, left_pulse=cfg.STEERING_LEFT_PWM, right_pulse=cfg.STEERING_RIGHT_PWM) from donkeycar.parts.actuator import Mini_HBridge_DC_Motor_PWM motor = Mini_HBridge_DC_Motor_PWM(cfg.HBRIDGE_PIN_FWD, cfg.HBRIDGE_PIN_BWD) V.add(steering, inputs=['angle']) V.add(motor, inputs=["throttle"]) #add tub to save data inputs=['cam/image_array', 'user/angle', 'user/throttle', 'user/mode'] types=['image_array', 'float', 'float', 'str'] if cfg.TRAIN_BEHAVIORS: inputs += ['behavior/state', 'behavior/label', "behavior/one_hot_state_array"] types += ['int', 'str', 'vector'] if cfg.HAVE_IMU: inputs += ['imu/acl_x', 'imu/acl_y', 'imu/acl_z', 'imu/gyr_x', 'imu/gyr_y', 'imu/gyr_z'] types +=['float', 'float', 'float', 'float', 'float', 'float'] if cfg.RECORD_DURING_AI: inputs += ['pilot/angle', 'pilot/throttle'] types += ['float', 'float'] th = TubHandler(path=cfg.DATA_PATH) tub = th.new_tub_writer(inputs=inputs, types=types, user_meta=meta) V.add(tub, inputs=inputs, outputs=["tub/num_records"], run_condition='recording') if cfg.PUB_CAMERA_IMAGES: from donkeycar.parts.network import TCPServeValue from donkeycar.parts.image import ImgArrToJpg pub = TCPServeValue("camera") V.add(ImgArrToJpg(), inputs=['cam/image_array'], outputs=['jpg/bin']) V.add(pub, inputs=['jpg/bin']) if type(ctr) is LocalWebController: print("You can now go to <your pi ip address>:8887 to drive your car.") elif isinstance(ctr, JoystickController): print("You can now move your joystick to drive your car.") #tell the controller about the tub ctr.set_tub(tub) if cfg.BUTTON_PRESS_NEW_TUB: def new_tub_dir(): V.parts.pop() tub = th.new_tub_writer(inputs=inputs, types=types, user_meta=meta) V.add(tub, inputs=inputs, outputs=["tub/num_records"], run_condition='recording') ctr.set_tub(tub) ctr.set_button_down_trigger('cross', new_tub_dir) ctr.print_controls() #run the vehicle for 20 seconds V.start(rate_hz=cfg.DRIVE_LOOP_HZ, max_loop_count=cfg.MAX_LOOPS)
def test_pad(use_debug=False): import donkeycar as dk cfg = dk.load_config() V = dk.vehicle.Vehicle() from parts import get_js_controller ctr = get_js_controller(cfg) V.add(ctr, inputs=['cam/image_array'], outputs=['user/angle', 'user/throttle', 'user/mode', 'recording'], threaded=True) V.mem['pilot/angle'] = 0.0 V.mem['pilot/throttle'] = 0.0 class DriveMode: def run(self, mode, user_angle, user_throttle, pilot_angle, pilot_throttle): if mode == 'user': return user_angle, user_throttle elif mode == 'local_angle': return pilot_angle, user_throttle else: return pilot_angle, pilot_throttle * cfg.AI_THROTTLE_MULT V.add(DriveMode(), inputs=['user/mode', 'user/angle', 'user/throttle', 'pilot/angle', 'pilot/throttle'], outputs=['angle', 'throttle']) class TestPad: def run(self, angle, throttle, mode, recording): print('an:{} th:{} mode:{}, rec:{}'.format( str(angle), str(throttle), str(mode), str(recording) )) def shutdown(self): pass V.add(TestPad(), inputs=['angle', 'throttle', 'user/mode', 'recording']) from parts import CaterpillerMotorDriver V.add(CaterpillerMotorDriver( left_balance=cfg.LEFT_PWM_BALANCE, right_balance=cfg.RIGHT_PWM_BALANCE, debug=False), inputs=['throttle', 'angle'], outputs=['left_motor_vref', 'left_motor_in1', 'left_motor_in2', 'right_motor_vref', 'right_motor_in1', 'right_motor_in2']) class TestDriver: def run(self, left_in1, left_in2, left_pwm, right_in1, right_in2, right_pwm): print('lin1:{} lin2:{} lpwm:{} rin1:{} rin2:{} rpwm:{}'.format( str(left_in1), str(left_in2), str(left_pwm), str(right_in1), str(right_in2), str(right_pwm) )) def shutdown(self): pass V.add(TestDriver(), inputs=['left_motor_in1', 'left_motor_in2', 'left_motor_vref', 'right_motor_in1', 'right_motor_in2', 'right_motor_vref']) try: print('Start driving') #run the vehicle for 20 seconds V.start(rate_hz=cfg.DRIVE_LOOP_HZ, max_loop_count=cfg.MAX_LOOPS) except KeyboardInterrupt: print('Halt driving') finally: print('Stop driving')
def drive(cfg, model_path=None, use_joystick=False, use_hedge=False, use_aws=False, use_map=False, use_debug=False, model_type=None, camera_type='single', write_both_images=False, meta=[]): ''' Construct a working robotic vehicle from many parts. Each part runs as a job in the Vehicle loop, calling either it's run or run_threaded method depending on the constructor flag `threaded`. All parts are updated one after another at the framerate given in cfg.DRIVE_LOOP_HZ assuming each part finishes processing in a timely manner. Parts may have named outputs and inputs. The framework handles passing named outputs to parts requesting the same named input. ''' if cfg.DONKEY_GYM: #the simulator will use cuda and then we usually run out of resources #if we also try to use cuda. so disable for donkey_gym. os.environ["CUDA_VISIBLE_DEVICES"] = "-1" if model_type is None: if cfg.TRAIN_LOCALIZER: model_type = "localizer" elif cfg.TRAIN_BEHAVIORS: model_type = "behavior" else: model_type = cfg.DEFAULT_MODEL_TYPE # pigpio 初期化 try: import pigpio except: raise pgio = pigpio.pi() #Initialize car V = dk.vehicle.Vehicle() ''' カメラ ''' if camera_type == "stereo": if cfg.CAMERA_TYPE == "WEBCAM": from donkeycar.parts.camera import Webcam camA = Webcam(image_w=cfg.IMAGE_W, image_h=cfg.IMAGE_H, image_d=cfg.IMAGE_DEPTH, iCam=0) camB = Webcam(image_w=cfg.IMAGE_W, image_h=cfg.IMAGE_H, image_d=cfg.IMAGE_DEPTH, iCam=1) elif cfg.CAMERA_TYPE == "CVCAM": from donkeycar.parts.cv import CvCam camA = CvCam(image_w=cfg.IMAGE_W, image_h=cfg.IMAGE_H, image_d=cfg.IMAGE_DEPTH, iCam=0) camB = CvCam(image_w=cfg.IMAGE_W, image_h=cfg.IMAGE_H, image_d=cfg.IMAGE_DEPTH, iCam=1) else: raise (Exception("Unsupported camera type: %s" % cfg.CAMERA_TYPE)) V.add(camA, outputs=['cam/image_array_a'], threaded=True) V.add(camB, outputs=['cam/image_array_b'], threaded=True) from donkeycar.parts.image import StereoPair V.add(StereoPair(), inputs=['cam/image_array_a', 'cam/image_array_b'], outputs=['cam/image_array']) elif write_both_images or (cfg.CAMERA_TYPE != "MAP" and use_map == False): print("cfg.CAMERA_TYPE", cfg.CAMERA_TYPE) if cfg.DONKEY_GYM: from donkeycar.parts.dgym import DonkeyGymEnv inputs = [] threaded = True #print("cfg.CAMERA_TYPE", cfg.CAMERA_TYPE) if cfg.DONKEY_GYM: from donkeycar.parts.dgym import DonkeyGymEnv cam = DonkeyGymEnv(cfg.DONKEY_SIM_PATH, env_name=cfg.DONKEY_GYM_ENV_NAME) threaded = True inputs = ['angle', 'throttle'] elif cfg.CAMERA_TYPE == "PICAM": from donkeycar.parts.camera import PiCamera cam = PiCamera(image_w=cfg.IMAGE_W, image_h=cfg.IMAGE_H, image_d=cfg.IMAGE_DEPTH) elif cfg.CAMERA_TYPE == "WEBCAM": from donkeycar.parts.camera import Webcam cam = Webcam(image_w=cfg.IMAGE_W, image_h=cfg.IMAGE_H, image_d=cfg.IMAGE_DEPTH) elif cfg.CAMERA_TYPE == "CVCAM": from donkeycar.parts.cv import CvCam cam = CvCam(image_w=cfg.IMAGE_W, image_h=cfg.IMAGE_H, image_d=cfg.IMAGE_DEPTH) elif cfg.CAMERA_TYPE == "CSIC": from donkeycar.parts.camera import CSICamera cam = CSICamera(image_w=cfg.IMAGE_W, image_h=cfg.IMAGE_H, image_d=cfg.IMAGE_DEPTH, framerate=cfg.CAMERA_FRAMERATE, gstreamer_flip=cfg.CSIC_CAM_GSTREAMER_FLIP_PARM) elif cfg.CAMERA_TYPE == "V4L": from donkeycar.parts.camera import V4LCamera cam = V4LCamera(image_w=cfg.IMAGE_W, image_h=cfg.IMAGE_H, image_d=cfg.IMAGE_DEPTH, framerate=cfg.CAMERA_FRAMERATE) elif cfg.CAMERA_TYPE == "MOCK": from donkeycar.parts.camera import MockCamera cam = MockCamera(image_w=cfg.IMAGE_W, image_h=cfg.IMAGE_H, image_d=cfg.IMAGE_DEPTH) else: raise (Exception("Unkown camera type: %s" % cfg.CAMERA_TYPE)) # 前方画像を保管するV.memキー名 if write_both_images: from parts.datastore import FWD_CAMERA_KEY outputs = [FWD_CAMERA_KEY] else: outputs = ['cam/image_array'] V.add(cam, inputs=inputs, outputs=outputs, threaded=threaded) ''' Marvelmind 位置情報システム ''' # Marvelmind USNav データ hedge_items = [] hedge_types = [] usnav_items = [ 'usnav/id', 'usnav/x', 'usnav/y', 'usnav/z', 'usnav/angle', 'usnav/timestamp', ] usnav_types = [ 'str', 'float', 'float', 'float', 'float', 'float', ] hedge_items += usnav_items hedge_types += usnav_types # Marvelmind USNav Raw データ usnav_raw_items = [ 'dist/id', 'dist/b1', 'dist/b1d', 'dist/b2', 'dist/b2d', 'dist/b3', 'dist/b3d', 'dist/b4', 'dist/b4d', 'dist/timestamp' ] usnav_raw_types = [ 'str', 'str', 'float', 'str', 'float', 'str', 'float', 'str', 'float', 'float', ] hedge_items += usnav_raw_items hedge_types += usnav_raw_types # Marvelmind IMU データ imu_items = [ 'imu/x', 'imu/y', 'imu/z', 'imu/qw', 'imu/qx', 'imu/qy', 'imu/qz', 'imu/vx', 'imu/vy', 'imu/vz', 'imu/ax', 'imu/ay', 'imu/az', 'imu/gx', 'imu/gy', 'imu/gz', 'imu/mx', 'imu/my', 'imu/mz', 'imu/timestamp', ] imu_types = [ 'float', 'float', 'float', 'float', 'float', 'float', 'float', 'float', 'float', 'float', 'float', 'float', 'float', 'float', 'float', 'float', 'float', 'float', 'float', 'float' ] # Marvelmind 全データ hedge_items += imu_items hedge_types += imu_types # Veichle上の Marvelmind 全データ初期化 for i in range(len(hedge_items)): _item = hedge_items[i] _type = hedge_types[i] if _type == 'str': V.mem[_item] = '0' elif _type == 'float': V.mem[_item] = 0.0 else: raise ValueError('unknown type:{}'.format(_type)) if cfg.HAVE_HEDGE and (use_hedge or cfg.USE_HEDGE_AS_DEFAULT): ''' Marvelmind システムを使用する場合 ''' print('Using Marvelmind Mobile Beacon') # Marvelmind モバイルビーコンパーツ追加 from parts import HedgehogController hedge = HedgehogController(tty=cfg.HEDGE_SERIAL_TTY, adr=cfg.HEDGE_ID) V.add(hedge, outputs=hedge_items) ''' IMU(MPU9250/MPU6050) ''' mpu6050_items = [ 'imu/acl_x', 'imu/acl_y', 'imu/acl_z', 'imu/gyr_x', 'imu/gyr_y', 'imu/gyr_z', 'imu/recent', 'imu/mpu_timestamp', ] mpu6050_types = [ 'float', 'float', 'float', 'float', 'float', 'float', 'str', 'float', ] mpu9250_items = [ 'imu/acl_x', 'imu/acl_y', 'imu/acl_z', 'imu/gyr_x', 'imu/gyr_y', 'imu/gyr_z', 'imu/mgt_x', 'imu/mgt_y', 'imu/mgt_z', 'imu/temp', 'imu/recent', 'imu/mpu_timestamp', ] mpu9250_types = [ 'float', 'float', 'float', 'float', 'float', 'float', 'float', 'float', 'float', 'float', 'str', 'float', ] if cfg.HAVE_IMU: mpu_items = [] mpu_types = [] if cfg.IMU_TYPE == 'mpu6050': ''' MPU6050を使用する場合 ''' mpu_items += mpu6050_items mpu_types += mpu6050_types from parts.sensors.imu import Mpu6050 imu = Mpu6050(pgio=pgio, bus=cfg.MPU6050_I2C_BUS, address=cfg.MPU6050_I2C_ADDRESS, depth=cfg.MPU6050_DEPTH, debug=use_debug) elif cfg.IMU_TYPE == 'mpu9250': ''' MPU9250を使用する場合 ''' mpu_items += mpu9250_items mpu_types += mpu9250_types from parts.sensors.imu import Mpu9250 imu = Mpu9250(pgio=pgio, bus=cfg.MPU9250_I2C_BUS, mpu9250_address=cfg.MPU9250_I2C_ADDRESS, ak8963_address=cfg.AK8963_I2C_ADDRESS, depth=cfg.MPU9250_DEPTH, debug=use_debug) else: raise ValueError('unknown IMU_TYPE = {}'.format(str(cfg.IMU_TYPE))) # Veihcle上のIMUデータを初期化 for i in range(len(mpu_items)): _item = mpu_items[i] _type = mpu_types[i] if _type == 'str': V.mem[_item] = '{}' elif _type == 'float': V.mem[_item] = 0.0 else: raise ValueError('unknown type:{}'.format(_type)) # IMUパーツの追加 V.add(imu, outputs=mpu_items) ''' 2D マップ画像 (usnav/x, usnav/y, imu/recent を使用する) ''' map_items = [ 'usnav/x', 'usnav/y', 'imu/recent', ] map_types = [ 'float', 'float', 'str', ] if use_map or cfg.CAMERA_TYPE == "MAP": ''' 2D マップ画像でカメラの代替とする場合 ''' if cfg.HAVE_HEDGE and (use_hedge or cfg.USE_HEDGE_AS_DEFAULT): ''' Marvelmindが有効である場合 ''' if (cfg.HAVE_IMU and cfg.IMU_TYPE == 'mpu9250'): ''' MPU9250を使用する場合 ''' # 2D マップ生成パーツを追加 from parts import MapImageCreator creator = MapImageCreator( base_image_path=cfg.MAP_BASE_IMAGE_PATH, debug=use_debug) V.add(creator, inputs=map_items, outputs=['cam/image_array']) else: raise ValueError('2D map needs mpu9250 data') else: raise ValueError('2D map needs Marvelmind USNav data') ''' ジョイスティック ''' user_items = [ 'user/angle', 'user/throttle', 'user/lift_throttle', 'user/mode', ] user_types = [ 'float', 'float', 'float', 'str', ] joystick_items = [] joystick_types = [] joystick_items += user_items joystick_types += user_types joystick_items += ['recording'] joystick_types += ['boolean'] # ジョイスティックパーツのアウトプット値なのでここでは初期化せず if use_joystick or cfg.USE_JOYSTICK_AS_DEFAULT: ''' ジョイスティックを使用する場合 ''' # フォークリフト用ジョイスティックパーツ取得ファクトリ関数を呼び出す from parts import get_js_controller ctr = get_js_controller(cfg) if cfg.USE_NETWORKED_JS: ''' ネットワーク経由で操作する場合(Donkeycar標準) ''' from donkeycar.parts.controller import JoyStickSub netwkJs = JoyStickSub(cfg.NETWORK_JS_SERVER_IP) V.add(netwkJs, threaded=True) ctr.js = netwkJs # リフト値の追加 V.add(ctr, inputs=['cam/image_array'], outputs=joystick_items, threaded=True) else: ''' Webコントローラを使用する ''' # user/lift_throttle 値が常に0となるWebコントローラパーツを生成 from parts import LocalWebForkliftController ctr = LocalWebForkliftController() V.add(ctr, inputs=['cam/image_array'], outputs=joystick_items, threaded=True) # 変数ctrはこの後でも使用する ''' スロットルフィルタ(ワンタップESC後進) ''' # 入力値はジョイスティックパーツがアウトプット #this throttle filter will allow one tap back for esc reverse th_filter = ThrottleFilter() V.add(th_filter, inputs=['user/throttle'], outputs=['user/throttle']) ''' 手動運転・自動運転判定フラグ設定 ''' #See if we should even run the pilot module. #This is only needed because the part run_condition only accepts boolean class PilotCondition: def run(self, mode): if mode == 'user': return False else: return True V.add(PilotCondition(), inputs=['user/mode'], outputs=['run_pilot']) # run_pilot 値はboolean(偽:手動運転、真:一部もしくは全て自動運転) ''' 3色LED表示 ''' class LedConditionLogic: def __init__(self, cfg): self.cfg = cfg def run(self, mode, recording, recording_alert, behavior_state, model_file_changed, track_loc): #returns a blink rate. 0 for off. -1 for on. positive for rate. if track_loc is not None: led.set_rgb(*self.cfg.LOC_COLORS[track_loc]) return -1 if model_file_changed: led.set_rgb(self.cfg.MODEL_RELOADED_LED_R, self.cfg.MODEL_RELOADED_LED_G, self.cfg.MODEL_RELOADED_LED_B) return 0.1 else: led.set_rgb(self.cfg.LED_R, self.cfg.LED_G, self.cfg.LED_B) if recording_alert: led.set_rgb(*recording_alert) return self.cfg.REC_COUNT_ALERT_BLINK_RATE else: led.set_rgb(self.cfg.LED_R, self.cfg.LED_G, self.cfg.LED_B) if behavior_state is not None and model_type == 'behavior': r, g, b = self.cfg.BEHAVIOR_LED_COLORS[behavior_state] led.set_rgb(r, g, b) return -1 #solid on if recording: return -1 #solid on elif mode == 'user': return 1 elif mode == 'local_angle': return 0.5 elif mode == 'local': return 0.1 return 0 if cfg.HAVE_RGB_LED and not cfg.DONKEY_GYM: #from donkeycar.parts.led_status import RGB_LED #led = RGB_LED(cfg.LED_PIN_R, cfg.LED_PIN_G, cfg.LED_PIN_B, cfg.LED_INVERT) from parts import RGB_LED led = RGB_LED(pgio, cfg.LED_PIN_R, cfg.LED_PIN_G, cfg.LED_PIN_B, cfg.LED_INVERT) led.set_rgb(cfg.LED_R, cfg.LED_G, cfg.LED_B) V.add(LedConditionLogic(cfg), inputs=[ 'user/mode', 'recording', "records/alert", 'behavior/state', 'modelfile/modified', "pilot/loc" ], outputs=['led/blink_rate']) V.add(led, inputs=['led/blink_rate']) def get_record_alert_color(num_records): """ Tubデータ件数を色PWMタプル(r, g, b):各0-100に変換する。 件数範囲、PWMタプルはconfig.py上のRECORD_ALERT_COLOR_ARR を参照している。 引数: num_records Tubデータ件数 戻り値 色PWMタプル (r, g, b)形式(0-100) """ col = (0, 0, 0) for count, color in cfg.RECORD_ALERT_COLOR_ARR: if num_records >= count: col = color return col ''' レコードトラッカ ''' class RecordTracker: def __init__(self): self.last_num_rec_print = 0 self.dur_alert = 0 self.force_alert = 0 def run(self, num_records): if num_records is None: return 0 if self.last_num_rec_print != num_records or self.force_alert: self.last_num_rec_print = num_records if num_records % 10 == 0: print("recorded", num_records, "records") if num_records % cfg.REC_COUNT_ALERT == 0 or self.force_alert: self.dur_alert = num_records // cfg.REC_COUNT_ALERT * cfg.REC_COUNT_ALERT_CYC self.force_alert = 0 if self.dur_alert > 0: self.dur_alert -= 1 if self.dur_alert != 0: return get_record_alert_color(num_records) return 0 rec_tracker_part = RecordTracker() V.add(rec_tracker_part, inputs=["tub/num_records"], outputs=['records/alert']) ''' 自動記録 ''' if cfg.AUTO_RECORD_ON_THROTTLE and isinstance(ctr, JoystickController): """ 自動記録指定されておりかつジョイスティックを使用する場合 """ #then we are not using the circle button. hijack that to force a record count indication def show_record_acount_status(): rec_tracker_part.last_num_rec_print = 0 rec_tracker_part.force_alert = 1 # ジョイスティックのボタンにレコード件数表示用のボタン割当 # F710 if cfg.CONTROLLER_TYPE == 'F710' or cfg.CONTROLLER_TYPE == 'F710_Forklift': ctr.set_button_down_trigger('back', show_record_acount_status) # JC-U3912T elif cfg.CONTROLLER_TYPE == 'JC-U3912T': ctr.set_button_down_trigger('7', show_record_acount_status) # default else: ctr.set_button_down_trigger('circle', show_record_acount_status) ''' Sombero HAT ''' #Sombrero if cfg.HAVE_SOMBRERO: from donkeycar.parts.sombrero import Sombrero _ = Sombrero() # この段階でカメラ or 2Dマップ画像が cam/image_array に格納されている ''' 画像前処理 ''' class ImgPreProcess(): ''' preprocess camera image for inference. normalize and crop if needed. ''' def __init__(self, cfg): self.cfg = cfg def run(self, img_arr): return normalize_and_crop(img_arr, self.cfg) if "coral" in model_type: ''' coral を使用する場合は画像をそのまま使用 ''' inf_input = 'cam/image_array' else: ''' 自動運転時のみ正規化・CROP処理する ''' inf_input = 'cam/normalized/cropped' V.add(ImgPreProcess(cfg), inputs=['cam/image_array'], outputs=[inf_input], run_condition='run_pilot') #Behavioral state if cfg.TRAIN_BEHAVIORS: ''' behaviorモデルを使用する場合 ''' bh = BehaviorPart(cfg.BEHAVIOR_LIST) V.add(bh, outputs=[ 'behavior/state', 'behavior/label', "behavior/one_hot_state_array" ]) try: # L1ボタンにbehavior状態更新を割当 ctr.set_button_down_trigger('L1', bh.increment_state) except: pass inputs = [inf_input, "behavior/one_hot_state_array"] #IMU elif model_type == "imu": ''' IMU(MPU9250/MPU6050)モデル ''' #assert(cfg.HAVE_IMU) #Run the pilot if the mode is not user. if cfg.HAVE_IMU: # 機械学習モデルのインプットにMPU9250/6050の加速度、角速度を追加 inputs = [ 'cam/image_array', 'imu/acl_x', 'imu/acl_y', 'imu/acl_z', 'imu/gyr_x', 'imu/gyr_y', 'imu/gyr_z', ] elif cfg.HAVE_HEDGE and ( use_hedge or cfg.USE_HEDGE_AS_DEFAULT) and cfg.USE_HEDGE_IMU: # 機械学習モデルのインプットにMarvelmindの加速度、角速度を追加 inputs = [ 'cam/image_array', 'imu/ax', 'imu/ay', 'imu/az', 'imu/vx', 'imu/vy', 'imu/vz' ] else: raise ValueError('can not use imu model without imu data') else: # 機械学習モデルのインプットは画像のみ inputs = [inf_input] ''' 機械学習モデル ''' def load_model(kl, model_path): """ 機械学習モデルに学習済みパラメータをロードする。 引数: kl 機械学習モデルオブジェクト model_path 学習済みパラーメータファイルのパス 戻り値: なし """ start = time.time() print('loading model', model_path) kl.load(model_path) print('finished loading in %s sec.' % (str(time.time() - start))) def load_weights(kl, weights_path): """ 機械学習モデルに学習済みパラメータをロードする。 引数: kl 機械学習モデルオブジェクト model_path 学習済みパラーメータファイルのパス 戻り値: なし """ start = time.time() try: print('loading model weights', weights_path) kl.model.load_weights(weights_path) print('finished loading in %s sec.' % (str(time.time() - start))) except Exception as e: print(e) print('ERR>> problems loading weights', weights_path) def load_model_json(kl, json_fnm): start = time.time() print('loading model json', json_fnm) try: from tensorflow.python import keras except: raise try: with open(json_fnm, 'r') as handle: contents = handle.read() kl.model = keras.models.model_from_json(contents) print('finished loading json in %s sec.' % (str(time.time() - start))) except Exception as e: print(e) print("ERR>> problems loading model json", json_fnm) if model_path: #When we have a model, first create an appropriate Keras part kl = dk.utils.get_model_by_type(model_type, cfg) model_reload_cb = None if '.h5' in model_path or '.uff' in model_path or 'tflite' in model_path or '.pkl' in model_path: #when we have a .h5 extension #load everything from the model file load_model(kl, model_path) def reload_model(filename): load_model(kl, filename) model_reload_cb = reload_model elif '.json' in model_path: #when we have a .json extension #load the model from there and look for a matching #.wts file with just weights load_model_json(kl, model_path) weights_path = model_path.replace('.json', '.weights') load_weights(kl, weights_path) def reload_weights(filename): weights_path = filename.replace('.json', '.weights') load_weights(kl, weights_path) model_reload_cb = reload_weights else: print("ERR>> Unknown extension type on model file!!") return ''' モデルファイル更新監視 ''' #this part will signal visual LED, if connected V.add(FileWatcher(model_path, verbose=True), outputs=['modelfile/modified']) #these parts will reload the model file, but only when ai is running so we don't interrupt user driving V.add(FileWatcher(model_path), outputs=['modelfile/dirty'], run_condition="ai_running") V.add(DelayedTrigger(100), inputs=['modelfile/dirty'], outputs=['modelfile/reload'], run_condition="ai_running") V.add(TriggeredCallback(model_path, model_reload_cb), inputs=["modelfile/reload"], run_condition="ai_running") outputs = ['pilot/angle', 'pilot/throttle'] ''' ローカライザモデルの場合の出力編集 ''' if cfg.TRAIN_LOCALIZER: outputs.append("pilot/loc") V.add(kl, inputs=inputs, outputs=outputs, run_condition='run_pilot') ''' モデルがない場合の自動運転結果初期値設定 ''' V.mem['pilot/throttle'] = 0.0 V.mem['pilot/angle'] = 0.0 V.mem['pilot/lift_throttle'] = 0.0 ''' 運転モードから、モータ入力値決定 ''' #Choose what inputs should change the car. class DriveMode: def run(self, mode, user_angle, user_throttle, user_lift_throttle, pilot_angle, pilot_throttle, pilot_lift_throttle): if mode == 'user': return user_angle, user_throttle, user_lift_throttle elif mode == 'local_angle': return pilot_angle, user_throttle, user_lift_throttle else: return pilot_angle, (pilot_throttle * cfg.AI_THROTTLE_MULT), ( pilot_lift_throttle * cfg.AI_THROTTLE_MULT) V.add(DriveMode(), inputs=[ 'user/mode', 'user/angle', 'user/throttle', 'user/lift_throttle', 'pilot/angle', 'pilot/throttle', 'pilot/lift_throttle' ], outputs=['angle', 'throttle', 'lift_throttle']) # Donkeycarへの最終的な指示が angle, throttle, lift_throttle 値となる ''' AIランチャ ''' #to give the car a boost when starting ai mode in a race. aiLauncher = AiLaunch(cfg.AI_LAUNCH_DURATION, cfg.AI_LAUNCH_THROTTLE, cfg.AI_LAUNCH_KEEP_ENABLED) V.add(aiLauncher, inputs=['user/mode', 'throttle'], outputs=['throttle']) if isinstance(ctr, JoystickController): ctr.set_button_down_trigger(cfg.AI_LAUNCH_ENABLE_BUTTON, aiLauncher.enable_ai_launch) ''' AIによる自動運転かどうかの判定 ''' class AiRunCondition: ''' A bool part to let us know when ai is running. ''' def run(self, mode): if mode == "user": return False return True V.add(AiRunCondition(), inputs=['user/mode'], outputs=['ai_running']) ''' AI運転時の記録モード ''' #Ai Recording class AiRecordingCondition: ''' return True when ai mode, otherwize respect user mode recording flag ''' def run(self, mode, recording): if mode == 'user': return recording return True if cfg.RECORD_DURING_AI: V.add(AiRecordingCondition(), inputs=['user/mode', 'recording'], outputs=['recording']) ''' モータ駆動 ''' #Drive train setup if cfg.DONKEY_GYM: pass elif cfg.DRIVE_TRAIN_TYPE == "SERVO_ESC": from donkeycar.parts.actuator import PCA9685, PWMSteering, PWMThrottle steering_controller = PCA9685(cfg.STEERING_CHANNEL, cfg.PCA9685_I2C_ADDR, busnum=cfg.PCA9685_I2C_BUSNUM) steering = PWMSteering(controller=steering_controller, left_pulse=cfg.STEERING_LEFT_PWM, right_pulse=cfg.STEERING_RIGHT_PWM) throttle_controller = PCA9685(cfg.THROTTLE_CHANNEL, cfg.PCA9685_I2C_ADDR, busnum=cfg.PCA9685_I2C_BUSNUM) throttle = PWMThrottle(controller=throttle_controller, max_pulse=cfg.THROTTLE_FORWARD_PWM, zero_pulse=cfg.THROTTLE_STOPPED_PWM, min_pulse=cfg.THROTTLE_REVERSE_PWM) V.add(steering, inputs=['angle']) V.add(throttle, inputs=['throttle']) V.mem['lift_throttle'] = 0 elif cfg.DRIVE_TRAIN_TYPE == "DC_STEER_THROTTLE": from donkeycar.parts.actuator import Mini_HBridge_DC_Motor_PWM steering = Mini_HBridge_DC_Motor_PWM(cfg.HBRIDGE_PIN_LEFT, cfg.HBRIDGE_PIN_RIGHT) throttle = Mini_HBridge_DC_Motor_PWM(cfg.HBRIDGE_PIN_FWD, cfg.HBRIDGE_PIN_BWD) V.add(steering, inputs=['angle']) V.add(throttle, inputs=['throttle']) V.mem['lift_throttle'] = 0 elif cfg.DRIVE_TRAIN_TYPE == "DC_TWO_WHEEL": from donkeycar.parts.actuator import TwoWheelSteeringThrottle, Mini_HBridge_DC_Motor_PWM left_motor = Mini_HBridge_DC_Motor_PWM(cfg.HBRIDGE_PIN_LEFT_FWD, cfg.HBRIDGE_PIN_LEFT_BWD) right_motor = Mini_HBridge_DC_Motor_PWM(cfg.HBRIDGE_PIN_RIGHT_FWD, cfg.HBRIDGE_PIN_RIGHT_BWD) two_wheel_control = TwoWheelSteeringThrottle() V.add(two_wheel_control, inputs=['throttle', 'angle'], outputs=['left_motor_speed', 'right_motor_speed']) V.add(left_motor, inputs=['left_motor_speed']) V.add(right_motor, inputs=['right_motor_speed']) V.mem['lift_motor_throttle'] = 0 # Forklift 駆動モータ操作 elif cfg.DRIVE_TRAIN_TYPE == "THREE_MOTORS_PIGPIO": ''' フォークリフト3モータ駆動 ''' from parts import PIGPIO_OUT, PIGPIO_PWM, ForkliftMotorDriver motor_driver = ForkliftMotorDriver(left_balance=cfg.LEFT_PWM_BALANCE, right_balance=cfg.RIGHT_PWM_BALANCE, debug=False) V.add(motor_driver, inputs=['throttle', 'angle', 'lift_throttle'], outputs=[ 'left_motor_vref', 'left_motor_in1', 'left_motor_in2', 'right_motor_vref', 'right_motor_in1', 'right_motor_in2', 'lift_motor_vref', 'lift_motor_in1', 'lift_motor_in2' ]) if use_debug: class Prt: def run(self, left_vref, left_in1, left_in2, right_vref, right_in1, right_in2, lift_vref, lift_in1, lift_in2): print('ForkliftMD left vref:{}, in1:{}, in2:{}'.format( str(left_vref), str(left_in1), str(left_in2))) print('ForkliftMD right vref:{}, in1:{}, in2:{}'.format( str(right_vref), str(right_in1), str(right_in2))) print('ForkliftMD lift vref:{}, in1:{}, in2:{}'.format( str(lift_vref), str(lift_in1), str(lift_in2))) V.add(Prt(), inputs=[ 'left_motor_vref', 'left_motor_in1', 'left_motor_in2', 'right_motor_vref', 'right_motor_in1', 'right_motor_in2', 'lift_motor_vref', 'lift_motor_in1', 'lift_motor_in2' ]) # TB6612#1 A系(右モータ、左折時駆動する) V.add(PIGPIO_OUT(pin=cfg.LEFT_MOTOR_IN1_GPIO, pgio=pgio), inputs=['left_motor_in1']) V.add(PIGPIO_OUT(pin=cfg.LEFT_MOTOR_IN2_GPIO, pgio=pgio), inputs=['left_motor_in2']) V.add(PIGPIO_PWM(pin=cfg.LEFT_MOTOR_PWM_GPIO, pgio=pgio, freq=cfg.PWM_FREQ, range=cfg.PWM_RANGE, threshold=cfg.PWM_INPUT_THRESHOLD), inputs=['left_motor_vref']) # TB6612#1 B系(左モータ、右折時駆動する) V.add(PIGPIO_OUT(pin=cfg.RIGHT_MOTOR_IN1_GPIO, pgio=pgio), inputs=['right_motor_in1']) V.add(PIGPIO_OUT(pin=cfg.RIGHT_MOTOR_IN2_GPIO, pgio=pgio), inputs=['right_motor_in2']) V.add(PIGPIO_PWM(pin=cfg.RIGHT_MOTOR_PWM_GPIO, pgio=pgio, freq=cfg.PWM_FREQ, range=cfg.PWM_RANGE, threshold=cfg.PWM_INPUT_THRESHOLD), inputs=['right_motor_vref']) # TB6612#2 A系(リフトモータ) V.add(PIGPIO_OUT(pin=cfg.LIFT_MOTOR_IN1_GPIO, pgio=pgio), inputs=['lift_motor_in1']) V.add(PIGPIO_OUT(pin=cfg.LIFT_MOTOR_IN2_GPIO, pgio=pgio), inputs=['lift_motor_in2']) V.add(PIGPIO_PWM(pin=cfg.LIFT_MOTOR_PWM_GPIO, pgio=pgio, freq=cfg.PWM_FREQ, range=cfg.PWM_RANGE, threshold=cfg.PWM_INPUT_THRESHOLD), inputs=['lift_motor_vref']) elif cfg.DRIVE_TRAIN_TYPE == "SERVO_HBRIDGE_PWM": from donkeycar.parts.actuator import ServoBlaster, PWMSteering steering_controller = ServoBlaster(cfg.STEERING_CHANNEL) #really pin #PWM pulse values should be in the range of 100 to 200 assert (cfg.STEERING_LEFT_PWM <= 200) assert (cfg.STEERING_RIGHT_PWM <= 200) steering = PWMSteering(controller=steering_controller, left_pulse=cfg.STEERING_LEFT_PWM, right_pulse=cfg.STEERING_RIGHT_PWM) from donkeycar.parts.actuator import Mini_HBridge_DC_Motor_PWM motor = Mini_HBridge_DC_Motor_PWM(cfg.HBRIDGE_PIN_FWD, cfg.HBRIDGE_PIN_BWD) V.add(steering, inputs=['angle']) V.add(motor, inputs=["throttle"]) V.mem['lift_throttle'] = 0 ''' Tubデータ ''' # ベースとなるTubデータ if write_both_images: from parts.datastore import FWD_CAMERA_KEY inputs = ['cam/image_array', FWD_CAMERA_KEY] types = ['image_array', 'image_array'] else: inputs = ['cam/image_array'] types = ['image_array'] inputs += user_items types += user_types if cfg.TRAIN_BEHAVIORS: ''' behaviorモデルを使用する場合の入力データを追加 ''' inputs += [ 'behavior/state', 'behavior/label', 'behavior/one_hot_state_array' ] types += ['int', 'str', 'vector'] ''' IMU(MPU6050)データ追加 ''' # Tubへ格納する加速度、角速度 tub_imu_inputs = [ 'imu/acl_x', 'imu/acl_y', 'imu/acl_z', 'imu/gyr_x', 'imu/gyr_y', 'imu/gyr_z', ] tub_imu_input_types = [ 'float', 'float', 'float', 'float', 'float', 'float', ] if not cfg.HAVE_IMU: ''' MPU9250/MPU6050を持っていない場合 ''' if cfg.HAVE_HEDGE and (use_hedge or cfg.USE_HEDGE_AS_DEFAULT): ''' Marvelmind が有効な場合 ''' if cfg.USE_HEDGE_IMU: ''' Marvelmind IMUが使用可能な場合 ''' # 必要な Marvelmind IMUデータを移動させる class MoveIMU: def run(self, ax, ay, az, gx, gy, gz): return ax, ay, az, gx, gy, gz move = MoveIMU() V.add(move, inputs=[ 'imu/ax', 'imu/ay', 'imu/az', 'imu/gx', 'imu/gy', 'imu/gz', ], outputs=tub_imu_inputs) inputs += tub_imu_inputs types += tub_imu_input_types else: inputs += tub_imu_inputs types += tub_imu_input_types if cfg.RECORD_DURING_AI: ''' 自動運転中であっても記録しておくモードの場合 ''' # pilot/* を Tub データに加える inputs += ['pilot/angle', 'pilot/throttle', 'pilot/lift_throttle'] types += ['float', 'float', 'float'] ''' Tubデータ書き込み ''' th = TubHandler(path=cfg.DATA_PATH) if write_both_images: from parts.datastore import TubHandler as NewTubHandler th = NewTubHandler(path=cfg.DATA_PATH) tub = th.new_tub_writer(inputs=inputs, types=types, user_meta=meta) V.add(tub, inputs=inputs, outputs=["tub/num_records"], run_condition='recording') if cfg.PUB_CAMERA_IMAGES: ''' カメライメージを通信して貰う場合(Donkeycar標準) ''' from donkeycar.parts.network import TCPServeValue from donkeycar.parts.image import ImgArrToJpg pub = TCPServeValue("camera") V.add(ImgArrToJpg(), inputs=['cam/image_array'], outputs=['jpg/bin']) V.add(pub, inputs=['jpg/bin']) ''' 操縦系統別 ''' if type(ctr) is LocalWebController: ''' LocalWebController を使用している場合の usage を表示 ''' print("You can now go to <your pi ip address>:8887 to drive your car.") elif isinstance(ctr, JoystickController): ''' ジョイスティックを使用している場合の usage を表示 ''' print("You can now move your joystick to drive your car.") #tell the controller about the tub ctr.set_tub(tub) if cfg.BUTTON_PRESS_NEW_TUB: ''' cfg.BUTTON_PRESS_NEW_TUB が True の場合、ボタンを押すごとに別のTubディレクトリに データが格納されるようになる(デフォルト:False)。 ''' def new_tub_dir(): V.parts.pop() tub = th.new_tub_writer(inputs=inputs, types=types, user_meta=meta) V.add(tub, inputs=inputs, outputs=["tub/num_records"], run_condition='recording') ctr.set_tub(tub) # ボタン割当 # F710 if cfg.CONTROLLER_TYPE == 'F710' or cfg.CONTROLLER_TYPE == 'F710_Forklift': ctr.set_button_down_trigger('A', new_tub_dir) # JC-U3912T elif cfg.CONTROLLER_TYPE == 'JC-U3912T' or cfg.CONTROLLER_TYPE == 'JC-U3912T_Forklift': ctr.set_button_down_trigger('12', new_tub_dir) # default else: ctr.set_button_down_trigger('cross', new_tub_dir) # 現時点のボタン割当状態を表示 ctr.print_controls() """ AWS IoT Core """ if use_aws or cfg.USE_AWS_AS_DEFAULT: print('Start aws configuration') from parts.broker import AWSShadowClientFactory, PowerReporter factory = AWSShadowClientFactory(cfg.AWS_CONFIG_PATH, cfg.AWS_THING_NAME) # Power ON 情報の送信 power = PowerReporter(factory, debug=use_debug) power.on() # Tubデータ(json)送信 from parts.broker.pub import Publisher pub_tub = Publisher(factory, debug=use_debug) V.add(pub_tub, inputs=[ 'user/angle', 'user/throttle', 'user/lift_throttle', 'pilot/angle', 'pilot/throttle', 'pilot/lift_throttle', 'user/mode' ]) # Tubデータ(イメージ)送信 from parts.broker.pub import ImagePublisher pub_img = ImagePublisher(factory, debug=use_debug) V.add(pub_img, inputs=[ 'cam/image_array', ]) # fwdイメージデータ送信 if write_both_images: from parts.broker.pub import FwdImagePublisher pub_fwd_img = FwdImagePublisher(factory, debug=use_debug) from parts.datastore import FWD_CAMERA_KEY V.add(pub_fwd_img, inputs=[ FWD_CAMERA_KEY, ]) ''' ジョイスティックデータの送信 ''' if use_joystick or cfg.USE_JOYSTICK_AS_DEFAULT: ''' ジョイスティックを使用している場合 ''' from parts.broker.pub import JoystickPublisher pub_joy = JoystickPublisher(factory, debug=use_debug) V.add(pub_joy, inputs=joystick_items) ''' Marvelmind システムデータの送信 ''' if cfg.HAVE_HEDGE and (use_hedge or cfg.USE_HEDGE_AS_DEFAULT): ''' Marvelmind システムを使用している場合 ''' if cfg.USE_HEDGE_USNAV: ''' Marvelmind 位置情報を使用している場合 ''' from parts.broker.pub import USNavPublisher pub_usn = USNavPublisher(factory, debug=use_debug) V.add(pub_usn, inputs=usnav_items) if cfg.USE_HEDGE_USNAV_RAW: ''' Marvelmind 距離情報を使用している場合 ''' from parts.broker.pub import USNavRawPublisher pub_raw = USNavRawPublisher(factory, debug=use_debug) V.add(pub_raw, inputs=usnav_raw_items) if cfg.USE_HEDGE_IMU: ''' Marvelmind IMU情報を使用している場合 ''' from parts.broker.pub import IMUPublisher pub_imu = IMUPublisher(factory, debug=use_debug) V.add(pub_imu, inputs=imu_items) ''' MPU9250/MPU6050 データの送信 ''' if cfg.HAVE_IMU: ''' MPU9250/MPU6050 を使用している場合 ''' if cfg.IMU_TYPE == 'mpu6050': ''' MPU6050を使用している場合 ''' from parts.broker.pub import Mpu6050Publisher pub_mpu = Mpu6050Publisher(factory, debug=use_debug) V.add(pub_mpu, inputs=mpu_items) elif cfg.IMU_TYPE == 'mpu9250': ''' MPU6050を使用している場合 ''' from parts.broker.pub import Mpu9250Publisher pub_mpu = Mpu9250Publisher(factory, debug=use_debug) V.add(pub_mpu, inputs=mpu_items) ''' 運転ループ ''' try: print('Start running') #run the vehicle for 20 seconds V.start(rate_hz=cfg.DRIVE_LOOP_HZ, max_loop_count=cfg.MAX_LOOPS) except KeyboardInterrupt: # Ctrl+C押下時 pass finally: # デバッグ if use_debug: print('Stop running') # pigpio 利用停止 pgio.stop() if use_aws or cfg.USE_AWS_AS_DEFAULT: if power is not None: # Power Off 情報の送信 if use_debug: print('Sending power off') power.off() # 送信するまで待機 time.sleep(1) print('Stopped')