dynamixel
Version:
Node.js library for controlling DYNAMIXEL servo motors via U2D2 interface with Protocol 2.0 support
436 lines (404 loc) • 12.1 kB
JavaScript
/**
* Motor Profile System for DYNAMIXEL devices
* Provides predefined configurations for different motor models and use cases
* Inspired by DynaNode's MotorProfile architecture
*/
export class MotorProfiles {
constructor() {
this.profiles = new Map();
this.customProfiles = new Map();
this.initializeDefaultProfiles();
}
initializeDefaultProfiles() {
// AX Series Profiles
this.profiles.set('AX-12A', {
modelNumber: 12,
series: 'AX',
specs: {
stallTorque: 1.5, // kg·cm
maxSpeed: 59, // RPM
operatingVoltage: [9.0, 12.0], // V
resolution: 1024,
positionRange: [0, 1023],
temperatureRange: [-5, 70], // Celsius
weight: 54.6, // grams
dimensions: [32, 50, 40] // mm [width, depth, height]
},
defaultSettings: {
torqueEnable: 1,
goalPosition: 512,
movingSpeed: 32,
torqueLimit: 1023,
alarmLED: 36,
alarmShutdown: 36,
returnDelay: 250,
statusReturnLevel: 2
},
operatingModes: ['joint', 'wheel'],
recommendedProfiles: {
precision: {
movingSpeed: 10,
torqueLimit: 512,
description: 'High precision, low speed positioning'
},
balanced: {
movingSpeed: 32,
torqueLimit: 1023,
description: 'Balanced speed and torque'
},
fast: {
movingSpeed: 100,
torqueLimit: 1023,
description: 'Fast positioning with full torque'
}
}
});
this.profiles.set('MX-28', {
modelNumber: 29,
series: 'MX',
specs: {
stallTorque: 2.5, // kg·cm
maxSpeed: 55, // RPM
operatingVoltage: [10.0, 14.8], // V
resolution: 4096,
positionRange: [0, 4095],
temperatureRange: [-5, 80], // Celsius
weight: 77, // grams
dimensions: [35.6, 50.6, 35.5] // mm
},
defaultSettings: {
torqueEnable: 1,
goalPosition: 2048,
goalVelocity: 50,
goalPWM: 885,
operatingMode: 3, // Position control
returnDelay: 250,
statusReturnLevel: 2
},
operatingModes: ['current', 'velocity', 'position', 'extended_position', 'current_position', 'pwm'],
recommendedProfiles: {
servo: {
operatingMode: 3, // Position control
goalVelocity: 30,
description: 'Standard servo operation'
},
wheel: {
operatingMode: 1, // Velocity control
goalVelocity: 50,
description: 'Continuous rotation wheel mode'
},
compliant: {
operatingMode: 0, // Current control
goalCurrent: 100,
description: 'Force-controlled compliant operation'
}
}
});
this.profiles.set('XM430-W350', {
modelNumber: 1030,
series: 'X',
specs: {
stallTorque: 4.1, // kg·cm
maxSpeed: 46, // RPM
operatingVoltage: [10.0, 14.8], // V
resolution: 4096,
positionRange: [0, 4095],
temperatureRange: [-5, 80], // Celsius
weight: 82, // grams
dimensions: [28.5, 46.5, 34] // mm
},
defaultSettings: {
torqueEnable: 1,
goalPosition: 2048,
goalVelocity: 50,
goalCurrent: 200,
operatingMode: 3, // Position control
returnDelay: 250,
statusReturnLevel: 2,
velocityLimit: 480,
accelerationLimit: 32767
},
operatingModes: ['current', 'velocity', 'position', 'extended_position', 'current_position', 'pwm'],
recommendedProfiles: {
precision: {
operatingMode: 3,
goalVelocity: 20,
accelerationLimit: 1000,
description: 'High precision positioning'
},
dynamic: {
operatingMode: 3,
goalVelocity: 100,
accelerationLimit: 10000,
description: 'Dynamic positioning with smooth acceleration'
},
force_control: {
operatingMode: 0,
goalCurrent: 150,
description: 'Force-controlled operation'
}
}
});
// Add more motor profiles...
this.addRobotArmProfile();
this.addWheelRobotProfile();
this.addGripperProfile();
}
addRobotArmProfile() {
this.profiles.set('ROBOT_ARM_6DOF', {
type: 'application_profile',
description: '6DOF Robot Arm Configuration',
joints: {
base: {
motorModel: 'XM430-W350',
settings: {
operatingMode: 3,
goalVelocity: 30,
accelerationLimit: 2000,
positionPGain: 800,
positionIGain: 0,
positionDGain: 0
},
limits: {
minPosition: 0,
maxPosition: 4095,
maxVelocity: 100
}
},
shoulder: {
motorModel: 'XM430-W350',
settings: {
operatingMode: 3,
goalVelocity: 40,
accelerationLimit: 3000,
positionPGain: 900
}
},
elbow: {
motorModel: 'MX-28',
settings: {
operatingMode: 3,
goalVelocity: 50,
accelerationLimit: 4000
}
},
wrist1: { motorModel: 'AX-12A', settings: { movingSpeed: 50 } },
wrist2: { motorModel: 'AX-12A', settings: { movingSpeed: 50 } },
wrist3: { motorModel: 'AX-12A', settings: { movingSpeed: 30 } }
}
});
}
addWheelRobotProfile() {
this.profiles.set('MOBILE_ROBOT_4WD', {
type: 'application_profile',
description: '4-Wheel Drive Mobile Robot',
wheels: {
frontLeft: {
motorModel: 'XM430-W350',
settings: {
operatingMode: 1, // Velocity control
goalVelocity: 0,
accelerationLimit: 5000
}
},
frontRight: {
motorModel: 'XM430-W350',
settings: {
operatingMode: 1,
goalVelocity: 0,
accelerationLimit: 5000
}
},
backLeft: {
motorModel: 'XM430-W350',
settings: {
operatingMode: 1,
goalVelocity: 0,
accelerationLimit: 5000
}
},
backRight: {
motorModel: 'XM430-W350',
settings: {
operatingMode: 1,
goalVelocity: 0,
accelerationLimit: 5000
}
}
},
kinematics: {
wheelbase: 200, // mm
trackWidth: 150, // mm
wheelRadius: 30 // mm
}
});
}
addGripperProfile() {
this.profiles.set('ADAPTIVE_GRIPPER', {
type: 'application_profile',
description: 'Adaptive Gripper with Force Control',
fingers: {
finger1: {
motorModel: 'MX-28',
settings: {
operatingMode: 0, // Current control
goalCurrent: 100,
currentLimit: 200
}
},
finger2: {
motorModel: 'MX-28',
settings: {
operatingMode: 0,
goalCurrent: 100,
currentLimit: 200
}
}
},
graspingModes: {
gentle: { goalCurrent: 50, description: 'Gentle grasp for delicate objects' },
firm: { goalCurrent: 150, description: 'Firm grasp for secure holding' },
maximum: { goalCurrent: 200, description: 'Maximum force grasp' }
}
});
}
/**
* Get profile for a motor model
*/
getProfile(modelName) {
return this.profiles.get(modelName) || this.customProfiles.get(modelName);
}
/**
* Get recommended settings for a motor model and use case
*/
getRecommendedSettings(modelName, useCase = 'balanced') {
const profile = this.getProfile(modelName);
if (!profile) return null;
const baseSettings = { ...profile.defaultSettings };
const recommendedProfile = profile.recommendedProfiles?.[useCase];
if (recommendedProfile) {
return { ...baseSettings, ...recommendedProfile };
}
return baseSettings;
}
/**
* Create a custom profile
*/
createCustomProfile(name, profileData) {
this.customProfiles.set(name, {
...profileData,
custom: true,
createdAt: Date.now()
});
}
/**
* Get all available profiles
*/
getAllProfiles() {
const allProfiles = new Map();
// Add default profiles
for (const [name, profile] of this.profiles) {
allProfiles.set(name, { ...profile, type: profile.type || 'motor_profile' });
}
// Add custom profiles
for (const [name, profile] of this.customProfiles) {
allProfiles.set(name, { ...profile, type: 'custom_profile' });
}
return allProfiles;
}
/**
* Get profiles by motor series
*/
getProfilesBySeries(series) {
const profiles = [];
for (const [name, profile] of this.profiles) {
if (profile.series === series) {
profiles.push({ name, ...profile });
}
}
return profiles;
}
/**
* Get application profiles
*/
getApplicationProfiles() {
const profiles = [];
for (const [name, profile] of this.profiles) {
if (profile.type === 'application_profile') {
profiles.push({ name, ...profile });
}
}
return profiles;
}
/**
* Validate profile compatibility
*/
validateProfile(motorModel, profileSettings) {
const profile = this.getProfile(motorModel);
if (!profile) return { valid: false, errors: ['Unknown motor model'] };
const errors = [];
const specs = profile.specs;
// Validate position range
if (profileSettings.goalPosition !== undefined) {
if (profileSettings.goalPosition < specs.positionRange[0] ||
profileSettings.goalPosition > specs.positionRange[1]) {
errors.push(`Goal position ${profileSettings.goalPosition} outside valid range [${specs.positionRange[0]}, ${specs.positionRange[1]}]`);
}
}
// Validate velocity
if (profileSettings.goalVelocity !== undefined && specs.maxSpeed) {
const maxVelUnits = this.rpmToVelocityUnits(specs.maxSpeed, profile);
if (profileSettings.goalVelocity > maxVelUnits) {
errors.push(`Goal velocity ${profileSettings.goalVelocity} exceeds maximum ${maxVelUnits}`);
}
}
return {
valid: errors.length === 0,
errors,
warnings: []
};
}
/**
* Convert RPM to velocity units for a specific motor
*/
rpmToVelocityUnits(rpm, profile) {
// This varies by motor series - simplified calculation
const resolution = profile.specs.resolution;
return Math.round((rpm * resolution) / 60);
}
/**
* Get optimal settings for multi-motor synchronization
*/
getSynchronizationSettings(motorModels) {
const profiles = motorModels.map(model => this.getProfile(model)).filter(Boolean);
if (profiles.length === 0) return null;
// Find common denominator settings for synchronized operation
const minMaxSpeed = Math.min(...profiles.map(p => p.specs.maxSpeed));
const commonReturnDelay = Math.max(...profiles.map(p => p.defaultSettings.returnDelay || 250));
return {
recommendedVelocity: Math.round(minMaxSpeed * 0.7), // 70% of slowest motor
returnDelay: commonReturnDelay,
statusReturnLevel: 1, // Reduce traffic for sync
recommendedUpdateRate: Math.max(50, commonReturnDelay * 2) // ms
};
}
/**
* Export profile as JSON
*/
exportProfile(profileName) {
const profile = this.getProfile(profileName);
return profile ? JSON.stringify(profile, null, 2) : null;
}
/**
* Import profile from JSON
*/
importProfile(name, jsonData) {
try {
const profileData = JSON.parse(jsonData);
this.createCustomProfile(name, profileData);
return { success: true };
} catch (error) {
return { success: false, error: error.message };
}
}
}