l1-050 倒数第n个字符串 (15 分)

时间: 2023-05-31 17:01:51 浏览: 216
### 回答1: 题目:请用中文回答:L1-050 倒数第n个字符串。 回答: 假设有一个字符串集合S,按照字典序从小到大排列,其中第一个字符串为"aaa",第二个字符串为"aab",第三个字符串为"aac",以此类推。 现在要求找出倒数第n个字符串。可以通过以下步骤实现: 1. 计算出字符串总数m,即m=26^3。其中26表示字母表中不同字母的数量,这里为26,而3表示字符串的长度,也就是三个字母。 2. 计算出目标字符串的索引位置,即index = m - n + 1。这里需要注意的是,因为字符串集合是按照字典序排列的,所以倒数第n个字符串的索引位置为m-n+1。 3. 将索引位置转化为字符串。例如,若索引位置为123,即倒数第n个字符串为S[123],则可以将123转化为三位三进制数(因为字符串的长度为3),然后将每个数字对应到字母表中的字母即可。具体来说,将数字k转化为字母的方法为将k-1除以26的x次方,x表示当前是第几个字母,从左往右数,然后取余数,再加上字母a即可。 4. 输出得到的字符串即为倒数第n个字符串。 需要注意的是,如果n的值大于m,即倒数第n个字符串不存在,需要进行相应的处理。
相关问题

l1-050 倒数第n个字符串

### 获取倒数第N个字符串的算法 为了实现获取倒数第N个字符串的功能,可以基于字母表和字符串生成逻辑构建解决方案。以下是详细的解释以及示例代码。 #### 算法描述 1. 字符串等差递增序列是从 `L` 个 `'a'` 开始,逐步增加直到最后一个可能的组合(即全为 `'z'` 的字符串)。 2. 倒数第N个字符串可以通过反向计算得出。具体来说,可以从最大值减去偏移量得到目标字符串。 3. 使用类似于进制转换的方法处理字符变化。例如,假设长度为 `L=3`,则每个位置上的字符范围是 `'a'` 到 `'z'`,相当于一个26进制计数器[^3]。 #### 示例代码 (Python) 以下是一个 Python 实现: ```python def get_nth_last_string(L, N): # 初始化结果字符串为空列表 result = ['a'] * L # 计算总共有多少种排列方式 total_combinations = 26 ** L # 如果超出有效范围,则返回错误提示 if N > total_combinations or N <= 0: return "Invalid input" # 找到最后一个字符串的位置并向前推N位 position = total_combinations - N # 将position转化为26进制表示形式 for i in range(L - 1, -1, -1): # 从右往左遍历每一位 remainder = position % 26 result[i] = chr(ord('a') + remainder) position //= 26 return ''.join(result) # 测试函数 if __name__ == "__main__": L = 3 # 长度为3 N = 27 # 寻找倒数第27个字符串 print(get_nth_last_string(L, N)) # 输出应为 'zyz' ``` 上述代码通过模拟26进制计数的方式实现了字符串的逆序查找功能。它先确定总的排列数量,再利用模运算逐位填充最终的结果字符串。 #### 复杂度分析 - 时间复杂度:O(L),其中 L 是字符串长度。因为只需要对每一段进行一次操作即可完成转化。 - 空间复杂度:O(L),用于存储中间状态及输出结果。 --- ###

L1-050 倒数第N个字符串java

<think>嗯,用户想用Java实现倒数第N个字符串的算法,题目是L1-050。首先,我需要理解题目要求。题目说给定一个由小写字母组成的等差递增序列,每个字符串长度固定为L,从L个a开始,步长为1递增。比如L=3时,序列是aaa, aab, aac,...,aaz, aba,...,zzz。倒数第N个字符串需要输出。例如,倒数第27个是zyz。 那用户已经提供了一些引用,其中引用1是Java代码的例子。我需要看看这个代码的逻辑。代码里用到了将输入的L和N转换为对应的字符串。例如,当输入3和7417时,输出pat。代码的思路可能是将问题转化为进制转换的问题,因为每个位置有26个字母,类似26进制。 首先,我需要确定如何将倒数第N个转换为正数的位置。总共有26^L个字符串,倒数第N个的位置应该是总数量减去N,也就是num = 26^L - N。例如,当L=3时,总数是26^3=17576,倒数第27个的位置是17576-27=17549,对应的字符串是zyz。但这里的例子倒数第27个是zyz,对应的num应该是26^3 -27=17576-27=17549吗?或者可能有其他计算方式? 然后,如何将num转换为对应的字符串。这里需要将num转换为26进制的数,每一位对应一个字母。例如,num=17549分解为各个位的值。但需要注意的是,这里可能使用的是从低位到高位分解,然后反转得到结果。例如,Java代码中的循环是每次取模26得到当前位的字符,然后除以26,循环处理,最后反转输出。比如,在例子中输入3和7417,得到num=26^3-7417=17576-7417=10159。然后分解10159:10159%26=10159-26*390=10159-10140=19,对应字母表中的t(索引从0开始的话,a=0,t=19)。然后num=10159/26=390。390%26=390-26*15=0,对应a。num=15,15%26=15,对应p。所以字符是p,a,t,反转后得到pat。所以逻辑是对的。 那步骤应该是: 1. 计算总数量:total = 26^L 2. 计算正数位置:num = total - N 3. 将num转换为26进制,每一位对应一个字符,从低位到高位处理,最后反转得到结果。 需要注意,当num为0时,对应的是第一个字符串aaa的情况吗?比如当L=3,N=17576时,num=0,分解后每一位都是0,反转后还是aaa,但此时倒数第N个是第一个元素,所以正确。 那Java实现的关键点在于正确计算num,并处理每一位的转换。可能需要处理当L较大的情况,比如L=6时,26^6可能会超出int的范围,需要用long类型。例如在引用1的代码中,x是输入的L,这里用的是Math.pow(26, x),但Math.pow返回的是double,当x较大时可能会有精度问题。例如,当x=6时,26^6=308915776,这在int范围内,但若x更大,就可能溢出。不过题目中给出的约束是L≤6,所以Java的int可以存储,因为26^6=308915776,而Java的int最大值是2^31-1≈2e9,所以没问题。 那用户提供的Java代码中,num=(int) Math.pow(26, x)-y。这里x是输入的L,y是输入的N。例如,输入L=3,N=7417时,得到num=26^3 -7417=17576-7417=10159。然后循环处理每一位:取余26得到当前位的字符索引,加到print字符串中,然后除以26。循环结束后,反转输出。例如,循环中得到的字符是t, a, p,然后反转得到pat。 那这个算法的正确性如何?比如,当num分解时,每一位的处理是否是正确的。例如,假设L=3,num=17549(对应zyz的情况)。分解: 17549 /26=675余 17549-675*26=17549-17550= -1?这显然不对。可能我哪里弄错了。或者可能是计算的方式不同? 或者我的计算方式有问题。比如,17549 ÷26等于675余 17549 - 675*26。计算675*26:26*600=15600,26*75=1950,总和是15600+1950=17550。所以余数是17549-17550= -1?这显然有问题,说明在num的计算上有错误。这时候可能实际的处理方式需要处理余数为负数的情况吗? 或者可能题目中的例子倒数第27个字符串是zyz,对应的num应该是26^3-27=17576-27=17549。然后如何处理这个num? 让我们手动分解17549: 17549 ÷26= 675. 675*26=17550 → 17549-17550= -1?这时候余数是负数,这显然有问题。所以这说明Java代码中的处理可能存在错误? 这时候应该如何处理这种情况?例如,可能需要调整商和余数,使得余数保持非负数。例如,余数如果是负数,就加26,同时商加1。例如: 当num=17549,第一次取余: 17549 ÷26=675. 余数-1。这时调整余数为25(-1+26=25),商变为675-1=674?或者商应该是675+1? 这里可能需要正确的数学处理。例如,正确的计算方式是: 余数应该满足0 ≤r <26,所以当余数为负时,应调整商,使得余数变为正数。 例如,计算num=17549: 17549 ÷26=675.0 → 675*26=17550 →余数=17549-17550=-1。这时候,余数-1不符合条件,所以需要调整:将商增加1变为675+1=676,此时余数变为-1+26=25。所以正确的余数是25,商是674? 或者应该商=675-1=674,余数=17549 - 674*26=17549 - (674*26). 674*26是多少?计算:600*26=15600,74*26=1924 →15600+1924=17524 →17549-17524=25。所以余数是25,商674。这时候,第一位余数是25,对应字母z。然后商是674。继续分解: 674 ÷26=25. 余数674 -25*26=674-650=24 →对应y。商25。继续分解: 25 ÷26=0余25 →对应y。所以得到的余数序列是25,24,25。对应的字符是z y y?但正确的倒数第27个应该是zyz? 或者我哪里出错了? 可能我的手动计算有误。比如,题目中的例子倒数第27个字符串是zyz。对应的num是26^3 -27=17549。分解这个num: 首先,17549除以26: 17549 ÷26 = 675 → 675*26=17550 →余数是17549-17550=-1。这时候,必须调整余数为25,商减少1变成674。这样: 674*26=674*20=13480, 674*6=4044 →总和是13480+4044=17524 →余数是17549-17524=25。所以第一位余数是25,对应z。商是674。 接下来处理674: 674 ÷26=25. 余数674-25*26=674-650=24 →对应y。商25。 处理25: 25 ÷26=0余25 →对应z。所以余数序列是25,24,25 →对应的字符是z y z,反转后是z y z →zyz。这样就正确了。但根据Java代码中的处理方式,是否能够正确得到这样的结果? Java代码中的处理方式是: for(int i=0;i<x;i++) { print+=str.charAt(num%26); num=num/26; } 然后反转输出。 当num=17549时,第一次循环: num%26=17549%26 →计算17549 ÷26的余数。Java中的%运算符对于负数如何处理?例如,当num为负数时,余数会是负数吗? 这里num是正数,所以num%26的结果应该是正确的。但计算17549%26: 计算26*675=17550,比17549大,所以最大的k是674,使得26*674=17524。17549-17524=25。所以17549%26=25。然后num=17549/26=674(整数除法)。在Java中,当两个整数相除时,会自动取整,即向零取整。例如17549/26=674.9615... →取674。 接下来,num=674。674%26=674-26*25=674-650=24 →余数24。num=674/26=25。 第三次循环:num=25。25%26=25,余数25。num=25/26=0。 所以print字符串是25,24,25对应的字符:z y z。然后反转后得到zyz,正确。 哦,原来如此。这里的关键是Java中的除法是整数除法,自动截断小数部分,即向零取整。所以在处理正数时,num%26得到的是正确的余数,而num=num/26得到的是正确的商。因此,Java代码中的处理是正确的,不需要调整余数。例如,当num=17549时,num%26=25,num=17549/26=674;接着num=674,num%26=24,num=674/26=25;第三次num=25%26=25,num=25/26=0。循环三次后,print字符串是z y z,反转后得到zyz。所以代码是正确的。 这说明,Java中的整数除法在处理正数时是正确的,无需担心余数为负的情况。因此,用户提供的引用1中的代码是正确的。 那现在用户的问题是,如何用Java实现这个算法。步骤总结如下: 1. 输入L和N。 2. 计算总数量:total = 26^L。 3. 计算正序的位置:num = total - N。 4. 将num转换为26进制的各个位,每一位对应一个字母。 5. 注意转换时是从低位到高位,所以需要反转结果。 现在,根据引用1的代码,具体实现是: - 使用字符串"abcdefghijklmnopqrstuvwxyz"来映射字符。 - 循环L次,每次取num%26得到当前位的索引,添加到字符串print中,然后num = num/26。 - 最后反转字符串print,得到结果。 例如,当L=3,N=7417时: num = 26^3 -7417 = 17576 -7417=10159. 分解: 10159 ÷26= 390 →余数10159 -390*26=10159-10140=19 →字符't',添加到print。num=390. 390 ÷26=15 →余数390-15*26=390-390=0 →字符'a',添加到print。num=15. 15 ÷26=0 →余数15 →字符'p',添加到print。此时print是"ta p"?或者顺序是t a p? 循环三次,每次添加余数对应的字符,所以顺序是t a p。然后反转得到p a t →pat。 所以代码是正确的。 那用户的问题是如何实现这个算法。现在需要将这个过程用Java代码写出来,并注意数据类型。因为当L=6时,26^6=308915776,这在Java的int范围内(最大是2^31-1=2147483647),所以用int没问题。但需要注意Math.pow返回的是double,转换为int可能会有精度损失。例如,当计算Math.pow(26,6)时,得到的是308915776.0,转换为int没有问题。但如果数字很大时可能会有问题,但题目中给出的L≤6,所以没问题。 综上,用户提供的引用1中的代码是正确的。那用户的问题可能已经解决,但用户可能需要更详细的解释。或者用户可能在实现过程中遇到问题,比如为什么用这样的方法,或者如何处理边界条件。 可能的问题点: 1. 为什么将总数量减去N得到正数的位置? 因为序列是从0开始的?比如,总共有M=26^L个字符串,倒数第1个是最后一个,即索引M-1,倒数第N个是M-N。例如,当M=27,倒数第1个是第26个索引,即M-1=26。倒数第N个的索引是M-N。 是的,例如,当N=1时,正数是第M-1个,即最后一个。所以正确。 2. 如何处理当num=0的情况? 例如,当L=3,N=17576时,num=0。分解后每一位都是0,所以字符串是aaa。正确。 3. 是否需要考虑前导零的情况?比如,当num分解后的位数不足L位? 例如,L=3,num=25。分解后的余数是25,0,0?或者在循环中处理L次,所以每次分解都会得到足够的位数。例如,当num=25时,循环3次: 第一次num=25 →25%26=25,添加到print,num=25/26=0。 第二次num=0 →0%26=0,添加到print,num=0/26=0. 第三次num=0 →同上。所以print是25,0,0 →对应的字符是z a a。反转后是a a z?或者原代码中的循环是添加顺序是第一次是低位,第三次是高位吗? 例如,循环i从0到x-1,每次处理num%26,然后num=num/26。所以,当L=3时,第一次得到的是最低位的字符,第三次得到的是最高位的字符。比如,num=25对应的分解是: num=25 →25%26=25 →z,添加到print →print的第0位是z。num=0. 第二次循环,num=0 →0%26=0 →a,添加到print →现在print是z a. 第三次循环,num=0 →0%26=0 →a →print是z a a。然后反转整个字符串得到a a z?即字符串是aaz? 但根据题目中的序列,当L=3时,第一个字符串是aaa,第二个是aab,依此类推。所以当num=0时,对应的应该是第一个字符串aaa。但根据上述分解,得到的是z a a →反转后是 a a z,也就是aaz。这显然不对。 这说明,用户提供的代码是否存在错误? 啊,这里发现了问题。当num=0时,分解后的余数应该是0三次,得到字符串aaa。但根据上述处理方式,当num=0时: 第一次循环,num=0 →0%26=0 →a。num=0/26=0. 第二次循环,同样得到a,num=0. 第三次循环,同样得到a. 所以print是aaa,反转后还是aaa,正确。那刚才的例子中当num=25时,比如L=3,N=17576-25=17551。此时num=25,处理三次: 第一次循环,num=25%26=25 →z. num=25/26=0. 第二次循环,num=0 →a. num=0. 第三次循环,num=0 →a. 所以print是 z a a →反转后是a a z →对应的字符串是 aaz,这是正确的吗? 在序列中,当L=3时,顺序是: 索引0:aaa 索引1:aab ... 索引25:aaz 索引26:aba 所以当num=25时,对应的字符串应该是aaz。而反转后的结果是aaz。这是正确的吗? 是的,因为处理顺序是低位到高位。例如,num=25分解为三位的话,是25在最低位,0在中间位,0在最高位。所以对应的字符串应该是a a z。因为高位是0(对应a),中间是0(对应a),低位是25(对应z)。所以字符串是a a z →aaz。这与序列中的索引25对应正确。所以代码是正确的。 这说明,在循环中处理每一位时,顺序是低位到高位,所以反转后得到正确的高位到低位顺序。例如,当处理得到字符顺序是z a a(低位到高位),反转后是a a z →即高位到低位,正确的字符串。 因此,代码中的处理是正确的。 那用户的问题可以通过参考引用1中的Java代码来解决。现在需要将这个过程用中文解释清楚,确保用户理解步骤,并正确实现。 可能的错误点: - 忘记反转字符串,导致结果顺序错误。 - 没有处理足够多的位数,比如循环次数不够L次。 - 在计算26^L时使用Math.pow可能导致精度问题,但由于题目中L的范围是2到6,所以不会有问题。 总结,正确的Java实现步骤如下: 1. 读取输入的L和N。 2. 计算总数量:total = 26^L。 3. 计算正序的位置:num = total - N。 4. 循环L次,每次取num%26得到当前位的字符,添加到字符串末尾,然后num = num / 26。 5. 反转字符串,得到最终结果。 现在,根据这些步骤,可以编写出正确的Java代码。</think>### Java实现倒数第N个字符串的算法步骤 #### 问题分析 题目要求根据长度$L$和小写字母序列的倒数第$N$个字符串,将其转换为正向位置后,按26进制逐位映射到字母。例如,当$L=3$且$N=27$时,总共有$26^3=17576$个字符串,倒数第27个对应正向位置$17576-27=17549$,转换为26进制后得到`zyz`[^3]。 #### 核心思路 1. **位置转换**:倒数第$N$个字符串的正向位置为$\text{total} - N$,其中$\text{total}=26^L$。 2. **进制转换**:将正向位置按26进制分解,每位对应字母表中的字符。 3. **字符串反转**:由于计算时从低位到高位生成字符,最终需反转字符串。 #### Java代码实现 ```java import java.util.Scanner; public class Main { public static void main(String[] args) { String alphabet = "abcdefghijklmnopqrstuvwxyz"; StringBuilder result = new StringBuilder(); Scanner scanner = new Scanner(System.in); int L = scanner.nextInt(); int N = scanner.nextInt(); // 计算正向位置 int total = (int) Math.pow(26, L); int num = total - N; // 分解26进制并生成字符串 for (int i = 0; i < L; i++) { int remainder = num % 26; result.append(alphabet.charAt(remainder)); num /= 26; } // 反转后输出 System.out.println(result.reverse().toString()); } } ``` #### 关键点说明 1. **位置计算**: $\text{total}=26^L$表示总字符串数,$\text{num}=\text{total}-N$将倒数位置转为正向索引。 2. **逐位分解**: 每次循环取`num % 26`得到当前位的字母索引,再更新`num = num / 26`,类似10进制转2进制的逻辑。 3. **字符串反转**: 由于字符按低位到高位生成(如个位→十位→百位),需反转得到正确顺序[^1]。 #### 示例验证 - **输入**:`3 7417` $\text{total}=26^3=17576$,$\text{num}=17576-7417=10159$ 分解过程: $10159 \div 26 = 390$ 余 $19$ → `t` $390 \div 26 = 15$ 余 $0$ → `a` $15 \div 26 = 0$ 余 $15$ → `p` 反转后得到`pat`,与题目要求一致[^1]。
阅读全文

相关推荐

# 导入必要的库和模块 from smartcar import ticker, encoder from seekfree import KEY_HANDLER from machine import * import gc import time from display import * from seekfree import TSL1401, MOTOR_CONTROLLER, WIRELESS_UART, IMU963RX import math import ustruct # 初始化硬件设备 imu = IMU963RX() # IMU传感器 ccd = TSL1401(10) # CCD传感器,采集周期10次 key = KEY_HANDLER(5) # 按键处理 motor_l = MOTOR_CONTROLLER(MOTOR_CONTROLLER.PWM_C25_DIR_C27, 13000, duty=0, invert=True) # 左电机 motor_r = MOTOR_CONTROLLER(MOTOR_CONTROLLER.PWM_C24_DIR_C26, 13000, duty=0, invert=True) # 右电机 encoder_l = encoder("D0", "D1", True) # 左编码器 encoder_r = encoder("D2", "D3") # 右编码器 wireless = WIRELESS_UART(115200) # 无线通信 uart2 = UART(2) # 串口2 uart2.init(115200) # 初始化显示设备 cs = Pin('C5', Pin.OUT, pull=Pin.PULL_UP_47K, value=1) # 片选引脚 # 拉高拉低一次 CS 片选确保屏幕通信时序正常 cs.high() cs.low() rst = Pin('B9', Pin.OUT, pull=Pin.PULL_UP_47K, value=1) # 复位引脚 dc = Pin('B8', Pin.OUT, pull=Pin.PULL_UP_47K, value=1) # 数据/命令选择 blk = Pin('C4', Pin.OUT, pull=Pin.PULL_UP_47K, value=1) # 背光控制 drv = LCD_Drv(SPI_INDEX=1, BAUDRATE=60000000, DC_PIN=dc, RST_PIN=rst, LCD_TYPE=LCD_Drv.LCD200_TYPE) # LCD驱动 lcd = LCD(drv) # LCD实例 lcd.color(0x0000, 0xFFFF) # 设置颜色 lcd.mode(2) # 设置显示模式 lcd.clear(0xDEF7) # 清屏使用淡灰色背景 # 初始化引脚 led = Pin('C4', Pin.OUT, pull=Pin.PULL_UP_47K, value=True) # LED指示灯 beep = Pin('C9', Pin.OUT, pull=Pin.PULL_UP_47K, value=False) # 蜂鸣器 switch1 = Pin('D8', Pin.IN, pull=Pin.PULL_UP_47K, value=True) # 拨码开关1 switch2 = Pin('D9', Pin.IN, pull=Pin.PULL_UP_47K, value=True) # 拨码开关2 end_switch = Pin('C19', Pin.IN, pull=Pin.PULL_UP_47K, value=True) # 终点开关 # 全局变量定义 # PID控制相关变量 err_1 = err_sum_1 = err_x_1 = err_last_1 = 0 # PID1误差项 err_2 = err_sum_2 = err_x_2 = err_last_2 = 0 # PID2误差项 err_3 = err_sum_3 = err_x_3 = err_last_3 = 0 # PID3误差项 errt = errt_sum = errt_x = errt_last = 0 # 转向PID误差项 # 系统状态标志 ticker_flag = ticker_flag2 = False # 定时器标志 ticker_count = ticker_count0 = ticker_count3 = 0 # 定时器计数器 motor_dir = 1 # 电机方向 motor_duty = 0 # 电机占空比 motor_duty_max = 1000 # 最大占空比 turn = 0 # 转向量 state1 = switch1.value() # 开关1状态 state2 = switch2.value() # 开关2状态 end_state = end_switch.value() # 终点开关状态 # CCD相关变量 ccd1_zhongzhi = ccd2_zhongzhi = 64 # CCD中心值 left1 = right1 = left2 = right2 = 0 # CCD边界 flag_stute1 = flag_stute2 = 0 # 状态标志 flag_shizi1 = flag_shizi2 = 0 # 十字标志 shreshlod1 = shreshlod2 = 0 # 阈值 # 路径跟踪相关变量 lline = rline = lline2 = rline2 = 0 # 左右边界线 zThreshold = zThreshold2 = 15 # 阈值 zhong = zhong2 = 64 # 中心位置 banma_flag = stop = ysbzw = 0 # 标志位 track_stop = huang_tag = 0 # 跟踪停止标志 huang_l_flag = huang_r_flag = 0 # 黄线标志 huang_l_zt = huang_r_zt = 0 # 黄线状态 bizhan_l = bizhan_r = txzb = 0 # 避障相关 bizhan_flag = cha2 = 0 # 避障标志和差值 zuocha = zuocha2 = 0 # 左差值 zebra = road2 = jifen = 0 # 斑马线和道路标志 art1 = art2 = art3 = puodao_flag = 0 # 其他标志 road = fix = circle = zebra2 = banma_slow = stop_flag = 0 # 控制参数 delta_T = 0.001 # 采样周期为5ms I_ex, I_ey, I_ez = 0.0, 0.0, 0.0 # 积分误差 """""""""""""""主要调节以下参数""""""""""""""" """根据屏幕显示Pitch确定平衡角度""" med_roll_angle = 34 # 平衡角度 前倾+ 后仰- med_speed1 = -185#-1.9 # 速度(速度为零即静止) med_speed = med_speed1 #0 当前速度 # 角速度PID参数##高频震动:增大kd或减小kp angle_kp = -650#2280.0 调节响应速度 angle_ki = -2.25#-2.25 控制车辆在平衡点附近移动 angle_kd = -320 #-122 # 角度PID参数+- roll_angle_Kp = 0.055 #0.06585 控制车辆平衡角度 roll_angle_Ki = 0.00001 #0.00001 roll_angle_Kd = 0.38 #0.2 低频震动 # 速度PID参数 speed_Kp = -0.070#0.0792 加速/减速过慢,调节绝对值 speed_Ki = 0.00000001 #0.00000001 speed_Kd = 0.014 #0.01055 速度波动过大则增大kd或减小kp """""""""""""""主要调节以上参数""""""""""""""" CCD_dir = [64,64] # 转向PID参数 a = 0.35#0.152 Kpc = 0.45#0.2 turn_ki = 20#25 #中线位置调节 turn_kd = 2.0 #防止左右摆动幅度过大 turn_kd2 = -18 #转向速度 # 按键控制变量 param_index = 0 # 当前选择的参数索引 param_editing = True # 是否处于参数编辑模式 # 参数菜单定义 PARAM_MENU = [ "1.AngVel Kp", "2.AngVel Ki", "3.AngVel Kd", "4.Angle Kp", "5.Angle Ki", "6.Angle Kd", "7.Speed Kp", "8.Speed Ki","9.Speed Kd", "10.med_roll_angle", "11.Launch!" ] pid_params = [ # 可调参数列表 angle_kp, angle_ki, angle_kd, roll_angle_Kp, roll_angle_Ki, roll_angle_Kd, speed_Kp, speed_Ki,speed_Kd,med_roll_angle ] PARAM_STEP = [ # 各参数调节步长 10.0, 0.01, 1.0, # 角速度环 0.001, 0.00001, 0.001,# 角度环 0.0000001, 0.00001, 0.00001, # 速度环 0.5#角度 ] # 编码器卡尔曼滤波参数 KAL_P = KAL_P2 = 0.02 # 估算协方差 KAL_G = KAL_G2 = 0.0 # 卡尔曼增益 KAL_Q = KAL_Q2 = 0.70 # 过程噪声协方差 KAL_R = KAL_R2 = 200 # 测量噪声协方差 KAL_Output = KAL_Output2 = 0.0 # 卡尔曼滤波器输出 # 中间变量 angle_1 = speed_1 = counts = motor1 = motor2 = 0 angle_2 = speed_2 =0 distance = id_number = 0 # 距离和ID号 Filter_data = [0, 0, 0] # 滤波数据 PI = 3.14159265358 # 圆周率 last_yaw = 0 # 上次偏航角 max_gyro_x = 0 # 最大角速度X # 无线数据传输缓冲区 data_flag = wireless.data_analysis() data_wave = [0]*8 # 类定义 class anjian: """按键处理类""" def __init__(self, KEY_1, KEY_2, KEY_3, KEY_4): self.One = KEY_1 self.Two = KEY_2 self.Three = KEY_3 self.Four = KEY_4 class bianmaqi: """编码器类""" def __init__(self, KAL_templ_pluse, KAL_tempr_pluse): self.KAL_templ_pluse = KAL_templ_pluse self.KAL_tempr_pluse = KAL_tempr_pluse class Imu_element: """IMU数据类""" def __init__(self, acc_x, acc_y, acc_z, gyro_x, gyro_y, gyro_z, Pitch, Roll, Yaw, X, Y, Z, Total_Yaw): self.acc_x = acc_x self.acc_y = acc_y self.acc_z = acc_z self.gyro_x = gyro_x self.gyro_y = gyro_y self.gyro_z = gyro_z self.Pitch = Pitch self.Roll = Roll self.Yaw = Yaw self.X = X self.Y = Y self.Z = Z self.Total_Yaw = Total_Yaw class param: """PID参数类""" def __init__(self, param_Kp, param_Ki): self.param_Kp = param_Kp self.param_Ki = param_Ki class QInfo: """四元数信息类""" def __init__(self): self.q0 = 1.0 self.q1 = 0.0 self.q2 = 0.0 self.q3 = 0.0 # 实例化类 KEY = anjian(0, 0, 0, 0) Encoders = bianmaqi(0, 0) Imu = Imu_element(0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00) Param = param(15.5, 0.006) Q_info = QInfo() # 函数定义 def limit(value, min_value, max_value): """限制数值范围""" return min(max(value, min_value), max_value) def calculate_pid(err, err_sum, err_last, med, value, kp, ki, kd): """计算PID控制值""" err = med - value err_sum += err err_x = err - err_last pwm = kp * err + ki * err_sum + kd * err_x err_last = err return pwm def pid_position_1(med, value, kp, ki, kd): """PID控制器1""" global err_1, err_sum_1, err_last_1 pwm_1 = calculate_pid(err_1, err_sum_1, err_last_1, med, value, kp, ki, kd) err_last_1 = err_1 return pwm_1 def pid_position_2(med, value, kp, ki, kd): """PID控制器2""" global err_2, err_sum_2, err_last_2 pwm_2 = calculate_pid(err_2, err_sum_2, err_last_2, med, value, kp, ki, kd) err_last_2 = err_2 return pwm_2 def pid_position_3(med, value, kp, ki, kd): """PID控制器3""" global err_3, err_sum_3, err_last_3 pwm_3 = calculate_pid(err_3, err_sum_3, err_last_3, med, value, kp, ki, kd) err_last_3 = err_3 return pwm_3 def pid_turn(med, value, kp, ki, kd): """转向PID控制器""" global errt, errt_sum, errt_last pwmt = calculate_pid(errt, errt_sum, errt_last, med, value, kp, ki, kd) errt_last = errt return pwmt def Key(): global param_index, param_editing, pid_params, angle_kp, angle_ki, angle_kd global roll_angle_Kp, roll_angle_Ki, roll_angle_Kd, speed_Kp, speed_Ki key_data = key.get() beep_triggered = False # 标记是否已经触发蜂鸣器 if param_editing: if key_data[0]: # 参数增加 pid_params[param_index] += PARAM_STEP[param_index] print("Key1") beep_triggered = True if key_data[1]: # 参数减少 pid_params[param_index] -= PARAM_STEP[param_index] print("Key2") beep_triggered = True if key_data[2]: # 切换参数 param_index = (param_index + 1) % len(PARAM_MENU) print("Key3") beep_triggered = True if param_index == 10: # 到达"启动!"选项 param_index = 0 if key_data[3]: # 确认参数并控制电机启动的停止 print("Key4:",param_editing) # 更新实际PID参数 angle_kp, angle_ki, angle_kd = pid_params[0], pid_params[1], pid_params[2] roll_angle_Kp = pid_params[3] roll_angle_Ki = pid_params[4] roll_angle_Kd = pid_params[5] speed_Kp, speed_Ki,speed_Kd= pid_params[6], pid_params[7],pid_params[8] med_roll_angle = pid_params[9] param_editing = not param_editing beep_triggered = True # 如果有按键触发,则蜂鸣器提示 if beep_triggered: beep.on() # 蜂鸣器提示 time.sleep_ms(200) beep.off() def invSqrt(x): """快速平方根倒数""" return 1.0 # 简化实现,实际使用时需要优化 def Imu963(): """IMU数据处理函数""" global imu_data, max_gyro_x, last_yaw alpha = 0.3 # 滤波系数 # 数据有效性检查 for i in range(3, 6): if abs(imu_data[i]) < 30 or abs(imu_data[i]) > 30000: imu_data[i] = 0 # 原始数据处理 Imu.X = int(imu_data[3] / 16.4) Imu.Y = int(imu_data[4] / 16.4) Imu.Z = int(imu_data[5] / 16.4) # 角速度处理 Imu.gyro_x = round((float(imu_data[3]) - Filter_data[0]), 3) * PI / 180 / 16.4 Imu.gyro_y = round((float(imu_data[4]) - Filter_data[1]), 3) * PI / 180 / 16.4 Imu.gyro_z = round((float(imu_data[5]) - Filter_data[2]), 3) * PI / 180 / 14.4 # 加速度滤波处理 Imu.acc_x = round(((float(imu_data[0]) * alpha) / 4096 + Imu.acc_x * (1 - alpha)), 3) Imu.acc_y = round(((float(imu_data[1]) * alpha) / 4096 + Imu.acc_y * (1 - alpha)), 3) Imu.acc_z = round(((float(imu_data[2]) * alpha) / 4096 + Imu.acc_z * (1 - alpha)), 3) # 更新AHRS姿态 IMU_AHRSupdate(Imu.gyro_x, Imu.gyro_y, Imu.gyro_z, Imu.acc_x, Imu.acc_y, Imu.acc_z) # 记录最大角速度 if abs(max_gyro_x) < abs(Imu.Pitch): max_gyro_x = Imu.Pitch def Imu963ra_Init(): """IMU初始化校准""" global Filter_data, imu_data Filter_data = [0, 0, 0] # 采集1000次数据进行平均校准 for i in range(1000): imu_data = imu.get() Filter_data[0] += imu_data[3] Filter_data[1] += imu_data[4] Filter_data[2] += imu_data[5] time.sleep_ms(1) # 计算平均值 Filter_data[0] = float(Filter_data[0] / 1000) Filter_data[1] = float(Filter_data[1] / 1000) Filter_data[2] = float(Filter_data[2] / 1000) def IMU_AHRSupdate(gx, gy, gz, ax, ay, az): """AHRS姿态更新算法""" global I_ex, I_ey, I_ez, last_yaw halfT = 0.5 * delta_T # 计算当前重力单位向量 q0q0 = Q_info.q0 * Q_info.q0 q0q1 = Q_info.q0 * Q_info.q1 q0q2 = Q_info.q0 * Q_info.q2 q1q1 = Q_info.q1 * Q_info.q1 q1q3 = Q_info.q1 * Q_info.q3 q2q2 = Q_info.q2 * Q_info.q2 q2q3 = Q_info.q2 * Q_info.q3 q3q3 = Q_info.q3 * Q_info.q3 # 加速度归一化 norm = invSqrt(ax*ax + ay*ay + az*az) ax *= norm ay *= norm az *= norm # 计算当前重力单位向量 vx = 2 * (q1q3 - q0q2) vy = 2 * (q0q1 + q2q3) vz = q0q0 - q1q1 - q2q2 + q3q3 # 计算误差 ex = ay * vz - az * vy ey = az * vx - ax * vz ez = ax * vy - ay * vx # PI修正 I_ex += delta_T * ex I_ey += delta_T * ey I_ez += delta_T * ez gx += Param.param_Kp * ex + Param.param_Ki * I_ex gy += Param.param_Kp * ey + Param.param_Ki * I_ey gz += Param.param_Kp * ez + Param.param_Ki * I_ez # 四元数微分方程 q0 = Q_info.q0 q1 = Q_info.q1 q2 = Q_info.q2 q3 = Q_info.q3 Q_info.q0 = q0 + (-q1*gx - q2*gy - q3*gz) * halfT Q_info.q1 = q1 + (q0*gx + q2*gz - q3*gy) * halfT Q_info.q2 = q2 + (q0*gy - q1*gz + q3*gx) * halfT Q_info.q3 = q3 + (q0*gz + q1*gy - q2*gx) * halfT # 四元数归一化 norm = invSqrt(Q_info.q0**2 + Q_info.q1**2 + Q_info.q2**2 + Q_info.q3**2) Q_info.q0 *= norm Q_info.q1 *= norm Q_info.q2 *= norm Q_info.q3 *= norm # 计算欧拉角 value1 = limit(-2 * Q_info.q1 * Q_info.q3 + 2 * Q_info.q0 * Q_info.q2, -1, 1) Imu.Roll = round(math.asin(value1) * 180 / math.pi, 3) # 横滚角 Imu.Pitch = round(math.atan2(2 * Q_info.q2 * Q_info.q3 + 2 * Q_info.q0 * Q_info.q1, -2 * Q_info.q1**2 - 2 * Q_info.q2**2 + 1) * 180 / math.pi, 3) # 俯仰角 Imu.Yaw = round(math.atan2(2 * Q_info.q1 * Q_info.q2 + 2 * Q_info.q0 * Q_info.q3, -2 * Q_info.q2**2 - 2 * Q_info.q3**2 + 1) * 180 / math.pi, 3) # 偏航角 # 计算总偏航角 error_yaw = Imu.Yaw - last_yaw if error_yaw < -360: error_yaw += 360 if error_yaw > 360: error_yaw -= 360 Imu.Total_Yaw += error_yaw last_yaw = Imu.Yaw # 保证总偏航角在0-360度范围内 if Imu.Total_Yaw > 360: Imu.Total_Yaw -= 360 if Imu.Total_Yaw < 0: Imu.Total_Yaw += 360 def Wireless(): """无线数据传输处理""" global data_flag, data_wave data_flag = wireless.data_analysis() # 数据解析 # 更新虚拟示波器数据 data_wave[0] = angle_1 data_wave[1] = speed_1 data_wave[2] = Imu.acc_z data_wave[3] = Imu.gyro_x data_wave[4] = Imu.gyro_y data_wave[5] = Imu.Roll data_wave[6] = Imu.Pitch # 发送数据到上位机 wireless.send_oscilloscope(*data_wave) def KalmanFilter(input): """卡尔曼滤波器""" global KAL_P, KAL_G, KAL_Output KAL_P = KAL_P + KAL_Q # 估算协方差更新 KAL_G = KAL_P / (KAL_P + KAL_R) # 计算卡尔曼增益 KAL_Output = KAL_Output + KAL_G * (input - KAL_Output) # 更新最优估计值 KAL_P = (1 - KAL_G) * KAL_P # 更新协方差 return KAL_Output def KalmanFilter2(input): """第二个卡尔曼滤波器""" global KAL_P2, KAL_G2, KAL_Output2 KAL_P2 = KAL_P2 + KAL_Q2 KAL_G2 = KAL_P2 / (KAL_P2 + KAL_R2) KAL_Output2 = KAL_Output2 + KAL_G2 * (input - KAL_Output2) KAL_P2 = (1 - KAL_G2) * KAL_P2 return KAL_Output2 def draw_rect(x, y, width, height, color, thick=1, fill=False): """用line方法实现矩形绘制""" if fill: # 填充矩形 - 用垂直线条填充 for i in range(height): lcd.line(x, y+i, x+width-1, y+i, color=color, thick=thick) else: # 只画边框 lcd.line(x, y, x+width-1, y, color=color, thick=thick) # 上边 lcd.line(x, y+height-1, x+width-1, y+height-1, color=color, thick=thick) # 下边 lcd.line(x, y, x, y+height-1, color=color, thick=thick) # 左边 lcd.line(x+width-1, y, x+width-1, y+height-1, color=color, thick=thick) # 右边 last_title = None last_param_value = None last_param_index = None def ips200_display(): """美化后的LCD显示函数""" global last_title,last_motors global last_param_value, last_param_index #1. ---- 顶部状态栏 ---- if last_title is None: draw_rect(0, 0, 320, 20, 0x0000, fill=True) # 黑色背景 title = " RT1021 Dashboard " colors = [0xF800, 0xFC00, 0xFFE0, 0x07E0, 0x07FF, 0x001F, 0x801F, 0xF81F] # 彩虹色 # 阴影效果(偏移1像素) for i, char in enumerate(title): x_pos = 40 + i * 8 lcd.str16(x_pos + 1, 2 + 1, char, 0x3186) # 灰色阴影 for i, char in enumerate(title): x_pos = 40 + i * 8 color = colors[i % len(colors)] lcd.str16(x_pos, 2, char, color) last_title = True # 2. IMU数据区域 (带边框) draw_rect(0, 20, 105, 65, 0x001F, thick=1) # 蓝色边框 lcd.str16(15, 20, "Attitude", 0x001F) # 蓝色标题 lcd.str16(5, 40, f"Pitch:{Imu.Pitch:6.2f}", 0x0000) # 黑色数据 lcd.str16(5, 60, f"Yaw:{Imu.Yaw:6.2f}", 0x0000) # 3. 运动控制区域 (带边框) draw_rect(105, 20, 135, 65, 0xF800, thick=1) # 红色边框 lcd.str16(127, 20, "Motion Ctrl", 0xF800) lcd.str16(117, 40, f"Speed: {med_speed:4.1f}", 0x0000) lcd.str16(117, 60, f"Turn: {turn:6.3f}", 0x0000) # 4. 电机信息区域 (带边框) draw_rect(0, 85, 310, 75, 0x0000, thick=1) # 黑色边框 lcd.str16(5, 110, "Motor", 0x0000) lcd.str16(5, 126, "Output", 0x0000) # 5. 陀螺仪数据区域 (带边框) draw_rect(0, 160, 250, 50, 0xFC80, thick=1) # 橙色边框 lcd.str16(5, 165, "Gyroscope", 0xFC80) lcd.str16(5, 185, f"X:{Imu.gyro_x:7.3f} Y:{Imu.gyro_y:7.3f} Z:{Imu.gyro_z:7.3f}", 0x0000) # 6. 编码器数据区域 (带边框) draw_rect(0, 210, 250, 40, 0x79FF, thick=1) # 天蓝色边框 lcd.str16(5, 220, f"Encoder L:{Encoders.KAL_templ_pluse:6.1f} R:{Encoders.KAL_tempr_pluse:6.1f}", 0x0000) # 7. CCD数据区域 (带边框) draw_rect(0, 250, 250, 70, 0xAA55, thick=1) # 紫色边框 lcd.str16(5, 255, "CCD Line Position", 0xAA55) lcd.str16(5, 275, f"L1:{lline:3d} R1:{rline:3d} Mid:{zhong:3d}", 0x0000) lcd.str16(5, 295, f"L2:{lline2:3d} R2:{rline2:3d} Mid2:{zhong2:3d}", 0x0000) # 8. 道路识别信息 lcd.str16(5, 325, f"Road Type: {road:.2f}", 0x0000) # 9. 参数编辑模式特殊显示 lcd.str16(60, 90, PARAM_MENU[param_index], 0xFFFF) # 白色标题 # 操作提示可以只绘制一次 if last_param_index is None: draw_rect(60, 135, 190, 20, 0xFFFF, thick=1, fill=True) lcd.str16(65, 137, "1:+ 2:- 3:Next 4:OK", 0x0000) # 参数值显示 (带背景色) if pid_params[param_index] != last_param_value or param_index != last_param_index: color = 0x07E0 if pid_params[param_index] >=0 else 0xF800 #绿/红区分正负 draw_rect(60, 110, 190, 20, color, thick=1, fill=True) lcd.str16(65, 112, f"Value: {pid_params[param_index]:.4f}", 0x0000) last_param_value = pid_params[param_index] last_param_index = param_index class ALL_CCD: def __init__(self): self.num=0 self.dir = 0 self.dir_0 = 0 self.left = 38 self.right = 82 self.left_0 = 43 self.right_0 = 78 self.lode_wid = 44 self.lode_wid_0 = 35 self.mid = 60 self.mid_line = 60 self.mid_line_0 = 60 self.last_angle = 62 self.last_angle_1 = 62 def CCD1(self,ccd_data): global av_dir0,a8 #计算左右平均像素值 value_av = (max(ccd_data)+min(ccd_data))//2 #二次处理 ccd_data = [250 if num > value_av else 0 for num in ccd_data] #计算左右平均像素值 av_l = int(sum(ccd_data[15: 64])/49) av_r = int(sum(ccd_data[64:113])/49) if av_l>200 and av_r>200: a8=8 else: a8=0 print("ccd1",av_l,av_r) for i in range(127): if ccd_data[i]==0 and ccd_data[i] != ccd_data[i-1] and ccd_data[i] != ccd_data[i+1]: ccd_data[i] = 250 elif ccd_data[i]==250 and ccd_data[i] != ccd_data[i-1] and ccd_data[i] != ccd_data[i+1]: ccd_data[i] =0 ccd_dataA = ccd_data #差比和 new_data = [int(abs(ccd_dataA[i]-ccd_dataA[i+1]))for i in range(len(ccd_dataA)-1)]#127 result_l = [self.mid_line - i for i,x in enumerate(new_data[self.mid_line:15+a8:-1]) if x > 0] result_r = [i + self.mid_line for i,x in enumerate(new_data[self.mid_line:len(new_data)-15-a8]) if x >0] #print("1bianjie",result_l,result_r) #判断边界 if len(result_l)>0 and len(result_r)>0: self.dir = 0 av_dir0 = 0 self.left = result_l[0] self.right = result_r[0] if abs(self.left - self.right) > 92: self.mid_line = 62 self.dir = 1 av_dir0 = 404 #边界二次处理 if abs(self.left - self.right) - self.lode_wid > 15: if abs(self.left - self.mid) > abs(self.right- self.mid): self.left = self.right - self.lode_wid elif abs(self.left - self.mid) < abs(self.right- self.mid): self.right = self.left + self.lode_wid elif len(result_l)== 0 and len(result_r)>0 and result_r[0] < 108: if av_r < av_l and av_l>=240 and av_r<180: av_dir0 = 11 else: av_dir0 = 1 if av_r < av_l: self.right = result_r[0] self.left = self.right - self.lode_wid else: self.left = result_r[0] self.right = self.left + self.lode_wid elif len(result_l)>0 and len(result_r)==0 and result_l[0] > 20: if av_r > av_l and av_r>=240 and av_l<180: av_dir0 = 22 else: av_dir0 = 2 if av_r < av_l: self.right = result_l[0] self.left = self.right - self.lode_wid else: self.left = result_l[0] self.right = self.left + self.lode_wid else: self.mid_line = 62 self.dir = 1 av_dir0 = 404 track_info = (self.left + self.right)//2# 计算赛道中心线位置 if abs(track_info - self.mid_line) > 30: track_info = self.mid_line self.dir = 1 self.last_angle = track_info self.mid_line = track_info if self.dir == 1: if abs(track_info-self.last_angle)<4: self.dir =0 else: return self.last_angle else: return track_info def CCD2(self,ccd_data): global av_dir1,a8,speed_l,speed_r,mortor_dir self.num+=1 count=0 value_av = (max(ccd_data)+min(ccd_data))//2 #二次处理 ccd_data = [250 if num > value_av else 0 for num in ccd_data] zebra_crossing = [i for i in range(127) if (ccd_data[i] == 0 and ccd_data[i + 1] == 250) or (ccd_data[i] == 250 and ccd_data[i + 1] == 0)] for i in range(len(zebra_crossing) - 1): if abs(zebra_crossing[i] - zebra_crossing[i + 1]) < 5: count += 1 if count >= 10 and self.num>250 and -25<int(imu_z)<25: speed_l=0 speed_r=0 mortor_dir=2 self.num=0 #计算左右平均像素值 av_l = int(sum(ccd_data[15:64])/49) av_r = int(sum(ccd_data[64:113])/49) if av_l>200 and av_r>200: a8=8 else: a8=0 #print("ccd2",av_l,av_r) for i in range(127): if ccd_data[i]==0 and ccd_data[i] != ccd_data[i-1] and ccd_data[i] != ccd_data[i+1]: ccd_data[i] = 250 elif ccd_data[i]==250 and ccd_data[i] != ccd_data[i-1] and ccd_data[i] != ccd_data[i+1]: ccd_data[i] =0 ccd_dataA = ccd_data #差比和 new_data = [int(abs(ccd_dataA[i]-ccd_dataA[i+1]))for i in range(len(ccd_dataA)-1)]#127 result_l = [self.mid_line_0 - i for i,x in enumerate(new_data[self.mid_line_0:15+a8:-1]) if x > 0] result_r = [i + self.mid_line_0 for i,x in enumerate(new_data[self.mid_line_0:len(new_data)-15-a8]) if x >0] #print("2bianjie",result_l,result_r) #判断边界 if len(result_l)>0 and len(result_r)>0: av_dir1 = 0 self.dir_0 = 0 self.left_0 = result_l[0] self.right_0 = result_r[0] if abs(self.left_0 - self.right_0) > 92: self.mid_line_0 = 62 av_dir1 = 404 self.dir_0 = 1 #边界二次处理 if abs(self.left_0 - self.right_0) - self.lode_wid_0 > 15: if abs(self.left_0 - self.mid) > abs(self.right_0- self.mid): self.left_0 = self.right_0 - self.lode_wid_0 elif abs(self.left_0 - self.mid) < abs(self.right_0- self.mid): self.right_0 = self.left_0 + self.lode_wid_0 elif len(result_l)== 0 and len(result_r)>0 and result_r[0] < 108: if av_r < av_l and av_l>=240 and av_r<180: av_dir1 = 11 else: av_dir1 = 1 if av_r < av_l: self.right_0 = result_r[0] self.left_0 = self.right_0 - self.lode_wid_0 else: self.left_0 = result_r[0] self.right_0 = self.left_0 + self.lode_wid_0 elif len(result_l)>0 and len(result_r)==0 and result_l[0] > 20: if av_r > av_l and av_r>=240 and av_l<180: av_dir1 = 22 else: av_dir1 = 2 if av_r < av_l: self.right_0 = result_l[0] self.left_0 = self.right_0 - self.lode_wid_0 else: self.left_0 = result_l[0] self.right_0 = self.left_0 + self.lode_wid_0 else: self.mid_line_0 = 62 av_dir1 = 404 self.dir_0 = 1 track_info = (self.left_0 + self.right_0)//2# 计算赛道中心线位置 if abs(track_info - self.mid_line_0) > 30 : track_info = self.mid_line_0 self.dir_0 = 1 self.last_angle_1 = track_info self.mid_line_0 = track_info if self.dir_0 == 1: if abs(track_info-self.last_angle_1)<4: self.dir_0 =0 else: return self.last_angle_1 else: return track_info def control_turn(zhong1_2): """转向控制函数""" if zhong1_2 is None: # 如果输入无效,使用默认值(居中) zhong1_2 = 64 errt = (zhong1_2 - 63) turn_kp = a * abs(errt) + Kpc turn = pid_turn(63, zhong1_2, turn_kp, turn_ki, turn_kd) + Imu.gyro_z * turn_kd2 return turn def angle_speed1(med_gyro, cur_gyro): """角速度控制函数""" motor = pid_position_1(med_gyro, cur_gyro, angle_kp, angle_ki, angle_kd) return limit(motor, -4000, 4000) def angle(med_roll_angle, cur_roll_angle): """角度控制函数""" global angle_1 angle_1 = pid_position_2(med_roll_angle, cur_roll_angle, roll_angle_Kp, roll_angle_Ki, roll_angle_Kd) return angle_1 def speed(med_speed, cur_speed): """速度控制函数""" global speed_1 speed_1 = pid_position_3(med_speed, cur_speed, speed_Kp, speed_Ki, speed_Kd) return speed_1 def parse_data(): """解析串口数据""" if uart2.any(): line = uart2.readline() # 读取一行数据 if line is None: return None, None try: line_str = line.decode('utf-8').strip() # 转换为字符串 parts = line_str.split(',') # 按逗号分割 if len(parts) >= 2: var1 = float(parts[0]) # 第一个参数为浮点数 var2 = int(parts[1]) # 第二个参数为整数 return var1, var2 except (UnicodeDecodeError, ValueError): pass return None, None # 定时器中断处理函数 def time_pit_handler(time): """定时器1中断处理""" global ticker_flag, ticker_count, speed_1, angle_1, motor1, motor2 ticker_flag = True ticker_count = (ticker_count + 1) if (ticker_count < 10) else 1 # 1ms周期任务 if ticker_count % 1 == 0: Imu963() # 更新IMU数据 # 电机控制 motor1 = angle_speed1(angle_1, Imu.gyro_x) motor2 = angle_speed1(angle_1, Imu.gyro_x) motor1 = limit(motor1 + control_turn(CCD_dir[0]), -4000, 4000) motor2 = limit(motor2 - control_turn(CCD_dir[0]), -4000, 4000) if not param_editing: motor_l.duty(-motor1) motor_r.duty(-motor2) else: motor_l.duty(0) motor_r.duty(0) # 5ms周期任务 if ticker_count % 5 == 0: angle_1 = angle(med_roll_angle - speed_1, Imu.Pitch) # 10ms周期任务 if ticker_count % 10 == 0: speed_1 = speed(med_speed, Encoders.KAL_templ_pluse) speed_2 = speed(med_speed, Encoders.KAL_tempr_pluse) #print("左误差 ={:>6f}, 右误差 ={:>6f}\r\n".format(speed_1,speed_2)) def time_pit2_handler(time): """定时器2中断处理""" global ticker_flag2 ticker_flag2 = True Encoders.KAL_templ_pluse = KalmanFilter(encoder_l.get()) # 左编码器滤波 Encoders.KAL_tempr_pluse = KalmanFilter2(encoder_r.get()) # 右编码器滤波 CCD = ALL_CCD()#实例化 # 初始化定时器 pit1 = ticker(1) pit2 = ticker(2) pit3 = ticker(3) # 关联数据采集 pit1.capture_list(imu) pit2.capture_list(ccd) pit3.capture_list(encoder_l, encoder_r,key) # 设置定时器回调函数 pit1.callback(time_pit_handler) pit2.callback(time_pit2_handler) # 初始化IMU Imu963ra_Init() # 启动定时器 pit1.start(1) # 1ms周期 pit2.start(6) # 6ms周期 pit3.start(10) # 10ms周期 # 主循环 while True: Key() # 处理按键输入 if ticker_flag: ccd_data1 = ccd.get(0) ccd_data2 = ccd.get(1) CCD_dir[0] =CCD.CCD1(ccd_data1) CCD_dir[1] =CCD.CCD2(ccd_data2) # print(ccd_data1) # print(ccd_data2) if CCD_dir[0] is None: zhong2 = 404 else : zhong2=CCD_dir[0] # print(ccd_data1) # print("CCD1:",CCD_dir[0]) # ticker_flag = False #电机启动屏幕关闭刷新 if not param_editing and last_title: lcd.str16(165, 90, "RUN>>>", 0x0000) last_title = None #与上相反 if param_editing: ips200_display() # 检查拨码开关状态 if switch2.value() != state2: print("Test program stop.") break # 垃圾回收 gc.collect() 小车运行代码,不根据轨道走,蛇形走,我应该如何调参

最新推荐

recommend-type

基于双向长短期记忆网络(BILSTM)的MATLAB数据分类预测代码实现与应用

基于双向长短期记忆网络(BILSTM)的数据分类预测技术及其在MATLAB中的实现方法。首先解释了BILSTM的工作原理,强调其在处理时间序列和序列相关问题中的优势。接着讨论了数据预处理的重要性和具体步骤,如数据清洗、转换和标准化。随后提供了MATLAB代码示例,涵盖从数据导入到模型训练的完整流程,特别指出代码适用于MATLAB 2019版本及以上。最后总结了BILSTM模型的应用前景和MATLAB作为工具的优势。 适合人群:对机器学习尤其是深度学习感兴趣的科研人员和技术开发者,特别是那些希望利用MATLAB进行数据分析和建模的人群。 使用场景及目标:①研究时间序列和其他序列相关问题的有效解决方案;②掌握BILSTM模型的具体实现方式;③提高数据分类预测的准确性。 阅读建议:读者应该具备一定的编程基础和对深度学习的理解,在实践中逐步深入理解BILSTM的工作机制,并尝试调整参数以适应不同的应用场景。
recommend-type

路径规划人工势场法及其改进Matlab代码,包括斥力引力合力势场图,解决机器人目标点徘徊问题

用于机器人路径规划的传统人工势场法及其存在的问题,并提出了一种改进方法来解决机器人在接近目标点时出现的徘徊动荡现象。文中提供了完整的Matlab代码实现,包括引力场、斥力场和合力场的计算,以及改进后的斥力公式引入的距离衰减因子。通过对比传统和改进后的势场图,展示了改进算法的有效性和稳定性。此外,还给出了主循环代码片段,解释了关键参数的选择和调整方法。 适合人群:对机器人路径规划感兴趣的科研人员、工程师和技术爱好者,尤其是有一定Matlab编程基础并希望深入了解人工势场法及其改进算法的人群。 使用场景及目标:适用于需要进行机器人路径规划的研究项目或应用场景,旨在提高机器人在复杂环境中导航的能力,确保其能够稳定到达目标点而不发生徘徊或震荡。 其他说明:本文不仅提供理论解析,还包括具体的代码实现细节,便于读者理解和实践。建议读者在学习过程中结合提供的代码进行实验和调试,以便更好地掌握改进算法的应用技巧。
recommend-type

基于LBP特征与DBN算法的人脸识别MATLAB程序实现及优化

基于LBP特征与深度信念网络(DBN)的人脸识别MATLAB程序。该程序利用LBP进行特征提取,构建了一个四层DBN模型,其中包括三层隐含层和一层输出层。通过RBM预训练和BP反向传播算法微调,实现了高精度的人脸识别,准确率达到98%,错误率为1.67%。此外,程序还包括学习曲线绘制功能,用于调整正则化参数和其他超参数,以优化模型性能。 适合人群:对机器学习、深度学习以及人脸识别感兴趣的科研人员和技术开发者。 使用场景及目标:适用于需要高效人脸识别的应用场景,如安防监控、身份验证等。主要目标是提高人脸识别的准确性和效率,同时提供详细的代码实现和优化技巧。 其他说明:文中提到的ORL数据库已做对齐处理,直接应用LBP可能会导致一定的误差。硬件方面,程序在i7 CPU上运行约需半小时完成一次完整的训练。为加快速度,可以考虑将RBM预训练改为GPU版本,但需额外配置MATLAB的并行计算工具箱。
recommend-type

三菱FX3U六轴标准程序:实现3轴本体控制与3个1PG定位模块,轴点动控制、回零控制及定位功能,搭配气缸与DD马达转盘的多工位流水作业方式

三菱FX3U PLC的六轴控制程序,涵盖本体三轴和扩展的三个1PG定位模块的控制。程序实现了轴点动、回零、相对定位和绝对定位等功能,并集成了多个气缸和一个由DD马达控制的转盘,用于转盘多工位流水作业。文中展示了部分核心代码,解释了各个功能的具体实现方法和技术细节。同时,强调了该程序在工业自动化领域的广泛应用及其重要性。 适合人群:从事工业自动化领域的工程师和技术人员,尤其是熟悉三菱PLC编程的专业人士。 使用场景及目标:适用于需要实现复杂多轴控制和流水线作业自动化的工业环境,如制造业生产线。目标是提高生产效率和精度,减少人工干预。 其他说明:该程序不仅展示了具体的编程技巧,还提供了对系统架构和逻辑控制的理解,有助于进一步优化和扩展工业自动化解决方案。
recommend-type

北京交通大学机器人技术与应用研究性学习

完整报告(PDF)、完整代码
recommend-type

基于Debian Jessie的Kibana Docker容器部署指南

Docker是一种开源的容器化平台,它允许开发者将应用及其依赖打包进一个可移植的容器中。Kibana则是由Elastic公司开发的一款开源数据可视化插件,主要用于对Elasticsearch中的数据进行可视化分析。Kibana与Elasticsearch以及Logstash一起通常被称为“ELK Stack”,广泛应用于日志管理和数据分析领域。 在本篇文档中,我们看到了关于Kibana的Docker容器化部署方案。文档提到的“Docker-kibana:Kibana 作为基于 Debian Jessie 的Docker 容器”实际上涉及了两个版本的Kibana,即Kibana 3和Kibana 4,并且重点介绍了它们如何被部署在Docker容器中。 Kibana 3 Kibana 3是一个基于HTML和JavaScript构建的前端应用,这意味着它不需要复杂的服务器后端支持。在Docker容器中运行Kibana 3时,容器实际上充当了一个nginx服务器的角色,用以服务Kibana 3的静态资源。在文档中提及的配置选项,建议用户将自定义的config.js文件挂载到容器的/kibana/config.js路径。这一步骤使得用户能够将修改后的配置文件应用到容器中,以便根据自己的需求调整Kibana 3的行为。 Kibana 4 Kibana 4相较于Kibana 3,有了一个质的飞跃,它基于Java服务器应用程序。这使得Kibana 4能够处理更复杂的请求和任务。文档中指出,要通过挂载自定义的kibana.yml文件到容器的/kibana/config/kibana.yml路径来配置Kibana 4。kibana.yml是Kibana的主要配置文件,它允许用户配置各种参数,比如Elasticsearch服务器的地址,数据索引名称等等。通过Docker容器部署Kibana 4,用户可以很轻松地利用Docker提供的环境隔离和可复制性特点,使得Kibana应用的部署和运维更为简洁高效。 Docker容器化的优势 使用Docker容器化技术部署Kibana,有几个显著的优势: - **一致性**:Docker容器确保应用在开发、测试和生产环境中的行为保持一致。 - **轻量级**:相比传统虚拟机,Docker容器更加轻量,启动快速,资源占用更少。 - **隔离性**:容器之间的环境隔离,确保应用之间互不干扰。 - **可移植性**:容器可以在任何支持Docker的环境中运行,提高了应用的可移植性。 - **易于维护**:通过Dockerfile可以轻松构建和分发应用镜像,便于维护和升级。 在文档中,我们还看到了文件名“docker-kibana-master”。这个名称很可能是指向了存放Docker相关文件的源代码仓库,其中可能包含Dockerfile、构建和运行脚本以及可能的配置模板文件等。开发者可以从这个仓库中克隆或下载所需的Docker相关文件,并根据这些文件来构建和部署Kibana的Docker容器。 根据以上信息,对于希望利用Docker容器部署和管理Kibana应用的用户,需要掌握Docker的基本使用方法,包括Docker的安装、镜像管理、容器的创建和配置等。同时,还需要了解Kibana的基本原理和配置方法,这样才能充分利用Docker容器化带来的便利,优化Kibana应用的部署和管理流程。
recommend-type

Coze智能体工作流:打造钦天监视频内容的创新与实践

# 1. Coze智能体工作流概述 Coze智能体工作流是业界领先的智能工作流程管理系统,它通过整合最新的智能技术与人工智能算法,旨在为视频内容创造与优化带来革命性的变革。本章将对Coze智能体工作流进行全面的概述,揭示其在IT行业中的应用范围、流程结构以及技术基础。通过了解Coze智能体工作流,读者将能够把握它在当前和未
recommend-type

使用git仓库的利与弊

<think>好的,我需要分析Git仓库的优缺点。首先,根据用户提供的引用,看看有哪些点需要覆盖。引用[1]提到了Git有本地和远程仓库,需要push命令来更新远程,这应该属于优点还是缺点呢?可能优点在于本地操作灵活,缺点是需要额外命令。 引用[2]指出Git的权限管理薄弱,学习曲线陡峭,这显然是缺点。另外,引用[4]提到Git使用SHA-1哈希,内容完整性更好,属于优点。引用[5]比较了Git的分支管理简单,而缺少全局版本号,这可以作为优点和缺点分别说明。 现在需要将这些信息整理成结构化的优缺点,可能分点列出。同时,用户要求回答结构清晰,逐步解决问题,所以可能需要先介绍Git的基本概念,
recommend-type

TextWorld:基于文本游戏的强化学习环境沙箱

在给出的文件信息中,我们可以提取到以下IT知识点: ### 知识点一:TextWorld环境沙箱 **标题**中提到的“TextWorld”是一个专用的学习环境沙箱,专为强化学习(Reinforcement Learning,简称RL)代理的训练和测试而设计。在IT领域中,尤其是在机器学习的子领域中,环境沙箱是指一个受控的计算环境,允许实验者在隔离的条件下进行软件开发和测试。强化学习是一种机器学习方法,其中智能体(agent)通过与环境进行交互来学习如何在某个特定环境中执行任务,以最大化某种累积奖励。 ### 知识点二:基于文本的游戏生成器 **描述**中说明了TextWorld是一个基于文本的游戏生成器。在计算机科学中,基于文本的游戏(通常被称为文字冒险游戏)是一种游戏类型,玩家通过在文本界面输入文字指令来与游戏世界互动。TextWorld生成器能够创建这类游戏环境,为RL代理提供训练和测试的场景。 ### 知识点三:强化学习(RL) 强化学习是**描述**中提及的关键词,这是一种机器学习范式,用于训练智能体通过尝试和错误来学习在给定环境中如何采取行动。在强化学习中,智能体在环境中探索并执行动作,环境对每个动作做出响应并提供一个奖励或惩罚,智能体的目标是学习一个策略,以最大化长期累积奖励。 ### 知识点四:安装与支持的操作系统 **描述**提到TextWorld的安装需要Python 3,并且当前仅支持Linux和macOS系统。对于Windows用户,提供了使用Docker作为解决方案的信息。这里涉及几个IT知识点: - **Python 3**:一种广泛使用的高级编程语言,适用于快速开发,是进行机器学习研究和开发的常用语言。 - **Linux**和**macOS**:两种流行的操作系统,分别基于Unix系统和类Unix系统。 - **Windows**:另一种广泛使用的操作系统,具有不同的软件兼容性。 - **Docker**:一个开源的应用容器引擎,允许开发者打包应用及其依赖环境为一个轻量级、可移植的容器,使得在任何支持Docker的平台上一致地运行。 ### 知识点五:系统库和依赖 **描述**提到在基于Debian/Ubuntu的系统上,可以安装一些系统库来支持TextWorld的本机组件。这里涉及的知识点包括: - **Debian/Ubuntu**:基于Debian的Linux发行版,是目前最流行的Linux发行版之一。 - **系统库**:操作系统中包含的一系列预编译的软件包和库,供应用程序在运行时使用。 - **包管理工具**,如**apt**(Advanced Package Tool),它是一个在Debian及其衍生系统中用于安装、删除和管理软件包的命令行工具。 ### 知识点六:与创建者联系方式 **描述**提供了与TextWorld创建者的联系方式,包括电子邮件地址和一个Gitter频道。这说明了如何与开源项目的维护者进行沟通与反馈: - **电子邮件**是常见的沟通方式,允许用户与开发者直接交流。 - **Gitter**是一个基于GitHub的即时消息工具,通常用于开源项目中的实时协作和交流。 ### 结语 综合以上信息,我们可以了解到TextWorld是一个专为强化学习设计的学习环境沙箱,它通过创建基于文本的游戏环境,让研究者和开发者训练和测试RL代理。它主要针对Linux和macOS系统,不过也有适合Windows用户的替代方案。此外,了解如何安装和配置TextWorld,以及如何与创建者沟通,对于开发者来说是十分重要的基础技能。
recommend-type

Coze智能体工作流全攻略

# 1. Coze智能体工作流概述 在现代企业中,工作流管理不仅是提高效率的关键因素,而且