/* * 作成日: 2008/06/18 */ package jp.ac.fit.asura.nao.vision.perception; import static jp.ac.fit.asura.nao.vision.GCD.cORANGE; import java.util.List; import javax.vecmath.Vector3f; import jp.ac.fit.asura.nao.Camera; import jp.ac.fit.asura.nao.Context; import jp.ac.fit.asura.nao.Camera.CameraID; import jp.ac.fit.asura.nao.misc.Coordinates; import jp.ac.fit.asura.nao.misc.MathUtils; import jp.ac.fit.asura.nao.physical.Ball; import jp.ac.fit.asura.nao.physical.Robot.Frames; import jp.ac.fit.asura.nao.sensation.SomaticContext; import jp.ac.fit.asura.nao.vision.VisualContext; import jp.ac.fit.asura.nao.vision.VisualObjects; import jp.ac.fit.asura.nao.vision.VisualParam.Float; import jp.ac.fit.asura.nao.vision.VisualParam.Int; import jp.ac.fit.asura.nao.strategy.StrategyContext; import jp.ac.fit.asura.nao.vision.perception.BlobVision.Blob; import org.apache.log4j.Logger; /** * @author sey * * @version $Id: BallVision.java 717 2008-12-31 18:16:20Z sey $ * */ public class BallVision extends AbstractVision { private Logger log = Logger.getLogger(BallVision.class); private VisualContext context; public void init(){ context = getContext(); } public void findBall() { int threshold = getContext().getParam(Int.BALL_BLOB_THRESHOLD); List<Blob> blobs = getContext().blobVision.findBlobs(cORANGE, 10, threshold); // 編集中↓ 編集: aqua & hachi BallVisualObject ball = (BallVisualObject) getContext().get( VisualObjects.Ball); int i = 0; while (i <= 2) { if (!blobs.isEmpty()) { log.debug("Ball blob found." + blobs.get(i)); ball.clear(); ball.getBlobs().add(blobs.get(i)); getContext().generalVision.processObject(ball); if (!checkCameraedge(ball)) if (!checkRobotAngle(ball)) if (!checkBlobSize(ball)) if (!checkBlobCompare(ball)) break; ball.confidence = 0; i++; if (blobs.size() <= i) break; } else { break; } } float dist; // カメラからボールの距離を計算する. どちらか選んで. // 角度ベースの距離計算 // dist = calculateCameraDistanceByAngle(ball); // 画像の大きさベースの距離計算. dist = calculateCameraDistanceBySize(ball); calculateDistance(ball, dist); } private float calculateCameraDistanceByAngle(BallVisualObject ball) { SomaticContext sc = getMotionFrame().getSomaticContext(); // 姿勢が当てにならない if (sc.getConfidence() < 100) { // log.debug("Invalid posture. set confidence to 0."); // obj.confidence = 0; // return; } // image座標系の角度から長さ未知数(100mmとする)とする極座標ベクトルをつくる. // ボールはこのベクトルの延長線上にある. // カメラの高さを求める Vector3f cameraPos = new Vector3f(); Coordinates.body2robotCoord(sc, sc.get(Frames.NaoCam).getBodyPosition(), cameraPos); cameraPos.y += sc.getBodyHeight(); // log.trace("cameraPos:" + cameraPos); // カメラ座標系での距離を計算 return (cameraPos.y - Ball.Radius) / (float) Math.sin(-ball.robotAngle.y); } /** * blobの大きさから距離を求める。 * * ここでいう距離とは、カメラからボールまでの直線距離 !注意! 今のところ、正方形のみ! * * 以下の式で距離を求めてる。 * * f(x) = a / (x) + b, a = BALL_DIST_CALIBa, b = BALL_DIST_CALIBb */ private float calculateCameraDistanceBySize(BallVisualObject ball) { int size = getBlobSize(ball); float a = getContext().getParam(Float.BALL_DIST_CALIBa); float b = getContext().getParam(Float.BALL_DIST_CALIBb); return a / size + b; } /** * blobの大きさを返す<br> * 画面端で切れていたらとりあえず長い方をblobの大きさにする<br> * TODO: 将来的には、切れていても形から本来の大きさを予測したいところ * * @param ball * @return blobSize */ private int getBlobSize(BallVisualObject ball) { int size = 0; if (ball.isBottomTouched || ball.isTopTouched) size = ball.area.width; else if (ball.isLeftTouched || ball.isRightTouched) size = ball.area.height; else { size = ball.area.width; if (size < ball.area.height) size = ball.area.height; } log.debug("BallVision: size=" + size); return size; } private void calculateDistance(BallVisualObject ball, float cameraDist) { SomaticContext sc = getMotionFrame().getSomaticContext(); // 求めた距離をつかって,カメラ座標系からみたボールの位置を極座標表示 Vector3f robotPosition = new Vector3f(); Coordinates.angle2carthesian(cameraDist, ball.angle, robotPosition); log.trace("angle in image coord: " + ball.angle); log.trace("angle in robot coord: " + ball.robotAngle); log.trace("dist: " + cameraDist); // ロボット座標系に変換しておわり. log.trace("pos in image coord: " + robotPosition); Coordinates.image2cameraCoord(robotPosition, robotPosition); log.trace("pos in camera coord: " + robotPosition); Coordinates .toBodyCoord(sc, Frames.NaoCam, robotPosition, robotPosition); log.trace("pos in body coord: " + robotPosition); Coordinates.body2robotCoord(sc, robotPosition, robotPosition); log.trace("pos in robot coord: " + robotPosition); int d = (int) Math.sqrt(MathUtils.square(robotPosition.x) + MathUtils.square(robotPosition.z)); float h = MathUtils.normalizeAnglePI((float) Math.atan2( robotPosition.x, robotPosition.z)); log.debug("d:" + d + " h:" + Math.toDegrees(h)); // 後ろのボールがみえたらおかしい if (Math.abs(h) > MathUtils.PIf) { ball.confidence = 0; } ball.distanceUsable = true; ball.distance = d; ball.robotPosition.set(robotPosition); } /** * 画面端に映った時に縦横比で切れる前にボールとして認識させる。 * 大きさ、高さ、縦横比を計算する前にやっておく。 * この条件がない場合画面端に映ったものを認識しないときがある。 * * @param ball * @return */ private boolean checkCameraedge(BallVisualObject ball) { if (ball.isBottomTouched || ball.isTopTouched || ball.isLeftTouched || ball.isRightTouched) { } return false; } /** * ある高さになると切る。 * hy。ボールは高いところにない。naoの顔の少し上ぐらいで切れるようになっている。 * * @param ball * @return */ // private boolean checkRobotAngle(BallVisualObject ball) { // if (ball.robotAngle.y && -0.15f || ball.robotAngle.y) { // log.info("Ball sanity too high angle." + ball.robotAngle.y); // return true; // } // return false; // } private boolean checkRobotAngle(BallVisualObject ball) { context = getContext(); Camera cam =context.getSuperContext().getCamera(); if (cam.getSelectedId() == CameraID.TOP) { if (ball.robotAngle.y > 5.0f) { log.debug("Ball sanity too high angle TOP." + ball.robotAngle.y); return true; } } return false; } // if (cam.getSelectedId() == CameraID.BOTTOM) // if (ball.robotAngle.y > -0.15f) { // log.info("Ball sanity too high angle BOTTOM." + ball.robotAngle.y); // return true; // } // return false; // } /** * ボールのサイズで切る。ある大きさより大きかった場合切る。 * 見え方によって状況は変わる。色きりの重要性。 * 小さいものでボールの色に見えた場合ボールとして認識してしまうので注意。 * * @param ball * @return */ private boolean checkBlobSize(BallVisualObject ball) { if (getBlobSize(ball) > 70) { log.debug("Ball sanity too big." + getBlobSize(ball)); return true; } return false; } /** * * 縦横比を計算する。 * ボールは縦長く細長くないから。色の見え方、位置によって変化する。 * * * @author hachi & aqua * @param ball * @return */ private boolean checkBlobCompare(BallVisualObject ball) { // context = getContext(); // Camera cam =context.getSuperContext().getCamera(); // if (cam.getSelectedId() == CameraID.TOP){ float h; float w; float proportion; h = (float) ball.area.height; w = (float) ball.area.width; if (h > w) { proportion = w / h; } else { proportion = h / w; } // float x;←↓意味ない。変更済み。 // x = (float)ball.area.height / ball.area.width; if (getContext().getParam(Float.BALL_COMPARE) > proportion) { log.debug("Ball sanity unblance." + proportion); return true; } // } return false; } }