feat: 机械臂联调
This commit is contained in:
parent
2d12fc3f89
commit
e4058c6475
3
.env
3
.env
@ -1,3 +1,4 @@
|
|||||||
VITE_DEVICE_ID = 'agv_src2200'
|
VITE_AGV_DEVICE_ID = 'agv_src2200'
|
||||||
|
VITE_ARM_DEVICE_ID = 'huayan_arm'
|
||||||
|
|
||||||
VITE_SERVICE_PORT = 5175
|
VITE_SERVICE_PORT = 5175
|
||||||
@ -6,7 +6,6 @@ services:
|
|||||||
container_name: inspection-host-computer
|
container_name: inspection-host-computer
|
||||||
ports:
|
ports:
|
||||||
- "5175:5175" # 映射端口
|
- "5175:5175" # 映射端口
|
||||||
- "5173:5173" # 映射端口
|
|
||||||
environment:
|
environment:
|
||||||
- NODE_ENV=production
|
- NODE_ENV=production
|
||||||
- VITE_SERVICE_PORT=5175 # 传给 Express 的环境变量
|
- VITE_SERVICE_PORT=5175 # 传给 Express 的环境变量
|
||||||
|
|||||||
56
server/grpcClient.js
Normal file
56
server/grpcClient.js
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import path from 'path';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
import grpc from '@grpc/grpc-js'
|
||||||
|
import protoLoader from '@grpc/proto-loader'
|
||||||
|
|
||||||
|
export const __filename = fileURLToPath(import.meta.url)
|
||||||
|
export const __dirname = path.dirname(__filename)
|
||||||
|
|
||||||
|
|
||||||
|
// ========== 加载 Proto ==========
|
||||||
|
const PROTO_ROOT = path.resolve(__dirname, './proto') // 指向 server/proto/
|
||||||
|
const AGV_PROTO_ENTRY = path.join(PROTO_ROOT, 'cmvr/api/agv_service.proto')
|
||||||
|
const ARM_PROTO_ENTRY = path.join(PROTO_ROOT, 'cmvr/api/arm_service.proto')
|
||||||
|
|
||||||
|
const agvPackageDefinition = protoLoader.loadSync(
|
||||||
|
AGV_PROTO_ENTRY,
|
||||||
|
{
|
||||||
|
includeDirs: [PROTO_ROOT , path.join(PROTO_ROOT, 'cmvr/api')], // 关键:让加载器从 PROTO_ROOT 查找 import
|
||||||
|
keepCase: true,
|
||||||
|
longs: String,
|
||||||
|
enums: String,
|
||||||
|
defaults: true,
|
||||||
|
oneofs: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const armPackageDefinition = protoLoader.loadSync(
|
||||||
|
ARM_PROTO_ENTRY,
|
||||||
|
{
|
||||||
|
includeDirs: [PROTO_ROOT , path.join(PROTO_ROOT, 'cmvr/api')], // 关键:让加载器从 PROTO_ROOT 查找 import
|
||||||
|
keepCase: true,
|
||||||
|
longs: String,
|
||||||
|
enums: String,
|
||||||
|
defaults: true,
|
||||||
|
oneofs: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const agvProto = grpc.loadPackageDefinition(agvPackageDefinition).cmvr.api
|
||||||
|
const armProto = grpc.loadPackageDefinition(armPackageDefinition).cmvr.api
|
||||||
|
|
||||||
|
export const createAGVGrpcClient = (address) => {
|
||||||
|
const grpcClient = new agvProto.AgvService(
|
||||||
|
address,
|
||||||
|
grpc.credentials.createInsecure()
|
||||||
|
)
|
||||||
|
return grpcClient
|
||||||
|
}
|
||||||
|
|
||||||
|
export const createARMGrpcClient = (address) => {
|
||||||
|
const grpcClient = new armProto.ArmService(
|
||||||
|
address,
|
||||||
|
grpc.credentials.createInsecure()
|
||||||
|
)
|
||||||
|
return grpcClient
|
||||||
|
}
|
||||||
216
server/index.js
216
server/index.js
@ -1,46 +1,16 @@
|
|||||||
// server/index.js
|
// server/index.js
|
||||||
import express from 'express'
|
import express from 'express'
|
||||||
import cors from 'cors'
|
import cors from 'cors'
|
||||||
import grpc from '@grpc/grpc-js'
|
|
||||||
import protoLoader from '@grpc/proto-loader'
|
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { fileURLToPath } from 'url';
|
|
||||||
import 'dotenv/config';
|
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url)
|
import 'dotenv/config';
|
||||||
const __dirname = path.dirname(__filename)
|
import { createAGVGrpcClient, createARMGrpcClient, __dirname } from './grpcClient.js';
|
||||||
|
|
||||||
const app = express()
|
const app = express()
|
||||||
app.use(express.static(path.join(__dirname, '../dist')));
|
app.use(express.static(path.join(__dirname, '../dist')));
|
||||||
app.use(cors())
|
app.use(cors())
|
||||||
app.use(express.json())
|
app.use(express.json())
|
||||||
|
|
||||||
// ========== 加载 Proto ==========
|
|
||||||
const PROTO_ROOT = path.resolve(__dirname, './proto') // 指向 server/proto/
|
|
||||||
const PROTO_ENTRY = path.join(PROTO_ROOT, 'cmvr/api/agv_service.proto')
|
|
||||||
|
|
||||||
const packageDefinition = protoLoader.loadSync(
|
|
||||||
PROTO_ENTRY,
|
|
||||||
{
|
|
||||||
includeDirs: [PROTO_ROOT , path.join(PROTO_ROOT, 'cmvr/api')], // 关键:让加载器从 PROTO_ROOT 查找 import
|
|
||||||
keepCase: true,
|
|
||||||
longs: String,
|
|
||||||
enums: String,
|
|
||||||
defaults: true,
|
|
||||||
oneofs: true
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const cmvrProto = grpc.loadPackageDefinition(packageDefinition).cmvr.api
|
|
||||||
|
|
||||||
const createGrpcClient = (address) => {
|
|
||||||
const grpcClient = new cmvrProto.AgvService(
|
|
||||||
address,
|
|
||||||
grpc.credentials.createInsecure()
|
|
||||||
)
|
|
||||||
return grpcClient
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取机器人当前地图名称
|
// 获取机器人当前地图名称
|
||||||
app.post('/api/agv/getCurrentMapName', async (req, res) => {
|
app.post('/api/agv/getCurrentMapName', async (req, res) => {
|
||||||
const request = {
|
const request = {
|
||||||
@ -49,7 +19,7 @@ app.post('/api/agv/getCurrentMapName', async (req, res) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const grpcClient = createGrpcClient(req.body.ip)
|
const grpcClient = createAGVGrpcClient(req.body.ip)
|
||||||
|
|
||||||
const resp = await new Promise((resolve, reject) => {
|
const resp = await new Promise((resolve, reject) => {
|
||||||
grpcClient.getMapStatus(request, (err, res) => {
|
grpcClient.getMapStatus(request, (err, res) => {
|
||||||
@ -76,7 +46,7 @@ app.post('/api/agv/getCurrentMapName', async (req, res) => {
|
|||||||
|
|
||||||
// 获取机器人当前地图名称
|
// 获取机器人当前地图名称
|
||||||
app.post('/api/agv/getCurrentMap', async (req, res) => {
|
app.post('/api/agv/getCurrentMap', async (req, res) => {
|
||||||
const grpcClient = createGrpcClient(req.body.ip)
|
const grpcClient = createAGVGrpcClient(req.body.ip)
|
||||||
const request = {
|
const request = {
|
||||||
header: {
|
header: {
|
||||||
device_id: req.body.device_id || 'agv_src1100'
|
device_id: req.body.device_id || 'agv_src1100'
|
||||||
@ -109,6 +79,7 @@ app.post('/api/agv/getCurrentMap', async (req, res) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 获取机器人状态
|
||||||
app.post('/api/agv/getRobotState', async (req, res) => {
|
app.post('/api/agv/getRobotState', async (req, res) => {
|
||||||
const request = {
|
const request = {
|
||||||
header: {
|
header: {
|
||||||
@ -116,7 +87,7 @@ app.post('/api/agv/getRobotState', async (req, res) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const grpcClient = createGrpcClient(req.body.ip)
|
const grpcClient = createAGVGrpcClient(req.body.ip)
|
||||||
|
|
||||||
const response = await new Promise((resolve, reject) => {
|
const response = await new Promise((resolve, reject) => {
|
||||||
grpcClient.GetRobotLocation(request, (err, res) => {
|
grpcClient.GetRobotLocation(request, (err, res) => {
|
||||||
@ -168,8 +139,9 @@ app.post('/api/agv/getRobotState', async (req, res) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 机器人运动控制
|
||||||
app.post('/api/agv/moveRobot', async (req, res) => {
|
app.post('/api/agv/moveRobot', async (req, res) => {
|
||||||
const grpcClient = createGrpcClient(req.body.ip)
|
const grpcClient = createAGVGrpcClient(req.body.ip)
|
||||||
const request = {
|
const request = {
|
||||||
header: {
|
header: {
|
||||||
device_id: req.body.device_id || 'agv_src1100'
|
device_id: req.body.device_id || 'agv_src1100'
|
||||||
@ -208,8 +180,9 @@ app.post('/api/agv/moveRobot', async (req, res) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 机器人停止运动
|
||||||
app.post('/api/agv/stopRobot', async (req, res) => {
|
app.post('/api/agv/stopRobot', async (req, res) => {
|
||||||
const grpcClient = createGrpcClient(req.body.ip)
|
const grpcClient = createAGVGrpcClient(req.body.ip)
|
||||||
const request = {
|
const request = {
|
||||||
header: {
|
header: {
|
||||||
device_id: req.body.device_id || 'agv_src1100'
|
device_id: req.body.device_id || 'agv_src1100'
|
||||||
@ -242,6 +215,175 @@ app.post('/api/agv/stopRobot', async (req, res) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 获取机械臂末端位姿
|
||||||
|
app.post('/api/arm/getPose', async (req, res) => {
|
||||||
|
const request = {
|
||||||
|
header: {
|
||||||
|
device_id: req.body.deviceId || 'huayan_arm'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const grpcClient = createARMGrpcClient(req.body.ip)
|
||||||
|
|
||||||
|
const resp = await new Promise((resolve, reject) => {
|
||||||
|
grpcClient.getPose(request, (err, res) => {
|
||||||
|
if (err) {
|
||||||
|
resolve({
|
||||||
|
code: 500,
|
||||||
|
data: `grpc服务端错误`
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
resolve({
|
||||||
|
code: 200,
|
||||||
|
data: res.pose
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
if (resp.code === 200) {
|
||||||
|
res.json(resp)
|
||||||
|
} else {
|
||||||
|
res.status(500).json({ error: 'grpc服务端错误' })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 获取机械臂节点状态
|
||||||
|
app.post('/api/arm/getJointState', async (req, res) => {
|
||||||
|
const request = {
|
||||||
|
header: {
|
||||||
|
device_id: req.body.deviceId || 'huayan_arm'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const grpcClient = createARMGrpcClient(req.body.ip)
|
||||||
|
|
||||||
|
const resp = await new Promise((resolve, reject) => {
|
||||||
|
grpcClient.getJointState(request, (err, res) => {
|
||||||
|
if (err) {
|
||||||
|
resolve({
|
||||||
|
code: 500,
|
||||||
|
data: `grpc服务端错误`
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
resolve({
|
||||||
|
code: 200,
|
||||||
|
data: res.state
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
if (resp.code === 200) {
|
||||||
|
res.json(resp)
|
||||||
|
} else {
|
||||||
|
res.status(500).json({ error: 'grpc服务端错误' })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 机械臂关节运动
|
||||||
|
app.post('/api/arm/speedJ', async (req, res) => {
|
||||||
|
const request = {
|
||||||
|
header: {
|
||||||
|
device_id: req.body.deviceId || 'huayan_arm'
|
||||||
|
},
|
||||||
|
velocity: {
|
||||||
|
velocity: req.body.velocities
|
||||||
|
},
|
||||||
|
acceleration: req.body.acceleration,
|
||||||
|
duration: req.body.duration
|
||||||
|
};
|
||||||
|
const grpcClient = createARMGrpcClient(req.body.ip)
|
||||||
|
|
||||||
|
const resp = await new Promise((resolve, reject) => {
|
||||||
|
grpcClient.speedJ(request, (err, res) => {
|
||||||
|
if (err) {
|
||||||
|
resolve({
|
||||||
|
code: 500,
|
||||||
|
data: `grpc服务端错误`
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
resolve({
|
||||||
|
code: 200,
|
||||||
|
data: res
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
if (resp.code === 200) {
|
||||||
|
res.json(resp)
|
||||||
|
} else {
|
||||||
|
res.status(500).json({ error: 'grpc服务端错误' })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
app.post('/api/arm/speedL', async (req, res) => {
|
||||||
|
const request = {
|
||||||
|
header: {
|
||||||
|
device_id: req.body.deviceId || 'huayan_arm'
|
||||||
|
},
|
||||||
|
velocity: req.body.velocity,
|
||||||
|
acceleration: req.body.acceleration,
|
||||||
|
duration: req.body.duration
|
||||||
|
};
|
||||||
|
const grpcClient = createARMGrpcClient(req.body.ip)
|
||||||
|
|
||||||
|
const resp = await new Promise((resolve, reject) => {
|
||||||
|
grpcClient.speedL(request, (err, res) => {
|
||||||
|
if (err) {
|
||||||
|
resolve({
|
||||||
|
code: 500,
|
||||||
|
data: `grpc服务端错误`
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
resolve({
|
||||||
|
code: 200,
|
||||||
|
data: res
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
if (resp.code === 200) {
|
||||||
|
res.json(resp)
|
||||||
|
} else {
|
||||||
|
res.status(500).json({ error: 'grpc服务端错误' })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 机械臂停止运动
|
||||||
|
app.post('/api/arm/stopMotion', async (req, res) => {
|
||||||
|
const request = {
|
||||||
|
// header: {
|
||||||
|
device_id: req.body.deviceId || 'huayan_arm',
|
||||||
|
// },
|
||||||
|
};
|
||||||
|
const grpcClient = createARMGrpcClient(req.body.ip)
|
||||||
|
|
||||||
|
const resp = await new Promise((resolve, reject) => {
|
||||||
|
grpcClient.stopMotion(request, (err, res) => {
|
||||||
|
if (err) {
|
||||||
|
resolve({
|
||||||
|
code: 500,
|
||||||
|
data: `grpc服务端错误`
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
resolve({
|
||||||
|
code: 200,
|
||||||
|
data: res.success
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
if (resp.code === 200) {
|
||||||
|
res.json(resp)
|
||||||
|
} else {
|
||||||
|
res.status(500).json({ error: 'grpc服务端错误' })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const isProd = process.env.NODE_ENV === 'production';
|
const isProd = process.env.NODE_ENV === 'production';
|
||||||
const PORT = process.env.VITE_SERVICE_PORT || 3000;
|
const PORT = process.env.VITE_SERVICE_PORT || 3000;
|
||||||
|
|
||||||
|
|||||||
185
server/proto/cmvr/api/arm_command.proto
Normal file
185
server/proto/cmvr/api/arm_command.proto
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package cmvr.api;
|
||||||
|
|
||||||
|
import "cmvr/api/common.proto";
|
||||||
|
|
||||||
|
enum ArmFrameType {
|
||||||
|
ARM_FRAME_BASE = 0;
|
||||||
|
ARM_FRAME_TOOL = 1;
|
||||||
|
ARM_FRAME_WORLD = 2;
|
||||||
|
ARM_FRAME_USER = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message JointPositionCommand {
|
||||||
|
repeated double position = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message JointVelocityCommand {
|
||||||
|
repeated double velocity = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message MotionOptions {
|
||||||
|
double velocity = 1;
|
||||||
|
double acceleration = 2;
|
||||||
|
double blend_radius = 3;
|
||||||
|
double jerk = 4;
|
||||||
|
repeated double joint_velocity_limits = 5;
|
||||||
|
bool asynchronous = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
message CartesianPose {
|
||||||
|
double x = 1;
|
||||||
|
double y = 2;
|
||||||
|
double z = 3;
|
||||||
|
double rx = 4;
|
||||||
|
double ry = 5;
|
||||||
|
double rz = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
message CartesianVelocity {
|
||||||
|
double vx = 1;
|
||||||
|
double vy = 2;
|
||||||
|
double vz = 3;
|
||||||
|
double wx = 4;
|
||||||
|
double wy = 5;
|
||||||
|
double wz = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
message TransformMatrix4x4 {
|
||||||
|
double m00 = 1; double m01 = 2; double m02 = 3; double m03 = 4;
|
||||||
|
double m10 = 5; double m11 = 6; double m12 = 7; double m13 = 8;
|
||||||
|
double m20 = 9; double m21 = 10; double m22 = 11; double m23 = 12;
|
||||||
|
double m30 = 13; double m31 = 14; double m32 = 15; double m33 = 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
message MoveJ {
|
||||||
|
message Request {
|
||||||
|
CommandHeader.Request header = 1;
|
||||||
|
JointPositionCommand target = 2;
|
||||||
|
MotionOptions options = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Response {
|
||||||
|
CommandHeader.Feedback header = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message MoveL {
|
||||||
|
message Request {
|
||||||
|
CommandHeader.Request header = 1;
|
||||||
|
CartesianPose target = 2;
|
||||||
|
MotionOptions options = 3;
|
||||||
|
ArmFrameType frame = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Response {
|
||||||
|
CommandHeader.Feedback header = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message SpeedJ {
|
||||||
|
message Request {
|
||||||
|
CommandHeader.Request header = 1;
|
||||||
|
JointVelocityCommand velocity = 2;
|
||||||
|
double acceleration = 3;
|
||||||
|
double duration = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Response {
|
||||||
|
CommandHeader.Feedback header = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message SpeedL {
|
||||||
|
message Request {
|
||||||
|
CommandHeader.Request header = 1;
|
||||||
|
CartesianVelocity velocity = 2;
|
||||||
|
double acceleration = 3;
|
||||||
|
double duration = 4;
|
||||||
|
ArmFrameType frame = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Response {
|
||||||
|
CommandHeader.Feedback header = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message ServoJ {
|
||||||
|
message Request {
|
||||||
|
CommandHeader.Request header = 1;
|
||||||
|
JointPositionCommand target = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Response {
|
||||||
|
CommandHeader.Feedback header = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message JointState {
|
||||||
|
repeated string name = 1;
|
||||||
|
repeated double position = 2;
|
||||||
|
repeated double velocity = 3;
|
||||||
|
repeated double effort = 4;
|
||||||
|
double timestamp = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message JointRequest {
|
||||||
|
CommandHeader.Request header = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message JointResponse {
|
||||||
|
CommandHeader.Feedback header = 1;
|
||||||
|
JointState state = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetPose {
|
||||||
|
message Request {
|
||||||
|
CommandHeader.Request header = 1;
|
||||||
|
string base_link = 2;
|
||||||
|
string ee_link = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Response {
|
||||||
|
CommandHeader.Feedback header = 1;
|
||||||
|
CartesianPose pose = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message CalibrateZeroQ {
|
||||||
|
message Request {
|
||||||
|
CommandHeader.Request header = 1;
|
||||||
|
string joint_name = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Response {
|
||||||
|
CommandHeader.Feedback header = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetPoseMatrix {
|
||||||
|
message Request {
|
||||||
|
CommandHeader.Request header = 1;
|
||||||
|
string base_link = 2;
|
||||||
|
string ee_link = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Response {
|
||||||
|
CommandHeader.Feedback header = 1;
|
||||||
|
TransformMatrix4x4 matrix = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message ComputeForwardKinematics {
|
||||||
|
message Request {
|
||||||
|
CommandHeader.Request header = 1;
|
||||||
|
string base_link = 2;
|
||||||
|
string ee_link = 3;
|
||||||
|
JointPositionCommand joints = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Response {
|
||||||
|
CommandHeader.Feedback header = 1;
|
||||||
|
TransformMatrix4x4 matrix = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
22
server/proto/cmvr/api/arm_service.proto
Normal file
22
server/proto/cmvr/api/arm_service.proto
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package cmvr.api;
|
||||||
|
|
||||||
|
import "cmvr/api/common.proto";
|
||||||
|
import "cmvr/api/arm_command.proto";
|
||||||
|
|
||||||
|
service ArmService {
|
||||||
|
rpc torqueOff(CommandHeader.Request) returns (CommandHeader.Feedback);
|
||||||
|
rpc torqueOn(CommandHeader.Request) returns (CommandHeader.Feedback);
|
||||||
|
rpc moveJ(MoveJ.Request) returns (MoveJ.Response);
|
||||||
|
rpc moveL(MoveL.Request) returns (MoveL.Response);
|
||||||
|
rpc speedJ(SpeedJ.Request) returns (SpeedJ.Response);
|
||||||
|
rpc speedL(SpeedL.Request) returns (SpeedL.Response);
|
||||||
|
rpc servoJ(ServoJ.Request) returns (ServoJ.Response);
|
||||||
|
rpc stopMotion(CommandHeader.Request) returns (CommandHeader.Feedback);
|
||||||
|
rpc getJointState(JointRequest) returns (JointResponse);
|
||||||
|
rpc getPose(GetPose.Request) returns (GetPose.Response);
|
||||||
|
rpc calibrateZeroQ(CalibrateZeroQ.Request) returns (CalibrateZeroQ.Response);
|
||||||
|
rpc getPoseMatrix(GetPoseMatrix.Request) returns (GetPoseMatrix.Response);
|
||||||
|
rpc computeForwardKinematics(ComputeForwardKinematics.Request) returns (ComputeForwardKinematics.Response);
|
||||||
|
}
|
||||||
@ -306,9 +306,6 @@ export class XacroAdapter {
|
|||||||
* @returns {Promise<string>}
|
* @returns {Promise<string>}
|
||||||
*/
|
*/
|
||||||
static async loadFileFromMap(path, fileMap, workingPath) {
|
static async loadFileFromMap(path, fileMap, workingPath) {
|
||||||
console.log('path', path)
|
|
||||||
console.log('fileMap', fileMap)
|
|
||||||
console.log('workingPath', workingPath)
|
|
||||||
// Clean path - remove leading slash if present
|
// Clean path - remove leading slash if present
|
||||||
let cleanPath = path;
|
let cleanPath = path;
|
||||||
// if (cleanPath.startsWith('/')) {
|
// if (cleanPath.startsWith('/')) {
|
||||||
@ -318,7 +315,6 @@ export class XacroAdapter {
|
|||||||
// // Remove leading ./
|
// // Remove leading ./
|
||||||
// cleanPath = cleanPath.replace(/^\.\//, '');
|
// cleanPath = cleanPath.replace(/^\.\//, '');
|
||||||
cleanPath = cleanPath.split('///')[1];
|
cleanPath = cleanPath.split('///')[1];
|
||||||
console.log('cleanPath', cleanPath)
|
|
||||||
// Try different path combinations
|
// Try different path combinations
|
||||||
const possiblePaths = [
|
const possiblePaths = [
|
||||||
cleanPath,
|
cleanPath,
|
||||||
@ -328,13 +324,10 @@ export class XacroAdapter {
|
|||||||
// Also try without the first directory component
|
// Also try without the first directory component
|
||||||
cleanPath.includes('/') ? cleanPath.substring(cleanPath.indexOf('/') + 1) : cleanPath,
|
cleanPath.includes('/') ? cleanPath.substring(cleanPath.indexOf('/') + 1) : cleanPath,
|
||||||
];
|
];
|
||||||
console.log('possiblePaths', possiblePaths)
|
|
||||||
// Try each path
|
// Try each path
|
||||||
for (const tryPath of possiblePaths) {
|
for (const tryPath of possiblePaths) {
|
||||||
console.log('tryPath', tryPath)
|
|
||||||
const file = fileMap.get(tryPath);
|
const file = fileMap.get(tryPath);
|
||||||
if (file) {
|
if (file) {
|
||||||
console.log(12345)
|
|
||||||
const content = await file.text();
|
const content = await file.text();
|
||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,4 @@
|
|||||||
const fetchFn = async (url, params = {}, method = 'GET', headers = { 'Content-Type': 'application/json' }) => {
|
import request from '@/utils/request'
|
||||||
const res = await fetch(url, {
|
|
||||||
method: method,
|
|
||||||
headers: headers,
|
|
||||||
body: JSON.stringify(params)
|
|
||||||
})
|
|
||||||
|
|
||||||
const data = await res.json()
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 机器人前往目标站点
|
* 机器人前往目标站点
|
||||||
@ -15,21 +6,21 @@ const fetchFn = async (url, params = {}, method = 'GET', headers = { 'Content-Ty
|
|||||||
* @param {string} [params.deviceId] - 设备ID,默认 agv_src1100
|
* @param {string} [params.deviceId] - 设备ID,默认 agv_src1100
|
||||||
*/
|
*/
|
||||||
export function getCurrentMapName(params) {
|
export function getCurrentMapName(params) {
|
||||||
return fetchFn('/api/agv/getCurrentMapName', params, 'POST')
|
return request('/api/agv/getCurrentMapName', params, 'POST')
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getCurrentMap(params) {
|
export function getCurrentMap(params) {
|
||||||
return fetchFn('/api/agv/getCurrentMap', params, 'POST')
|
return request('/api/agv/getCurrentMap', params, 'POST')
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getRobotState(params) {
|
export function getRobotState(params) {
|
||||||
return fetchFn('/api/agv/getRobotState', params, 'POST')
|
return request('/api/agv/getRobotState', params, 'POST')
|
||||||
}
|
}
|
||||||
|
|
||||||
export function moveRobot(params) {
|
export function moveRobot(params) {
|
||||||
return fetchFn('/api/agv/moveRobot', params, 'POST')
|
return request('/api/agv/moveRobot', params, 'POST')
|
||||||
}
|
}
|
||||||
|
|
||||||
export function stopRobot(params) {
|
export function stopRobot(params) {
|
||||||
return fetchFn('/api/agv/stopRobot', params, 'POST')
|
return request('/api/agv/stopRobot', params, 'POST')
|
||||||
}
|
}
|
||||||
29
src/api/arm.js
Normal file
29
src/api/arm.js
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
export function getPoseApi(params) {
|
||||||
|
return request('/api/arm/getPose', params, 'POST')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getJointStateApi(params) {
|
||||||
|
return request('/api/arm/getJointState', params, 'POST')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function speedLApi(data) {
|
||||||
|
return request('/api/arm/speedL', data, 'POST')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function moveJApi(data) {
|
||||||
|
return request('/api/arm/moveJ', data, 'POST')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function stopMotionApi(params) {
|
||||||
|
return request('/api/arm/stopMotion', params, 'POST')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function torqueOnApi(params) {
|
||||||
|
return request('/api/arm/torqueOn', params, 'POST')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function speedJApi(data) {
|
||||||
|
return request('/api/arm/speedJ', data, 'POST')
|
||||||
|
}
|
||||||
12
src/utils/request.js
Normal file
12
src/utils/request.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
const request = async (url, params = {}, method = 'GET', headers = { 'Content-Type': 'application/json' }) => {
|
||||||
|
const res = await fetch(url, {
|
||||||
|
method: method,
|
||||||
|
headers: headers,
|
||||||
|
body: JSON.stringify(params)
|
||||||
|
})
|
||||||
|
|
||||||
|
const data = await res.json()
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
export default request
|
||||||
@ -28,8 +28,7 @@ const robotStore = useRobotStore()
|
|||||||
|
|
||||||
const ruleFormRef = ref()
|
const ruleFormRef = ref()
|
||||||
const ruleForm = ref({
|
const ruleForm = ref({
|
||||||
ip: '',
|
ip: ''
|
||||||
port: 80
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const rules = ref({
|
const rules = ref({
|
||||||
@ -39,21 +38,29 @@ const rules = ref({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const testConnect = async () => {
|
const testConnect = async () => {
|
||||||
const valid = await ruleFormRef.value.validate();
|
robotStore.setIp(ruleForm.value.ip)
|
||||||
if (valid) {
|
// const valid = await ruleFormRef.value.validate();
|
||||||
const result = await getCurrentMapName({
|
// if (valid) {
|
||||||
ip: robotStore.ip,
|
// const result = await getCurrentMapName({
|
||||||
deviceId: VITE_DEVICE_ID,
|
// ip: robotStore.ip,
|
||||||
})
|
// deviceId: VITE_AGV_DEVICE_ID,
|
||||||
if (result.code === 200) {
|
// })
|
||||||
router.push({
|
// if (result.code === 200) {
|
||||||
path: '/home',
|
// router.push({
|
||||||
query: {
|
// path: '/home',
|
||||||
mapName: result.data.current_map
|
// query: {
|
||||||
}
|
// mapName: result.data.current_map
|
||||||
})
|
// }
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
router.push({
|
||||||
|
path: '/home',
|
||||||
|
query: {
|
||||||
|
mapName: 'test'
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -42,15 +42,27 @@
|
|||||||
<div class="title">关节控制</div>
|
<div class="title">关节控制</div>
|
||||||
<div v-for="joint in jointControls" :key="joint.name" class="joint-item">
|
<div v-for="joint in jointControls" :key="joint.name" class="joint-item">
|
||||||
<label :for="joint.name">{{ joint.name }}</label>
|
<label :for="joint.name">{{ joint.name }}</label>
|
||||||
<el-slider v-model="joint.value" :min="joint.min" :max="joint.max" :step="0.01" @input="updateJoint(joint.name, joint.value)"/>
|
<el-input v-model="joint.value" readonly>
|
||||||
<span class="angle">{{ (joint.value * 180 / Math.PI).toFixed(1) }}°</span>
|
<template #prepend>
|
||||||
|
<el-button @mousedown="updateJoint(joint.name, '-')" @mouseup="stop"
|
||||||
|
@touchstart.prevent="updateJoint(joint.name, '-')" @touchend.prevent="stop">
|
||||||
|
-
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
<template #append>
|
||||||
|
<el-button @mousedown="updateJoint(joint.name, '+')" @mouseup="stop"
|
||||||
|
@touchstart.prevent="updateJoint(joint.name, '+')" @touchend.prevent="stop">
|
||||||
|
+
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="speed-container">
|
<div class="speed-container">
|
||||||
<div class="label">速度:</div>
|
<div class="label">速度:</div>
|
||||||
<el-slider v-model="speed" :min="1" :max="100" />
|
<el-slider v-model="jointSpeed" :min="0.1" :step="0.1" :max="9" />
|
||||||
<div class="proportion">{{ speed }} %</div>
|
<div class="proportion">{{ (jointSpeed / 9 * 100).toFixed(0) }} %</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -76,11 +88,17 @@
|
|||||||
<div class="axis-slider-wrap">
|
<div class="axis-slider-wrap">
|
||||||
<div class="axis-name">前后平移</div>
|
<div class="axis-name">前后平移</div>
|
||||||
<div>
|
<div>
|
||||||
<el-button type="primary" circle>
|
<el-button type="primary" circle @mousedown="move('x', '-', 'end')" @mouseup="stop"
|
||||||
<el-icon size="24"><Remove /></el-icon>
|
@touchstart.prevent="move('x', '-')" @touchend.prevent="stop">
|
||||||
|
<el-icon size="24">
|
||||||
|
<Remove />
|
||||||
|
</el-icon>
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button type="primary" circle >
|
<el-button type="primary" circle @mousedown="move('x', '+', 'end')" @mouseup="stop"
|
||||||
<el-icon size="24"><CirclePlus /></el-icon>
|
@touchstart.prevent="move('x', '+')" @touchend.prevent="stop">
|
||||||
|
<el-icon size="24">
|
||||||
|
<CirclePlus />
|
||||||
|
</el-icon>
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -92,11 +110,17 @@
|
|||||||
<div class="axis-slider-wrap">
|
<div class="axis-slider-wrap">
|
||||||
<div class="axis-name">左右平移</div>
|
<div class="axis-name">左右平移</div>
|
||||||
<div>
|
<div>
|
||||||
<el-button type="primary" circle>
|
<el-button type="primary" circle @mousedown="move('y', '-', 'end')" @mouseup="stop"
|
||||||
<el-icon size="24"><Remove /></el-icon>
|
@touchstart.prevent="move('y', '-')" @touchend.prevent="stop">
|
||||||
|
<el-icon size="24">
|
||||||
|
<Remove />
|
||||||
|
</el-icon>
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button type="primary" circle >
|
<el-button type="primary" circle @mousedown="move('y', '+', 'end')" @mouseup="stop"
|
||||||
<el-icon size="24"><CirclePlus /></el-icon>
|
@touchstart.prevent="move('y', '+')" @touchend.prevent="stop">
|
||||||
|
<el-icon size="24">
|
||||||
|
<CirclePlus />
|
||||||
|
</el-icon>
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -108,16 +132,27 @@
|
|||||||
<div class="axis-slider-wrap">
|
<div class="axis-slider-wrap">
|
||||||
<div class="axis-name">上下平移</div>
|
<div class="axis-name">上下平移</div>
|
||||||
<div>
|
<div>
|
||||||
<el-button type="primary" circle>
|
<el-button type="primary" circle @mousedown="move('z', '-', 'end')" @mouseup="stop"
|
||||||
<el-icon size="24"><Remove /></el-icon>
|
@touchstart.prevent="move('z', '-')" @touchend.prevent="stop">
|
||||||
|
<el-icon size="24">
|
||||||
|
<Remove />
|
||||||
|
</el-icon>
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button type="primary" circle >
|
<el-button type="primary" circle @mousedown="move('z', '+', 'end')" @mouseup="stop"
|
||||||
<el-icon size="24"><CirclePlus /></el-icon>
|
@touchstart.prevent="move('z', '+')" @touchend.prevent="stop">
|
||||||
|
<el-icon size="24">
|
||||||
|
<CirclePlus />
|
||||||
|
</el-icon>
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="speed-container">
|
||||||
|
<div class="label">速度:</div>
|
||||||
|
<el-slider v-model="endSpeed" :min="0.1" :step="0.1" :max="4" />
|
||||||
|
<div class="proportion">{{ (endSpeed / 4 * 100).toFixed(0) }} %</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="controller">
|
<div class="controller">
|
||||||
@ -129,11 +164,17 @@
|
|||||||
<div class="axis-slider-wrap">
|
<div class="axis-slider-wrap">
|
||||||
<div class="axis-name">前后平移</div>
|
<div class="axis-name">前后平移</div>
|
||||||
<div>
|
<div>
|
||||||
<el-button type="primary" circle>
|
<el-button type="primary" circle @mousedown="move('x', '-', 'posture')" @mouseup="stop"
|
||||||
<el-icon size="24"><Remove /></el-icon>
|
@touchstart.prevent="move('x', '-')" @touchend.prevent="stop">
|
||||||
|
<el-icon size="24">
|
||||||
|
<Remove />
|
||||||
|
</el-icon>
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button type="primary" circle >
|
<el-button type="primary" circle @mousedown="move('x', '+', 'posture')" @mouseup="stop"
|
||||||
<el-icon size="24"><CirclePlus /></el-icon>
|
@touchstart.prevent="move('x', '+')" @touchend.prevent="stop">
|
||||||
|
<el-icon size="24">
|
||||||
|
<CirclePlus />
|
||||||
|
</el-icon>
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -145,11 +186,17 @@
|
|||||||
<div class="axis-slider-wrap">
|
<div class="axis-slider-wrap">
|
||||||
<div class="axis-name">左右平移</div>
|
<div class="axis-name">左右平移</div>
|
||||||
<div>
|
<div>
|
||||||
<el-button type="primary" circle>
|
<el-button type="primary" circle @mousedown="move('y', '-', 'posture')" @mouseup="stop"
|
||||||
<el-icon size="24"><Remove /></el-icon>
|
@touchstart.prevent="move('y', '-')" @touchend.prevent="stop">
|
||||||
|
<el-icon size="24">
|
||||||
|
<Remove />
|
||||||
|
</el-icon>
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button type="primary" circle >
|
<el-button type="primary" circle @mousedown="move('y', '+', 'posture')" @mouseup="stop"
|
||||||
<el-icon size="24"><CirclePlus /></el-icon>
|
@touchstart.prevent="move('y', '+')" @touchend.prevent="stop">
|
||||||
|
<el-icon size="24">
|
||||||
|
<CirclePlus />
|
||||||
|
</el-icon>
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -161,16 +208,27 @@
|
|||||||
<div class="axis-slider-wrap">
|
<div class="axis-slider-wrap">
|
||||||
<div class="axis-name">上下平移</div>
|
<div class="axis-name">上下平移</div>
|
||||||
<div>
|
<div>
|
||||||
<el-button type="primary" circle>
|
<el-button type="primary" circle @mousedown="move('z', '-', 'posture')" @mouseup="stop"
|
||||||
<el-icon size="24"><Remove /></el-icon>
|
@touchstart.prevent="move('z', '-')" @touchend.prevent="stop">
|
||||||
|
<el-icon size="24">
|
||||||
|
<Remove />
|
||||||
|
</el-icon>
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button type="primary" circle >
|
<el-button type="primary" circle @mousedown="move('z', '+', 'posture')" @mouseup="stop"
|
||||||
<el-icon size="24"><CirclePlus /></el-icon>
|
@touchstart.prevent="move('z', '+')" @touchend.prevent="stop">
|
||||||
|
<el-icon size="24">
|
||||||
|
<CirclePlus />
|
||||||
|
</el-icon>
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="speed-container">
|
||||||
|
<div class="label">速度:</div>
|
||||||
|
<el-slider v-model="postureSpeed" :min="0.1" :step="0.1" :max="4" />
|
||||||
|
<div class="proportion">{{ (postureSpeed / 4 * 100).toFixed(0) }} %</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -178,9 +236,14 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { nextTick, onMounted, ref } from 'vue'
|
import { nextTick, onMounted, onUnmounted, ref } from 'vue'
|
||||||
import UrdfView from './UrdfView.vue'
|
import UrdfView from './UrdfView.vue'
|
||||||
|
import { getPoseApi, getJointStateApi, moveJApi, torqueOnApi, speedJApi, stopMotionApi, speedLApi } from '@/api/arm'
|
||||||
import { Remove, CirclePlus } from '@element-plus/icons-vue'
|
import { Remove, CirclePlus } from '@element-plus/icons-vue'
|
||||||
|
import { useRobotStore } from '@/stores/robot'
|
||||||
|
|
||||||
|
const robotStore = useRobotStore()
|
||||||
|
const deviceId = import.meta.env.VITE_ARM_DEVICE_ID
|
||||||
|
|
||||||
const urdfViewerRef = ref()
|
const urdfViewerRef = ref()
|
||||||
const jointControls = ref()
|
const jointControls = ref()
|
||||||
@ -195,18 +258,166 @@ const tcp = ref({
|
|||||||
rz: -72.937
|
rz: -72.937
|
||||||
})
|
})
|
||||||
|
|
||||||
// 速度
|
const getPose = async () => {
|
||||||
const speed = ref(56)
|
const res = await getPoseApi({
|
||||||
|
ip: robotStore.ip,
|
||||||
const updateJoint = (jointName, angle) => {
|
deviceId: deviceId
|
||||||
urdfViewerRef.value.updateJoint(jointName, angle)
|
})
|
||||||
|
if (res.code === 200) {
|
||||||
|
const { x, y, z, rx, ry, rz } = res.data
|
||||||
|
tcp.value = { x, y, z, rx, ry, rz }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const jointNamsMap = {
|
||||||
|
'joint_1': 'elfin_joint1',
|
||||||
|
'joint_2': 'elfin_joint2',
|
||||||
|
'joint_3': 'elfin_joint3',
|
||||||
|
'joint_4': 'elfin_joint4',
|
||||||
|
'joint_5': 'elfin_joint5',
|
||||||
|
'joint_6': 'elfin_joint6',
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取关节状态
|
||||||
|
* @param robot
|
||||||
|
*/
|
||||||
|
const getJointState = async (robot) => {
|
||||||
|
try {
|
||||||
|
const res = await getJointStateApi({
|
||||||
|
deviceId: deviceId,
|
||||||
|
ip: robotStore.ip,
|
||||||
|
})
|
||||||
|
if (res.code === 200) {
|
||||||
|
setTimeout(() => {
|
||||||
|
const names = res.data.name
|
||||||
|
const positions = res.data.position
|
||||||
|
names.forEach((name, index) => {
|
||||||
|
urdfViewerRef.value.updateJoint(jointNamsMap[name] || name, positions[index])
|
||||||
|
})
|
||||||
|
}, 500)
|
||||||
|
} else {
|
||||||
|
console.error('获取关节状态失败:', res.message)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取关节状态失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 速度
|
||||||
|
const jointSpeed = ref(0.8)
|
||||||
|
const updateJoint = async (jointName, _dir) => {
|
||||||
|
const velocities = jointControls.value.map(joint => {
|
||||||
|
return 0
|
||||||
|
})
|
||||||
|
const index = jointControls.value.findIndex(joint => joint.name === jointName)
|
||||||
|
velocities[index] = jointSpeed.value * (_dir === '+' ? 1 : -1)
|
||||||
|
const res = await speedJApi({
|
||||||
|
deviceId: deviceId,
|
||||||
|
ip: robotStore.ip,
|
||||||
|
acceleration: jointSpeed.value * (_dir === '+' ? 1 : -1) + 0.1,
|
||||||
|
duration: 60,
|
||||||
|
velocities: velocities
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const postureSpeed = ref(0.8)
|
||||||
|
const endSpeed = ref(0.8)
|
||||||
|
|
||||||
|
const speedData = reactive({
|
||||||
|
vx: 0,
|
||||||
|
vy: 0,
|
||||||
|
vz: 0,
|
||||||
|
wx: 0,
|
||||||
|
wy: 0,
|
||||||
|
wz: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 机械臂移动
|
||||||
|
* @param axis 坐标轴
|
||||||
|
* @param direction 方向
|
||||||
|
*/
|
||||||
|
const move = async (axis, direction, type) => {
|
||||||
|
speedData.vx = 0
|
||||||
|
speedData.vy = 0
|
||||||
|
speedData.vz = 0
|
||||||
|
speedData.wx = 0
|
||||||
|
speedData.wy = 0
|
||||||
|
speedData.wz = 0
|
||||||
|
if (type === 'end') {
|
||||||
|
// 末端移动
|
||||||
|
const speedValue = endSpeed.value
|
||||||
|
switch (axis) {
|
||||||
|
case 'x':
|
||||||
|
speedData.vx = direction === '+' ? speedValue : -speedValue
|
||||||
|
break
|
||||||
|
case 'y':
|
||||||
|
speedData.vy = direction === '+' ? speedValue : -speedValue
|
||||||
|
break
|
||||||
|
case 'z':
|
||||||
|
speedData.vz = direction === '+' ? speedValue : -speedValue
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else if (type === 'posture') {
|
||||||
|
// 关节移动
|
||||||
|
const jointSpeed = postureSpeed.value
|
||||||
|
switch (axis) {
|
||||||
|
case 'x':
|
||||||
|
speedData.wx = direction === '+' ? jointSpeed : -jointSpeed
|
||||||
|
break
|
||||||
|
case 'y':
|
||||||
|
speedData.wy = direction === '+' ? jointSpeed : -jointSpeed
|
||||||
|
break
|
||||||
|
case 'z':
|
||||||
|
speedData.wz = direction === '+' ? jointSpeed : -jointSpeed
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
speedLApi({
|
||||||
|
deviceId: deviceId,
|
||||||
|
ip: robotStore.ip,
|
||||||
|
duration: 60,
|
||||||
|
acceleration: type === 'posture' ? postureSpeed.value + 0.1 : endSpeed.value + 0.1,
|
||||||
|
velocity: {
|
||||||
|
...speedData
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 停止机械臂移动
|
||||||
|
*/
|
||||||
|
const stop = async () => {
|
||||||
|
await stopMotionApi({
|
||||||
|
deviceId: deviceId,
|
||||||
|
ip: robotStore.ip
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const timer = ref(null)
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
jointControls.value = urdfViewerRef.value.jointControls
|
jointControls.value = urdfViewerRef.value.jointControls
|
||||||
|
stop()
|
||||||
|
getPose()
|
||||||
|
if (timer.value) {
|
||||||
|
clearInterval(timer.value)
|
||||||
|
}
|
||||||
|
timer.value = setInterval(() => {
|
||||||
|
getJointState()
|
||||||
|
}, 100)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (timer.value) {
|
||||||
|
clearInterval(timer.value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@ -290,17 +501,7 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.speed-container {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
|
|
||||||
.label,
|
|
||||||
.proportion {
|
|
||||||
width: 100px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 中间3D区域
|
// 中间3D区域
|
||||||
@ -389,6 +590,18 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.speed-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.label,
|
||||||
|
.proportion {
|
||||||
|
width: 100px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -141,7 +141,7 @@ const props = defineProps({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const VITE_DEVICE_ID = import.meta.env.VITE_DEVICE_ID
|
const VITE_AGV_DEVICE_ID = import.meta.env.VITE_AGV_DEVICE_ID
|
||||||
|
|
||||||
const robotStore = useRobotStore()
|
const robotStore = useRobotStore()
|
||||||
|
|
||||||
@ -186,7 +186,7 @@ const onPress = async (_dir) => {
|
|||||||
moveRobotData.value.w = rotationAngle.value
|
moveRobotData.value.w = rotationAngle.value
|
||||||
const res = await moveRobot({
|
const res = await moveRobot({
|
||||||
ip: robotStore.ip,
|
ip: robotStore.ip,
|
||||||
deviceId: VITE_DEVICE_ID,
|
deviceId: VITE_AGV_DEVICE_ID,
|
||||||
...moveRobotData.value
|
...moveRobotData.value
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -196,7 +196,7 @@ function onRelease() {
|
|||||||
moveRobotData.value.vy = 0;
|
moveRobotData.value.vy = 0;
|
||||||
stopRobot({
|
stopRobot({
|
||||||
ip: robotStore.ip,
|
ip: robotStore.ip,
|
||||||
deviceId: VITE_DEVICE_ID
|
deviceId: VITE_AGV_DEVICE_ID
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -40,7 +40,7 @@ const props = defineProps({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const VITE_DEVICE_ID = import.meta.env.VITE_DEVICE_ID
|
const VITE_AGV_DEVICE_ID = import.meta.env.VITE_AGV_DEVICE_ID
|
||||||
|
|
||||||
const robotStore = useRobotStore()
|
const robotStore = useRobotStore()
|
||||||
|
|
||||||
@ -380,7 +380,7 @@ const centerCanvasView = () => {
|
|||||||
const loadSourceMap = async (mapName) => {
|
const loadSourceMap = async (mapName) => {
|
||||||
const res = await getCurrentMap({
|
const res = await getCurrentMap({
|
||||||
ip: robotStore.ip,
|
ip: robotStore.ip,
|
||||||
deviceId: VITE_DEVICE_ID,
|
deviceId: VITE_AGV_DEVICE_ID,
|
||||||
mapName: mapName
|
mapName: mapName
|
||||||
})
|
})
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
@ -419,7 +419,7 @@ const moveRobot = (robot, dx, dy, dtheta = 0) => {
|
|||||||
const getRobot = async () => {
|
const getRobot = async () => {
|
||||||
const res = await getRobotState({
|
const res = await getRobotState({
|
||||||
ip: robotStore.ip,
|
ip: robotStore.ip,
|
||||||
deviceId: VITE_DEVICE_ID,
|
deviceId: VITE_AGV_DEVICE_ID,
|
||||||
})
|
})
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
robotStore.setPosition(res.data.x, res.data.y, res.data.angle)
|
robotStore.setPosition(res.data.x, res.data.y, res.data.angle)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user