All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.robokind.impl.motion.dynamixel.DynamixelServo Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2011 Hanson Robokind LLC.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.robokind.impl.motion.dynamixel;

import org.robokind.impl.motion.dynamixel.enums.DynamixelBaudRate;
import org.robokind.impl.motion.dynamixel.enums.ErrorStatus;
import org.robokind.impl.motion.dynamixel.enums.Register;
import java.util.HashMap;
import java.util.Map;
import org.jflux.impl.services.rk.utils.HashCodeUtil;
import org.jflux.impl.services.rk.utils.LocalIdentifier;
import org.robokind.api.common.position.NormalizedDouble;
import org.robokind.api.common.utils.TimeUtils;
import org.robokind.api.common.utils.Utils;
import org.robokind.api.motion.servos.AbstractServo;
import org.robokind.api.motion.servos.config.ServoConfig;
import org.robokind.impl.motion.dynamixel.feedback.FeedbackUpdateValues;

/**
 * Represents a Dynamixel servo in a Dynamixel chain.
 * 
 * @author Matthew Stevenson 
 */
public class DynamixelServo extends AbstractServo<
        DynamixelServo.Id,
        ServoConfig,
        DynamixelController> {
	private final Map myCache;
	private Boolean myIsChanged;
    private DynamixelServo.Id myPhysicalId;
    private int myMinPosition;
    private int myMaxPosition;
    private int myPreviousPosition;
    private NormalizedDouble myDefaultPosition;
    private String myName;
    private FeedbackUpdateValues myFeedbackUpdateVals;

    /**
     * Creates a new DynamixelServo from the given JointConfig and 
     * DynamixelController.
     * @param params JointConfig for the new Joint
     * @param controller DynamixelController for the new Joint
     */
    public DynamixelServo(ServoConfig params, DynamixelController controller){
        super(params, controller);
        myPhysicalId = params.getServoId();
        
        myIsChanged = false;
        myCache = new HashMap();
		int[] vals = myController.readRegisters(getPhysicalId(), Register.CurrentPosition, Register.CurrentTemperature);
        myFeedbackUpdateVals =
                new FeedbackUpdateValues(
                        myPhysicalId, vals, TimeUtils.now());
        myPreviousPosition = vals[0];
		myCache.put(Register.GoalPosition, vals[0]);
        myMinPosition = params.getMinPosition();
        myMaxPosition = params.getMaxPosition();
        double defInt = params.getDefaultPosition() - myMinPosition;
        double range = myMaxPosition - myMinPosition;
        double val = defInt/range;
        if(!NormalizedDouble.isValid(val)){
            throw new IllegalArgumentException("Default Position invalid: " + 
                    params.getDefaultPosition());
        }
        myDefaultPosition = new NormalizedDouble(val);
        myName = params.getName();
	}
    
    public final DynamixelServo.Id getPhysicalId(){
        return myPhysicalId;
    }
    
    @Override
    public void setEnabled(Boolean enabled){
        Boolean old = getEnabled();
        setTorqueEnabled(enabled);
        firePropertyChange(PROP_ENABLED, old, enabled);
    }

    @Override
    public Boolean getEnabled(){
        return getTorqueEnable();
    }

    @Override
    public String getName() {
        return myName;
    }

    @Override
    public int getMinPosition() {
        return myMinPosition;
    }

    @Override
    public int getMaxPosition() {
        return myMaxPosition;
    }
    
    protected int getPreviousPosition(){
        return myPreviousPosition;
    }
    
    protected void setPreviousPosition(int pos){
        myPreviousPosition = pos;
    }

    @Override
    public NormalizedDouble getDefaultPosition() {
        return myDefaultPosition;
    }

    /**
     * Returns the value at the given register.
     * @param reg register to read
     * @return value at the given register
     */
    public Integer get(Register reg){
		if(!myCache.containsKey(reg))
			return -1;
		return myCache.get(reg);
	}
    /**
     * Sets the value of a given register.
     * @param reg register to set
     * @param value value to set
     */
    protected void put(Register reg, Integer value){
        myCache.put(reg, value);
    }

    @Override
    public void setGoalPosition(NormalizedDouble value){
        super.setGoalPosition(value);
        Integer goal = getAbsoluteGoalPosition();
        if(goal == null){
            return;
        }
        SetRegisterValue(Register.GoalPosition, goal);
    }

    /**
     * Returns the moving speed as a value 1(slow) - 1023(fast), with 0 being
     * full speed.
     * @return moving speed as a value 1(slow) - 1023(fast), with 0 being
     * full speed
     */
    public Integer getMovingSpeed(){
		return GetRegisterValue(Register.MovingSpeed);
	}
    /**
     * Sets the moving speed with a value 1(slow) - 1023(fast), and 0 being
     * full speed.
     * @param value moving speed value; 1(slow) - 1023(fast), and 0 being
     * full speed
     */
    public void setMovingSpeed(Integer value){
		SetRegisterValue(Register.MovingSpeed, value);
	}
    //TODO support multiple Alarm LED ErrorStatuses
    /**
     * Returns the ErrorStatus which triggers the Alarm LED.
     * @return ErrorStatus which triggers the Alarm LED
     */
    public ErrorStatus getAlarmLed(){
		return ErrorStatus.getStatus(GetRegisterValue(Register.AlarmLed));
	}
    /**
     * Sets the ErrorStatus which triggers the Alarm LED.
     * @param value ErrorStatus to triggers the Alarm LED
     */
    public void setAlarmLed(ErrorStatus value){
		SetRegisterValue(Register.AlarmLed, (int)value.getByte());
	}
    //TODO support multiple Alarm Shutdown ErrorStatuses
    /**
     * Returns the ErrorStatus which triggers a servo shutdown.
     * @return ErrorStatus which triggers a servo shutdown
     */
    public ErrorStatus getAlarmShutdown(){
		return ErrorStatus.getStatus(GetRegisterValue(Register.AlarmShutdown));
	}
    /**
     * Sets the ErrorStatus which triggers a servo shutdown.
     * @param value ErrorStatus to triggers a servo shutdown
     */
    public void setAlarmShutdown(ErrorStatus value){
		SetRegisterValue(Register.AlarmShutdown, (int)value.getByte());
	}

    /**
     * Returns the BaudRate expected by the DynamixelServo.
     * @return BaudRate expected by the DynamixelServo
     */
    public DynamixelBaudRate getBaudRate(){
		return DynamixelBaudRate.get((byte)GetRegisterValue(Register.BaudRate).intValue());
	}
    /**
     * Sets the BaudRate expected by the DynamixelServo
     * @param value BaudRate to be expected by the DynamixelServo
     */
    public void setBaudRate(DynamixelBaudRate value){
		SetRegisterValue(Register.BaudRate, (int)value.getByte());
	}

    /**
     *
     * @return
     */
    public Integer getCcwAngleLimit() {
		return GetRegisterValue(Register.CcwAngleLimit);
	}

    /**
     *
     * @param value
     */
    public void setCcwAngleLimit(Integer value) {
		SetRegisterValue(Register.CcwAngleLimit, value);
	}

    /**
     *
     * @return
     */
    public Integer getCwAngleLimit() {
		return GetRegisterValue(Register.CwAngleLimit);
	}

    /**
     *
     * @param value
     */
    public void setCwAngleLimit(Integer value) {
		SetRegisterValue(Register.CwAngleLimit, value);
	}

    /**
     *
     * @return
     */
    public Integer getCcwComplianceMargin() {
		return GetRegisterValue(Register.CcwComplianceMargin);
	}

    /**
     *
     * @param value
     */
    public void setCcwComplianceMargin(Integer value) {
		SetRegisterValue(Register.CcwComplianceMargin, value);
	}

    /**
     *
     * @return
     */
    public Integer getCwComplianceMargin() {
		return GetRegisterValue(Register.CwComplianceMargin);
	}

    /**
     *
     * @param value
     */
    public void setCwComplianceMargin(Integer value) {
		SetRegisterValue(Register.CwComplianceMargin, value);
	}

    /**
     *
     * @return
     */
    public Integer getCcwComplianceSlope() {
		return GetRegisterValue(Register.CcwComplianceSlope);
	}

    /**
     *
     * @param value
     */
    public void setCcwComplianceSlope(Integer value) {
		SetRegisterValue(Register.CcwComplianceSlope, value);
	}

    /**
     *
     * @return
     */
    public Integer getCwComplianceSlope() {
		return GetRegisterValue(Register.CwComplianceSlope);
	}

    /**
     *
     * @param value
     */
    public void setCwComplianceSlope(Integer value) {
		SetRegisterValue(Register.CwComplianceSlope, value);
	}

    /**
     *
     * @return
     */
    public Integer getCurrentLoad() {
		return myFeedbackUpdateVals.getCurrentLoad();
	}

    /**
     *
     * @return
     */
    public NormalizedDouble getCurrentPosition() {
        double min = myConfig.getMinPosition();
        double max = myConfig.getMaxPosition();
        Double range = max - min;
        Double abs = getCurrentPositionAbs() - min;
        Double pos = Utils.bound(abs/range, 0.0, 1.0);
        return new NormalizedDouble(pos);
	}
    /**
     * Returns the absolute position of the Dynamixel between 0-1023
     * @return absolute position of the Dynamixel between 0-1023
     */
    public Integer getCurrentPositionAbs() {
		return myFeedbackUpdateVals.getCurrentPosition();
	}

    /**
     *
     * @return
     */
    public Integer getCurrentSpeed() {
		return myFeedbackUpdateVals.getCurrentSpeed();
	}

    /**
     *
     * @return
     */
    public Integer getCurrentTemperature() {
		return myFeedbackUpdateVals.getCurrentTemperature();
	}

    /**
     *
     * @return
     */
    public Integer getCurrentVoltage() {
		return myFeedbackUpdateVals.getCurrentVoltage();
	}

    /**
     *
     * @return
     */
    public Boolean getTorqueEnable() {
		return GetRegisterValue(Register.TorqueEnable) != 0;
	}

    /**
     *
     * @param value
     */
    public void setTorqueEnabled(Boolean value) {
		SetRegisterValue(Register.TorqueEnable, value ? 1 : 0);
	}

    /**
     *
     * @return
     */
    public Integer getFirmwareVersion() {
		return GetRegisterValue(Register.FirmwareVersion);
	}

     /*public void setPhysicalId(Integer value) throws Throwable {
		if ( ( value < 0 || value >= DynamixelController.BROADCAST_ID ) ) {
			throw new Exception("Value must be in the range 0 to 253");
		}
		if ( value.equals(getPhysicalId()) ) {
			return;
		}
		myController.writeRegister(getPhysicalId(), Register.Id, value, false);
	}*/

    /**
     *
     * @return
     */
    public Boolean getLed() {
		return GetRegisterValue(Register.Led) != 0;
	}

    /**
     *
     * @param value
     */
    public void setLed(Boolean value) {
		SetRegisterValue(Register.Led, value ? 1 : 0);
	}

    /**
     *
     * @return
     */
    public Boolean getLock() {
		return GetRegisterValue(Register.Lock) != 0;
	}

    /**
     *
     * @return
     */
    public Integer getTemperatureLimit() {
		return GetRegisterValue(Register.TemperatureLimit);
	}

    /**
     *
     * @param value
     */
    public void setTemperatureLimit(Integer value) {
		SetRegisterValue(Register.TemperatureLimit, value);
	}

    /**
     *
     * @return
     */
    public Integer getMaxTorque() {
		return GetRegisterValue(Register.MaxTorque);
	}

    /**
     *
     * @param value
     */
    public void setMaxTorque(Integer value) {
		SetRegisterValue(Register.MaxTorque, value);
	}

    /**
     *
     * @return
     */
    public float getHighVoltageLimit() {
		return (float) (GetRegisterValue(Register.HighVoltageLimit)/10.0);
	}

    /**
     *
     * @param value
     */
    public void setHighVoltageLimit(float value) {
		SetRegisterValue(Register.HighVoltageLimit, (int)Math.round(value*10.0));
	}

    /**
     *
     * @return
     */
    public float getLowVoltageLimit() {
		return (float) (GetRegisterValue(Register.LowVoltageLimit)/10.0);
	}

    /**
     *
     * @param value
     */
    public void setLowVoltageLimit(float value) {
		SetRegisterValue(Register.LowVoltageLimit, (int)Math.round(value*10.0));
	}

    /**
     *
     * @return
     */
    public Integer getModelNumber() {
		return GetRegisterValue(Register.ModelNumber);
	}

    /**
     *
     * @return
     */
    public Boolean getMoving() {
		return ( myIsChanged || myController.readRegister(getPhysicalId(), Register.Moving) != 0 );
	}

    /**
     *
     * @return
     */
    public Integer getPunch() {
		return GetRegisterValue(Register.Punch);
	}

    /**
     *
     * @param value
     */
    public void setPunch(Integer value) {
		SetRegisterValue(Register.Punch, value);
	}

    /**
     *
     * @return
     */
    public Boolean getRegisteredInstruction() {
		return myController.readRegister(getPhysicalId(), Register.RegisteredInstruction) != 0;
	}

    /**
     *
     * @param value
     */
    public void setRegisteredInstruction(Boolean value) {
		SetRegisterValue(Register.RegisteredInstruction, value ? 1 : 0);
	}

    /**
     *
     * @return
     */
    public Integer getReturnDelay() {
		return GetRegisterValue(Register.ReturnDelay)*2;
	}

    /**
     *
     * @param value
     */
    public void setReturnDelay(Integer value) {
		SetRegisterValue(Register.ReturnDelay, value/2);
	}

    /**
     *
     * @return
     */
    public Integer getStatusReturnLevel() {
		return (Integer) GetRegisterValue(Register.StatusReturnLevel);
	}

    /**
     *
     * @param value
     */
    public void setStatusReturnLevel(Integer value) {
		SetRegisterValue(Register.StatusReturnLevel, (Integer) value);
	}

    /**
     *
     * @return
     */
    public Integer getTorqueLimit() {
		return GetRegisterValue(Register.TorqueLimit);
	}

    /**
     *
     * @param value
     */
    public void setTorqueLimit(Integer value) {
		SetRegisterValue(Register.TorqueLimit, value);
	}

    /**
     * Resets the DynamixelServo to the default factory settings.
     * WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! 
     * USE THIS METHOD WITH CAUTION.  THIS WILL CAUSE THE JOINT'S
     * PHYSICAL ID AND BAUD RATE TO CHANGE.  
     * This method should not be used with robots from HansonRobokind as it will 
     * cause the Joint to stop functioning properly.  This is included only to 
     * provide complete Dynamixel functionality for use in other applications.
     * @return true if successful
     */
    protected synchronized boolean reset(){
//		myController.writeInstruction(getPhysicalId(), Instruction.Reset, null);
//		return !myController.readPacket((byte)0).hasError();
        return false;
	}

    /**
     *
     * @param reg
     * @return
     */
    protected Integer GetRegisterValue(Register reg){
		switch (reg){
			case GoalPosition:
			case MovingSpeed:
				return get(reg);
		}

		if (!reg.isCached()){
			return myController.readRegister(getPhysicalId(), reg);
        }

		Integer v = get(reg);
		if (v == -1){
			put(reg, myController.readRegister(getPhysicalId(), reg));
			v = get(reg);
		}
		return v;
	}

    /**
     * Sets the value for the given Register.
     * @param reg Register to set
     * @param value value to set
     */
    protected synchronized void SetRegisterValue(Register reg, Integer value){
		if(reg.isSynchronized()){
            put(reg, value);
            myIsChanged = true;
            return;
		}else if (!reg.isCached()){
			myController.writeRegister(getPhysicalId(), reg, value, false);
			return;
		}
		Integer v = get(reg);
		if (v == value)
			return;

		myController.writeRegister(getPhysicalId(), reg, value, false);
		put(reg, value);
	}

    /**
     * Reads all Registers for the DynamixelServo and updates the cache.
     */
    public synchronized void cacheValues(){
		Register[] regs = Register.values();
		int[] values = myController.readRegisters(getPhysicalId(), Register.ModelNumber, Register.Punch);
		for (Integer i = 0; i < regs.length; i++){
			put(regs[i], values[i]);
		}
	}
    
    //TODO fix Dynamixel register caching updates.
    /**
     * Returns true if Register values have been changed.
     * @return true if Register values have been changed
     */
    public boolean changed(){
        return myIsChanged;
    }
    
    public final static class Id implements LocalIdentifier{
        private final static int theIdCount = 254;
        private int myPhysicalJointId;        
        
        /**
         * Creates the Broadcast Id
         */
        Id(){
            myPhysicalJointId = theIdCount;
        }
        /**
         * Creates a JointId from the given integer id.
         * @param id the jointId
         */
        public Id(int id){
            if(!isValidId(id)){
                throw new IllegalArgumentException("PhysicalId out of range.");
            }
            myPhysicalJointId = id;
        }
        
        /**
         * Returns the integer value of the Id.
         * @return the integer value of the Id
         */
        final public int getIntValue(){
            return myPhysicalJointId;
        }
        
        @Override
        public boolean equals(Object obj){
            if(obj == null || obj.getClass() != this.getClass()){
                return false;
            }
            return myPhysicalJointId == 
                    ((Id)obj).myPhysicalJointId;
        }
        
        /**
         * Returns true is the id would make a valid Id.
         * @param id value to check
         * @return true is the id would make a valid Id.
         */
        public static boolean isValidId(int id){
            return id >= 0 && id < theIdCount;
        }
        
        @Override
        public int hashCode() {
            return HashCodeUtil.hash(HashCodeUtil.SEED, myPhysicalJointId);
        }

        @Override
        public String toString() {
            return "" + myPhysicalJointId;
        }
    }
    
    public void setFeedbackVals(FeedbackUpdateValues vals){
        if(vals == null){
            return;
        }
        myFeedbackUpdateVals = vals;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy