From c57cb0cd9feb4495c89246b2faec6d5e45c23c30 Mon Sep 17 00:00:00 2001
From: 张世豪 <979909237@qq.com>
Date: 星期一, 15 十二月 2025 15:46:49 +0800
Subject: [PATCH] 新增了控制指令

---
 image/dengoff.png                    |    0 
 src/gecaoji/Device.java              |  107 ++++
 device.properties                    |    3 
 image/starton.png                    |    0 
 image/xia2.png                       |    0 
 src/yaokong/Control05.java           |  199 ++++++++
 set.properties                       |    6 
 src/yaokong/RemoteControlDialog.java |  563 ++++++++++++++++++++++++
 image/onanddown.png                  |    0 
 image/xia20.png                      |    0 
 src/yaokong/Control07.java           |  198 ++++++++
 image/xia10.png                      |    0 
 image/off.png                        |    0 
 image/xia1.png                       |    0 
 src/yaokong/Control06.java           |  245 ++++++++++
 image/dengon.png                     |    0 
 src/yaokong/Control03.java           |   10 
 17 files changed, 1,316 insertions(+), 15 deletions(-)

diff --git a/device.properties b/device.properties
index f23bf1d..542d7ea 100644
--- a/device.properties
+++ b/device.properties
@@ -24,3 +24,6 @@
 positioningStatus=-1
 satelliteCount=-1
 differentialAge=-1
+mowerStartStatus=-1
+mowerLightStatus=-1
+mowerBladeHeight=-1
diff --git a/image/dengoff.png b/image/dengoff.png
new file mode 100644
index 0000000..66aa325
--- /dev/null
+++ b/image/dengoff.png
Binary files differ
diff --git a/image/dengon.png b/image/dengon.png
new file mode 100644
index 0000000..742b5b0
--- /dev/null
+++ b/image/dengon.png
Binary files differ
diff --git a/image/off.png b/image/off.png
new file mode 100644
index 0000000..0b358be
--- /dev/null
+++ b/image/off.png
Binary files differ
diff --git a/image/onanddown.png b/image/onanddown.png
new file mode 100644
index 0000000..5981fef
--- /dev/null
+++ b/image/onanddown.png
Binary files differ
diff --git a/image/starton.png b/image/starton.png
new file mode 100644
index 0000000..f5d581e
--- /dev/null
+++ b/image/starton.png
Binary files differ
diff --git a/image/xia1.png b/image/xia1.png
new file mode 100644
index 0000000..300bfad
--- /dev/null
+++ b/image/xia1.png
Binary files differ
diff --git a/image/xia10.png b/image/xia10.png
new file mode 100644
index 0000000..f0d4eaa
--- /dev/null
+++ b/image/xia10.png
Binary files differ
diff --git a/image/xia2.png b/image/xia2.png
new file mode 100644
index 0000000..9b26e37
--- /dev/null
+++ b/image/xia2.png
Binary files differ
diff --git a/image/xia20.png b/image/xia20.png
new file mode 100644
index 0000000..7da633b
--- /dev/null
+++ b/image/xia20.png
Binary files differ
diff --git a/set.properties b/set.properties
index c823fc5..432da08 100644
--- a/set.properties
+++ b/set.properties
@@ -1,5 +1,5 @@
-#Current work land selection updated
-#Fri Dec 12 17:21:33 CST 2025
+#Serial Port Preferences Updated
+#Mon Dec 15 15:45:14 CST 2025
 appVersion=-1
 currentWorkLandNumber=LAND1
 cuttingWidth=200
@@ -9,5 +9,5 @@
 mowerId=1234
 serialAutoConnect=true
 serialBaudRate=115200
-serialPortName=COM13
+serialPortName=COM15
 simCardNumber=-1
diff --git a/src/gecaoji/Device.java b/src/gecaoji/Device.java
index ee13487..aa4b77d 100644
--- a/src/gecaoji/Device.java
+++ b/src/gecaoji/Device.java
@@ -2,7 +2,10 @@
 import baseStation.BaseStation;
 import set.Setsys;
 import java.io.FileInputStream;
+import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.nio.charset.StandardCharsets;
 import java.util.Locale;
 import java.util.Properties;
 
@@ -74,6 +77,12 @@
     // 宸垎鏃堕棿
     private String selfCheckStatus = "-1";
     // 鍓茶崏鏈鸿嚜妫�鐘舵��
+    private String mowerStartStatus = "-1";
+    // 鍓茶崏鏈哄惎鍔ㄧ姸鎬侊細1寮�鍚紝0鐔勭伀锛�-1鏈煡
+    private String mowerLightStatus = "-1";
+    // 鍓茶崏鏈虹伅寮�鍏崇姸鎬侊細1寮�鍚紝0鍏抽棴锛�-1鏈煡
+    private String mowerBladeHeight = "-1";
+    // 鍓茶崏鏈哄垁鐩橀珮搴︼細-1鏈煡
 
     private static final double METERS_PER_DEGREE_LAT = 111320.0d;
     
@@ -104,6 +113,58 @@
         applyDefaults(gecaoji);
     }
 
+    /**
+     * 淇濆瓨鎵�鏈夊睘鎬у埌device.properties鏂囦欢
+     */
+    public void saveToProperties() {
+        Properties properties = new Properties();
+        
+        // 鍔犺浇鐜版湁灞炴�э紙淇濈暀鍏朵粬灞炴�э級
+        try (FileInputStream input = new FileInputStream("device.properties")) {
+            properties.load(input);
+        } catch (IOException e) {
+            // 濡傛灉鏂囦欢涓嶅瓨鍦紝缁х画鍒涘缓鏂版枃浠�
+        }
+        
+        // 璁剧疆鎵�鏈夎澶囧睘鎬�
+        if (mowerName != null) properties.setProperty("mowerName", mowerName);
+        if (mowerModel != null) properties.setProperty("mowerModel", mowerModel);
+        if (mowerNumber != null) properties.setProperty("mowerNumber", mowerNumber);
+        if (mowingWidth != null) properties.setProperty("mowingWidth", mowingWidth);
+        if (mowingHeight != null) properties.setProperty("mowingHeight", mowingHeight);
+        if (baseStationNumber != null) properties.setProperty("baseStationNumber", baseStationNumber);
+        if (baseStationCardNumber != null) properties.setProperty("baseStationCardNumber", baseStationCardNumber);
+        if (baseStationCoordinates != null) properties.setProperty("baseStationCoordinates", baseStationCoordinates);
+        if (deviceCardnumber != null) properties.setProperty("deviceCardnumber", deviceCardnumber);
+        if (createTime != null) properties.setProperty("createTime", createTime);
+        if (GupdateTime != null) properties.setProperty("GupdateTime", GupdateTime);
+        if (BupdateTime != null) properties.setProperty("BupdateTime", BupdateTime);
+        if (realtimeLatitude != null) properties.setProperty("realtimeLatitude", realtimeLatitude);
+        if (realtimeLongitude != null) properties.setProperty("realtimeLongitude", realtimeLongitude);
+        if (realtimeAltitude != null) properties.setProperty("realtimeAltitude", realtimeAltitude);
+        if (realtimeX != null) properties.setProperty("realtimeX", realtimeX);
+        if (realtimeY != null) properties.setProperty("realtimeY", realtimeY);
+        if (realtimeSpeed != null) properties.setProperty("realtimeSpeed", realtimeSpeed);
+        if (heading != null) properties.setProperty("heading", heading);
+        if (pitch != null) properties.setProperty("pitch", pitch);
+        if (battery != null) properties.setProperty("battery", battery);
+        if (positioningStatus != null) properties.setProperty("positioningStatus", positioningStatus);
+        if (satelliteCount != null) properties.setProperty("satelliteCount", satelliteCount);
+        if (differentialAge != null) properties.setProperty("differentialAge", differentialAge);
+        if (selfCheckStatus != null) properties.setProperty("selfCheckStatus", selfCheckStatus);
+        if (mowerStartStatus != null) properties.setProperty("mowerStartStatus", mowerStartStatus);
+        if (mowerLightStatus != null) properties.setProperty("mowerLightStatus", mowerLightStatus);
+        if (mowerBladeHeight != null) properties.setProperty("mowerBladeHeight", mowerBladeHeight);
+        
+        // 淇濆瓨鍒版枃浠�
+        try (FileOutputStream output = new FileOutputStream("device.properties");
+             OutputStreamWriter writer = new OutputStreamWriter(output, StandardCharsets.UTF_8)) {
+            properties.store(writer, "Updated device properties");
+        } catch (IOException ex) {
+            System.err.println("鏃犳硶淇濆瓨 device.properties: " + ex.getMessage());
+        }
+    }
+
     private void loadPropertiesInto(Device target, Properties properties) {
         if (target == null) {
             return;
@@ -133,7 +194,10 @@
         target.positioningStatus = properties.getProperty("positioningStatus", "-1");
         target.satelliteCount = properties.getProperty("satelliteCount", "-1");
         target.differentialAge = properties.getProperty("differentialAge", "-1");
-    target.selfCheckStatus = properties.getProperty("selfCheckStatus", "-1");
+        target.selfCheckStatus = properties.getProperty("selfCheckStatus", "-1");
+        target.mowerStartStatus = properties.getProperty("mowerStartStatus", "-1");
+        target.mowerLightStatus = properties.getProperty("mowerLightStatus", "-1");
+        target.mowerBladeHeight = properties.getProperty("mowerBladeHeight", "-1");
     }
 
     private void applyDefaults(Device target) {
@@ -165,7 +229,10 @@
         target.positioningStatus = "-1";
         target.satelliteCount = "-1";
         target.differentialAge = "-1";
-    target.selfCheckStatus = "-1";
+        target.selfCheckStatus = "-1";
+        target.mowerStartStatus = "-1";
+        target.mowerLightStatus = "-1";
+        target.mowerBladeHeight = "-1";
     }
 
     public static synchronized Device initializeActiveDevice(String mowerId) { // 鏍规嵁璁惧ID鍒濆鍖栨椿璺冭澶�
@@ -267,6 +334,15 @@
             case "selfCheckStatus":
                 this.selfCheckStatus = value;
                 return true;
+            case "mowerStartStatus":
+                this.mowerStartStatus = value;
+                return true;
+            case "mowerLightStatus":
+                this.mowerLightStatus = value;
+                return true;
+            case "mowerBladeHeight":
+                this.mowerBladeHeight = value;
+                return true;
             default:
                 System.err.println("鏈煡瀛楁: " + fieldName);
                 return false;
@@ -711,6 +787,30 @@
         this.selfCheckStatus = selfCheckStatus;
     }
 
+    public String getMowerStartStatus() { // 鑾峰彇鍓茶崏鏈哄惎鍔ㄧ姸鎬�
+        return mowerStartStatus;
+    }
+
+    public void setMowerStartStatus(String mowerStartStatus) { // 璁剧疆鍓茶崏鏈哄惎鍔ㄧ姸鎬�
+        this.mowerStartStatus = mowerStartStatus;
+    }
+
+    public String getMowerLightStatus() { // 鑾峰彇鍓茶崏鏈虹伅寮�鍏崇姸鎬�
+        return mowerLightStatus;
+    }
+
+    public void setMowerLightStatus(String mowerLightStatus) { // 璁剧疆鍓茶崏鏈虹伅寮�鍏崇姸鎬�
+        this.mowerLightStatus = mowerLightStatus;
+    }
+
+    public String getMowerBladeHeight() { // 鑾峰彇鍓茶崏鏈哄垁鐩橀珮搴�
+        return mowerBladeHeight;
+    }
+
+    public void setMowerBladeHeight(String mowerBladeHeight) { // 璁剧疆鍓茶崏鏈哄垁鐩橀珮搴�
+        this.mowerBladeHeight = mowerBladeHeight;
+    }
+
     @Override
     public String toString() { // 杈撳嚭瀵硅薄淇℃伅
         return "Device{" +
@@ -739,6 +839,9 @@
                 ", satelliteCount='" + satelliteCount + '\'' +
                 ", differentialAge='" + differentialAge + '\'' +
                 ", selfCheckStatus='" + selfCheckStatus + '\'' +
+                ", mowerStartStatus='" + mowerStartStatus + '\'' +
+                ", mowerLightStatus='" + mowerLightStatus + '\'' +
+                ", mowerBladeHeight='" + mowerBladeHeight + '\'' +
                 '}';
     }
 }
\ No newline at end of file
diff --git a/src/yaokong/Control03.java b/src/yaokong/Control03.java
index 01c28db..6401d72 100644
--- a/src/yaokong/Control03.java
+++ b/src/yaokong/Control03.java
@@ -198,6 +198,16 @@
         return currentSteeringSpeed;
     }
 
+    /**
+     * 鐩存帴璁剧疆骞跺彂閫佽浆鍚戝拰鍓嶈繘閫熷害锛堢敤浜庢寔缁彂閫佹帶鍒舵寚浠わ級
+     * @param steeringSpeed 杞悜閫熷害锛岃寖鍥�-100鍒�100
+     * @param forwardSpeed 鍓嶈繘閫熷害锛岃寖鍥�-100鍒�100
+     * @return 鏄惁鍙戦�佹垚鍔�
+     */
+    public static synchronized boolean setAndSendSpeeds(int steeringSpeed, int forwardSpeed) {
+        return sendSpeedsIfDebugSerialOpen(steeringSpeed, forwardSpeed);
+    }
+
     private static boolean sendSpeedsIfDebugSerialOpen(int nextSteering, int nextForward) {
         SerialPortService service = sendmessage.getActiveService();
         if (service == null || !service.isOpen()) {
diff --git a/src/yaokong/Control05.java b/src/yaokong/Control05.java
new file mode 100644
index 0000000..39e8aa2
--- /dev/null
+++ b/src/yaokong/Control05.java
@@ -0,0 +1,199 @@
+package yaokong;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+import chuankou.SerialPortService;
+import chuankou.sendmessage;
+
+/**
+ * Control05.java - 閬ユ帶鏈哄櫒鍚姩鍏抽棴鎸囦护绫�
+ * 
+ * 鎸囦护绫诲瀷: 0x05
+ * 鍔熻兘: 鎺у埗鍓茶崏鏈烘暣鏈虹數婧愮殑鍚姩涓庡叧闂�
+ * 
+ * 鏁版嵁鏍煎紡:
+ *   鎺у埗鍊�(1瀛楄妭): 0=鍏抽棴锛�1=鍚姩
+ *   淇濈暀瀛楁(3瀛楄妭): 0x00濉厖
+ * 
+ * 绀轰緥鐢ㄦ硶:
+ *   1. 鍚姩鏈哄櫒: Control05.buildPowerCommandHex("1") 鎴� Control05.powerOn()
+ *   2. 鍏抽棴鏈哄櫒: Control05.buildPowerCommandHex("0") 鎴� Control05.powerOff()
+ *   3. 鍙戦�佹寚浠�: Control05.sendPowerOnIfDebugSerialOpen()
+ */
+public class Control05 {
+    
+    /**
+     * 鏋勫缓閬ユ帶鏈哄櫒鍚姩鍏抽棴鎸囦护锛堟寚浠ょ被鍨�0x05锛夌殑HEX鏍煎紡瀛楃涓�
+     * 
+     * @param powerValue 鐢垫簮鎺у埗鍊煎瓧绗︿覆锛�"0"琛ㄧず鍏抽棴锛�"1"琛ㄧず鍚姩
+     * @return 鐢垫簮鎺у埗鎸囦护鐨凥EX鏍煎紡瀛楃涓�
+     */
+    public static String buildPowerCommandHex(String powerValue) {
+        // 瑙f瀽鎺у埗鍊�
+        int power = parsePowerValue(powerValue);
+        
+        byte[] commandBytes = buildPowerCommandBytes((byte) power);
+        return bytesToHex(commandBytes);
+    }
+    
+    /**
+     * 鏋勫缓閬ユ帶鏈哄櫒鍚姩鍏抽棴鎸囦护鐨勫瓧鑺傛暟缁�
+     * 
+     * @param powerValue 鐢垫簮鎺у埗鍊硷細0=鍏抽棴锛�1=鍚姩
+     * @return 鐢垫簮鎺у埗鎸囦护鐨勫瓧鑺傛暟缁�
+     */
+    public static byte[] buildPowerCommandBytes(byte powerValue) {
+        // 楠岃瘉鎺у埗鍊艰寖鍥�
+        if (powerValue != 0 && powerValue != 1) {
+            throw new IllegalArgumentException("鐢垫簮鎺у埗鍊煎繀椤绘槸0鎴�1");
+        }
+        
+        int dataLength = 4; // 鎺у埗鍊�1瀛楄妭 + 淇濈暀瀛楁3瀛楄妭
+        
+        ByteBuffer buffer = ByteBuffer.allocate(14); // 鎬婚暱搴�14瀛楄妭
+        buffer.order(ByteOrder.LITTLE_ENDIAN);
+        
+        // 甯уご
+        buffer.put(BluetoothProtocol.FRAME_HEADER);
+        
+        // 鎸囦护绫诲瀷 (0x05)
+        buffer.put((byte) 0x05);
+        
+        // 鏁版嵁闀垮害
+        buffer.putShort((short) dataLength);
+        
+        // 搴忓垪鍙�
+        short sequence = (short) BluetoothProtocol.getNextSequence();
+        buffer.putShort(sequence);
+        
+        // 鎺у埗鍊�
+        buffer.put(powerValue);
+        
+        // 淇濈暀瀛楁 (3瀛楄妭锛屽叏閮ㄥ~0)
+        buffer.put((byte) 0x00);
+        buffer.put((byte) 0x00);
+        buffer.put((byte) 0x00);
+        
+        // 璁$畻CRC16锛堜粠鎸囦护绫诲瀷寮�濮嬪埌鏁版嵁鍐呭缁撴潫锛�
+        byte[] dataForCRC = new byte[9];
+        ByteBuffer tempBuffer = ByteBuffer.allocate(9);
+        tempBuffer.order(ByteOrder.LITTLE_ENDIAN);
+        tempBuffer.put((byte) 0x05); // 鎸囦护绫诲瀷
+        tempBuffer.putShort((short) dataLength); // 鏁版嵁闀垮害
+        tempBuffer.putShort(sequence); // 搴忓垪鍙�
+        tempBuffer.put(powerValue); // 鎺у埗鍊�
+        tempBuffer.put((byte) 0x00); // 淇濈暀瀛楁1
+        tempBuffer.put((byte) 0x00); // 淇濈暀瀛楁2
+        tempBuffer.put((byte) 0x00); // 淇濈暀瀛楁3
+        
+        dataForCRC = tempBuffer.array();
+        int crc = CRC16.calculateCRC16(dataForCRC, 0, dataForCRC.length);
+        buffer.putShort((short) crc);
+        
+        // 甯у熬
+        buffer.put((byte) 0x0D);
+        
+        return buffer.array();
+    }
+    
+    /**
+     * 褰撹皟璇曚覆鍙f墦寮�鏃跺彂閫佸惎鍔ㄦ満鍣ㄦ寚浠�
+     * 
+     * @return 鍙戦�佹垚鍔熻繑鍥瀟rue锛屽惁鍒欒繑鍥瀎alse
+     */
+    public static boolean sendPowerOnIfDebugSerialOpen() {
+        return sendPowerCommandIfDebugSerialOpen((byte) 1);
+    }
+    
+    /**
+     * 褰撹皟璇曚覆鍙f墦寮�鏃跺彂閫佸叧闂満鍣ㄦ寚浠�
+     * 
+     * @return 鍙戦�佹垚鍔熻繑鍥瀟rue锛屽惁鍒欒繑鍥瀎alse
+     */
+    public static boolean sendPowerOffIfDebugSerialOpen() {
+        return sendPowerCommandIfDebugSerialOpen((byte) 0);
+    }
+    
+    /**
+     * 蹇嵎鏂规硶锛氭瀯寤哄惎鍔ㄦ満鍣ㄦ寚浠�
+     * 
+     * @return 鍚姩鎸囦护鐨凥EX瀛楃涓�
+     */
+    public static String powerOn() {
+        return buildPowerCommandHex("1");
+    }
+    
+    /**
+     * 蹇嵎鏂规硶锛氭瀯寤哄叧闂満鍣ㄦ寚浠�
+     * 
+     * @return 鍏抽棴鎸囦护鐨凥EX瀛楃涓�
+     */
+    public static String powerOff() {
+        return buildPowerCommandHex("0");
+    }
+    
+    /**
+     * 瑙f瀽鎺у埗鍊煎瓧绗︿覆
+     */
+    private static int parsePowerValue(String powerStr) {
+        try {
+            int power = Integer.parseInt(powerStr.trim());
+            if (power != 0 && power != 1) {
+                throw new IllegalArgumentException("鐢垫簮鎺у埗鍊煎繀椤绘槸0鎴�1");
+            }
+            return power;
+        } catch (NumberFormatException e) {
+            throw new IllegalArgumentException("鐢垫簮鎺у埗鍊煎繀椤绘槸鏁存暟: " + powerStr);
+        }
+    }
+    
+    /**
+     * 鍙戦�佺數婧愭帶鍒舵寚浠わ紙鍐呴儴鏂规硶锛�
+     */
+    private static boolean sendPowerCommandIfDebugSerialOpen(byte powerValue) {
+        SerialPortService service = sendmessage.getActiveService();
+        if (service == null || !service.isOpen()) {
+            return false;
+        }
+        byte[] payload = buildPowerCommandBytes(powerValue);
+        
+        // 璋冭瘯锛氭墦鍗板彂閫佺殑鏁版嵁
+        System.out.println("鍙戦�佺數婧愭帶鍒舵寚浠�: " + bytesToHex(payload));
+        
+        return sendmessage.sendViaActive(payload);
+    }
+    
+    /**
+     * 瀛楄妭鏁扮粍杞崲涓篐EX瀛楃涓�
+     */
+    private static String bytesToHex(byte[] bytes) {
+        StringBuilder hexString = new StringBuilder();
+        for (int i = 0; i < bytes.length; i++) {
+            String hex = Integer.toHexString(bytes[i] & 0xFF);
+            if (hex.length() == 1) {
+                hexString.append('0');
+            }
+            hexString.append(hex);
+            if (i < bytes.length - 1) {
+                hexString.append(' ');
+            }
+        }
+        return hexString.toString().toUpperCase();
+    }
+    
+    /**
+     * 娴嬭瘯鍑芥暟锛氶獙璇佺數婧愭帶鍒舵寚浠ゆ瀯寤�
+     */
+    public static void testBuildPowerCommand() {
+        System.out.println("=== 娴嬭瘯鐢垫簮鎺у埗鎸囦护鏋勫缓 ===");
+        
+        // 娴嬭瘯1锛氬惎鍔ㄦ寚浠�
+        byte[] cmd1 = buildPowerCommandBytes((byte)1);
+        System.out.println("鍚姩鏈哄櫒: " + bytesToHex(cmd1));
+        
+        // 娴嬭瘯2锛氬叧闂寚浠�
+        byte[] cmd2 = buildPowerCommandBytes((byte)0);
+        System.out.println("鍏抽棴鏈哄櫒: " + bytesToHex(cmd2));
+    }
+}
\ No newline at end of file
diff --git a/src/yaokong/Control06.java b/src/yaokong/Control06.java
new file mode 100644
index 0000000..5e367ff
--- /dev/null
+++ b/src/yaokong/Control06.java
@@ -0,0 +1,245 @@
+package yaokong;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+import chuankou.SerialPortService;
+import chuankou.sendmessage;
+
+/**
+ * Control06.java - 閬ユ帶鍓茶崏鍒�鐩樺崌闄嶆寚浠ょ被
+ * 
+ * 鎸囦护绫诲瀷: 0x06
+ * 鍔熻兘: 鎺у埗鍓茶崏鏈哄垁鐩樼殑鍗囬檷楂樺害
+ * 
+ * 鏁版嵁鏍煎紡:
+ *   鍗囬檷鍊�(1瀛楄妭): -100~100锛屾鏁拌〃绀哄悜涓婃彁鍗�
+ *   淇濈暀瀛楁(3瀛楄妭): 0x00濉厖
+ * 
+ * 绀轰緥鐢ㄦ硶:
+ *   1. 鎻愬崌鍒�鐩�50%: Control06.buildBladeCommandHex("50")
+ *   2. 闄嶄綆鍒�鐩�30%: Control06.buildBladeCommandHex("-30")
+ *   3. 鍙戦�佹彁鍗囨寚浠�: Control06.sendBladeUpIfDebugSerialOpen(50)
+ *   4. 鍙戦�侀檷浣庢寚浠�: Control06.sendBladeDownIfDebugSerialOpen(30)
+ */
+public class Control06 {
+    
+    private static final int MAX_BLADE_VALUE = 100;
+    private static final int MIN_BLADE_VALUE = -100;
+    private static final int BLADE_STEP = 10; // 榛樿姝ヨ繘鍊�
+    
+    private static int currentBladeHeight = 0; // 褰撳墠鍒�鐩橀珮搴︾櫨鍒嗘瘮
+    
+    /**
+     * 鏋勫缓閬ユ帶鍓茶崏鍒�鐩樺崌闄嶆寚浠わ紙鎸囦护绫诲瀷0x06锛夌殑HEX鏍煎紡瀛楃涓�
+     * 
+     * @param bladeValueStr 鍒�鐩橀珮搴﹀�煎瓧绗︿覆锛岃寖鍥�-100鍒�100锛屾鏁拌〃绀哄悜涓婃彁鍗�
+     * @return 鍒�鐩樺崌闄嶆寚浠ょ殑HEX鏍煎紡瀛楃涓�
+     */
+    public static String buildBladeCommandHex(String bladeValueStr) {
+        int bladeValue = parseBladeValue(bladeValueStr);
+        byte[] commandBytes = buildBladeCommandBytes((byte) bladeValue);
+        return bytesToHex(commandBytes);
+    }
+    
+    /**
+     * 鏋勫缓閬ユ帶鍓茶崏鍒�鐩樺崌闄嶆寚浠ょ殑瀛楄妭鏁扮粍
+     * 
+     * @param bladeValue 鍒�鐩橀珮搴﹀�硷細-100~100锛屾鏁拌〃绀哄悜涓婃彁鍗�
+     * @return 鍒�鐩樺崌闄嶆寚浠ょ殑瀛楄妭鏁扮粍
+     */
+    public static byte[] buildBladeCommandBytes(byte bladeValue) {
+        // 楠岃瘉鍒�鐩橀珮搴﹁寖鍥�
+        if (bladeValue < -100 || bladeValue > 100) {
+            throw new IllegalArgumentException("鍒�鐩橀珮搴﹀�煎繀椤诲湪-100鍒�100涔嬮棿");
+        }
+        
+        int dataLength = 4; // 鍗囬檷鍊�1瀛楄妭 + 淇濈暀瀛楁3瀛楄妭
+        
+        ByteBuffer buffer = ByteBuffer.allocate(14); // 鎬婚暱搴�14瀛楄妭
+        buffer.order(ByteOrder.LITTLE_ENDIAN);
+        
+        // 甯уご
+        buffer.put(BluetoothProtocol.FRAME_HEADER);
+        
+        // 鎸囦护绫诲瀷 (0x06)
+        buffer.put((byte) 0x06);
+        
+        // 鏁版嵁闀垮害
+        buffer.putShort((short) dataLength);
+        
+        // 搴忓垪鍙�
+        short sequence = (short) BluetoothProtocol.getNextSequence();
+        buffer.putShort(sequence);
+        
+        // 鍒�鐩橀珮搴﹀��
+        buffer.put(bladeValue);
+        
+        // 淇濈暀瀛楁 (3瀛楄妭锛屽叏閮ㄥ~0)
+        buffer.put((byte) 0x00);
+        buffer.put((byte) 0x00);
+        buffer.put((byte) 0x00);
+        
+        // 璁$畻CRC16锛堜粠鎸囦护绫诲瀷寮�濮嬪埌鏁版嵁鍐呭缁撴潫锛�
+        byte[] dataForCRC = new byte[9];
+        ByteBuffer tempBuffer = ByteBuffer.allocate(9);
+        tempBuffer.order(ByteOrder.LITTLE_ENDIAN);
+        tempBuffer.put((byte) 0x06); // 鎸囦护绫诲瀷
+        tempBuffer.putShort((short) dataLength); // 鏁版嵁闀垮害
+        tempBuffer.putShort(sequence); // 搴忓垪鍙�
+        tempBuffer.put(bladeValue); // 鍒�鐩橀珮搴﹀��
+        tempBuffer.put((byte) 0x00); // 淇濈暀瀛楁1
+        tempBuffer.put((byte) 0x00); // 淇濈暀瀛楁2
+        tempBuffer.put((byte) 0x00); // 淇濈暀瀛楁3
+        
+        dataForCRC = tempBuffer.array();
+        int crc = CRC16.calculateCRC16(dataForCRC, 0, dataForCRC.length);
+        buffer.putShort((short) crc);
+        
+        // 甯у熬
+        buffer.put((byte) 0x0D);
+        
+        return buffer.array();
+    }
+    
+    /**
+     * 褰撹皟璇曚覆鍙f墦寮�鏃跺彂閫佹彁鍗囧垁鐩樻寚浠�
+     * 
+     * @param value 鎻愬崌鐧惧垎姣斿�硷紙0-100锛�
+     * @return 鍙戦�佹垚鍔熻繑鍥瀟rue锛屽惁鍒欒繑鍥瀎alse
+     */
+    public static boolean sendBladeUpIfDebugSerialOpen(int value) {
+        int targetHeight = Math.min(currentBladeHeight + value, MAX_BLADE_VALUE);
+        return sendBladeHeightIfDebugSerialOpen(targetHeight);
+    }
+    
+    /**
+     * 褰撹皟璇曚覆鍙f墦寮�鏃跺彂閫侀檷浣庡垁鐩樻寚浠�
+     * 
+     * @param value 闄嶄綆鐧惧垎姣斿�硷紙0-100锛�
+     * @return 鍙戦�佹垚鍔熻繑鍥瀟rue锛屽惁鍒欒繑鍥瀎alse
+     */
+    public static boolean sendBladeDownIfDebugSerialOpen(int value) {
+        int targetHeight = Math.max(currentBladeHeight - value, MIN_BLADE_VALUE);
+        return sendBladeHeightIfDebugSerialOpen(targetHeight);
+    }
+    
+    /**
+     * 褰撹皟璇曚覆鍙f墦寮�鏃跺彂閫佸垁鐩樺綊闆舵寚浠�
+     * 
+     * @return 鍙戦�佹垚鍔熻繑鍥瀟rue锛屽惁鍒欒繑鍥瀎alse
+     */
+    public static boolean sendBladeResetIfDebugSerialOpen() {
+        return sendBladeHeightIfDebugSerialOpen(0);
+    }
+    
+    /**
+     * 蹇嵎鏂规硶锛氭瀯寤烘彁鍗囧垁鐩樻寚浠�
+     * 
+     * @param value 鎻愬崌鐧惧垎姣斿�硷紙0-100锛�
+     * @return 鎻愬崌鎸囦护鐨凥EX瀛楃涓�
+     */
+    public static String bladeUp(int value) {
+        int targetValue = Math.min(Math.max(value, 0), MAX_BLADE_VALUE);
+        return buildBladeCommandHex(String.valueOf(targetValue));
+    }
+    
+    /**
+     * 蹇嵎鏂规硶锛氭瀯寤洪檷浣庡垁鐩樻寚浠�
+     * 
+     * @param value 闄嶄綆鐧惧垎姣斿�硷紙0-100锛�
+     * @return 闄嶄綆鎸囦护鐨凥EX瀛楃涓�
+     */
+    public static String bladeDown(int value) {
+        int targetValue = Math.max(Math.min(-value, 0), MIN_BLADE_VALUE);
+        return buildBladeCommandHex(String.valueOf(targetValue));
+    }
+    
+    /**
+     * 瑙f瀽鍒�鐩橀珮搴﹀�煎瓧绗︿覆
+     */
+    private static int parseBladeValue(String bladeStr) {
+        try {
+            int value = Integer.parseInt(bladeStr.trim());
+            if (value < MIN_BLADE_VALUE || value > MAX_BLADE_VALUE) {
+                throw new IllegalArgumentException("鍒�鐩橀珮搴﹀�煎繀椤诲湪-100鍒�100涔嬮棿");
+            }
+            return value;
+        } catch (NumberFormatException e) {
+            throw new IllegalArgumentException("鍒�鐩橀珮搴﹀�煎繀椤绘槸鏁存暟: " + bladeStr);
+        }
+    }
+    
+    /**
+     * 鍙戦�佸垁鐩橀珮搴︽帶鍒舵寚浠わ紙鍐呴儴鏂规硶锛�
+     */
+    private static boolean sendBladeHeightIfDebugSerialOpen(int targetHeight) {
+        SerialPortService service = sendmessage.getActiveService();
+        if (service == null || !service.isOpen()) {
+            return false;
+        }
+        byte[] payload = buildBladeCommandBytes((byte) targetHeight);
+        
+        // 璋冭瘯锛氭墦鍗板彂閫佺殑鏁版嵁
+        System.out.println("鍙戦�佸垁鐩樺崌闄嶆寚浠�: " + bytesToHex(payload));
+        
+        boolean success = sendmessage.sendViaActive(payload);
+        if (success) {
+            currentBladeHeight = targetHeight;
+        }
+        return success;
+    }
+    
+    /**
+     * 鑾峰彇褰撳墠鍒�鐩橀珮搴�
+     * 
+     * @return 褰撳墠鍒�鐩橀珮搴︾櫨鍒嗘瘮锛�-100鍒�100锛�
+     */
+    public static int getCurrentBladeHeight() {
+        return currentBladeHeight;
+    }
+    
+    /**
+     * 閲嶇疆鍒�鐩橀珮搴︿负0
+     */
+    public static void resetBladeHeight() {
+        currentBladeHeight = 0;
+    }
+    
+    /**
+     * 瀛楄妭鏁扮粍杞崲涓篐EX瀛楃涓�
+     */
+    private static String bytesToHex(byte[] bytes) {
+        StringBuilder hexString = new StringBuilder();
+        for (int i = 0; i < bytes.length; i++) {
+            String hex = Integer.toHexString(bytes[i] & 0xFF);
+            if (hex.length() == 1) {
+                hexString.append('0');
+            }
+            hexString.append(hex);
+            if (i < bytes.length - 1) {
+                hexString.append(' ');
+            }
+        }
+        return hexString.toString().toUpperCase();
+    }
+    
+    /**
+     * 娴嬭瘯鍑芥暟锛氶獙璇佸垁鐩樺崌闄嶆寚浠ゆ瀯寤�
+     */
+    public static void testBuildBladeCommand() {
+        System.out.println("=== 娴嬭瘯鍒�鐩樺崌闄嶆寚浠ゆ瀯寤� ===");
+        
+        // 娴嬭瘯1锛氭彁鍗囨寚浠�
+        byte[] cmd1 = buildBladeCommandBytes((byte)50);
+        System.out.println("鎻愬崌50%: " + bytesToHex(cmd1));
+        
+        // 娴嬭瘯2锛氶檷浣庢寚浠�
+        byte[] cmd2 = buildBladeCommandBytes((byte)-30);
+        System.out.println("闄嶄綆30%: " + bytesToHex(cmd2));
+        
+        // 娴嬭瘯3锛氬綊闆舵寚浠�
+        byte[] cmd3 = buildBladeCommandBytes((byte)0);
+        System.out.println("褰掗浂: " + bytesToHex(cmd3));
+    }
+}
\ No newline at end of file
diff --git a/src/yaokong/Control07.java b/src/yaokong/Control07.java
new file mode 100644
index 0000000..5c1ab2e
--- /dev/null
+++ b/src/yaokong/Control07.java
@@ -0,0 +1,198 @@
+package yaokong;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+import chuankou.SerialPortService;
+import chuankou.sendmessage;
+
+/**
+ * Control07.java - 閬ユ帶澶х伅寮�鍏虫寚浠ょ被
+ * 
+ * 鎸囦护绫诲瀷: 0x07
+ * 鍔熻兘: 鎺у埗鍓茶崏鏈哄ぇ鐏殑寮�鍏�
+ * 
+ * 鏁版嵁鏍煎紡:
+ *   寮�鍏崇姸鎬�(1瀛楄妭): 0=鍏抽棴锛�1=寮�鍚�
+ *   淇濈暀瀛楁(3瀛楄妭): 0x00濉厖
+ * 
+ * 绀轰緥鐢ㄦ硶:
+ *   1. 寮�鍚ぇ鐏�: Control07.buildLightCommandHex("1") 鎴� Control07.lightOn()
+ *   2. 鍏抽棴澶х伅: Control07.buildLightCommandHex("0") 鎴� Control07.lightOff()
+ *   3. 鍙戦�佸紑鐏寚浠�: Control07.sendLightOnIfDebugSerialOpen()
+ *   4. 鍙戦�佸叧鐏寚浠�: Control07.sendLightOffIfDebugSerialOpen()
+ */
+public class Control07 {
+    
+    /**
+     * 鏋勫缓閬ユ帶澶х伅寮�鍏虫寚浠わ紙鎸囦护绫诲瀷0x07锛夌殑HEX鏍煎紡瀛楃涓�
+     * 
+     * @param lightValueStr 澶х伅寮�鍏崇姸鎬佸瓧绗︿覆锛�"0"琛ㄧず鍏抽棴锛�"1"琛ㄧず寮�鍚�
+     * @return 澶х伅鎺у埗鎸囦护鐨凥EX鏍煎紡瀛楃涓�
+     */
+    public static String buildLightCommandHex(String lightValueStr) {
+        int lightValue = parseLightValue(lightValueStr);
+        byte[] commandBytes = buildLightCommandBytes((byte) lightValue);
+        return bytesToHex(commandBytes);
+    }
+    
+    /**
+     * 鏋勫缓閬ユ帶澶х伅寮�鍏虫寚浠ょ殑瀛楄妭鏁扮粍
+     * 
+     * @param lightValue 澶х伅寮�鍏崇姸鎬侊細0=鍏抽棴锛�1=寮�鍚�
+     * @return 澶х伅鎺у埗鎸囦护鐨勫瓧鑺傛暟缁�
+     */
+    public static byte[] buildLightCommandBytes(byte lightValue) {
+        // 楠岃瘉寮�鍏崇姸鎬佽寖鍥�
+        if (lightValue != 0 && lightValue != 1) {
+            throw new IllegalArgumentException("澶х伅寮�鍏崇姸鎬佸繀椤绘槸0鎴�1");
+        }
+        
+        int dataLength = 4; // 寮�鍏崇姸鎬�1瀛楄妭 + 淇濈暀瀛楁3瀛楄妭
+        
+        ByteBuffer buffer = ByteBuffer.allocate(14); // 鎬婚暱搴�14瀛楄妭
+        buffer.order(ByteOrder.LITTLE_ENDIAN);
+        
+        // 甯уご
+        buffer.put(BluetoothProtocol.FRAME_HEADER);
+        
+        // 鎸囦护绫诲瀷 (0x07)
+        buffer.put((byte) 0x07);
+        
+        // 鏁版嵁闀垮害
+        buffer.putShort((short) dataLength);
+        
+        // 搴忓垪鍙�
+        short sequence = (short) BluetoothProtocol.getNextSequence();
+        buffer.putShort(sequence);
+        
+        // 澶х伅寮�鍏崇姸鎬�
+        buffer.put(lightValue);
+        
+        // 淇濈暀瀛楁 (3瀛楄妭锛屽叏閮ㄥ~0)
+        buffer.put((byte) 0x00);
+        buffer.put((byte) 0x00);
+        buffer.put((byte) 0x00);
+        
+        // 璁$畻CRC16锛堜粠鎸囦护绫诲瀷寮�濮嬪埌鏁版嵁鍐呭缁撴潫锛�
+        byte[] dataForCRC = new byte[9];
+        ByteBuffer tempBuffer = ByteBuffer.allocate(9);
+        tempBuffer.order(ByteOrder.LITTLE_ENDIAN);
+        tempBuffer.put((byte) 0x07); // 鎸囦护绫诲瀷
+        tempBuffer.putShort((short) dataLength); // 鏁版嵁闀垮害
+        tempBuffer.putShort(sequence); // 搴忓垪鍙�
+        tempBuffer.put(lightValue); // 澶х伅寮�鍏崇姸鎬�
+        tempBuffer.put((byte) 0x00); // 淇濈暀瀛楁1
+        tempBuffer.put((byte) 0x00); // 淇濈暀瀛楁2
+        tempBuffer.put((byte) 0x00); // 淇濈暀瀛楁3
+        
+        dataForCRC = tempBuffer.array();
+        int crc = CRC16.calculateCRC16(dataForCRC, 0, dataForCRC.length);
+        buffer.putShort((short) crc);
+        
+        // 甯у熬
+        buffer.put((byte) 0x0D);
+        
+        return buffer.array();
+    }
+    
+    /**
+     * 褰撹皟璇曚覆鍙f墦寮�鏃跺彂閫佸紑鍚ぇ鐏寚浠�
+     * 
+     * @return 鍙戦�佹垚鍔熻繑鍥瀟rue锛屽惁鍒欒繑鍥瀎alse
+     */
+    public static boolean sendLightOnIfDebugSerialOpen() {
+        return sendLightCommandIfDebugSerialOpen((byte) 1);
+    }
+    
+    /**
+     * 褰撹皟璇曚覆鍙f墦寮�鏃跺彂閫佸叧闂ぇ鐏寚浠�
+     * 
+     * @return 鍙戦�佹垚鍔熻繑鍥瀟rue锛屽惁鍒欒繑鍥瀎alse
+     */
+    public static boolean sendLightOffIfDebugSerialOpen() {
+        return sendLightCommandIfDebugSerialOpen((byte) 0);
+    }
+    
+    /**
+     * 蹇嵎鏂规硶锛氭瀯寤哄紑鍚ぇ鐏寚浠�
+     * 
+     * @return 寮�鐏寚浠ょ殑HEX瀛楃涓�
+     */
+    public static String lightOn() {
+        return buildLightCommandHex("1");
+    }
+    
+    /**
+     * 蹇嵎鏂规硶锛氭瀯寤哄叧闂ぇ鐏寚浠�
+     * 
+     * @return 鍏崇伅鎸囦护鐨凥EX瀛楃涓�
+     */
+    public static String lightOff() {
+        return buildLightCommandHex("0");
+    }
+    
+    /**
+     * 瑙f瀽澶х伅寮�鍏崇姸鎬佸瓧绗︿覆
+     */
+    private static int parseLightValue(String lightStr) {
+        try {
+            int light = Integer.parseInt(lightStr.trim());
+            if (light != 0 && light != 1) {
+                throw new IllegalArgumentException("澶х伅寮�鍏崇姸鎬佸繀椤绘槸0鎴�1");
+            }
+            return light;
+        } catch (NumberFormatException e) {
+            throw new IllegalArgumentException("澶х伅寮�鍏崇姸鎬佸繀椤绘槸鏁存暟: " + lightStr);
+        }
+    }
+    
+    /**
+     * 鍙戦�佸ぇ鐏帶鍒舵寚浠わ紙鍐呴儴鏂规硶锛�
+     */
+    private static boolean sendLightCommandIfDebugSerialOpen(byte lightValue) {
+        SerialPortService service = sendmessage.getActiveService();
+        if (service == null || !service.isOpen()) {
+            return false;
+        }
+        byte[] payload = buildLightCommandBytes(lightValue);
+        
+        // 璋冭瘯锛氭墦鍗板彂閫佺殑鏁版嵁
+        System.out.println("鍙戦�佸ぇ鐏帶鍒舵寚浠�: " + bytesToHex(payload));
+        
+        return sendmessage.sendViaActive(payload);
+    }
+    
+    /**
+     * 瀛楄妭鏁扮粍杞崲涓篐EX瀛楃涓�
+     */
+    private static String bytesToHex(byte[] bytes) {
+        StringBuilder hexString = new StringBuilder();
+        for (int i = 0; i < bytes.length; i++) {
+            String hex = Integer.toHexString(bytes[i] & 0xFF);
+            if (hex.length() == 1) {
+                hexString.append('0');
+            }
+            hexString.append(hex);
+            if (i < bytes.length - 1) {
+                hexString.append(' ');
+            }
+        }
+        return hexString.toString().toUpperCase();
+    }
+    
+    /**
+     * 娴嬭瘯鍑芥暟锛氶獙璇佸ぇ鐏帶鍒舵寚浠ゆ瀯寤�
+     */
+    public static void testBuildLightCommand() {
+        System.out.println("=== 娴嬭瘯澶х伅鎺у埗鎸囦护鏋勫缓 ===");
+        
+        // 娴嬭瘯1锛氬紑鐏寚浠�
+        byte[] cmd1 = buildLightCommandBytes((byte)1);
+        System.out.println("寮�鍚ぇ鐏�: " + bytesToHex(cmd1));
+        
+        // 娴嬭瘯2锛氬叧鐏寚浠�
+        byte[] cmd2 = buildLightCommandBytes((byte)0);
+        System.out.println("鍏抽棴澶х伅: " + bytesToHex(cmd2));
+    }
+}
\ No newline at end of file
diff --git a/src/yaokong/RemoteControlDialog.java b/src/yaokong/RemoteControlDialog.java
index af45f17..850a14c 100644
--- a/src/yaokong/RemoteControlDialog.java
+++ b/src/yaokong/RemoteControlDialog.java
@@ -20,6 +20,14 @@
     private JLabel moveJoystickValueLabel;
     private JLabel turnJoystickValueLabel;
     private Timer speedUpdateTimer;  // 閫熷害鏇存柊瀹氭椂鍣�
+    // 鎽囨潌鎺у埗瀹氭椂鍣細鎸佺画鍙戦�佹帶鍒舵寚浠�
+    private Timer forwardControlTimer;  // 鍓嶈繘/鍚庨��鎺у埗瀹氭椂鍣�
+    private Timer steeringControlTimer;  // 杞悜鎺у埗瀹氭椂鍣�
+    private int targetForwardSpeed = 0;  // 鐩爣鍓嶈繘/鍚庨��閫熷害
+    private int targetSteeringSpeed = 0;  // 鐩爣杞悜閫熷害
+    private List<JButton> bladeButtons = new ArrayList<>();  // 瀛樺偍鍒�鐩樻帶鍒舵寜閽紝鐢ㄤ簬娓呯悊瀹氭椂鍣�
+    private String bladeUpDefaultText = "鍒�鐩樺崌";  // 鍒�鐩樺崌鎸夐挳榛樿鏂囧瓧
+    private String bladeDownDefaultText = "鍒�鐩橀檷";  // 鍒�鐩橀檷鎸夐挳榛樿鏂囧瓧
     
     public RemoteControlDialog(Component parent, Color themeColor, JLabel speedLabel) {
         super(parent != null ? (JFrame) SwingUtilities.getWindowAncestor(parent) : null,
@@ -31,7 +39,7 @@
         Control03.resetSpeeds();
         Control03.sendNeutralCommandIfDebugSerialOpen();
         int dialogWidth = UIConfig.DIALOG_WIDTH;  // 涓庨椤靛搴︿竴鑷�
-        int dialogHeight = (int) (UIConfig.DIALOG_HEIGHT / 3.0 * 0.7) + 50 + 40;  // 澧炲姞40鍍忕礌
+        int dialogHeight = (int) (UIConfig.DIALOG_HEIGHT / 3.0 * 0.7) +150;  // 澧炲姞90鍍忕礌锛堝師40+鏂板40+鍐嶅10锛�
         initializeDialog(dialogWidth, dialogHeight);
         initializeRemoteContent();
         startSpeedUpdateTimer();  // 鍚姩閫熷害鏇存柊瀹氭椂鍣�
@@ -54,13 +62,69 @@
         setSize(width, height);
         setLocationRelativeTo(getOwner());
         setResizable(false);
-        getContentPane().setBackground(new Color(26, 26, 46)); // 娣辫壊鑳屾櫙
+        Container contentPane = getContentPane();
+        if (contentPane instanceof JComponent) {
+            JComponent jContentPane = (JComponent) contentPane;
+            jContentPane.setBackground(new Color(0, 0, 0, 0)); // 閫忔槑鑳屾櫙
+            jContentPane.setOpaque(false);
+        }
     }
     
     private void initializeRemoteContent() {
         JPanel contentPanel = new JPanel(new BorderLayout());
         contentPanel.setBorder(BorderFactory.createEmptyBorder(16, 16, 16, 16));
-        contentPanel.setBackground(Color.WHITE);
+        contentPanel.setOpaque(false); // 璁剧疆涓洪�忔槑
+
+        // 鍒涘缓椤堕儴闈㈡澘锛屽寘鍚寜閽拰鏁板�兼樉绀�
+        JPanel topPanel = new JPanel(new BorderLayout());
+        topPanel.setOpaque(false);
+        
+        // 鍒涘缓鎸夐挳闈㈡澘锛�4涓寜閽紝闂撮殧30鍍忕礌锛�
+        JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 30, 0));
+        buttonPanel.setOpaque(false);
+        buttonPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 8, 0)); // 搴曢儴闂磋窛8鍍忕礌
+        
+        // 鎸夐挳1锛歰ff.png <-> starton.png锛屾枃瀛楋細鍚姩 <-> 鐔勭伀
+        JLabel label1 = new JLabel("鍚姩", SwingConstants.CENTER);
+        label1.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 12));
+        label1.setForeground(new Color(40, 40, 40));
+        JButton button1 = createIconButtonWithLabel("image/off.png", "image/starton.png", 27, 27, 
+                                                     label1, "鍚姩", "鐔勭伀", "POWER");
+        JPanel panel1 = createButtonWithLabel(button1, label1);
+        buttonPanel.add(panel1);
+        
+        // 鎸夐挳2锛歞engoff.png <-> dengon.png锛屾枃瀛楋細寮�鐏� <-> 鍏崇伅
+        JLabel label2 = new JLabel("寮�鐏�", SwingConstants.CENTER);
+        label2.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 12));
+        label2.setForeground(new Color(40, 40, 40));
+        JButton button2 = createIconButtonWithLabel("image/dengoff.png", "image/dengon.png", 30, 30,
+                                                     label2, "寮�鐏�", "鍏崇伅", "LIGHT");
+        JPanel panel2 = createButtonWithLabel(button2, label2);
+        buttonPanel.add(panel2);
+        
+        // 鎸夐挳3锛歺ia1.png锛堝浐瀹氬浘鏍囷級锛屾枃瀛楋細鍒�鐩樺崌
+        JButton button3 = createBladeControlButton("image/xia1.png", "image/xia20.png", 30, 30, true);
+        JLabel label3 = new JLabel("鍒�鐩樺崌", SwingConstants.CENTER);
+        label3.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 12));
+        label3.setForeground(new Color(40, 40, 40));
+        button3.putClientProperty("label", label3);
+        button3.putClientProperty("defaultText", bladeUpDefaultText);
+        bladeButtons.add(button3);  // 娣诲姞鍒板垪琛ㄤ互渚挎竻鐞�
+        JPanel panel3 = createButtonWithLabel(button3, label3);
+        buttonPanel.add(panel3);
+        
+        // 鎸夐挳4锛歺ia2.png锛堝浐瀹氬浘鏍囷級锛屾枃瀛楋細鍒�鐩橀檷
+        JButton button4 = createBladeControlButton("image/xia2.png", "image/xia10.png", 30, 30, false);
+        JLabel label4 = new JLabel("鍒�鐩橀檷", SwingConstants.CENTER);
+        label4.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 12));
+        label4.setForeground(new Color(40, 40, 40));
+        button4.putClientProperty("label", label4);
+        button4.putClientProperty("defaultText", bladeDownDefaultText);
+        bladeButtons.add(button4);  // 娣诲姞鍒板垪琛ㄤ互渚挎竻鐞�
+        JPanel panel4 = createButtonWithLabel(button4, label4);
+        buttonPanel.add(panel4);
+        
+        topPanel.add(buttonPanel, BorderLayout.NORTH);
 
         // 鍦ㄦ瘡涓憞鏉嗕笂鏂瑰垎鍒樉绀哄叾鐘舵��/鏁板�硷紙鍒嗕袱鍒楋級
         JPanel valuesPanel = new JPanel(new GridLayout(1, 2, 20, 0));
@@ -80,14 +144,16 @@
 
         valuesPanel.add(moveJoystickValueLabel);
         valuesPanel.add(turnJoystickValueLabel);
-        contentPanel.add(valuesPanel, BorderLayout.NORTH);
+        topPanel.add(valuesPanel, BorderLayout.CENTER);
+        
+        contentPanel.add(topPanel, BorderLayout.NORTH);
 
         // 鍒涘缓鎽囨潌闈㈡澘
         JPanel joystickPanel = new JPanel(new GridLayout(1, 2, 20, 0));
         joystickPanel.setOpaque(false);
 
         // 绉诲姩鎽囨潌锛堢豢鑹蹭富棰橈級
-        moveJoystick = new ModernJoystickComponent("鍓嶈繘/鍚庨��", 
+        moveJoystick = new ModernJoystickComponent(" ", 
             new Color(46, 204, 113), true);  // 缁胯壊涓婚
         moveJoystick.setJoystickListener(new JoystickListener() {
             @Override
@@ -98,10 +164,15 @@
                 // 闄愬埗鍦� [-100, 100]
                 forwardVal = Math.max(-100, Math.min(100, forwardVal));
 
+                // 鏇存柊鐩爣閫熷害
+                targetForwardSpeed = forwardVal;
+
                 if (Math.abs(y) > 0.1) {
-                    applyForwardSpeed(forwardVal);
+                    // 鎽囨潌涓嶅湪涓績浣嶇疆锛屽惎鍔ㄦ寔缁彂閫佸畾鏃跺櫒
+                    startForwardControlTimer();
                 } else {
-                    // 鍓嶅悗
+                    // 鎽囨潌鍥炲埌涓績浣嶇疆锛屽仠姝㈠彂閫�
+                    stopForwardControlTimer();
                     stopForward();
                 }
 
@@ -111,7 +182,7 @@
             }
         });
         // 杞悜鎽囨潌锛堣摑鑹蹭富棰橈級
-        turnJoystick = new ModernJoystickComponent("宸﹁浆/鍙宠浆", 
+        turnJoystick = new ModernJoystickComponent("", 
             new Color(52, 152, 219), false);  // 钃濊壊涓婚
         turnJoystick.setJoystickListener(new JoystickListener() {
             @Override
@@ -121,10 +192,15 @@
                 int steeringVal = (int) Math.round(x * 100.0);
                 steeringVal = Math.max(-100, Math.min(100, steeringVal));
 
+                // 鏇存柊鐩爣閫熷害
+                targetSteeringSpeed = steeringVal;
+
                 if (Math.abs(x) > 0.1) {
-                    applySteeringSpeed(steeringVal);
+                    // 鎽囨潌涓嶅湪涓績浣嶇疆锛屽惎鍔ㄦ寔缁彂閫佸畾鏃跺櫒
+                    startSteeringControlTimer();
                 } else {
-                    // 宸﹀彸
+                    // 鎽囨潌鍥炲埌涓績浣嶇疆锛屽仠姝㈠彂閫�
+                    stopSteeringControlTimer();
                     stopSteering();
                 }
 
@@ -142,6 +218,360 @@
         getContentPane().add(contentPanel);
     }
 
+    /**
+     * 鍒涘缓甯︽爣绛剧殑鎸夐挳闈㈡澘锛堝瀭鐩村竷灞�锛氭寜閽湪涓婏紝鏍囩鍦ㄤ笅锛�
+     * @param button 鎸夐挳
+     * @param label 鏍囩
+     * @return 鍖呭惈鎸夐挳鍜屾爣绛剧殑闈㈡澘
+     */
+    private JPanel createButtonWithLabel(JButton button, JLabel label) {
+        JPanel panel = new JPanel(new BorderLayout());
+        panel.setOpaque(false);
+        panel.add(button, BorderLayout.CENTER);
+        panel.add(label, BorderLayout.SOUTH);
+        panel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
+        return panel;
+    }
+
+    /**
+     * 鍒涘缓鍙垏鎹㈠浘鏍囩殑鎸夐挳锛堝甫鏍囩鏂囧瓧鍒囨崲锛�
+     * @param defaultIconPath 榛樿鍥炬爣璺緞
+     * @param clickedIconPath 鐐瑰嚮鍚庣殑鍥炬爣璺緞
+     * @param width 鍥炬爣瀹藉害
+     * @param height 鍥炬爣楂樺害
+     * @param label 鏍囩缁勪欢
+     * @param defaultText 榛樿鏂囧瓧
+     * @param clickedText 鐐瑰嚮鍚庣殑鏂囧瓧
+     * @param controlType 鎺у埗鎸囦护绫诲瀷锛�"POWER"琛ㄧず鍚姩/鐔勭伀锛�"LIGHT"琛ㄧず寮�鐏�/鍏崇伅锛宯ull琛ㄧず鏃犳帶鍒舵寚浠�
+     * @return 閰嶇疆濂界殑鎸夐挳
+     */
+    private JButton createIconButtonWithLabel(String defaultIconPath, String clickedIconPath, 
+                                               int width, int height, JLabel label, 
+                                               String defaultText, String clickedText, String controlType) {
+        JButton button = new JButton();
+        button.setPreferredSize(new Dimension(width, height));
+        button.setMinimumSize(new Dimension(width, height));
+        button.setMaximumSize(new Dimension(width, height));
+        button.setContentAreaFilled(false);
+        button.setBorderPainted(false);
+        button.setFocusPainted(false);
+        
+        // 鍔犺浇榛樿鍥炬爣鍜岀偣鍑诲浘鏍�
+        ImageIcon defaultIcon = loadIcon(defaultIconPath, width, height);
+        ImageIcon clickedIcon = loadIcon(clickedIconPath, width, height);
+        
+        if (defaultIcon != null) {
+            button.setIcon(defaultIcon);
+        }
+        
+        // 浣跨敤 clientProperty 鏉ヨ窡韪寜閽姸鎬侊紙false=榛樿鍥炬爣锛宼rue=鐐瑰嚮鍥炬爣锛�
+        button.putClientProperty("isClicked", false);
+        button.putClientProperty("defaultIcon", defaultIcon);
+        button.putClientProperty("clickedIcon", clickedIcon);
+        button.putClientProperty("label", label);
+        button.putClientProperty("defaultText", defaultText);
+        button.putClientProperty("clickedText", clickedText);
+        button.putClientProperty("controlType", controlType);
+        
+        // 娣诲姞鐐瑰嚮浜嬩欢锛屽垏鎹㈠浘鏍囧拰鏂囧瓧锛屽苟鍙戦�佹帶鍒舵寚浠�
+        button.addActionListener(e -> {
+            Boolean isClicked = (Boolean) button.getClientProperty("isClicked");
+            ImageIcon defaultIconRef = (ImageIcon) button.getClientProperty("defaultIcon");
+            ImageIcon clickedIconRef = (ImageIcon) button.getClientProperty("clickedIcon");
+            JLabel labelRef = (JLabel) button.getClientProperty("label");
+            String defaultTextRef = (String) button.getClientProperty("defaultText");
+            String clickedTextRef = (String) button.getClientProperty("clickedText");
+            String controlTypeRef = (String) button.getClientProperty("controlType");
+            
+            if (isClicked == null || !isClicked) {
+                // 褰撳墠鏄粯璁ゅ浘鏍囷紝鍒囨崲鍒扮偣鍑诲浘鏍�
+                if (clickedIconRef != null) {
+                    button.setIcon(clickedIconRef);
+                    button.putClientProperty("isClicked", true);
+                }
+                // 鏇存柊鏍囩鏂囧瓧
+                if (labelRef != null && clickedTextRef != null) {
+                    labelRef.setText(clickedTextRef);
+                }
+                // 鍙戦�佹帶鍒舵寚浠わ紙寮�鍚級
+                if ("POWER".equals(controlTypeRef)) {
+                    boolean success = Control05.sendPowerOnIfDebugSerialOpen();
+                    if (!success) {
+                        showSerialClosedWarning();
+                    }
+                } else if ("LIGHT".equals(controlTypeRef)) {
+                    boolean success = Control07.sendLightOnIfDebugSerialOpen();
+                    if (!success) {
+                        showSerialClosedWarning();
+                    }
+                }
+            } else {
+                // 褰撳墠鏄偣鍑诲浘鏍囷紝鍒囨崲鍥為粯璁ゅ浘鏍�
+                if (defaultIconRef != null) {
+                    button.setIcon(defaultIconRef);
+                    button.putClientProperty("isClicked", false);
+                }
+                // 鏇存柊鏍囩鏂囧瓧
+                if (labelRef != null && defaultTextRef != null) {
+                    labelRef.setText(defaultTextRef);
+                }
+                // 鍙戦�佹帶鍒舵寚浠わ紙鍏抽棴锛�
+                if ("POWER".equals(controlTypeRef)) {
+                    boolean success = Control05.sendPowerOffIfDebugSerialOpen();
+                    if (!success) {
+                        showSerialClosedWarning();
+                    }
+                } else if ("LIGHT".equals(controlTypeRef)) {
+                    boolean success = Control07.sendLightOffIfDebugSerialOpen();
+                    if (!success) {
+                        showSerialClosedWarning();
+                    }
+                }
+            }
+        });
+        
+        return button;
+    }
+
+    /**
+     * 鍒涘缓鍙垏鎹㈠浘鏍囩殑鎸夐挳
+     * @param defaultIconPath 榛樿鍥炬爣璺緞
+     * @param clickedIconPath 鐐瑰嚮鍚庣殑鍥炬爣璺緞
+     * @param width 鍥炬爣瀹藉害
+     * @param height 鍥炬爣楂樺害
+     * @return 閰嶇疆濂界殑鎸夐挳
+     */
+    private JButton createIconButton(String defaultIconPath, String clickedIconPath, int width, int height) {
+        JButton button = new JButton();
+        button.setPreferredSize(new Dimension(width, height));
+        button.setMinimumSize(new Dimension(width, height));
+        button.setMaximumSize(new Dimension(width, height));
+        button.setContentAreaFilled(false);
+        button.setBorderPainted(false);
+        button.setFocusPainted(false);
+        
+        // 鍔犺浇榛樿鍥炬爣鍜岀偣鍑诲浘鏍�
+        ImageIcon defaultIcon = loadIcon(defaultIconPath, width, height);
+        ImageIcon clickedIcon = loadIcon(clickedIconPath, width, height);
+        
+        if (defaultIcon != null) {
+            button.setIcon(defaultIcon);
+        }
+        
+        // 浣跨敤 clientProperty 鏉ヨ窡韪寜閽姸鎬侊紙false=榛樿鍥炬爣锛宼rue=鐐瑰嚮鍥炬爣锛�
+        button.putClientProperty("isClicked", false);
+        button.putClientProperty("defaultIcon", defaultIcon);
+        button.putClientProperty("clickedIcon", clickedIcon);
+        
+        // 娣诲姞鐐瑰嚮浜嬩欢锛屽垏鎹㈠浘鏍�
+        button.addActionListener(e -> {
+            Boolean isClicked = (Boolean) button.getClientProperty("isClicked");
+            ImageIcon defaultIconRef = (ImageIcon) button.getClientProperty("defaultIcon");
+            ImageIcon clickedIconRef = (ImageIcon) button.getClientProperty("clickedIcon");
+            
+            if (isClicked == null || !isClicked) {
+                // 褰撳墠鏄粯璁ゅ浘鏍囷紝鍒囨崲鍒扮偣鍑诲浘鏍�
+                if (clickedIconRef != null) {
+                    button.setIcon(clickedIconRef);
+                    button.putClientProperty("isClicked", true);
+                }
+            } else {
+                // 褰撳墠鏄偣鍑诲浘鏍囷紝鍒囨崲鍥為粯璁ゅ浘鏍�
+                if (defaultIconRef != null) {
+                    button.setIcon(defaultIconRef);
+                    button.putClientProperty("isClicked", false);
+                }
+            }
+        });
+        
+        return button;
+    }
+
+    /**
+     * 鍒涘缓鍥哄畾鍥炬爣鐨勬寜閽�
+     * @param iconPath 鍥炬爣璺緞
+     * @param width 鍥炬爣瀹藉害
+     * @param height 鍥炬爣楂樺害
+     * @return 閰嶇疆濂界殑鎸夐挳
+     */
+    private JButton createFixedIconButton(String iconPath, int width, int height) {
+        JButton button = new JButton();
+        button.setPreferredSize(new Dimension(width, height));
+        button.setMinimumSize(new Dimension(width, height));
+        button.setMaximumSize(new Dimension(width, height));
+        button.setContentAreaFilled(false);
+        button.setBorderPainted(false);
+        button.setFocusPainted(false);
+        
+        ImageIcon icon = loadIcon(iconPath, width, height);
+        if (icon != null) {
+            button.setIcon(icon);
+        }
+        
+        return button;
+    }
+
+    /**
+     * 鍒涘缓鍒�鐩樺崌闄嶆帶鍒舵寜閽紙鏀寔鐐瑰嚮鍜岄暱鎸夛級
+     * @param defaultIconPath 榛樿鍥炬爣璺緞
+     * @param clickedIconPath 鐐瑰嚮/闀挎寜鏃剁殑鍥炬爣璺緞
+     * @param width 鍥炬爣瀹藉害
+     * @param height 鍥炬爣楂樺害
+     * @param isUp true琛ㄧず鍒�鐩樺崌锛宖alse琛ㄧず鍒�鐩橀檷
+     * @return 閰嶇疆濂界殑鎸夐挳
+     */
+    private JButton createBladeControlButton(String defaultIconPath, String clickedIconPath, 
+                                             int width, int height, boolean isUp) {
+        JButton button = new JButton();
+        button.setPreferredSize(new Dimension(width, height));
+        button.setMinimumSize(new Dimension(width, height));
+        button.setMaximumSize(new Dimension(width, height));
+        button.setContentAreaFilled(false);
+        button.setBorderPainted(false);
+        button.setFocusPainted(false);
+        
+        // 鍔犺浇鍥炬爣
+        ImageIcon defaultIcon = loadIcon(defaultIconPath, width, height);
+        ImageIcon clickedIcon = loadIcon(clickedIconPath, width, height);
+        
+        if (defaultIcon != null) {
+            button.setIcon(defaultIcon);
+        }
+        
+        button.putClientProperty("defaultIcon", defaultIcon);
+        button.putClientProperty("clickedIcon", clickedIcon);
+        button.putClientProperty("isUp", isUp);
+        button.putClientProperty("isPressed", false);
+        
+        // 闀挎寜瀹氭椂鍣�
+        Timer longPressTimer = new Timer(100, e -> {  // 姣�100ms鎵ц涓�娆�
+            if (button.getClientProperty("isPressed") == Boolean.TRUE) {
+                boolean isUpButton = (Boolean) button.getClientProperty("isUp");
+                int currentHeight = Control06.getCurrentBladeHeight();
+                
+                // 浣跨敤Control06鐨勬柟娉曞彂閫佹寚浠�
+                boolean success;
+                if (isUpButton) {
+                    success = Control06.sendBladeUpIfDebugSerialOpen(1);
+                } else {
+                    success = Control06.sendBladeDownIfDebugSerialOpen(1);
+                }
+                
+                if (!success) {
+                    showSerialClosedWarning();
+                } else {
+                    // 鏇存柊鎸夐挳鏍囩鏄剧ず褰撳墠鏁板��
+                    updateBladeButtonLabel(button);
+                }
+            }
+        });
+        longPressTimer.setInitialDelay(500);  // 闀挎寜500ms鍚庡紑濮嬭繛缁彉鍖�
+        button.putClientProperty("longPressTimer", longPressTimer);  // 瀛樺偍瀹氭椂鍣ㄥ紩鐢�
+        
+        // 榧犳爣鎸変笅浜嬩欢
+        button.addMouseListener(new MouseAdapter() {
+            @Override
+            public void mousePressed(MouseEvent e) {
+                button.putClientProperty("isPressed", true);
+                
+                // 绔嬪嵆鍒囨崲鍥炬爣
+                ImageIcon clickedIconRef = (ImageIcon) button.getClientProperty("clickedIcon");
+                if (clickedIconRef != null) {
+                    button.setIcon(clickedIconRef);
+                }
+                
+                // 鐐瑰嚮鏃剁珛鍗冲鍔�/鍑忓皯10锛屼娇鐢–ontrol06鐨勬柟娉曞彂閫佹寚浠�
+                boolean isUpButton = (Boolean) button.getClientProperty("isUp");
+                boolean success;
+                if (isUpButton) {
+                    success = Control06.sendBladeUpIfDebugSerialOpen(10);
+                } else {
+                    success = Control06.sendBladeDownIfDebugSerialOpen(10);
+                }
+                
+                if (!success) {
+                    showSerialClosedWarning();
+                } else {
+                    // 鏇存柊鎸夐挳鏍囩鏄剧ず褰撳墠鏁板��
+                    updateBladeButtonLabel(button);
+                }
+                
+                // 鍚姩闀挎寜瀹氭椂鍣�
+                longPressTimer.start();
+            }
+            
+            @Override
+            public void mouseReleased(MouseEvent e) {
+                button.putClientProperty("isPressed", false);
+                
+                // 鍋滄闀挎寜瀹氭椂鍣�
+                longPressTimer.stop();
+                
+                // 鎭㈠榛樿鍥炬爣
+                ImageIcon defaultIconRef = (ImageIcon) button.getClientProperty("defaultIcon");
+                if (defaultIconRef != null) {
+                    button.setIcon(defaultIconRef);
+                }
+                
+                // 鎭㈠榛樿鏂囧瓧
+                JLabel labelRef = (JLabel) button.getClientProperty("label");
+                String defaultTextRef = (String) button.getClientProperty("defaultText");
+                if (labelRef != null && defaultTextRef != null) {
+                    labelRef.setText(defaultTextRef);
+                }
+            }
+        });
+        
+        return button;
+    }
+
+    /**
+     * 鏇存柊鍒�鐩樻寜閽爣绛炬樉绀哄綋鍓嶆暟鍊�
+     * @param button 鎸夐挳
+     */
+    private void updateBladeButtonLabel(JButton button) {
+        JLabel labelRef = (JLabel) button.getClientProperty("label");
+        if (labelRef != null) {
+            String defaultTextRef = (String) button.getClientProperty("defaultText");
+            int currentHeight = Control06.getCurrentBladeHeight();
+            String displayText = defaultTextRef + " " + currentHeight;
+            labelRef.setText(displayText);
+        }
+    }
+
+    /**
+     * 鍔犺浇骞剁缉鏀惧浘鏍�
+     * @param iconPath 鍥炬爣璺緞
+     * @param width 鐩爣瀹藉害
+     * @param height 鐩爣楂樺害
+     * @return 缂╂斁鍚庣殑鍥炬爣
+     */
+    private ImageIcon loadIcon(String iconPath, int width, int height) {
+        try {
+            java.net.URL imgURL = getClass().getClassLoader().getResource(iconPath);
+            if (imgURL == null) {
+                // 灏濊瘯浠庢枃浠剁郴缁熷姞杞�
+                java.io.File imgFile = new java.io.File(iconPath);
+                if (imgFile.exists()) {
+                    ImageIcon originalIcon = new ImageIcon(imgFile.getAbsolutePath());
+                    Image scaledImage = originalIcon.getImage().getScaledInstance(width, height, Image.SCALE_SMOOTH);
+                    ImageIcon scaledIcon = new ImageIcon(scaledImage);
+                    scaledIcon.setDescription(iconPath);
+                    return scaledIcon;
+                }
+            } else {
+                ImageIcon originalIcon = new ImageIcon(imgURL);
+                Image scaledImage = originalIcon.getImage().getScaledInstance(width, height, Image.SCALE_SMOOTH);
+                ImageIcon scaledIcon = new ImageIcon(scaledImage);
+                scaledIcon.setDescription(iconPath);
+                return scaledIcon;
+            }
+        } catch (Exception e) {
+            System.err.println("鏃犳硶鍔犺浇鍥炬爣: " + iconPath + " - " + e.getMessage());
+        }
+        return null;
+    }
+
     private JPanel createStatusPanel(String label, String value, Color color) {
         JPanel panel = new JPanel(new BorderLayout(0, 5));
         panel.setOpaque(false);
@@ -235,6 +665,102 @@
         }
     }
 
+    /**
+     * 鍚姩鍓嶈繘/鍚庨��鎺у埗瀹氭椂鍣紝鎸佺画鍙戦�佹帶鍒舵寚浠�
+     */
+    private void startForwardControlTimer() {
+        // 濡傛灉瀹氭椂鍣ㄥ凡缁忓湪杩愯锛屽厛鍋滄瀹�
+        if (forwardControlTimer != null && forwardControlTimer.isRunning()) {
+            forwardControlTimer.stop();
+        }
+        
+        // 鍒涘缓鏂扮殑瀹氭椂鍣紝姣�100ms鍙戦�佷竴娆℃寚浠�
+        forwardControlTimer = new Timer(100, new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent e) {
+                // 鎸佺画鍙戦�佺洰鏍囬�熷害鐨勬寚浠�
+                applyForwardSpeedContinuously(targetForwardSpeed);
+            }
+        });
+        forwardControlTimer.setInitialDelay(0);
+        forwardControlTimer.start();
+    }
+
+    /**
+     * 鍋滄鍓嶈繘/鍚庨��鎺у埗瀹氭椂鍣�
+     */
+    private void stopForwardControlTimer() {
+        if (forwardControlTimer != null && forwardControlTimer.isRunning()) {
+            forwardControlTimer.stop();
+        }
+    }
+
+    /**
+     * 鍚姩杞悜鎺у埗瀹氭椂鍣紝鎸佺画鍙戦�佹帶鍒舵寚浠�
+     */
+    private void startSteeringControlTimer() {
+        // 濡傛灉瀹氭椂鍣ㄥ凡缁忓湪杩愯锛屽厛鍋滄瀹�
+        if (steeringControlTimer != null && steeringControlTimer.isRunning()) {
+            steeringControlTimer.stop();
+        }
+        
+        // 鍒涘缓鏂扮殑瀹氭椂鍣紝姣�100ms鍙戦�佷竴娆℃寚浠�
+        steeringControlTimer = new Timer(100, new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent e) {
+                // 鎸佺画鍙戦�佺洰鏍囬�熷害鐨勬寚浠�
+                applySteeringSpeedContinuously(targetSteeringSpeed);
+            }
+        });
+        steeringControlTimer.setInitialDelay(0);
+        steeringControlTimer.start();
+    }
+
+    /**
+     * 鍋滄杞悜鎺у埗瀹氭椂鍣�
+     */
+    private void stopSteeringControlTimer() {
+        if (steeringControlTimer != null && steeringControlTimer.isRunning()) {
+            steeringControlTimer.stop();
+        }
+    }
+
+    /**
+     * 鎸佺画鍙戦�佸墠杩�/鍚庨��閫熷害鎸囦护
+     */
+    private void applyForwardSpeedContinuously(int targetSpeed) {
+        int currentSpeed = Control03.getCurrentForwardSpeed();
+        int currentSteeringSpeed = Control03.getCurrentSteeringSpeed();
+        
+        // 濡傛灉宸茬粡杈惧埌鐩爣閫熷害锛岀洿鎺ュ彂閫佷竴娆′互淇濇寔鐘舵��
+        if (currentSpeed == targetSpeed) {
+            // 鐩存帴鍙戦�佺洰鏍囬�熷害鎸囦护浠ヤ繚鎸佺姸鎬侊紙鍗充娇閫熷害鐩稿悓涔熻鍙戦�侊級
+            Control03.setAndSendSpeeds(currentSteeringSpeed, targetSpeed);
+        } else {
+            // 閫愭璋冩暣鍒扮洰鏍囬�熷害
+            int delta = targetSpeed > currentSpeed ? 10 : -10;
+            Control03.adjustForwardSpeed(delta);
+        }
+    }
+
+    /**
+     * 鎸佺画鍙戦�佽浆鍚戦�熷害鎸囦护
+     */
+    private void applySteeringSpeedContinuously(int targetSpeed) {
+        int currentSpeed = Control03.getCurrentSteeringSpeed();
+        int currentForwardSpeed = Control03.getCurrentForwardSpeed();
+        
+        // 濡傛灉宸茬粡杈惧埌鐩爣閫熷害锛岀洿鎺ュ彂閫佷竴娆′互淇濇寔鐘舵��
+        if (currentSpeed == targetSpeed) {
+            // 鐩存帴鍙戦�佺洰鏍囬�熷害鎸囦护浠ヤ繚鎸佺姸鎬侊紙鍗充娇閫熷害鐩稿悓涔熻鍙戦�侊級
+            Control03.setAndSendSpeeds(targetSpeed, currentForwardSpeed);
+        } else {
+            // 閫愭璋冩暣鍒扮洰鏍囬�熷害
+            int delta = targetSpeed > currentSpeed ? 15 : -15;
+            Control03.adjustSteeringSpeed(delta);
+        }
+    }
+
     // 鏇存柊椤堕儴鏄剧ず鐨勬憞鏉嗘暟鍊硷紙鍦� EDT 涓婅皟鐢級锛屾枃瀛楁牴鎹暟鍊兼槧灏勪负鏂瑰悜鎻忚堪
     private void updateJoystickValues(int forwardVal, int steeringVal) {
         // 璁$畻绉诲姩/杞悜鎻忚堪鏂囨湰
@@ -330,6 +856,15 @@
 
     @Override
     public void dispose() {
+        // 鍋滄骞舵竻鐞嗘帶鍒跺畾鏃跺櫒
+        stopForwardControlTimer();
+        stopSteeringControlTimer();
+        if (forwardControlTimer != null) {
+            forwardControlTimer = null;
+        }
+        if (steeringControlTimer != null) {
+            steeringControlTimer = null;
+        }
         // 鍓嶅悗閫熷害鏇存柊瀹氭椂鍣�
         if (speedUpdateTimer != null) {
             speedUpdateTimer.stop();
@@ -349,6 +884,14 @@
         if (turnJoystick != null) {
             turnJoystick.dispose();
         }
+        // 鍋滄骞舵竻鐞嗗垁鐩樻寜閽殑瀹氭椂鍣�
+        for (JButton bladeButton : bladeButtons) {
+            Timer timer = (Timer) bladeButton.getClientProperty("longPressTimer");
+            if (timer != null && timer.isRunning()) {
+                timer.stop();
+            }
+        }
+        bladeButtons.clear();
         super.dispose();
     }
 

--
Gitblit v1.10.0