/* * 作成日: 2008/07/01 */ package jp.ac.fit.asura.nao.sensation; import static jp.ac.fit.asura.nao.PressureSensor.LFsrBL; import static jp.ac.fit.asura.nao.PressureSensor.LFsrBR; import static jp.ac.fit.asura.nao.PressureSensor.LFsrFL; import static jp.ac.fit.asura.nao.PressureSensor.LFsrFR; import static jp.ac.fit.asura.nao.PressureSensor.RFsrBL; import static jp.ac.fit.asura.nao.PressureSensor.RFsrBR; import static jp.ac.fit.asura.nao.PressureSensor.RFsrFL; import static jp.ac.fit.asura.nao.PressureSensor.RFsrFR; import java.util.EnumMap; import javax.vecmath.Matrix3f; import javax.vecmath.Point2f; import javax.vecmath.Vector3f; import jp.ac.fit.asura.nao.MotionCycle; import jp.ac.fit.asura.nao.MotionFrameContext; import jp.ac.fit.asura.nao.RobotContext; import jp.ac.fit.asura.nao.SensorContext; import jp.ac.fit.asura.nao.Camera.CameraID; import jp.ac.fit.asura.nao.Sensor.Function; import jp.ac.fit.asura.nao.misc.Kinematics; import jp.ac.fit.asura.nao.misc.MatrixUtils; import jp.ac.fit.asura.nao.misc.NDFilter; import jp.ac.fit.asura.nao.physical.Robot; import jp.ac.fit.asura.nao.physical.RobotFrame; import jp.ac.fit.asura.nao.physical.Robot.Frames; import org.apache.log4j.Logger; ; /** * 体性感覚野. * * 姿勢などのセンサー情報を抽象化します. * * @author sey * * @version $Id: SomatoSensoryCortex.java 721 2009-02-18 03:40:44Z sey $ * */ public class SomatoSensoryCortex implements MotionCycle { private static final Logger log = Logger .getLogger(SomatoSensoryCortex.class); private Robot robot; private boolean useInertial; private float lastBodyHeight; private EnumMap<Frames, NDFilter.Float> ndFilters; /** * */ public SomatoSensoryCortex() { ndFilters = new EnumMap<Frames, NDFilter.Float>(Frames.class); for (Frames f : Frames.values()) ndFilters.put(f, new NDFilter.Float()); } @Override public void init(RobotContext rctx) { robot = new Robot(new RobotFrame(Frames.Body)); useInertial = rctx.getSensor().isSupported(Function.INERTIAL); } @Override public void start() { } @Override public void step(MotionFrameContext frameContext) { SensorContext sensor = frameContext.getSensorContext(); SomaticContext context = frameContext.getSomaticContext(); // contextを初期化. if (context == null) { context = new SomaticContext(robot); frameContext.setSomaticContext(context); } // Robotが更新されていれば再作成する. if (context.getRobot() != robot) { context = new SomaticContext(robot); frameContext.setSomaticContext(context); } context.setTime(frameContext.getTime()); // quick hack for bottom camera if (frameContext.getRobotContext().getCamera().getSelectedId() == CameraID.TOP) { context.get(Frames.CameraSelect).setAngle(0); } else { // FIXME この処理ではCameraSelect仮想関節を回転させるだけなので、カメラの位置に53.90 - // sqrt((67.90-23.81)*(48.8)) = 7.514mmの誤差がでる. context.get(Frames.CameraSelect).setAngle(0.6981f); } updateJoints(context, sensor); updateFSR(context, sensor); updateBodyPosture(context, sensor); } @Override public void stop() { } public void updateRobot(Robot robot) { this.robot = robot; } public Robot getRobot() { return robot; } /** * 関節角度を更新し運動学計算をする. * * @param context * @param sensor */ private void updateJoints(SomaticContext context, SensorContext sensor) { for (FrameState joint : context.getFrames()) { if (joint.getId().isJoint()) { joint.setAngle(sensor.getJoint(joint.getId().toJoint())); joint.setAngularVelocity(ndFilters.get(joint.getId()).eval( joint.getAngle())); } } Kinematics.calculateForward(context); Kinematics.calculateCenterOfMass(context); } /** * 圧力センサーの情報を処理し、接地しているかどうかを判断する. * * @param context * @param sensor */ private void updateFSR(SomaticContext context, SensorContext sensor) { float rfl = sensor.getForce(RFsrFL); float rfr = sensor.getForce(RFsrFR); float rbl = sensor.getForce(RFsrBL); float rbr = sensor.getForce(RFsrBR); float lfl = sensor.getForce(LFsrFL); float lfr = sensor.getForce(LFsrFR); float lbl = sensor.getForce(LFsrBL); float lbr = sensor.getForce(LFsrBR); float leftp = lfl + lfr + lbl + lbr; float rightp = rfl + rfr + rbl + rbr; float pressure = leftp + rightp; int leftCount = 0; final float TOUCH_THRESH = 0.5f; if (lfl > TOUCH_THRESH) leftCount++; if (lfr > TOUCH_THRESH) leftCount++; if (lbl > TOUCH_THRESH) leftCount++; if (lbr > TOUCH_THRESH) leftCount++; context.setLeftOnGround(leftCount >= 2); int rightCount = 0; if (rfl > TOUCH_THRESH) rightCount++; if (rfr > TOUCH_THRESH) rightCount++; if (rbl > TOUCH_THRESH) rightCount++; if (rbr > TOUCH_THRESH) rightCount++; context.setRightOnGround(rightCount >= 2); int cf = 0; if (context.isLeftOnGround()) cf += 500; if (context.isRightOnGround()) cf += 500; context.setConfidence(cf); Point2f left = context.getLeftCOP(); left.x = 0; left.y = 0; left.x += lfl * context.get(Frames.LFsrFL).getBodyPosition().x; left.y += lfl * context.get(Frames.LFsrFL).getBodyPosition().z; left.x += lfr * context.get(Frames.LFsrFR).getBodyPosition().x; left.y += lfr * context.get(Frames.LFsrFR).getBodyPosition().z; left.x += lbl * context.get(Frames.LFsrBL).getBodyPosition().x; left.y += lbl * context.get(Frames.LFsrBL).getBodyPosition().z; left.x += lbr * context.get(Frames.LFsrBR).getBodyPosition().x; left.y += lbr * context.get(Frames.LFsrBR).getBodyPosition().z; Point2f right = context.getLeftCOP(); right.x = 0; right.y = 0; right.x += rfl * context.get(Frames.RFsrFL).getBodyPosition().x; right.y += rfl * context.get(Frames.RFsrFL).getBodyPosition().z; right.x += rfr * context.get(Frames.RFsrFR).getBodyPosition().x; right.y += rfr * context.get(Frames.RFsrFR).getBodyPosition().z; right.x += rbl * context.get(Frames.RFsrBL).getBodyPosition().x; right.y += rbl * context.get(Frames.RFsrBL).getBodyPosition().z; right.x += rbr * context.get(Frames.RFsrBR).getBodyPosition().x; right.y += rbr * context.get(Frames.RFsrBR).getBodyPosition().z; Point2f cop = context.getCenterOfPressure(); cop.x = left.x + right.x; cop.y = left.y + right.y; if (leftp != 0) { left.x /= leftp; left.y /= leftp; } if (rightp != 0) { right.x /= rightp; right.y /= rightp; } if (pressure != 0) { cop.x /= pressure; cop.y /= pressure; } context.setLeftPressure(leftp); context.setRightPressure(rightp); log.trace("Left pressure:" + leftp); log.trace("Right pressure:" + rightp); } /** * Bodyの地面からの高さと傾きを計算する. * * @param context * @param sensor */ private void updateBodyPosture(SomaticContext context, SensorContext sensor) { context.setBodyHeight(calculateBodyHeight(context)); if (context.isOnGround()) lastBodyHeight = context.getBodyHeight(); if (useInertial) { Matrix3f posture = context.getBodyPosture(); Vector3f tmp = new Vector3f(); tmp.x = sensor.getInertialX(); tmp.z = sensor.getInertialZ(); MatrixUtils.pyr2rot(tmp, posture); } else { Matrix3f posture = context.getBodyPosture(); calculateBodyRotation(context, posture); } } /** * BodyのWorld座標系での回転行列(pitch, rollのみ)を返します. * * @param context * @param mat */ private void calculateBodyRotation(SomaticContext context, Matrix3f mat) { // FIXME 未テスト Matrix3f rot; if (context.isLeftOnGround()) rot = context.get(Frames.LSole).getBodyRotation(); else if (context.isRightOnGround()) rot = context.get(Frames.RSole).getBodyRotation(); else { mat.setIdentity(); return; } Vector3f tmp = new Vector3f(); MatrixUtils.rot2pyr(rot, tmp); tmp.y = 0; MatrixUtils.pyr2rot(tmp, mat); } private float calculateBodyHeight(SomaticContext context) { // FIXME 未実装 if (context.isLeftOnGround()) { return -context.get(Frames.LSole).getBodyPosition().y; } if (context.isRightOnGround()) return -context.get(Frames.RSole).getBodyPosition().y; log.debug("Can't estimate body height. Use " + lastBodyHeight + " [mm] Left:" + context.getLeftPressure() + " Right:" + context.getRightPressure()); return lastBodyHeight; } }