news 2026/11/5 21:38:34

树莓派小车远程控制实战:低延迟WebSocket+硬件PWM方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
树莓派小车远程控制实战:低延迟WebSocket+硬件PWM方案

1. 项目概述:为什么一台树莓派+小车+远程控制,值得花三小时搭出来

“树莓派小车——远程控制”这八个字,表面看是学生课设、创客展台上的常见组合,但在我带过的二十多个嵌入式实践项目里,它其实是检验一个人是否真正吃透“软硬协同”能力的黄金试纸。不是简单连个WiFi跑个网页按钮,而是从底层电机驱动时序、GPIO电平稳定性、网络延迟抖动补偿,到上层控制逻辑的实时性设计,全链路都得经得起推敲。我见过太多人卡在“遥控指令发出去,小车愣了800毫秒才动一下”,最后归咎于“树莓派性能不行”,其实问题出在UDP包重传策略没做、PWM占空比更新没加锁、甚至电源纹波导致编码器信号误触发——这些细节,文档里不写,教程里不提,但实操中一个就足以让整台车原地罢工。

这个项目核心解决的是“人在物理空间之外,对移动实体设备进行低延迟、可反馈、可中断的双向交互”问题。它不追求工业级可靠性,但必须具备清晰的故障边界:比如网络断开时自动刹车、电机过热时强制降频、摄像头画面卡死时不阻塞控制通道。适合三类人直接抄作业:一是电子/自动化专业大二以上学生,用来打通单片机、Linux、Python、网络编程四门课的知识断点;二是中小学科技教师,把它拆解成6节实验课(电机驱动→传感器接入→Web界面→手机控制→路径记忆→避障扩展);三是硬件爱好者,作为家庭物联网中枢的移动探针——你让它巡检地下室湿度、推送门口快递通知、甚至夜间自动绕行障碍物去充电,底层能力都是一样的。

关键词“树莓派”“小车”“远程控制”背后藏着三层技术栈:最底下是硬件抽象层(L298N驱动芯片选型、TB6612FNG为何比L298N更省电、编码器AB相如何抗干扰布线);中间是系统服务层(systemd管理运动服务、uWSGI部署轻量Web API、motion检测与OpenCV视频流如何共存不抢CPU);最上面是交互协议层(为什么用WebSocket而不是HTTP轮询、JSON指令包里必须带timestamp字段防重放、手柄摇杆值如何映射成左右轮差速的非线性曲线)。接下来我会把这三层全部摊开,不讲原理图,只讲我焊过5块PCB板、烧过3次电机驱动芯片、重写4版控制逻辑后,真正能落地的方案。

2. 整体架构设计:放弃“全功能堆砌”,专注三个不可妥协的硬指标

2.1 为什么不用ROS?也别碰MicroPython

先说结论:如果你的目标是“两天内让小车动起来并能用手机控制”,ROS是自找麻烦。ROS2的DDS通信在树莓派4B上启动一个基础节点就要吃掉300MB内存,而我们真正需要的只是“按下↑键→左轮+右轮同时加速”。我测试过,在树莓派4B 4GB内存版本上,纯Python+GPIO+OpenCV的最小可行系统常驻内存仅86MB,启动时间12秒;装完ROS2 Foxy后,同样功能代码内存飙升至620MB,首次启动耗时217秒——这已经不是效率问题,而是可用性问题。至于MicroPython,它连标准Python的threading模块都不完整,而电机PID闭环控制必须用多线程隔离传感器读取和PWM输出,否则编码器计数会漏脉冲。所以最终方案锁定在:Raspberry Pi OS Lite(64位) + Python3.11 + systemd服务 + WebSocket长连接。

2.2 网络架构:为什么坚持用WebSocket而非HTTP API

很多人第一反应是写个Flask接口,前端用AJAX发POST请求。但实际测试发现,HTTP短连接在Wi-Fi环境下有致命缺陷:每次请求要经历DNS解析(平均42ms)、TCP三次握手(平均68ms)、TLS协商(如果加HTTPS则再加120ms),光建立连接就超200ms。而小车控制要求“指令发出到执行延迟<150ms”,否则人手松开按键时车还在猛冲。WebSocket完美解决这个问题:首次HTTP升级后,后续所有指令走复用的TCP连接,单次消息往返实测均值仅23ms(局域网环境)。更重要的是,WebSocket支持服务端主动推送——当小车撞到障碍物,它能立刻通过同一连接发回{"status":"collision","timestamp":1715234567},前端不用再轮询GET /api/status。我用Wireshark抓包对比过,100次控制指令下,WebSocket总流量1.2MB,HTTP轮询高达8.7MB,这对树莓派的SD卡寿命都是实打实的影响。

2.3 硬件分层:电源、驱动、传感的物理隔离设计

这是最容易被忽略却最致命的一环。树莓派GPIO输出电流上限16mA,而L298N使能引脚需要5V/5mA,看似够用,但实际运行中电机启停瞬间产生的反向电动势会通过GND线耦合进树莓派,导致USB设备断连、SD卡读写出错。我的解决方案是“三地分离”:

  • 电机地(Motor GND):单独接12V电池负极,不与任何其他电路共地;
  • 驱动芯片地(Driver GND):L298N的GND引脚只接电机地,通过光耦(PC817)隔离控制信号;
  • 数字地(Digital GND):树莓派、摄像头、Wi-Fi模块共用此地,与电机地之间仅通过10Ω/1W电阻连接(泄放静电,阻断低频噪声)。

实测数据:未隔离前,电机全速运行时树莓派USB端口电压波动达±1.2V;隔离后波动压缩至±0.03V。这个设计不需要额外PCB,用洞洞板就能实现,但能避免90%的“小车跑着跑着就失联”的玄学问题。

3. 核心模块实现:从GPIO初始化到WebSocket心跳保活的完整链路

3.1 电机驱动层:用硬件PWM替代软件模拟,精度提升10倍

树莓派的GPIO只有2个硬件PWM通道(GPIO12、GPIO13),但小车至少需要4路独立PWM(左轮正转/反转、右轮正转/反转)。常见错误是用soft_pwm库做软件模拟,结果是:CPU占用率飙到85%,PWM频率被压到50Hz以下,电机发出刺耳啸叫,且占空比误差超±15%。正确解法是用PCA9685——16路12位PWM驱动芯片,I²C接口,树莓派只需2根线(SDA/SCL)就能控制全部通道,且频率稳定在1000Hz(人耳听不到啸叫)。接线极简:PCA9685的VCC接5V,GND接数字地,OUT0~OUT3分别接L298N的IN1~IN4,树莓派GPIO2(SDA)和GPIO3(SCL)连PCA9685对应引脚。

初始化代码关键段:

# 使用adafruit-circuitpython-pca9685库 from adafruit_pca9685 import PCA9685 import board i2c = board.I2C() # 使用硬件I2C,非软件模拟 pca = PCA9685(i2c, address=0x40) # 默认地址0x40 pca.frequency = 1000 # 强制设为1000Hz # 左轮正转通道0,反转通道1;右轮正转通道2,反转通道3 LEFT_FORWARD = pca.channels[0] LEFT_BACKWARD = pca.channels[1] RIGHT_FORWARD = pca.channels[2] RIGHT_BACKWARD = pca.channels[3] # 设置占空比:0~65535对应0%~100% def set_motor(left_speed, right_speed): # left_speed/right_speed范围:-100~100,负值表示反转 if left_speed >= 0: LEFT_FORWARD.duty_cycle = int(65535 * left_speed / 100) LEFT_BACKWARD.duty_cycle = 0 else: LEFT_FORWARD.duty_cycle = 0 LEFT_BACKWARD.duty_cycle = int(65535 * abs(left_speed) / 100) # 右轮同理...

提示:PCA9685的duty_cycle值是16位无符号整数(0~65535),不是百分比。很多教程直接写duty_cycle = speed * 655是错的,会导致最大速度只有99.9%,长期运行电机温升异常。

3.2 远程控制协议:定义最小可行指令集,拒绝过度设计

我删掉了所有“高级功能”字段,最终只保留5个必选指令:

指令类型JSON示例说明
move{"cmd":"move","left":60,"right":60,"ts":1715234567}左右轮占空比(0~100),ts为Unix时间戳防重放
stop{"cmd":"stop"}立即停止所有电机,强制占空比归零
status{"cmd":"status"}请求当前状态(电池电压、电机温度、网络延迟)
camera{"cmd":"camera","action":"start"}启动/关闭摄像头流
calibrate{"cmd":"calibrate","param":"encoder"}编码器零点校准

为什么没有turn_left/turn_right?因为差速转向的本质就是左右轮速度差,前端UI用虚拟摇杆生成left/right值更灵活。为什么stop不带参数?因为紧急制动必须是原子操作,不能有任何条件判断——我曾因在stop里加了“检查是否正在录像”的if语句,导致急停延迟230ms,小车撞穿纸箱。协议设计原则就一条:每个指令的执行路径必须是线性的、无分支的、可预测的。

3.3 WebSocket服务端:用asyncio实现万级并发,但只留1个连接

用Flask-SocketIO会引入不必要的复杂度,我们直接上原生asyncio:

import asyncio import websockets import json from motor_control import set_motor, emergency_stop connected_clients = set() async def handle_client(websocket, path): connected_clients.add(websocket) print(f"Client connected: {websocket.remote_address}") try: async for message in websocket: try: data = json.loads(message) cmd = data.get("cmd") if cmd == "move": left = max(-100, min(100, int(data.get("left", 0)))) right = max(-100, min(100, int(data.get("right", 0)))) set_motor(left, right) elif cmd == "stop": emergency_stop() # 硬件级立即切断 elif cmd == "status": status = get_system_status() # 读取电压/温度等 await websocket.send(json.dumps(status)) except json.JSONDecodeError: await websocket.send('{"error":"invalid_json"}') except Exception as e: await websocket.send(f'{"error":"{str(e)}"}') finally: connected_clients.remove(websocket) print(f"Client disconnected: {websocket.remote_address}") # 启动服务 start_server = websockets.serve(handle_client, "0.0.0.0", 8765) asyncio.get_event_loop().run_until_complete(start_server) asyncio.get_event_loop().run_forever()

注意:emergency_stop()函数必须直接操作GPIO,不能调用set_motor(0,0)——后者还要走PCA9685 I²C通信,而硬件急停应直接将L298N的EN引脚拉低。我在GPIO26上接了一个MOSFET,emergency_stop()执行GPIO.output(26, GPIO.LOW),响应时间<1μs。

3.4 前端控制界面:用HTML5 Canvas实现零依赖虚拟摇杆

拒绝引入jQuery或Vue,纯原生JS实现:

<canvas id="joystick" width="200" height="200" style="background:#eee; border-radius:50%;"></canvas> <script> const canvas = document.getElementById('joystick'); const ctx = canvas.getContext('2d'); let isDragging = false; let centerX = canvas.width/2, centerY = canvas.height/2; let currentX = centerX, currentY = centerY; // 绘制摇杆 function drawJoystick() { ctx.clearRect(0, 0, canvas.width, canvas.height); // 外圈 ctx.beginPath(); ctx.arc(centerX, centerY, 80, 0, Math.PI*2); ctx.strokeStyle = '#999'; ctx.lineWidth = 4; ctx.stroke(); // 摇杆球 ctx.beginPath(); ctx.arc(currentX, currentY, 20, 0, Math.PI*2); ctx.fillStyle = '#4CAF50'; ctx.fill(); } // 计算左右轮速度(核心算法) function calculateSpeed(x, y) { const dx = x - centerX; const dy = y - centerY; const distance = Math.sqrt(dx*dx + dy*dy); const maxDistance = 80; if (distance > maxDistance) { // 归一化到外圈 const ratio = maxDistance / distance; return { left: Math.round((dy - dx) * ratio * 0.7), // 差速公式:左轮 = y - x right: Math.round((dy + dx) * ratio * 0.7) // 右轮 = y + x }; } return { left: Math.round(dy - dx), right: Math.round(dy + dx) }; } // 触摸/鼠标事件 canvas.addEventListener('mousedown', startDrag); canvas.addEventListener('touchstart', startDrag); function startDrag(e) { isDragging = true; updatePosition(e); } function updatePosition(e) { const rect = canvas.getBoundingClientRect(); let x, y; if (e.type === 'touchstart') { x = e.touches[0].clientX - rect.left; y = e.touches[0].clientY - rect.top; } else { x = e.clientX - rect.left; y = e.clientY - rect.top; } currentX = Math.max(20, Math.min(canvas.width-20, x)); currentY = Math.max(20, Math.min(canvas.height-20, y)); } // 发送指令(每50ms一次) setInterval(() => { if (isDragging) { const speed = calculateSpeed(currentX, currentY); const msg = JSON.stringify({ cmd: "move", left: speed.left, right: speed.right, ts: Math.floor(Date.now()/1000) }); if (ws && ws.readyState === WebSocket.OPEN) { ws.send(msg); } } }, 50); </script>

实操心得:摇杆算法里的0.7系数是经过23次实地测试确定的——系数太小(如0.3)导致小车蠕动无力;太大(如1.0)则轻微触碰就全速前进。这个值要根据你的电机扭矩、轮子直径、地面摩擦系数微调,建议用卷尺量1米直线距离,记录不同系数下的实际行驶时间,选时间最接近3.5秒的系数(兼顾响应与可控性)。

4. 实操部署与调试:从烧录系统到首航成功的全流程记录

4.1 系统精简:删除所有非必要服务,释放320MB内存

树莓派OS默认安装了大量桌面组件,而我们的小车只需要命令行。烧录完官方镜像后,执行以下清理:

# 删除图形界面相关包(节省1.2GB空间) sudo apt purge --auto-remove raspberrypi-ui-mods realvnc-vnc-server libreoffice* sudo apt autoremove # 禁用无用服务 sudo systemctl disable bluetooth.service hciuart.service triggerhappy.service sudo systemctl mask avahi-daemon.service # 防止mDNS广播占用CPU # 调整内存分配:GPU只留16MB(摄像头够用) echo "gpu_mem=16" | sudo tee -a /boot/config.txt # 启用SSH和I2C sudo raspi-config # 在Interface Options里启用SSH和I2C

清理后实测:空载内存占用从680MB降至360MB,CPU空闲率从45%升至89%,这对保证WebSocket服务的实时性至关重要。特别提醒:raspi-config里不要开VNC,它会偷偷占用一个GPU核心。

4.2 网络配置:让小车成为局域网里的“静默节点”

默认DHCP容易导致IP变动,而手机APP需要固定IP连接。但直接设静态IP又可能和路由器冲突。最优解是DHCP Reservation:在路由器后台将小车的MAC地址(ip link show eth0 | grep ether获取)绑定到固定IP(如192.168.1.150),这样既保持DHCP的灵活性,又获得静态IP的稳定性。同时禁用IPv6减少干扰:

# /etc/dhcpcd.conf末尾添加 denyinterfaces wlan0 interface eth0 static ip_address=192.168.1.150/24 static routers=192.168.1.1 static domain_name_servers=192.168.1.1 noipv6

注意:denyinterfaces wlan0是关键!树莓派4B的Wi-Fi和蓝牙共用一个天线,开启wlan0会显著降低蓝牙模块性能,而我们根本不用蓝牙。实测关闭后,Wi-Fi吞吐量从42MB/s提升至58MB/s。

4.3 服务守护:用systemd确保崩溃后自动复活

创建/etc/systemd/system/car-control.service:

[Unit] Description=Car Remote Control Service After=network.target [Service] Type=simple User=pi WorkingDirectory=/home/pi/car-control ExecStart=/usr/bin/python3 /home/pi/car-control/main.py Restart=always RestartSec=10 StandardOutput=journal StandardError=journal SyslogIdentifier=car-control [Install] WantedBy=multi-user.target

启用服务:

sudo systemctl daemon-reload sudo systemctl enable car-control.service sudo systemctl start car-control.service # 查看日志:sudo journalctl -u car-control -f

关键参数解读:RestartSec=10不是随便写的。我测试过,设为1秒会导致频繁重启掩盖真实错误;设为30秒则故障恢复太慢。10秒是平衡点——既能快速恢复(如WebSocket服务崩溃),又给日志足够时间写入磁盘供排查。

4.4 首航调试清单:按顺序验证,跳过一步就可能返工

我整理了一份必须逐项验证的清单,按物理层→网络层→应用层顺序排列:

步骤操作预期结果失败原因定位
1万用表测PCA9685 VCC引脚4.95V~5.05V电源适配器功率不足(需≥2A)
2i2cdetect -y 1显示0x40地址I²C线序接反(SDA/SCL互换)
3运行pca9685-test.py设通道0占空比50%电机微弱转动L298N使能引脚未接高电平
4ping 192.168.1.150丢包率0%,延迟<2ms路由器QoS策略限速
5curl http://192.168.1.150:8765返回WebSocket升级错误服务未启动或端口被占
6手机浏览器访问http://192.168.1.150:8000显示摇杆界面Nginx未配置静态文件路径
7拖动摇杆,journalctl -u car-control出现move left:45 right:45日志WebSocket连接未建立
8用万用表测电机两端电压与设定占空比成正比(如50%→6.0V)PWM信号未到达驱动芯片

特别强调第4步:如果ping延迟超过5ms,别急着调代码,先用另一台设备ping同一路由器,确认是网络问题而非小车问题。我曾为排查12ms延迟花了3小时,最后发现是路由器固件bug,升级后解决。

5. 常见问题与独家排障技巧:那些手册里不会写的坑

5.1 “小车乱转圈”问题:90%源于编码器AB相接反

现象:小车原地顺时针狂转,摇杆往左推反而右转。新手第一反应是代码逻辑错,其实90%是编码器A/B相线接反。编码器输出两路方波,相位差90°,MCU通过检测A相上升沿时B相电平来判断转向。如果A/B线互换,MCU就会把正转识别为反转。验证方法:用示波器看A/B波形,正常应是A相领先B相90°;若B相领先A相,则交换AB线。没有示波器?用万用表直流档测A/B对GND电压,用手匀速转动轮子,观察电压跳变顺序——先跳变的那根是A相。

5.2 “手机连不上”问题:iOS Safari的WebSocket冷知识

iOS 15+ Safari有个隐藏限制:如果页面不是通过HTTPS加载,WebSocket连接会被静默拒绝,且不报任何错误。很多教程让你直接http://192.168.1.150:8000访问,这在安卓Chrome上没问题,但在iPhone上必然失败。解决方案只有两个:

  • 临时方案:用npx local-web-server在树莓派上起一个HTTPS服务(需生成自签名证书);
  • 生产方案:在路由器上配置端口转发,用域名(如car.local)+ Let's Encrypt证书。

我选择前者,因为自签名证书在局域网内完全够用。生成命令:

openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ -keyout /home/pi/cert.key -out /home/pi/cert.pem \ -subj "/C=CN/ST=Beijing/L=Beijing/O=Car/CN=car.local"

然后用https-server -S -C cert.pem -K cert.key -p 443启动,手机访问https://192.168.1.150即可。

5.3 “视频卡顿”问题:OpenCV与motion服务的资源争夺战

很多人想同时开摄像头预览和运动控制,结果发现视频流卡成PPT,控制指令也延迟。根源在于树莓派的VideoCore GPU资源有限,OpenCV的cv2.VideoCapture(0)和motion服务都在争抢H.264编码器。解决方案是让motion独占编码器,OpenCV只负责读取已编码的流:

  1. 配置motion.conf:stream_port 8081,stream_localhost off;
  2. 前端不直接连OpenCV,而是用<img src="http://192.168.1.150:8081">;
  3. 控制服务完全不碰摄像头设备,避免资源竞争。

实测效果:motion在1080p@15fps下CPU占用率18%,而OpenCV直接采集同分辨率要42%。

5.4 “电池续航短”问题:深度睡眠模式的正确打开方式

树莓派没有真正关机的概念,即使sudo shutdown -h now,USB和GPIO仍供电。实测一块12V/2000mAh电池,小车待机8小时后电量耗尽。根本解法是加装TPS63020 DC-DC降压模块,其EN引脚可由树莓派GPIO控制:

import RPi.GPIO as GPIO GPIO.setmode(GPIO.BCM) GPIO.setup(27, GPIO.OUT) # EN引脚接GPIO27 def power_off(): GPIO.output(27, GPIO.LOW) # 切断TPS63020输出 os.system("sudo shutdown -h now")

按下遥控器“关机键”后,树莓派先拉低EN引脚切断所有外设供电,再执行关机。此时整机功耗降至0.3mA,2000mAh电池可待机270天。

6. 扩展可能性:从遥控小车到自主移动平台的演进路径

6.1 加装超声波避障:用硬件中断替代软件轮询

HC-SR04超声波模块的Echo引脚输出高电平持续时间即为距离,但用GPIO.input()轮询会丢失脉冲。正确做法是用GPIO的硬件中断:

def echo_callback(channel): global pulse_start, pulse_end if GPIO.input(ECHO_PIN): pulse_start = time.time() else: pulse_end = time.time() distance = (pulse_end - pulse_start) * 34300 / 2 # cm if distance < 20: emergency_stop() # 小于20cm立即急停 GPIO.add_event_detect(ECHO_PIN, GPIO.BOTH, callback=echo_callback)

注意:TRIG引脚仍需软件触发,但Echo检测完全由硬件完成,CPU占用率趋近于0。

6.2 路径记忆功能:用IMU+轮式里程计融合定位

仅靠编码器累计距离误差会随时间放大(轮胎打滑、电压变化影响电机转速)。加入MPU6050 IMU,用卡尔曼滤波融合数据:

  • 编码器提供短时精确位移;
  • IMU提供姿态角(俯仰/横滚)修正轮式里程计偏差;
  • 每10秒用GPS粗略位置校准(室外场景)。

开源库robot_localization已封装好ROS版,我们移植其核心算法到纯Python,用numpy矩阵运算实现,树莓派4B处理频率可达25Hz。

6.3 多车协同:用ZeroMQ替代WebSocket实现集群控制

单台WebSocket只能点对点,要控制5台小车需5个连接。ZeroMQ的PUB/SUB模式天生适合:主控端发{"cmd":"move","group":"all","left":50},所有订阅all主题的小车同时执行。ZeroMQ的UDP底层比TCP更适应局域网广播,实测5台车指令同步误差<3ms。

最后分享个小技巧:所有电机驱动芯片的散热片别用导热硅脂直接粘在PCB上,而要用铜柱垫高2mm——这样空气能从底部对流散热,芯片温度比贴片式低18℃。这个细节让我连续测试8小时没烧过一块L298N。现在,你可以把这篇内容当检查清单,从第一步开始搭建。当第一次看到手机摇杆推动小车平稳转弯时,那种软硬咬合的精准感,是任何仿真软件都无法替代的。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/11/1 13:24:40

ESP32+RC522 RFID刷卡门禁实战:从读卡到继电器控制

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/11/1 17:25:19

Muse Gadgets:让AI Agent真正感知与操控物理世界的硬件抽象层

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/11/1 22:02:55

MS5611气压计驱动开发:从I2C初始化到温度补偿的完整实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/11/3 6:03:40

均值方差协方差与矩:概率论核心概念解析与工程实践

1. 从一组“看起来不对劲”的数据说起很多人第一次真正意识到均值、方差这些概念有用&#xff0c;不是在课堂上&#xff0c;而是在某个具体场景里被数据“打脸”的时候。比如你拿到两份测试报告&#xff0c;A组平均响应时间是120毫秒&#xff0c;B组是118毫秒&#xff0c;直觉告…

作者头像 李华
网站建设 2026/11/3 0:47:33

面向对象程序设计-第一阶段补充-你好,构造函数

作者 向训文 单位 惠州学院 编写Point类&#xff0c;使得下列程序正确运行&#xff1a; 裁判测试程序样例&#xff1a; #include<iostream> using namespace std;// 请将答案填写在这里int main() {int x, y;cin >> x >> y;Point p1(x, y); // 构造函数…

作者头像 李华