simultaion

This commit is contained in:
Your Name 2024-06-04 13:19:22 +03:00
parent c5ae89d18a
commit 61118a7423
10 changed files with 277 additions and 58 deletions

View file

@ -21,7 +21,8 @@ import {
Quaternion,
MeshBasicMaterial,
PlaneGeometry,
BoxGeometry
BoxGeometry,
AxesHelper
} from "three";
import { TypedEvent } from "../helper/typed_event";
import { Result } from "../helper/result";
@ -34,7 +35,12 @@ import {
import { SceneMode } from "../../features/scene_manager/model/scene_view";
import { throttle } from "../helper/throttle";
import { Asset, InstanceRgbCamera, RobossemblerAssets, SceneSimpleObject } from "../model/robossembler_assets";
import { LoadingManager } from 'three';
import URDFLoader, { URDFLink } from 'urdf-loader';
import { UrdfTransforms, coordsToQuternios, coordsToVector } from "../../features/simulations/tranforms_model";
Object3D.DEFAULT_UP = new Vector3(0, 0, 1);
export enum UserData {
selectedObject = "selected_object",
cameraInitialization = "camera_initialization",
@ -51,6 +57,10 @@ interface IEmissiveCache {
status: boolean;
object3d: Object3D<Object3DEventMap>;
}
type SceneFrames = { [K in string]: URDFLink; }
export class CoreThreeRepository extends TypedEvent<BaseSceneItemModel> {
scene = new Scene();
@ -62,10 +72,14 @@ export class CoreThreeRepository extends TypedEvent<BaseSceneItemModel> {
orbitControls: OrbitControls;
htmlSceneWidth: number;
htmlSceneHeight: number;
sceneFrame?: SceneFrames;
objLoader = new OBJLoader();
glbLoader = new GLTFLoader();
daeLoader = new ColladaLoader();
stlLoader = new STLLoader();
manager = new LoadingManager();
urdfLoader = new URDFLoader(this.manager);
watcherSceneEditorObject: Function;
constructor(htmlCanvasRef: HTMLCanvasElement, watcherSceneEditorObject: Function) {
@ -125,12 +139,26 @@ export class CoreThreeRepository extends TypedEvent<BaseSceneItemModel> {
cube.position.copy(vector);
}
this.scene.add(cube);
// this.scene.children.
}
deleteSceneItem(item: BaseSceneItemModel) {
const updateScene = this.scene;
updateScene.children = item.deleteToScene(updateScene);
}
loadUrdf = (urlPath: string) => {
this.urdfLoader.load(
urlPath, // The path to the URDF within the package OR absolute
robot => {
this.scene.add(robot)
console.log(robot)
// @ts-expect-error
this.sceneFrame = robot.frames
}
);
}
loadInstances(robossemblerAssets: RobossemblerAssets) {
robossemblerAssets.instances.forEach(async (el) => {
if (el instanceof InstanceRgbCamera) {
@ -142,7 +170,7 @@ export class CoreThreeRepository extends TypedEvent<BaseSceneItemModel> {
const asset = robossemblerAssets.getAssetAtInstance(el.instanceAt as string);
this.loader(
asset.meshPath,
() => {},
() => { },
asset.name,
new Vector3(el.position.x, el.position.y, el.position.z),
new Quaternion(el.quaternion[0], el.quaternion[1], el.quaternion[2], el.quaternion[3])
@ -153,7 +181,7 @@ export class CoreThreeRepository extends TypedEvent<BaseSceneItemModel> {
loadInstance(asset: Asset, loadCallback?: Function) {
this.loader(
asset.meshPath,
loadCallback ? loadCallback : () => {},
loadCallback ? loadCallback : () => { },
asset.name,
new Vector3(Number(asset.posX), Number(asset.posY), Number(asset.posZ)),
new Quaternion(0, 0, 0, 0)
@ -282,21 +310,11 @@ export class CoreThreeRepository extends TypedEvent<BaseSceneItemModel> {
this.light();
this.addListeners();
const floor = new GridHelper(100, 100, 0x888888, 0x444444);
floor.geometry.rotateX(Math.PI * 0.5)
floor.userData = {};
floor.userData[UserData.cameraInitialization] = true;
floor.up.copy(new Vector3(0, 0, 1))
this.scene.add(floor);
const planeMesh = new Mesh(
new PlaneGeometry(10, 10, 10, 10),
new MeshBasicMaterial({ color: 0x808080, wireframe: true })
);
planeMesh.userData[UserData.selectedObject] = true;
planeMesh.userData[UserData.objectForMagnetism] = true;
planeMesh.rotation.x = -Math.PI / 2;
this.makeCube(1);
this.makeCube(2, new Vector3(20, 0, 10), "yellow");
this.scene.add(planeMesh);
}
render() {
@ -329,8 +347,8 @@ export class CoreThreeRepository extends TypedEvent<BaseSceneItemModel> {
case "glb":
this.glbLoader.load(
url,
(result) => {},
(err) => {}
(result) => { },
(err) => { }
);
break;
case "obj":
@ -349,22 +367,22 @@ export class CoreThreeRepository extends TypedEvent<BaseSceneItemModel> {
this.scene.add(el);
});
},
(err) => {}
(err) => { }
);
break;
case "dae":
this.daeLoader.load(
url,
(result) => {},
(err) => {}
(result) => { },
(err) => { }
);
break;
case "stl":
this.stlLoader.load(
url,
(result) => {},
(result) => { },
(err) => {}
(err) => { }
);
break;
}
@ -451,4 +469,15 @@ export class CoreThreeRepository extends TypedEvent<BaseSceneItemModel> {
this.camera.lookAt(bsWorld);
this.orbitControls = new OrbitControls(this.camera, this.htmlCanvasRef);
}
urdfTransforms = (urdfTransforms: UrdfTransforms) => {
urdfTransforms.transforms.forEach((transform) => {
if (this.sceneFrame) {
const currentLink = this.sceneFrame[transform?.child_frame_id ?? ""]
const currentLinkParrent = this.sceneFrame[transform?.header.frame_id ?? ""]
// currentLink.position.copy(coordsToVector(transform.transform.translation))
currentLink.quaternion.copy(coordsToQuternios(transform.transform.rotation))
}
})
}
}

View file

@ -0,0 +1,37 @@
import { FoxgloveClient } from "@foxglove/ws-protocol"
import { MessageReader } from '@foxglove/rosmsg2-serialization';
import { parse as parseMessageDefinition } from '@foxglove/rosmsg';
import { TypedEvent } from "../helper/typed_event";
export class RosWsBridgeRepository<T> extends TypedEvent<T> {
client?: FoxgloveClient;
constructor() {
super();
}
connect(topic: string) {
const client = new FoxgloveClient({
ws: new WebSocket(`ws://localhost:8765`, [FoxgloveClient.SUPPORTED_SUBPROTOCOL]),
});
const deserializers = new Map();
client.on("advertise", (channels) => {
for (const channel of channels) {
if (channel.topic.isEqual(topic)) {
const subId = client.subscribe(channel.id);
deserializers.set(subId, (data: any) => {
return {
data: data,
channel: channel
}
});
}
}
});
client.on("message", ({ subscriptionId, timestamp, data }) => {
const channel = deserializers.get(subscriptionId)(data);
this.emit(new MessageReader(parseMessageDefinition(channel.channel?.schema!, {
ros2: true,
})).readMessage(channel.data))
});
}
}

View file

@ -19,7 +19,7 @@ import {
import { DataSetScreen, DatasetsScreenPath } from "../../features/dataset/dataset_screen";
import DetailsScreen, { DetailsScreenPath } from "../../features/details/details_screen";
import { AssemblesScreen, AssemblesScreenPath } from "../../features/assembles/assembles_screen";
import SimulationScreen, { SimulationScreenPath } from "../../features/simulations/simulations_screen";
import { SimulationScreen, SimulationScreenPath } from "../../features/simulations/simulations_screen";
import { EstimateScreen, EstimateScreenPath } from "../../features/estimate/estimate_screen";
import { SkillPath, SkillScreen } from "../../features/skils/skills_screen";

View file

@ -24,7 +24,7 @@ export const AllProjectScreen: React.FunctionComponent = observer(() => {
needBackButton={false}
minText="create project?"
path={CreateProjectScreenPath}
isError={store.isError}
isError={false}
isLoading={store.isLoading}
children={
<div>

View file

@ -1,11 +1,15 @@
import * as React from "react";
import { MainPage } from "../../core/ui/pages/main_page";
export interface ISimulationScreenProps {}
import { SimulationStore } from "./simulations_store";
interface ISimulationScreenProps { }
export const SimulationScreenPath = "/simulation";
export default class SimulationScreen extends React.Component<ISimulationScreenProps> {
public render() {
return <MainPage page={"Симуляция"}></MainPage>;
}
}
export const SimulationScreen = (props: ISimulationScreenProps) => {
const [store] = React.useState(() => new SimulationStore());
const canvasRef = React.useRef<HTMLCanvasElement>(null);
React.useEffect(() => {
store.init();
store.loadScene(canvasRef.current!);
}, []);
return <MainPage page={"Симуляция"} bodyChildren={<canvas ref={canvasRef} style={{ overflow: "hidden" }} />} />;
}

View file

@ -0,0 +1,29 @@
import makeAutoObservable from "mobx-store-inheritance";
import { RosWsBridgeRepository } from "../../core/repository/ros_ws_bridge_repository";
import { CoreThreeRepository } from "../../core/repository/core_three_repository";
import { UrdfTransforms } from "./tranforms_model";
export class SimulationStore {
rosWsUrdfTransfomsListner: RosWsBridgeRepository<UrdfTransforms> = new RosWsBridgeRepository();
coreThereRepository?: CoreThreeRepository;
constructor() {
makeAutoObservable(this)
}
init = () => {
this.rosWsUrdfTransfomsListner.connect('/tf')
}
async loadScene(canvasRef: HTMLCanvasElement) {
await this.loadWebGl(canvasRef);
this.coreThereRepository?.loadUrdf('http://localhost:4001/robot.xml');
if (this.coreThereRepository?.urdfTransforms) {
this.rosWsUrdfTransfomsListner.on(this.coreThereRepository.urdfTransforms)
}
}
loadWebGl = async (canvasRef: HTMLCanvasElement) => {
this.coreThereRepository = new CoreThreeRepository(canvasRef as HTMLCanvasElement, () => { });
this.coreThereRepository.on(() => { });
this.coreThereRepository.render();
}
}

View file

@ -0,0 +1,40 @@
import { Quaternion, Vector3 } from "three";
export interface UrdfTransforms {
transforms: TransformElement[];
}
export interface TransformElement {
header: Header;
child_frame_id: string;
transform: TransformTransform;
}
export interface Header {
stamp: Stamp;
frame_id: string;
}
export interface Stamp {
sec: number;
nsec: number;
}
export interface TransformTransform {
translation: Coords;
rotation: Coords;
}
export interface Coords {
x: number;
y: number;
z: number;
w?: number;
}
export const coordsToVector = (coords: Coords): Vector3 => {
return new Vector3(coords.x, coords.y, coords.z)
}
export const coordsToQuternios = (coords: Coords): Quaternion => {
return new Quaternion(coords.x, coords.y, coords.z, coords.w)
}