feat: 机械臂

This commit is contained in:
zhanghao 2026-07-10 13:44:07 +08:00
parent 98031a0dcc
commit 0e12299816
11 changed files with 539 additions and 995 deletions

2
.env
View File

@ -1,4 +1,4 @@
# VITE_AGV_DEVICE_ID = 'agv_src2200'
# VITE_AGV_DEVICE_ID = 'src1100'
# VITE_ARM_DEVICE_ID = 'aubo_arm'
# 机械臂IP 192.168.1.109:50052

View File

@ -11,8 +11,8 @@ app.use(express.static(path.join(__dirname, '../dist')));
app.use(cors())
app.use(express.json())
// 获取机器人当前地图名称
app.post('/api/agv/getCurrentMapName', async (req, res) => {
// 获取agv运行时状态
app.post('/api/agv/getRuntimeState', async (req, res) => {
const request = {
header: {
device_id: req.body.deviceId || 'agv_src1100'
@ -22,7 +22,7 @@ app.post('/api/agv/getCurrentMapName', async (req, res) => {
const grpcClient = createAGVGrpcClient(req.body.ip)
const resp = await new Promise((resolve, reject) => {
grpcClient.getMapStatus(request, (err, res) => {
grpcClient.getRuntimeState(request, (err, res) => {
if (err) {
resolve({
code: 500,
@ -31,7 +31,7 @@ app.post('/api/agv/getCurrentMapName', async (req, res) => {
} else {
resolve({
code: 200,
data: res.status
data: res.state
});
}
});
@ -44,20 +44,18 @@ app.post('/api/agv/getCurrentMapName', async (req, res) => {
}
})
// 获取机器人当前地图名称
// 获取机器人当前地图
app.post('/api/agv/getCurrentMap', async (req, res) => {
const grpcClient = createAGVGrpcClient(req.body.ip)
const request = {
header: {
device_id: req.body.deviceId || 'agv_src1100'
},
data: {
map_name: req.body.mapName
}
map_name: req.body.mapName
};
const resp = await new Promise((resolve, reject) => {
grpcClient.RobotConfigDownloadMap(request, (err, res) => {
grpcClient.downloadMap(request, (err, res) => {
if (err) {
resolve({
code: 500,
@ -66,7 +64,7 @@ app.post('/api/agv/getCurrentMap', async (req, res) => {
} else {
resolve({
code: 200,
data: res.status
data: res.content
});
}
});
@ -79,83 +77,22 @@ app.post('/api/agv/getCurrentMap', async (req, res) => {
}
})
// 获取机器人状态
app.post('/api/agv/getRobotState', async (req, res) => {
const request = {
header: {
device_id: req.body.deviceId || 'agv_src1100'
}
};
const grpcClient = createAGVGrpcClient(req.body.ip)
const response = await new Promise((resolve, reject) => {
grpcClient.GetRobotLocation(request, (err, res) => {
if (err) {
resolve({
code: 500,
data: `grpc服务端错误`
});
} else {
resolve({
code: 200,
data: res.status
});
}
})
})
const resp = await new Promise((resolve, reject) => {
grpcClient.GetBatteryStatus(request, (err, res) => {
if (err) {
resolve({
code: 500,
data: `grpc服务端错误`
});
} else {
resolve({
code: 200,
data: res.status
});
}
});
})
if (response.code === 200 && resp.code ===200) {
res.json({
code: 200,
data: {
x: response.data.x,
y: response.data.y,
angle: response.data.angle,
confidence: response.data.confidence,
batteryLevel: resp.data.battery_level,
batteryTemp: resp.data.battery_temp,
charging: resp.data.charging,
voltage: resp.data.voltage
}
})
} else {
res.status(500).json({ error: 'grpc服务端错误' })
}
})
// 机器人运动控制
app.post('/api/agv/moveRobot', async (req, res) => {
const grpcClient = createAGVGrpcClient(req.body.ip)
const grpcClient = setVelocity(req.body.ip)
const request = {
header: {
device_id: req.body.deviceId || 'agv_src1100'
},
data: {
velocity: {
vx: req.body.vx,
vy: req.body.vy,
w: req.body.w,
duration: req.body.duration
vz: req.body.vz
}
};
const resp = await new Promise((resolve, reject) => {
grpcClient.RobotMotionControl(request, (err, res) => {
grpcClient.setVelocity(request, (err, res) => {
if (err) {
resolve({
code: 500,
@ -190,7 +127,7 @@ app.post('/api/agv/stopRobot', async (req, res) => {
};
const resp = await new Promise((resolve, reject) => {
grpcClient.RobotControlStop(request, (err, res) => {
grpcClient.stopVelocityControl(request, (err, res) => {
if (err) {
resolve({
code: 500,
@ -215,6 +152,9 @@ app.post('/api/agv/stopRobot', async (req, res) => {
}
})
// ==================================================
// 获取机械臂末端位姿
app.post('/api/arm/getPose', async (req, res) => {
const request = {

File diff suppressed because it is too large Load Diff

View File

@ -1,74 +1,72 @@
/**
* @file agv_service.proto
* @brief 定义AGV服务的gRPC接口,聚合所有AGV相关命令。
* 服务端实现类为 gRPCAGVServiceImpl。
*/
syntax = "proto3";
import "cmvr/api/agv_command.proto";
package cmvr.api;
import "cmvr/api/common.proto";
import "cmvr/api/agv_command.proto";
// AGV 通用服务。该服务只暴露控制器无关的能力,
// 厂商协议、地图文件格式和控制器特有参数由具体 AGV 适配器内部处理。
service AgvService {
// 基本控制
rpc GetStatusInfo(GetAgvStatusInfoCommand.Request) returns (GetAgvStatusInfoCommand.Feedback);
// 获取 AGV 当前运行状态快照。
rpc getRuntimeState(AgvRuntimeStateCommand.Request) returns (AgvRuntimeStateCommand.Feedback);
rpc GetBatteryStatus(RobotStatusBatteryCommand.Request) returns (RobotStatusBatteryCommand.Feedback);
// 获取当前导航任务状态。
rpc getNavigationStatus(AgvNavigationStatusCommand.Request) returns (AgvNavigationStatusCommand.Feedback);
rpc GetRobotLocation(RobotStatusLocCommand.Request) returns (RobotStatusLocCommand.Feedback);
// 执行急停或等效安全停止动作。
rpc emergencyStop(CommandHeader.Request) returns (CommandHeader.Feedback);
rpc RobotConfigDownloadMap(RobotConfigDownloadMapCommand.Request) returns (RobotConfigDownloadMapCommand.Feedback);
// 清除可恢复故障或告警。
rpc clearFault(CommandHeader.Request) returns (CommandHeader.Feedback);
// 导航到指定地图位姿。目标位姿 x/y 单位为米,theta 单位为弧度。
rpc navigateToPose(AgvNavigateToPoseCommand.Request) returns (AgvNavigateToPoseCommand.Feedback);
rpc GetMapStatus(RobotStatusMapCommand.Request) returns (RobotStatusMapCommand.Feedback);
// 导航到指定地图站点。
rpc navigateToStation(AgvNavigateToStationCommand.Request) returns (AgvNavigateToStationCommand.Feedback);
// 按显式站点路径执行导航。
rpc followPath(AgvFollowPathCommand.Request) returns (AgvFollowPathCommand.Feedback);
rpc RobotConfigUploadMap(RobotConfigUploadMapCommand.Request) returns (RobotConfigUploadMapCommand.Feedback);
// 暂停当前导航任务。
rpc pauseNavigation(CommandHeader.Request) returns (CommandHeader.Feedback);
// 抢占机器人控制权
rpc RobotConfigLock(RobotConfigLockCommand.Request) returns (RobotConfigLockCommand.Feedback);
// 恢复已暂停的导航任务。
rpc resumeNavigation(CommandHeader.Request) returns (CommandHeader.Feedback);
// 查询当前控制权所有者
rpc GetCurrentLockStatus(RobotStatusCurrentLockCommand.Request) returns (RobotStatusCurrentLockCommand.Feedback);
// 取消当前导航任务。
rpc cancelNavigation(CommandHeader.Request) returns (CommandHeader.Feedback);
// 开环速度运动控制
rpc RobotMotionControl(RobotMotionControlCommand.Request) returns (RobotMotionControlCommand.Feedback);
// 下发底盘速度控制指令。vx/vy 单位为米/秒,wz 单位为弧度/秒。
rpc setVelocity(AgvSetVelocityCommand.Request) returns (AgvSetVelocityCommand.Feedback);
// 切换载入地图 命令码2022
rpc RobotLoadMap(RobotLoadMapCommand.Request) returns (RobotLoadMapCommand.Feedback);
// 停止底盘速度控制。该接口不等价于取消导航任务。
rpc stopVelocityControl(CommandHeader.Request) returns (CommandHeader.Feedback);
// 查询 AGV 控制器可用地图名称列表。
rpc listMaps(AgvListMapsCommand.Request) returns (AgvListMapsCommand.Feedback);
// 查询地图载入状态 命令码1022
rpc QueryLoadMapStatus(RobotQueryLoadMapStatusCommand.Request) returns (RobotQueryLoadMapStatusCommand.Feedback);
// 查询当前地图中的站点列表。
rpc listStations(AgvListStationsCommand.Request) returns (AgvListStationsCommand.Feedback);
// 切换当前使用地图。
rpc switchMap(AgvMapCommand.Request) returns (AgvMapCommand.Feedback);
// 查询当前地图站点列表 命令码1301
rpc QueryStationList(QueryStationListCommand.Request) returns (QueryStationListCommand.Feedback);
// 上传地图内容到 AGV 控制器。地图内容字段由适配器解释。
rpc uploadMap(AgvMapCommand.Request) returns (AgvMapCommand.Feedback);
// 下载指定地图内容。
rpc downloadMap(AgvMapCommand.Request) returns (AgvMapCommand.Feedback);
// 指定路径导航 命令码3066
rpc RobotGoTargetList(RobotGoTargetListCommand.Request) returns (RobotGoTargetListCommand.Feedback);
// 开始建图/扫图会话。请求只选择 2D、3D 或二者都要,
// 控制器特有地图格式由 AGV 适配器内部转换。
rpc startMapping(AgvStartMappingCommand.Request) returns (AgvStartMappingCommand.Feedback);
// 查询当前导航状态 1020 robot_status_task_req
// 1020 查询当前实时导航 robot_status_task_req
rpc RobotStatusTaskCurrent(RobotStatusTaskCurrentCommand.Request) returns (RobotStatusTaskCurrentCommand.Feedback);
// 以服务端流方式发送统一地图。resume_token 为空时应先发送全量快照;
// 后续是否发送增量由 AGV 适配器能力决定,并通过 update_type 标记。
rpc streamMap(AgvMapStreamCommand.Request) returns (stream AgvMapStreamCommand.Feedback);
// 批量查询任务状态 1110 robot_status_task_status_package_req
rpc RobotStatusTaskPackage(RobotStatusTaskPackageCommand.Request) returns (RobotStatusTaskPackageCommand.Feedback);
// 3051 单点站点自动规划导航 robot_task_gotarget_req 0x0BEB
rpc RobotGoTarget(RobotGoTargetCommand.Request) returns (RobotGoTargetCommand.Feedback);
// 0x07D0 停止开环运动
rpc RobotControlStop(RobotControlStopCommand.Request) returns (RobotControlStopCommand.Feedback);
// 暂停当前导航任务 命令码 3001 (0x0BB9)
rpc RobotTaskPause(RobotTaskPauseCommand.Request) returns (RobotTaskPauseCommand.Feedback);
// 继续当前导航任务 命令码 3002 (0x0BBA)
rpc RobotTaskResume(RobotTaskResumeCommand.Request) returns (RobotTaskResumeCommand.Feedback);
// 取消当前导航任务 命令码 3003 (0x0BBB)
rpc RobotTaskCancel(RobotTaskCancelCommand.Request) returns (RobotTaskCancelCommand.Feedback);
}
// 停止当前建图/扫图会话。
rpc stopMapping(CommandHeader.Request) returns (CommandHeader.Feedback);
}

View File

@ -0,0 +1,310 @@
syntax = "proto3";
package cmvr.msgs;
// AGV 在地图平面坐标系中的二维位姿。
message AgvPose2d {
// X 坐标,单位:米。
double x = 1;
// Y 坐标,单位:米。
double y = 2;
// 航向角,单位:弧度,逆时针为正。
double theta = 3;
}
// AGV 车体坐标系下的平面速度。
message AgvVelocity {
// 车体 X 方向线速度,单位:米/秒。
double vx = 1;
// 车体 Y 方向线速度,单位:米/秒。
double vy = 2;
// 绕 Z 轴角速度,单位:弧度/秒。
double wz = 3;
}
// AGV 电池状态。
message AgvBatteryState {
// 电量比例,范围:[0, 1],例如 0.8 表示 80%。
double percentage = 1;
// 电池电压,单位:伏特。
double voltage = 2;
// 电池电流,单位:安培;正负号含义由具体 AGV 适配器保持一致。
double current = 3;
// 电池温度,单位:摄氏度。
double temperature = 4;
// 是否正在充电。
bool charging = 5;
}
// 导航任务的通用运动约束和执行选项。
message AgvMotionOptions {
// 最大线速度,单位:米/秒;0 表示使用 AGV 默认值。
double max_speed = 1;
// 最大角速度,单位:弧度/秒;0 表示使用 AGV 默认值。
double max_angular_speed = 2;
// 最大线加速度,单位:米/秒^2;0 表示使用 AGV 默认值。
double max_acceleration = 3;
// 最大角加速度,单位:弧度/秒^2;0 表示使用 AGV 默认值。
double max_angular_acceleration = 4;
// 到达目标点的距离容差,单位:米;0 表示使用 AGV 默认值。
double reach_distance = 5;
// 到达目标角度的角度容差,单位:弧度;0 表示使用 AGV 默认值。
double reach_angle = 6;
// 速度比例,范围通常为 [0, 1];1 表示不降速。
double speed_ratio = 7;
// 是否异步执行;true 表示下发任务后立即返回。
bool asynchronous = 8;
}
// AGV 适配器扩展参数。用于传递厂商或控制器特有的参数。
message AgvAdapterParams {
// 参数键值表。键和值都使用字符串,具体含义由 AGV 适配器解释。
map<string, string> values = 1;
}
// AGV 当前运行状态快照。
message AgvRuntimeState {
// 状态采样时间,Unix 时间戳,单位:秒。
double timestamp = 1;
// 运行模式,取值对应服务端内部 AgvMode 枚举的整数值。
int32 mode = 2;
// 是否已连接 AGV 控制器。
bool connected = 3;
// 是否已定位成功。
bool localized = 4;
// AGV 是否处于运动状态。
bool moving = 5;
// AGV 是否处于故障状态。
bool fault = 6;
// AGV 是否处于急停状态。
bool emergency_stopped = 7;
// 当前地图坐标系下的二维位姿。
AgvPose2d pose = 8;
// 当前车体速度。
AgvVelocity velocity = 9;
// 当前电池状态。
AgvBatteryState battery = 10;
// 当前加载地图名称;为空表示未知或控制器未返回。
string current_map = 11;
// 当前或最近站点 id;为空表示未知或当前不在站点附近。
string current_station = 12;
// 最近一次错误信息;为空表示无错误或未知。
string last_error = 13;
}
// 地图中的站点信息。
message AgvStation {
// 站点唯一 id。
string id = 1;
// 站点类型;具体枚举由地图或控制器定义。
string type = 2;
// 站点在地图坐标系下的二维位姿。
AgvPose2d pose = 3;
// 站点描述或备注。
string description = 4;
}
// 显式站点路径中的一段路径。
message AgvPathSegment {
// 起点站点 id。
string source_station = 1;
// 目标站点 id。
string target_station = 2;
}
// 地图维度请求。这里只描述上位机想要 2D、3D 还是二者都要,
// 不用于指定厂商文件格式;厂商格式必须在 AGV 适配器内部转换。
enum AgvMapDimension {
// 未指定。服务端应选择最有用的默认地图,通常是当前加载地图。
AGV_MAP_DIMENSION_UNSPECIFIED = 0;
// 只请求统一 2D 地图。
AGV_MAP_2D = 1;
// 只请求统一 3D 地图。
AGV_MAP_3D = 2;
// 在同一个流中请求统一 2D 和统一 3D 地图。
AGV_MAP_2D_AND_3D = 3;
}
// 地图流更新类型。上位机应根据该字段判断是全量、增量还是缓存重置。
enum AgvMapUpdateType {
// 未指定。
AGV_MAP_UPDATE_UNSPECIFIED = 0;
// 全量地图快照。首次请求或无法增量续传时应发送该类型。
AGV_MAP_UPDATE_SNAPSHOT = 1;
// 全量快照之后的增量更新。仅当适配器能够可靠生成差异时发送。
AGV_MAP_UPDATE_INCREMENTAL = 2;
// 上位机本地缓存已失效,应丢弃缓存并等待后续全量快照。
AGV_MAP_UPDATE_RESET = 3;
}
// 2D/3D 地图共用的语义对象类型。
enum AgvMapObjectType {
// 未指定。
AGV_MAP_OBJECT_UNSPECIFIED = 0;
// 导航站点或路径点。
AGV_MAP_OBJECT_STATION = 1;
// 路径线、禁行线、引导线等线对象。
AGV_MAP_OBJECT_LINE = 2;
// 多边形区域,例如禁行区、限速区、作业区。
AGV_MAP_OBJECT_AREA = 3;
// 二维码、天码或其他标签地标。
AGV_MAP_OBJECT_QR_TAG = 4;
// 反光板或反光柱地标。
AGV_MAP_OBJECT_REFLECTOR = 5;
// 库位、货位或储位。
AGV_MAP_OBJECT_BIN_LOCATION = 6;
// 门、电梯、充电桩等外部设备。
AGV_MAP_OBJECT_EXTERNAL_DEVICE = 7;
}
// 地图坐标系下的三维点。单位:米。
message AgvMapPoint3D {
// X 坐标,单位:米。
double x = 1;
// Y 坐标,单位:米。
double y = 2;
// Z 坐标,单位:米;纯 2D 几何可置为 0。
double z = 3;
}
// 统一 2D 地图。栅格数据按行优先排列:index = y * width + x。
message AgvUnifiedMap2D {
// 坐标系名称,例如 "map"。
string frame_id = 1;
// 地图采样或更新时间,Unix 时间戳,单位:秒。
double timestamp = 2;
// 栅格分辨率,单位:米/格。
double resolution = 3;
// 栅格宽度,单位:格。
uint32 width = 4;
// 栅格高度,单位:格。
uint32 height = 5;
// 栅格 (0, 0) 在世界/地图坐标系下的位姿;x/y 单位:米,theta 单位:弧度。
AgvPose2d origin = 6;
// 占据值:-1 表示未知,0 表示空闲,100 表示占据。
repeated int32 data = 7;
// 地图中的统一语义对象,例如站点、线、区域、标签、反光板等。
repeated AgvMapObject objects = 8;
}
// 统一 3D 点样本。坐标单位:米。
message AgvMapPointSample3D {
// X 坐标,单位:米。
double x = 1;
// Y 坐标,单位:米。
double y = 2;
// Z 坐标,单位:米。
double z = 3;
// 激光强度;无强度信息时置为 0。
float intensity = 4;
// 激光雷达线束/通道编号;无该信息时置为 0。
uint32 ring = 5;
// 相对地图时间戳的时间偏移,单位:秒;无该信息时置为 0。
double time_offset = 6;
}
// 统一 3D 体素。体素索引基于 AgvUnifiedMap3D.voxel_resolution。
message AgvMapVoxel3D {
// 体素 X 索引。
int32 x = 1;
// 体素 Y 索引。
int32 y = 2;
// 体素 Z 索引。
int32 z = 3;
// 占据概率,范围:[0, 1];未知时置为 -1。
float probability = 4;
}
// 统一 3D 平面特征。平面方程:
// normal.x * x + normal.y * y + normal.z * z + d = 0。
message AgvMapPlane3D {
// 平面中心点,单位:米。
AgvMapPoint3D center = 1;
// 平面单位法向量。
AgvMapPoint3D normal = 2;
// 平面方程偏移量,单位:米。
double d = 3;
// 平面特征近似半径,单位:米。
double radius = 4;
}
// 统一语义对象。几何点均使用地图坐标系,单位:米。
message AgvMapObject {
// 对象稳定 id 或名称。
string id = 1;
// 对象类型。
AgvMapObjectType type = 2;
// 对象几何点。点对象使用 1 个点,线对象使用多个点,区域对象使用多边形顶点。
repeated AgvMapPoint3D points = 3;
// 朝向角,单位:弧度;不适用时置为 0。
double heading = 4;
// 额外归一化属性。为兼容不同 AGV,属性值统一使用字符串。
map<string, string> properties = 5;
}
// 统一 3D 地图。虽然内部包含点、体素、平面和语义对象,
// 但对上位机来说它仍然是唯一的 3D 地图格式。
message AgvUnifiedMap3D {
// 坐标系名称,例如 "map"。
string frame_id = 1;
// 地图采样或更新时间,Unix 时间戳,单位:秒。
double timestamp = 2;
// 体素分辨率,单位:米/体素;没有体素数据时置为 0。
double voxel_resolution = 3;
// 统一点云点样本。
repeated AgvMapPointSample3D points = 4;
// 统一占据体素。
repeated AgvMapVoxel3D voxels = 5;
// 统一平面特征。
repeated AgvMapPlane3D planes = 6;
// 统一 3D 语义对象,例如站点、区域、标签、地标等。
repeated AgvMapObject objects = 7;
}
// 地图流中的单条更新。AGV 适配器必须先把厂商地图转换成 map_2d 或 map_3d,
// 再通过该消息发送给上位机。
message AgvUnifiedMapUpdate {
// 实际发送的地图 id 或名称。请求 map_name 为空时,AGV 可选择当前或最近地图。
string map_id = 1;
// 建图或地图流会话 id。
string session_id = 2;
// 会话内单调递增序号,从 0 或 1 开始均可,但同一会话内必须保持递增。
uint64 sequence = 3;
// 用于断点续传或增量订阅的不透明令牌。
string resume_token = 4;
// 本条更新的数据维度。
AgvMapDimension dimension = 5;
// 本条更新是全量、增量还是缓存重置。
AgvMapUpdateType update_type = 6;
// 坐标系名称,例如 "map"。
string frame_id = 7;
// 本条更新产生时间,Unix 时间戳,单位:秒。
double timestamp = 8;
// 是否为全量快照的第一条消息。
bool snapshot_begin = 9;
// 是否为全量快照的最后一条消息。
bool snapshot_end = 10;
// 大地图分片发送时的分片序号,从 0 开始。
uint32 chunk_index = 11;
// 大地图分片总数;0 表示未知或连续流。
uint32 chunk_count = 12;
oneof payload {
// 统一 2D 地图或 2D 地图增量。
AgvUnifiedMap2D map_2d = 20;
// 统一 3D 地图或 3D 地图增量。
AgvUnifiedMap3D map_3d = 21;
}
}
// 当前导航任务状态。
message AgvNavigationStatus {
// 导航状态,取值对应服务端内部 AgvTaskState 枚举的整数值。
int32 state = 1;
// 导航任务类型,取值对应服务端内部 AgvTaskType 枚举的整数值。
int32 type = 2;
// 当前任务进度,范围:[0, 1];未知时置为 0。
double progress = 3;
// 状态描述或错误信息。
string message = 4;
}

View File

@ -5,8 +5,8 @@ import request from '@/utils/request'
* @param {Object} params
* @param {string} [params.deviceId] - 设备ID,默认 agv_src1100
*/
export function getCurrentMapName(params) {
return request('/api/agv/getCurrentMapName', params, 'POST')
export function getRuntimeState(params) {
return request('/api/agv/getRuntimeState', params, 'POST')
}
export function getCurrentMap(params) {

View File

@ -3,12 +3,18 @@ import { defineStore } from 'pinia'
// 唯一id:robot
export const useRobotStore = defineStore('robot', {
state: () => ({
ip: '192.168.0.109:50052',
agvDeviceId:'agv_src2200',
ip: '192.168.1.110:50052',
agvDeviceId:'src1100',
almDeviceId: 'aubo_arm',
position: { x: 0, y: 0, angle: 0 },
battery: 100,
status: 'idle'
position: { x: 0, y: 0, theta: 0 },
battery: {
percentage: 0.99,
voltage: -0,
current: -0,
temperature: -0,
charging: false
},
current_map: ''
}),
getters: {
// 获取机器人坐标
@ -30,14 +36,15 @@ export const useRobotStore = defineStore('robot', {
this.almDeviceId = str
},
// 更新机器人位置
setPosition(x, y, angle) {
this.position.x = x
this.position.y = y
this.position.angle = angle
setPosition(data = {}) {
this.position = data
},
// 修改状态
setStatus(val) {
this.status = val
setBattery(data = {}) {
this.battery = data
},
setCurrentMap(str = '') {
this.current_map = str
},
// 重置仓库
resetRobot() {

View File

@ -25,7 +25,7 @@
<script setup>
import { ref } from 'vue'
import { useRouter } from 'vue-router'
import { getCurrentMapName } from '@/api/agv.js'
import { getRuntimeState } from '@/api/agv.js'
import { useRobotStore } from '@/stores/robot'
import { ElMessage } from 'element-plus'
@ -60,17 +60,17 @@ const testConnect = async () => {
robotStore.setIp(ruleForm.value.ip)
robotStore.setAgvDeviceId(ruleForm.value.agvDeviceId)
robotStore.setAlmDeviceId(ruleForm.value.almDeviceId)
const result = await getCurrentMapName({
const result = await getRuntimeState({
ip: robotStore.ip,
deviceId: ruleForm.value.agvDeviceId
})
loading.value = false
if (result.code === 200) {
robotStore.setPosition(result.data.pose)
robotStore.setBattery(result.data.battery)
robotStore.setCurrentMap(result.data.current_map)
router.push({
path: '/home',
query: {
mapName: result.data.current_map
}
path: '/home'
})
} else {
ElMessage.error(result?.message || '连接服务错误')

View File

@ -23,7 +23,7 @@
</div>
</el-header>
<el-main>
<Chassis :mapName="mapName" v-if="activePage === 'chassis'" />
<Chassis v-if="activePage === 'chassis'" />
<MechanicalArm v-if="activePage === 'mechanicalArm'" />
<Camera v-if="activePage === 'camera'" />
</el-main>
@ -34,13 +34,12 @@
import Chassis from './chassis/Chassis.vue';
import MechanicalArm from './arm/MechanicalArm.vue';
import Camera from './camera/index.vue'
import { useRoute, useRouter } from 'vue-router';
import { useRouter } from 'vue-router';
import autofit from 'autofit.js'
autofit.init()
const router = useRouter()
const route = useRoute()
const currentTime = ref('')
const weekText = ref('')
@ -79,15 +78,12 @@ const back = () => {
router.back()
}
const mapName = ref('')
let clockTimer, binaryTimer;
onMounted(() => {
updateClock();
clockTimer = setInterval(updateClock, 1000);
animateBinary()
binaryTimer = setInterval(animateBinary, 400);
mapName.value = route.query.mapName
})
onUnmounted(() => {

View File

@ -27,7 +27,7 @@
<div class="map-body">
<!-- <canvas ref="mapCanvas" /> -->
<MapCanvas :mapName="mapName" :isDragging="!isDragging" />
<MapCanvas :mapName="robotStore.current_map" :isDragging="!isDragging" />
</div>
</section>
@ -134,12 +134,12 @@ import { useRobotStore } from '@/stores/robot'
import { moveRobot, stopRobot } from '@/api/agv.js'
import MapCanvas from './MapCanvas.vue'
const props = defineProps({
mapName: {
type: String,
default: ''
}
})
// const props = defineProps({
// mapName: {
// type: String,
// default: ''
// }
// })
// const VITE_AGV_DEVICE_ID = import.meta.env.VITE_AGV_DEVICE_ID
@ -168,8 +168,7 @@ const isDragging = ref(false)
const moveRobotData = ref({
vx: 0,
vy: 0,
w: 0,
duration: -1
vz: 0
})
/* long-press stubs */
@ -183,10 +182,9 @@ const onPress = async (_dir) => {
} else {
moveRobotData.value.vy = -maxSpeed.value
}
moveRobotData.value.w = rotationAngle.value
moveRobotData.value.vz = rotationAngle.value
const res = await moveRobot({
ip: robotStore.ip,
// deviceId: VITE_AGV_DEVICE_ID,
deviceId: robotStore.agvDeviceId,
...moveRobotData.value
})

View File

@ -386,12 +386,11 @@ const loadSourceMap = async (mapName) => {
})
if (res.code === 200) {
try {
const rawJsonObject = JSON.parse(res.data.map_content); // 解析 JSON
const rawJsonObject = JSON.parse(res.data); // 解析 JSON
const parsedMap = parseSmapJson(rawJsonObject); // 转换为内部结构
await loadMapIntoApplication(parsedMap); // 加载到应用
setTimeout(() => {
bindEvent()
getRobot()
}, 300)
} catch (parseError) {
ElMessage.error('Parse error: ' + parseError.message); // 显示错误信息
@ -417,31 +416,21 @@ const moveRobot = (robot, dx, dy, dtheta = 0) => {
robot.angle += dtheta;
}
const getRobot = async () => {
const res = await getRobotState({
ip: robotStore.ip,
// deviceId: VITE_AGV_DEVICE_ID,
deviceId: robotStore.agvDeviceId,
})
if (res.code === 200) {
robotStore.setPosition(res.data.x, res.data.y, res.data.angle)
robotStore.battery = res.data.batteryLevel
addRobot(robotStore.ip, robotStore.ip, res.data.x, res.data.y, res.data.angle, '#00D4FF')
}
}
onMounted(() => {
init()
})
watch(() => props.mapName,
(newVal) => {
if (props.mapName) {
loadSourceMap(props.mapName)
async (newVal) => {
if (newVal) {
await loadSourceMap(newVal)
addRobot(robotStore.ip, robotStore.ip, robotStore.position.x, robotStore.position.y, robotStore.position.theta, '#00D4FF')
requestAnimationFrame(renderFrame);
window.addEventListener('resize', resizeCanvasToContainer); // 监听窗口大小变化
resizeCanvasToContainer(); // 初始调用一次
}
}, {
immediate: true
}
)