<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>2D无人驾车系统(积分预判防相撞)</title>
<style>
body { margin: 0; display: flex; flex-direction: column; align-items: center; background: #f0f0f0; font-family: Arial; }
#canvas { background: #fff; border: 3px solid #333; }
.controls { margin: 10px 0; display: flex; gap: 15px; align-items: center; }
input { width: 80px; padding: 5px; }
button { padding: 6px 18px; cursor: pointer; background: #4CAF50; color: white; border: none; border-radius: 4px; }
button:hover { background: #45a049; }
.tip { margin-top: 5px; font-size: 14px; color: #666; }
</style>
</head>
<body>
<div class="tip">红色顶点是目的地,红色顶点上方蓝色数字为车辆编号</div>
<canvas id="canvas" width="1000" height="600"></canvas>
<div class="controls">
<label>车辆数量: <input type="number" id="carCount" value="5" min="1" max="150" step="1"></label>
<button id="startBtn">启动系统</button>
<button id="stopBtn">停止系统</button>
</div>
<script>
// 原机械臂核心算法(完全保留)
function F(x, k) { return 1 + Math.pow(x, k) + x; }
function integral(eq, a, b, d=0.1, K=5) {
const g = new Function('x', `return ${eq};`);
let s = [[a, b]], r = 0.0;
while (s.length > 0) {
let n = [];
for (let [x0, x1] of s) {
const dx = x1 - x0;
if (dx < d) {
const m = (x0 + x1)/2;
const m_scaled = m/(2*Math.PI);
const f_values = Array.from({length:K}, (_,k) => F(m_scaled, k+1));
let W = 0.0, V = 0.0;
const g_m = g(m);
for (let k = 0; k < K; k++) {
const w = 1/(f_values[k] + 1e-12);
W += w; V += w * g_m;
}
r += dx * (W ? V/W : g_m);
continue;
}
const u = g(x0), v = g(x1), mid = (x0+x1)/2, mv = g(mid);
if (Math.abs(u-v) < 1e-8 && Math.abs(mv-u) < 1e-8) {
r += dx * mv; continue;
}
const h = dx/10;
for (let i = 0; i < 10; i++) n.push([x0+i*h, x0+(i+1)*h]);
}
s = n;
}
return r;
}
function c(seg) { let cnt = 0; for (let i=0;i<seg.length;i++) for(let j=i+1;j<seg.length;j++) if(seg[i]>seg[j]) cnt++; return cnt; }
function w(arr, wa) {
let a = [...arr], n = a.length;
if (n <=1) return a;
const exp_wa = Array.from({length:n}, (_,i) => Math.pow(wa, i+1));
for (let i=0;i<n;i++) {
let swapped = false;
for (let j=0;j<n-i-1;j++) {
const v1 = a[j]*(1 + wa + exp_wa[j]);
const v2 = a[j+1]*(1 + wa + exp_wa[j+1]);
if (Math.abs(v1-v2) <1e-8) {
const eq = `{a\[j\]}\*x - {a[j+1]}*x`;
const r = integral(eq, 0, 1);
if (r>0) { [a[j],a[j+1]] = [a[j+1],a[j]]; swapped=true; }
} else if (v1>v2) { [a[j],a[j+1]] = [a[j+1],a[j]]; swapped=true; }
}
if (!swapped) break;
}
return a;
}
function sortAngle(arr) {
let n = arr.length;
if (n<=1) return [...arr];
let segs = [], i=0;
while (i < n) {
let l=1;
while (i+l <n && arr[i+l]>=arr[i+l-1]) l++;
const s = arr.slice(i, i+l);
const r = l>1 ? c(s)/(l-1) : 0;
segs.push({s, r});
i += l;
}
let n_segs = [];
for (let {s, r} of segs) {
if (r>0 && s.length>1) {
const avg = s.reduce((a,b)=>a+b)/s.length;
const wa_sum = Array.from({length:20}, (_,k) => 1/(F(avg, k+1)+1e-12)).reduce((a,b)=>a+b);
n_segs.push({s:w(s, wa_sum), r: c(w(s, wa_sum))/(s.length-1)});
} else n_segs.push({s, r});
}
return n_segs.flatMap(item => item.s);
}
// 全局变量
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const [W, H] = [canvas.width, canvas.height];
let cars = [], isRunning = false;
// 性能优化:离屏画布(批量绘制避免重复渲染)
const offscreenCanvas = document.createElement('canvas');
const offscreenCtx = offscreenCanvas.getContext('2d');
offscreenCanvas.width = W;
offscreenCanvas.height = H;
// 速度归一化函数(机械臂旋转速度专用)
function normalizeSpeed(speed) {
return Math.max(0.01, Math.min(speed, 0.1)) * 0.5;
}
class Car {
constructor(id) {
this.id = id;
this.size = {w:30, h:15};
this.x = Math.random() * (W - 2*this.size.w) + this.size.w;
this.y = Math.random() * (H - 2*this.size.h) + this.size.h;
this.angle = Math.random() * Math.PI * 2;
this.speed = Math.random() * 1.5 + 1.2;
this.maxSpeed = 4;
this.target = this.getRandomTarget();
this.arriveTimer = 0;
this.emergency = false;
this.emergencyTimer = 0;
// 绑定机械臂(隐藏)
this.arm = {
len: [20, 15, 10],
angle: [Math.random()*Math.PI/2, Math.random()*Math.PI/2, Math.random()*Math.PI/2],
joint: []
};
this.rotateSpeed = 0.05;
// 缓存积分计算参数(减少重复创建)
this.predictTimeStep = 5;
this.predictD = 0.5;
}
getRandomTarget() {
return {
x: Math.random() * (W - 4*this.size.w) + 2*this.size.w,
y: Math.random() * (H - 4*this.size.h) + 2*this.size.h
};
}
updateArmJoint() {
let [x, y] = [this.x, this.y];
this.arm.joint = [{x, y}];
for (let i=0; i<this.arm.len.length; i++) {
x += this.arm.len[i] * Math.cos(this.arm.angle[i]);
y += this.arm.len[i] * Math.sin(this.arm.angle[i]);
this.arm.joint.push({x, y});
}
}
rotateArmWhenReverse() {
this.updateArmJoint();
const speed = normalizeSpeed(this.rotateSpeed);
const angleArr = [...this.arm.angle];
const sortedAngles = sortAngle(angleArr);
this.arm.angle = sortedAngles.map(ang => ang + speed * (Math.random() > 0.5 ? 1 : -1));
this.angle = this.arm.angle[this.arm.angle.length - 1];
}
// 积分预测未来位置(缓存参数优化)
predictFuturePos() {
const eqX = `({this.x}) + Math.cos({this.angle}) * ${this.speed} * x`;
const eqY = `({this.y}) + Math.sin({this.angle}) * ${this.speed} * x`;
const futureX = integral(eqX, 0, this.predictTimeStep, this.predictD);
const futureY = integral(eqY, 0, this.predictTimeStep, this.predictD);
return {x: futureX, y: futureY};
}
// 优化碰撞检测:减少重复计算
checkCollision(otherCars) {
const collisionRange = 60;
const predictRange = 80;
for (let car of otherCars) {
if (car.id === this.id) continue;
const dx = this.x - car.x;
const dy = this.y - car.y;
const dist = Math.hypot(dx, dy);
// 即时碰撞
if (dist < collisionRange * 0.7) return true;
// 预判碰撞(跳过已计算的车辆对,避免重复)
const myFuture = this.predictFuturePos();
const carFuture = car.predictFuturePos();
const futureDist = Math.hypot(myFuture.x - carFuture.x, myFuture.y - carFuture.y);
if (futureDist < predictRange) return true;
}
}
// 边界碰撞
if (this.x < this.size.w || this.x > W - this.size.w || this.y < this.size.h || this.y > H - this.size.h) {
return true;
}
return false;
}
// 强制分离逻辑(保留原逻辑)
forceSeparate(otherCars) {
for (let car of otherCars) {
if (car.id === this.id) continue;
const dx = this.x - car.x;
const dy = this.y - car.y;
const dist = Math.hypot(dx, dy);
if (dist < this.size.w * 1.5) {
const pushForce = 2;
const dirX = dx / dist;
const dirY = dy / dist;
this.x += dirX * pushForce;
this.y += dirY * pushForce;
car.x -= dirX * pushForce;
car.y -= dirY * pushForce;
this.rotateArmWhenReverse();
car.rotateArmWhenReverse();
}
}
}
control(otherCars) {
const dx = this.target.x - this.x;
const dy = this.target.y - this.y;
const targetAngle = Math.atan2(dy, dx);
const distToTarget = Math.hypot(dx, dy);
if (distToTarget < 20) {
this.arriveTimer++;
if (this.arriveTimer > 30) {
this.target = this.getRandomTarget();
this.arriveTimer = 0;
}
} else {
this.arriveTimer = 0;
}
// 碰撞检测与紧急处理
const isCollision = this.checkCollision(otherCars);
if (isCollision && !this.emergency) {
this.emergency = true;
this.emergencyTimer = 150;
this.rotateArmWhenReverse();
}
if (this.emergency) {
this.speed = -Math.abs(this.speed) * 0.8;
this.emergencyTimer--;
if (this.emergencyTimer <= 0) {
this.emergency = false;
this.speed = Math.abs(this.speed);
}
} else {
const angleDiff = targetAngle - this.angle;
const normalizedDiff = (angleDiff + Math.PI) % (2 * Math.PI) - Math.PI;
const steerFactor = distToTarget > 100 ? 0.1 : 0.02;
this.angle += normalizedDiff * steerFactor;
this.speed = Math.min(this.maxSpeed, distToTarget * 0.01);
}
// 位置更新
this.x += Math.cos(this.angle) * this.speed;
this.y += Math.sin(this.angle) * this.speed;
// 边界反弹
if (this.x < this.size.w) this.x = this.size.w + 5;
if (this.x > W - this.size.w) this.x = W - this.size.w -5;
if (this.y < this.size.h) this.y = this.size.h +5;
if (this.y > H - this.size.h) this.y = H - this.size.h -5;
// 强制分离
this.forceSeparate(otherCars);
}
// 绘制车辆+红色编号;绘制目的地+蓝色编号
draw() {
// 绘制车辆本体
offscreenCtx.save();
offscreenCtx.translate(this.x, this.y);
offscreenCtx.rotate(this.angle);
offscreenCtx.fillStyle = `hsl(${this.id * 30}, 70%, 50%)`;
offscreenCtx.fillRect(-this.size.w/2, -this.size.h/2, this.size.w, this.size.h);
// 绘制车辆红色编号
offscreenCtx.fillStyle = 'red';
offscreenCtx.font = '12px Arial bold';
offscreenCtx.textAlign = 'center';
offscreenCtx.fillText(this.id, 0, -this.size.h/2 - 5);
// 绘制车辆朝向箭头
offscreenCtx.fillStyle = 'black';
offscreenCtx.beginPath();
offscreenCtx.moveTo(10, 0);
offscreenCtx.lineTo(-5, -5);
offscreenCtx.lineTo(-5, 5);
offscreenCtx.fill();
offscreenCtx.restore();
// 绘制目的地(红色顶点)
offscreenCtx.fillStyle = 'red';
offscreenCtx.beginPath();
offscreenCtx.arc(this.target.x, this.target.y, 5, 0, 2*Math.PI);
offscreenCtx.fill();
// 绘制目的地上方蓝色编号
offscreenCtx.fillStyle = 'blue';
offscreenCtx.font = '12px Arial bold';
offscreenCtx.textAlign = 'center';
offscreenCtx.fillText(this.id, this.target.x, this.target.y - 10);
}
}
// 优化动画循环:离屏画布批量渲染
function animate() {
if (!isRunning) return;
// 清空离屏画布
offscreenCtx.clearRect(0, 0, W, H);
// 批量更新+绘制所有车辆
cars.forEach(car => {
car.control(cars);
car.draw();
});
// 一次性绘制到主画布(减少重绘次数)
ctx.clearRect(0, 0, W, H);
ctx.drawImage(offscreenCanvas, 0, 0);
requestAnimationFrame(animate);
}
// 按钮事件
document.getElementById('startBtn').addEventListener('click', () => {
if (isRunning) return;
isRunning = true;
const carCount = parseInt(document.getElementById('carCount').value);
cars = [];
for (let i=0; i<carCount; i++) {
cars.push(new Car(i));
}
animate();
});
document.getElementById('stopBtn').addEventListener('click', () => {
isRunning = false;
});
</script>
</body>
</html>