def debug_test_img(self): img = cv2.imread("./camera_cal/calibration1.jpg") gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) gray = self.undistort(gray) DEBUG.save_img(gray, "test_undistorted_calibration0.jpg") img = cv2.imread("./test_images/test1.jpg") img = self.undistort(img) DEBUG.save_img(img, "test_undistorted_test1.jpg")
def chanCommands(cmd, uvm): cmd = cmd.split() if cmd[0] in ('b', 'board'): DEBUG('executing board command') board(uvm, cmd[1]) if cmd[0] in ('t', 'thread'): DEBUG('executing thread command') thread(uvm, cmd[1], cmd[2])
def calibrate(self, sec=3, interval_sec=0.05): DEBUG('Imu.calibrate() start') count = int(sec / interval_sec) sum = 0 for i in range(count): gyro = self._mpu9250.readGyro() sum += gyro["z"] time.sleep(interval_sec) self._epsilon = sum / count DEBUG('Imu epsilon = ', self._epsilon)
def redditCommands(cmd, uvm): cmd = cmd.split() if cmd[0] in ('sub', 'subreddit'): DEBUG('executing subreddit command') subreddit(uvm, cmd[1]) elif cmd[0] in ('p', 'post'): DEBUG('executing post command') post(uvm, cmd[1], cmd[2]) elif cmd[0] in ('subpage'): DEBUG('executing url command') subredditpage(uvm, cmd[1], cmd[2])
def __init__(self, name, clients, updates=10): self.name = name self.updates = updates self.clients = clients self.svc1 = dis_add_service(DimTask.SRV1NAME + self.name, DimTask.SRV1FORMAT, self.service1, 1) self.svc2 = dis_add_service(DimTask.SRV2NAME + self.name, DimTask.SRV2FORMAT, self.service2, 2) self.svc3 = dis_add_service(DimTask.SRV3NAME + self.name, DimTask.SRV3FORMAT, self.service3, 3) DEBUG("Service 3 created with ID ", self.svc3) DEBUG("DIMTASK %s initialized" % self.name) sleep(1)
def imageProcessingMain(self, shmem): # 画像処理を行う with picamera.PiCamera() as camera: with picamera.array.PiRGBArray(camera) as stream: camera.resolution = (480, 480) cap = cv2.VideoCapture(0) while cap.isOpened(): # 画像を取得し、stream.arrayにRGBの順で映像データを格納 camera.capture(stream, 'bgr', use_video_port=True) ball_angle, ball_distance, station_angle, station_distance, wall_x, wall_size = self.imageProcessingFrame( stream.array, shmem) DEBUG('ball: angle =' + str(ball_angle).rjust(5) + ', distance = ' + str(ball_distance).rjust(5)) DEBUG('station: angle =' + str(station_angle).rjust(5) + ', distance = ' + str(station_distance).rjust(5)) DEBUG('wall: x =' + str(wall_x).rjust(5) + ', size = ' + str(wall_size).rjust(5)) # 結果表示 # 画角の前後左右と画像表示の上下左右を揃えるために画像を転置する。 if self.DEBUG_IMSHOW == self.ENABLE: cv2.imshow('Frame', cv2.flip(stream.array, -1)) cv2.moveWindow('Frame', 0, 30) cv2.moveWindow('MaskRED', 482, 30) cv2.moveWindow('MaskYELLOW', 964, 30) cv2.moveWindow('MaskBLUE', 1446, 30) #cv2.moveWindow('MaskGREEN', 1446, 30) cv2.moveWindow('MaskBLACK', 0, 530) # 共有メモリに書き込む shmem.ballAngle = int(ball_angle * 90 / 240) shmem.ballDis = int(ball_distance) shmem.stationAngle = int(station_angle * 90 / 240) shmem.stationDis = int(station_distance) shmem.wallX = int(wall_x) shmem.wallSize = int(wall_size) # "q"でウィンドウを閉じる if cv2.waitKey(1) & 0xFF == ord("q"): break # streamをリセット stream.seek(0) stream.truncate() cv2.destroyAllWindows()
def calcAndSendMotorPowers(self, shmem, serial): while 1: # ボール状態取得 ballState = self.getBallStateByTouch(shmem.isTouched) DEBUG('ballState = ', ballState) # モータ値計算 motorPowers = self.calcMotorPowers(ballState, shmem) # モーター値後処理(現在は首振り検知処理のみ) motorPowers = self.motorControlPostProcessor.escapeSwing(motorPowers) # 設定ファイルの内容を反映 setting = self.getSetting() if setting != 'NONE': motorPowers = MotorController.DIC_SETTING_TO_MOTOR_VALUE.get(setting) # モータ値を正常値にまるめる motorPowers = self.roundOffMotorSpeeds(motorPowers) # 送信伝文生成 sendText = str(int(motorPowers[0])) + "," + str(int(motorPowers[1])) + "\n" # モータ値送信 serial.write(sendText) # 0.05sごとに実行 # 本当は全力でぶん回したい # ラズパイ側はマルチプロセスを採用しているので問題ないと思うが # EV3側は計算資源を通信に占有される恐れがあるためとりあえず0.05sにしておく INFO('ball=' + str(ballState).rjust(15), 'motor=' + str(motorPowers[0]).rjust(4) + ',' + str(motorPowers[1]).rjust(4), 'IR=' + str(shmem.irAngle).rjust(4), 'touch=' + str(shmem.isTouched).rjust(4), 'enemy=' + str(shmem.enemyGoalAngle).rjust(4) + ',' + str(shmem.enemyGoalDis).rjust(4), 'my=' + str(shmem.myGoalAngle).rjust(4) + ',' + str(shmem.myGoalDis).rjust(4), 'center=' + str(shmem.fieldCenterAngle).rjust(4) + ',' + str(shmem.fieldCenterDis).rjust(4), ) time.sleep(0.1)
def roundOffMotorSpeeds(self, motorSpeeds): # モータ値それぞれについて絶対値を100に丸めた時の差分 diffPower = [0, 0] diffPower[0] = self.calcDiffRondOffWithin100(motorSpeeds[0]) diffPower[1] = self.calcDiffRondOffWithin100(motorSpeeds[1]) DEBUG('diffPower =', diffPower[0], ', ', diffPower[1]) returnSpeeds = self.roundOffWithin100(motorSpeeds[0]), self.roundOffWithin100(motorSpeeds[1]) # 両モータ絶対値が100を超えている場合は100に丸めるのみ if diffPower == [0, 0]: TRACE('both power abs over 100') return returnSpeeds # 左モータ値のみ絶対値が100を超える elif diffPower[0] != 0: TRACE('left power abs over 100') # 右モータ値のみ差分分だけ調整する return returnSpeeds[0], returnSpeeds[1] + diffPower[0] # 右モータ値のみ絶対値が100を超える elif diffPower[1] != 0: TRACE('right power abs over 100') # 左モータ値のみ差分分だけ調整する return returnSpeeds[0] + diffPower[1], returnSpeeds[1] # どちらも絶対値がを100を超えない else: TRACE('both power abs under 100') return returnSpeeds
def handleKey(self, key): if key == ':': self.mode = MODE.COMMAND self.commandBar.set_caption(':') self.body.focus_position = 'footer' self.renderScreen() if self.mode is MODE.NORMAL: rows, cols = urwid.raw_display.Screen().get_cols_rows() DEBUG(key) if key == 'h': self.body.keypress((rows, cols), 'left') if key == 'j': self.body.keypress((rows, cols), 'down') if key == 'k': self.body.keypress((rows, cols), 'up') if key == 'l': self.body.keypress((rows, cols), 'right') if key == 'H': self.body.keypress((rows, cols), 'left') if key == 'J': self.body.keypress((rows, cols), 'down') if key == 'K': self.body.keypress((rows, cols), 'up') if key == 'L': self.body.keypress((rows, cols), 'right') if key == 'r': pass
def get(self, key): if self.config.get(key, None): return self.config.get(key) else: try: return self.defaults[key] except: DEBUG('Error: No key {} in default config'.format(key))
def __init__(self): DEBUG('MotorController generated') # インスタンス変数初期化 # ドリブル状態に入ったかを記憶する変数 self.is_dribble_started = False # モータ制御後処理インスタンス生成 self.motorControlPostProcessor = MotorControlPostProcessor() # 設定値読み込み self.getParameterSetting()
def split(uvm, splitType, splitView): DEBUG(type(uvm.allViews)) DEBUG(uvm.allViews.focus) if splitType == 0: if type(uvm.allViews) is urwid.Pile: DEBUG('Pile supertype') uvm.allViews.contents.append((View(uvm), uvm.allViews.options())) else: DEBUG(uvm.allViews.contents) uvm.allViews.focus = urwid.Pile([uvm.currFocusView, View(uvm)]) elif splitType == 1: if type(uvm.allViews) is urwid.Columns: DEBUG('Columns supertype') uvm.allViews.contents.append((View(uvm), uvm.allViews.options())) else: uvm.allViews.focus = urwid.Columns([uvm.currFocusView, View(uvm)]) postCommand(uvm)
def print_angle(self): while 1: gyro = self._mpu9250.readGyro() self._degree += gyro["z"] - self._epsilon DEBUG( 'bodyAngle = ', -int( self.calc_shortcut_degree(self._degree, self.ONE_ROUND_VALUE))) time.sleep(self._update_interval_sec)
def _load(self, location): ''' load json from config file ''' try: with open(location) as cfg: return json.load(cfg) except: # add default config to location DEBUG('File {} does not exist'.format(location)) return None
def buildUrwidFromSplits(splitList): DEBUG(type(splitList) is View) if type(splitList) is Column: return urwid.Columns([buildUrwidFromSplits(x) for x in splitList.widgets]) elif type(splitList) is Row: return urwid.Pile([buildUrwidFromSplits(x) for x in splitList.widgets]) elif type(splitList) is View: return urwid.WidgetWrap(urwid.LineBox(splitList.frame))
def site(uvm): preCommand(uvm) if uvm.site is SITE.FCHAN: DEBUG(uvm.allViews.focus) uvm.allViews = View(uvm, IndexFrame(uvm)) # uvm.currFocusView.setFrame(IndexFrame(uvm)) # setattr(uvm.currFocusView, 'frame', IndexFrame(uvm)) # uvm.currFocusView = View(uvm, IndexFrame(uvm)) # uvm.allViews.set_focus = (View(uvm, IndexFrame(uvm)), uvm.allViews.options()) postCommand(uvm)
def buildAddHeaderView(self, focusView): try: headerWidget = urwid.AttrWrap( urwid.Text(focusView.frame.headerString), 'header') except: headerWidget = urwid.AttrWrap(urwid.Text(''), 'header') builtUrwidSplits = buildUrwidFromSplits(self.splitTuple) DEBUG(type(builtUrwidSplits)) self.body = urwid.Frame(urwid.AttrWrap(builtUrwidSplits, 'body')) self.body.header = headerWidget
def calcMotorPowers(self, shmem, motion_status): # 現在の状態に応じて追跡対象を変える if motion_status == MotionStateE.CHASE_BALL: TRACE('motion_status = CHASE_BALL') if shmem.ballDis != -1: return self.calcMotorPowersByBallAngle(shmem.ballAngle, MotorController.SPEED_CHASE, MotorController.K_CHASE_ANGLE) TRACE('shmem.ballDis is invalid value') if self.chaseBallMode.now() == ChaseMode.NORMAL: TRACE('chaseBallMode = NORMAL') return self.calcMotorPowersByBallAngle(shmem.bodyAngle / 10, MotorController.SPEED_CHASE, MotorController.K_CHASE_ANGLE) # ボールが視界になくて、首振りモードの時はボールを見つけるためにとりあえず旋回する DEBUG('chaseBallMode = SWING') return MotorController.SPEED_SWING_ROTATE elif motion_status == MotionStateE.GO_TO_STATION: TRACE('motion_status = GO_TO_STATION') if shmem.stationDis != -1: shmem.soundPhase = SoundPhaseE.DETECT_STATION return self.calcMotorPowersByBallAngle(shmem.stationAngle, MotorController.SPEED_CHASE, MotorController.K_CHASE_ANGLE) TRACE('shmem.ballDis is invalid value') # センターカメラから指令が来ていたらguide_info.jsonに有効な値が入っている with open('./guide_info.json', mode='r') as f: try: guide_info_json = json.load(f) guide_degree = guide_info_json.get('degree', 360) if guide_degree != 360: shmem.soundPhase = SoundPhaseE.RECV_CAMERA_INFO DEBUG('guide_degree = ' + str(guide_degree)) # センターから停止命令が来ていたらモータ止める if guide_info_json.get('wait', False): DEBUG('recieved stop order!') return 0, 0 return self.calcMotorPowersByBallAngle(-guide_degree, MotorController.SPEED_STATION_GUIDE, MotorController.K_STATION_GUIDE_ANGLE) except json.JSONDecodeError: ERROR('faild to load guide_info.json') body_angle = shmem.bodyAngle return self.calcMotorPowersByBallAngle(body_angle / 10 - (body_angle / abs(body_angle) * 180), MotorController.SPEED_CHASE, MotorController.K_CHASE_ANGLE) # PREPARE_RETART TRACE('motion_status = PREPARE_RESTART') return (0, 0)
def progressive_binary(saturation_warped, grayscale_warped, saturation_warped_threshold): # I think I'm supposed to be applying the threshold on the raw saturation value #raise Exception("I'm not certain I'm even supposed to be applying sobel on top of the saturation value.") # Sobel kernel of 3 for bottom half sobel_kernel = 3 sobelx_kernel_3_s = cv2.Sobel(saturation_warped, cv2.CV_64F, 1, 0, ksize=sobel_kernel) sobelx_kernel_3_gray = cv2.Sobel(grayscale_warped, cv2.CV_64F, 1, 0, ksize=sobel_kernel) # right now, no matter what i do I can't find the top mark in the test2 sobel_kernel = 5 sobelx_kernel_5_s = cv2.Sobel(saturation_warped, cv2.CV_64F, 1, 0, ksize=sobel_kernel) sobelx_kernel_5_gray = cv2.Sobel(grayscale_warped, cv2.CV_64F, 1, 0, ksize=sobel_kernel) DEBUG.save_img(grayscale_warped, "gray.png") DEBUG.save_img(sobelx_kernel_5_gray, "saturatsobelx_kernel_5_gray.png") DEBUG.save_img(sobelx_kernel_3_gray, "sobelx_kernel_3_gray.png") #sobel_kernel = 9 #sobelx_kernel_9_s = cv2.Sobel(saturation_warped, cv2.CV_64F, 1, 0, ksize=sobel_kernel) #sobelx_kernel_9_gray = cv2.Sobel(grayscale_warped, cv2.CV_64F, 1, 0, ksize=sobel_kernel) # TODO: don't just use the saturation binary. Also use the grayscale one. # the two of them are better toghether. # https://classroom.udacity.com/nanodegrees/nd013/parts/fbf77062-5703-404e-b60c-95b78b2f3f9e/modules/2b62a1c3-e151-4a0e-b6b6-e424fa46ceab/lessons/0bcd97c5-66f3-495b-9fe2-3f9f541bae25/concepts/a1b70df9-638b-46bb-8af0-12c43dcfd0b4 # bottom_half = img[img.shape[0]//2:,:] height = saturation_warped.shape[0] binary_sobelx = np.zeros_like(saturation_warped) apply_filter(sobelx_kernel_3_s, sobelx_kernel_3_gray, height//2, height, binary_sobelx, 30, 50) apply_filter(sobelx_kernel_3_s, sobelx_kernel_5_gray, height//4, height//2, binary_sobelx, 20, 100) apply_filter(sobelx_kernel_3_s, sobelx_kernel_5_gray, 0, height//4, binary_sobelx, 20, 500) return binary_sobelx
def message_received(client, server, message): DEBUG('Message has been received from ' + str(client['address'][0]) + ':' + str(client['address'][1])) try: message_dict = json.loads(message) if message_dict['type'] == 'guide_info': try: with open('./guide_info.json', mode='w') as f: f.write(message) except AttributeError: INFO('faild to get color data') except json.JSONDecodeError: INFO('faild to decode message to json')
def getBallStateByDistance(self, distance): # すでにドリブル状態に入っていた場合 if self.is_dribble_started: # 距離センサの値がinfinityの時もボール保持状態と判断する # (距離センサにボールが近づきすぎても値がinifinityになるため) if distance < MotorController.THRESHOLD_BALL_DETECT or distance == 100: return BallStateE.HAVE_BALL else: # 距離センサの値が閾値より大きくなっていたらドリブル状態は解除 self.is_dribble_started = False DEBUG('dribble END') return BallStateE.NOT_HAVE_BALL # ドリブル状態に入っていない場合 else: # 距離センサの値が閾値より小さかったらボール保持状態と判断 # ドリブル状態に入る if distance < MotorController.THRESHOLD_BALL_DETECT: self.is_dribble_started = True DEBUG('dribble START') return BallStateE.HAVE_BALL else: return BallStateE.NOT_HAVE_BALL
def systemCommands(cmd, uvm): cmd = cmd.split() if cmd[0] in ('qa', 'quitall'): DEBUG('Executing quit command') sys.exit() if cmd[0] in ('site'): DEBUG('executing site command') DEBUG(cmd) if cmd[1] in '4chan': DEBUG('4chan requested') setattr(uvm.currFocusView, 'site', SITE.FCHAN) setattr(uvm.currFocusView, 'boardList', uvm.cfg.get('FCHAN')['boards']) setattr(uvm.currFocusView, 'frame', IndexFrame(uvm)) elif cmd[1] in ['reddit', 'Reddit']: setattr(uvm.currFocusView, 'site', SITE.REDDIT) setattr(uvm.currFocusView, 'boardList', uvm.cfg.get('REDDIT')['boards']) setattr(uvm.currFocusView, 'frame', RedditIndexFrame(uvm)) if cmd[0] in ('split'): if type(uvm.splitTuple) is Row: uvm.splitTuple.widgets.append(View(uvm)) else: t = uvm.splitTuple uvm.splitTuple = Row() uvm.splitTuple.widgets.append(t) uvm.splitTuple.widgets.append(View(uvm)) pass elif cmd[0] in ('vsplit'): if type(uvm.splitTuple) is Column: uvm.splitTuple.widgets.append(View(uvm)) t = uvm.splitTuple uvm.splitTuple = Column() uvm.splitTuple.widgets.append(t) uvm.splitTuple.widgets.append(View(uvm))
def updateCountDerectionChangeDetectSwing(self, motorPowers): # 方向転換した場合 if motorPowers[0] * self.pre_motor_powers[0] < 0 and motorPowers[1] * self.pre_motor_powers[1] < 0 and motorPowers[0] != motorPowers[1]: # 前回方向転換検知時からの経過時間が閾値より小さかった場合 if (time.time() - self.pre_direction_change_time) > MotorControlPostProcessor.THRESHOLD_TIME_SWING_DETECT: TRACE('count_direction_change_detect_swing : increment') # 首振り検知用方向転換数をインクリメントする self.count_direction_change_detect_swing += 1 # 前回方向転換検知時から十分時間が経過していた場合 else: TRACE('count_direction_change_detect_swing : reset') # 首振り検知用方向転換数をリセット self.count_direction_change_detect_swing = 1 DEBUG('count_derection_change_detect_swing = ', self.count_direction_change_detect_swing)
def run(self): dis_start_serving(self.name) print self.clients for x in self.clients: DEBUG("DIMTASK %s : Registering to service %s" % (self.name, DimTask.SRV1NAME + x)) dic_info_service(DimTask.SRV1NAME + x, DimTask.SRV1FORMAT, self.client_callback1) DEBUG("DIMTASK %s : Registering to service %s" % (self.name, DimTask.SRV2NAME + x)) dic_info_service(DimTask.SRV2NAME + x, DimTask.SRV2FORMAT, self.client_callback2) DEBUG("DIMTASK %s : Registering to service %s" % (self.name, DimTask.SRV3NAME + x)) dic_info_service(DimTask.SRV3NAME + x, DimTask.SRV3FORMAT, self.client_callback3) counter = 0 DEBUG("DIMTASK %s : Starting service update " % self.name) while counter < self.updates: counter += 1 SAY("DIMTASK %s : Updating service nr. 1" % self.name) values = (counter, counter + 1, 999.0, 999.0, 'BAU', 'B') SAY('DIMTASK %s : Updated %d clients' % (self.name, dis_update_service(self.svc1, values))) SAY("DIMTASK %s : Updating service nr. 2" % self.name) values = (counter, ) SAY('DIMTASK %s : Updated %d clients' \ %(self.name, dis_update_service(self.svc2, values))) SAY("DIMTASK %s : Updating service nr. 3" % self.name) values = ('ALABALAP\x00ORTOCALA', ) SAY('DIMTASK %s : Updated %d clients' \ %(self.name, dis_update_service(self.svc3, values))) sleep(DimTask.DELAY) dis_stop_serving()
def isNeedToEscapeSwing(self, motorPowers): # すでに首振り回避状態に入っている if self.is_escaping_swing: # 首振り回避開始時からの経過時間が閾値より大きかった場合 if (time.time() - self.escape_swing_start_time) > MotorControlPostProcessor.TIME_ESCAPE_FROM_SWING: # 首振り回避終了 DEBUG('swing end') self.is_escaping_swing = False return False # 首振り回避開始時から十分時間が経過していない場合 else: # 首振り回避継続 return True # 首振り回避中でない else: # 首振り開始? if self.isSwingStarted(motorPowers): # 首振り回避開始 DEBUG('swing start') self.is_escaping_swing = True return True else: # 首振り回避引き続き必要なし return False
def __escape_press_wall(self, motorPowers, bodyAngle): # すでにバック中の場合 if self._pw_enable_force_back: if (time.time() - self._pw_start_back_time) > self._pw_back_time: DEBUG('### END back for escape press wall') self._pw_enable_force_back = False # 壁押し付け状態判定用変数初期化 self._pw_error_count_now = 0 TRACE('### CONTINUE back for escape press wall') return -self._pw_speed, -self._pw_speed # 旋回しようとしているかどうか # TODO: このパラメータも上位側で指定可能にする if abs(self._pw_pre_power[0] - self._pw_pre_power[1]) > 50 and abs(self._pw_pre_body_angle - bodyAngle) < self._pw_diff_angle: DEBUG('### pressing wall count +1') self._pw_error_count_now += 1 else: self._pw_error_count_now = 0 if self._pw_error_count_now >= self._pw_error_count_limit: INFO('### detect pressing wall count') self._pw_enable_force_back = True self._pw_start_back_time = time.time() # 別にこの時はバックしなくてもいいかもしれないが一応 return -self._pw_speed, -self._pw_speed return motorPowers
def is_going_into_corner(self, wall_size): if wall_size == -1: self._is_near_wall = False return False else: if self._is_near_wall: if time.time() - self._near_wall_start_time > MotorController.TIME_WALL_STUCK: INFO('### going into corner now') return True return False else: DEBUG('### detect near wall start') self._near_wall_start_time = time.time() self._is_near_wall = True return False
def getSetting(self): # モータ値設定ファイルを読み込み try: with open('/home/pi/Desktop/raspi_FcTronto/webiopi/motor_setting.txt') as f: motorSetting = f.read() if motorSetting != '': DEBUG('motor setting = ', motorSetting) return motorSetting else: TRACE('motor setting is NONE') return 'NONE' # ファイル読み込み失敗 except: WARN('motor setting file read failure') return 'NONE'
def update_loop(self, shmem): while 1: # どうやら10-20秒くらいで補正値を更新しないと、ドリフトの影響が無視出来なくなる # リスタート準備中フラグが立っていたら、他のモジュールが待っていてくれているので、補正値の調整をする # 補正が終わったらフラグをクリアして、他のモジュールに調整完了を知らせる if shmem.preparingRestart: INFO('calibrate IMU to prepare restart') self.calibrate() shmem.preparingRestart = False gyro = self._mpu9250.readGyro() self._degree += gyro["z"] - self._epsilon # 画像認識は右側正、左側負なので、そちらの挙動と合わせるために符号反転させる shmem.bodyAngle = -int( self.calc_shortcut_degree(self._degree, self.ONE_ROUND_VALUE)) DEBUG('bodyAngle = ', shmem.bodyAngle) time.sleep(self._update_interval_sec)
def __init__(self, location=None): if location and not os.path.isdir(location): self.location = location else: self.location = os.path.expanduser( '~') + '/.config/commandChan/config.json' # load default config self.defaults = self._load('./default_config.json') if not self.defaults: DEBUG('ERROR: No default file') # load user config self.config = self._load(self.location) if not self.config: self.config = self.defaults self.write(self.defaults)