<?xml version="1.0" encoding="UTF-8"?>        <rss version="2.0"
             xmlns:atom="http://www.w3.org/2005/Atom"
             xmlns:dc="http://purl.org/dc/elements/1.1/"
             xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
             xmlns:admin="http://webns.net/mvcb/"
             xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
             xmlns:content="http://purl.org/rss/1.0/modules/content/">
        <channel>
            <title>
									/systemCheck - Microcontroller Firmware (C++)				            </title>
            <link>https://theopenexo.nau.edu/community/firmware/systemcheck/</link>
            <description>Discussion Board</description>
            <language>en-US</language>
            <lastBuildDate>Mon, 13 Jul 2026 10:14:43 +0000</lastBuildDate>
            <generator>wpForo</generator>
            <ttl>60</ttl>
							                    <item>
                        <title>Answer to: /systemCheck</title>
                        <link>https://theopenexo.nau.edu/community/firmware/systemcheck/#post-47</link>
                        <pubDate>Fri, 26 Sep 2025 07:30:53 +0000</pubDate>
                        <description><![CDATA[Rebuild Gait.ino (Working on my building OpenExo instance in HongKong)

#include &lt;FlexCAN_T4.h&gt;
#include &quot;Arduino.h&quot;

// ==========================================================...]]></description>
                        <content:encoded><![CDATA[<p>Rebuild Gait.ino (Working on my building OpenExo instance in HongKong)<br /><br /></p>
<div>
<div><span>#include</span><span> </span><span>&lt;FlexCAN_T4.h&gt;</span></div>
<div><span>#include</span><span> </span><span>"Arduino.h"</span></div>
<br />
<div><span>// =================================================================</span></div>
<div><span>// Configuration / 配置</span></div>
<div><span>// =================================================================</span></div>
<br />
<div><span>// --- Motor CAN IDs / 电机CAN ID定义 ---</span></div>
<div><span>#define</span><span> LEFT_HIP_ID  </span><span>65</span></div>
<div><span>#define</span><span> RIGHT_HIP_ID </span><span>33</span></div>
<br />
<div><span>// --- Pin Definitions / 管脚定义 ---</span></div>
<div><span>const</span><span> </span><span>int</span><span> ENABLE_LEFT_PIN[]  </span><span>=</span><span> {</span><span>28</span><span>, </span><span>29</span><span>};</span></div>
<div><span>const</span><span> </span><span>int</span><span> ENABLE_RIGHT_PIN[] </span><span>=</span><span> {</span><span>8</span><span>, </span><span>7</span><span>};</span></div>
<div><span>const</span><span> </span><span>int</span><span> MOTOR_STOP_PIN     </span><span>=</span><span> </span><span>9</span><span>;</span></div>
<br />
<div><span>// --- Motion Control Mode Parameters (from documentation) / 运控模式参数(来自文档) ---</span></div>
<div><span>// Note: Position values are in RADIANS, not degrees.</span></div>
<div><span>// 注意：位置单位是弧度，不是角度。</span></div>
<div><span>const</span><span> </span><span>float</span><span> P_MIN </span><span>=</span><span> </span><span>-</span><span>12.5f</span><span>;</span><span>   // Min position (rad)</span></div>
<div><span>const</span><span> </span><span>float</span><span> P_MAX </span><span>=</span><span> </span><span>12.5f</span><span>;</span><span>    // Max position (rad)</span></div>
<div><span>const</span><span> </span><span>float</span><span> V_MIN </span><span>=</span><span> </span><span>-</span><span>30.0f</span><span>;</span><span>   // Min velocity (rad/s)</span></div>
<div><span>const</span><span> </span><span>float</span><span> V_MAX </span><span>=</span><span> </span><span>30.0f</span><span>;</span><span>    // Max velocity (rad/s)</span></div>
<div><span>const</span><span> </span><span>float</span><span> KP_MIN </span><span>=</span><span> </span><span>0.0f</span><span>;</span><span>    // Min Kp</span></div>
<div><span>const</span><span> </span><span>float</span><span> KP_MAX </span><span>=</span><span> </span><span>500.0f</span><span>;</span><span>  // Max Kp</span></div>
<div><span>const</span><span> </span><span>float</span><span> KD_MIN </span><span>=</span><span> </span><span>0.0f</span><span>;</span><span>    // Min Kd</span></div>
<div><span>const</span><span> </span><span>float</span><span> KD_MAX </span><span>=</span><span> </span><span>5.0f</span><span>;</span><span>    // Max Kd</span></div>
<div><span>const</span><span> </span><span>float</span><span> T_MIN </span><span>=</span><span> </span><span>-</span><span>18.0f</span><span>;</span><span>   // Min torque (N-m)</span></div>
<div><span>const</span><span> </span><span>float</span><span> T_MAX </span><span>=</span><span> </span><span>18.0f</span><span>;</span><span>    // Max torque (N-m)</span></div>
<br />
<div><span>// --- Control Parameters / 控制参数 ---</span></div>
<div><span>const</span><span> </span><span>float</span><span> KP </span><span>=</span><span> </span><span>80.0f</span><span>;</span><span>                 // Proportional gain / 比例增益</span></div>
<div><span>const</span><span> </span><span>float</span><span> KD </span><span>=</span><span> </span><span>1.5f</span><span>;</span><span>                  // Derivative gain / 微分增益</span></div>
<br />
<div><span>// =================================================================</span></div>
<div><span>// Global Variables / 全局变量</span></div>
<div><span>// =================================================================</span></div>
<div><span>FlexCAN_T4</span><span>&lt;</span><span>CAN1, RX_SIZE_256, TX_SIZE_16</span><span>&gt;</span><span> Can0;</span></div>
<br />
<div><span>// Actual motor states updated by CAN feedback</span></div>
<div><span>// 由CAN反馈更新的电机实际状态</span></div>
<div><span>volatile</span><span> </span><span>float</span><span> leftActualPos_rad </span><span>=</span><span> </span><span>0.0f</span><span>;</span></div>
<div><span>volatile</span><span> </span><span>float</span><span> leftActualVel_rad </span><span>=</span><span> </span><span>0.0f</span><span>;</span></div>
<div><span>volatile</span><span> </span><span>float</span><span> leftActualTorque </span><span>=</span><span> </span><span>0.0f</span><span>;</span></div>
<div><span>volatile</span><span> </span><span>float</span><span> rightActualPos_rad </span><span>=</span><span> </span><span>0.0f</span><span>;</span></div>
<div><span>volatile</span><span> </span><span>float</span><span> rightActualVel_rad </span><span>=</span><span> </span><span>0.0f</span><span>;</span></div>
<div><span>volatile</span><span> </span><span>float</span><span> rightActualTorque </span><span>=</span><span> </span><span>0.0f</span><span>;</span></div>
<br />
<div><span>bool</span><span> emergencyStop </span><span>=</span><span> </span><span>false</span><span>;</span></div>
<br />
<div><span>// =================================================================</span></div>
<div><span>// Utility Functions / 辅助函数</span></div>
<div><span>// =================================================================</span></div>
<br />
<div><span>// --- Wait for user input to proceed / 等待用户输入以继续 ---</span></div>
<div><span>void</span><span> </span><span>waitForKeyPress</span><span>() {</span></div>
<div><span>    </span><span>Serial</span><span>.</span><span>println</span><span>(</span><span>"</span><span>\n</span><span>请按空格键继续下一步..."</span><span>);</span></div>
<div><span>    </span><span>while</span><span> (</span><span>true</span><span>) {</span></div>
<div><span>        </span><span>if</span><span> (</span><span>Serial</span><span>.</span><span>available</span><span>() </span><span>&gt;</span><span> </span><span>0</span><span>) {</span></div>
<div><span>            </span><span>char</span><span> c </span><span>=</span><span> </span><span>Serial</span><span>.</span><span>read</span><span>();</span></div>
<div><span>            </span><span>if</span><span> (c </span><span>==</span><span> </span><span>' '</span><span>) {</span></div>
<div><span>                </span><span>Serial</span><span>.</span><span>println</span><span>(</span><span>"继续执行..."</span><span>);</span></div>
<div><span>                // Clear buffer</span></div>
<div><span>                </span><span>while</span><span>(</span><span>Serial</span><span>.</span><span>available</span><span>() </span><span>&gt;</span><span> </span><span>0</span><span>) </span><span>Serial</span><span>.</span><span>read</span><span>();</span></div>
<div><span>                </span><span>break</span><span>;</span></div>
<div><span>            }</span></div>
<div><span>        }</span></div>
<div><span>        </span><span>delay</span><span>(</span><span>50</span><span>);</span></div>
<div><span>    }</span></div>
<div><span>}</span></div>
<br />
<div><span>// --- Convert float to unsigned int for CAN packing / 浮点数转为整数用于CAN打包 ---</span></div>
<div><span>unsigned</span><span> </span><span>int</span><span> </span><span>float_to_uint</span><span>(</span><span>float</span><span> </span><span>x</span><span>, </span><span>float</span><span> </span><span>x_min</span><span>, </span><span>float</span><span> </span><span>x_max</span><span>, </span><span>int</span><span> </span><span>bits</span><span>) {</span></div>
<div><span>    </span><span>float</span><span> span </span><span>=</span><span> x_max </span><span>-</span><span> x_min;</span></div>
<div><span>    </span><span>float</span><span> offset </span><span>=</span><span> x_min;</span></div>
<div><span>    // Clamp the input value within the specified range</span></div>
<div><span>    </span><span>if</span><span> (x </span><span>&gt;</span><span> x_max) x </span><span>=</span><span> x_max;</span></div>
<div><span>    </span><span>else</span><span> </span><span>if</span><span> (x </span><span>&lt;</span><span> x_min) x </span><span>=</span><span> x_min;</span></div>
<div><span>    // Perform the conversion</span></div>
<div><span>    </span><span>return</span><span> (</span><span>unsigned</span><span> </span><span>int</span><span>)((x </span><span>-</span><span> offset) </span><span>*</span><span> ((</span><span>float</span><span>)((</span><span>1</span><span> </span><span>&lt;&lt;</span><span> bits) </span><span>-</span><span>1</span><span>)) </span><span>/</span><span> span);</span></div>
<div><span>}</span></div>
<br />
<div><span>// --- Convert unsigned int from CAN to float / CAN数据中的整数转为浮点数 ---</span></div>
<div><span>float</span><span> </span><span>uint_to_float</span><span>(</span><span>int</span><span> </span><span>x_int</span><span>, </span><span>float</span><span> </span><span>x_min</span><span>, </span><span>float</span><span> </span><span>x_max</span><span>, </span><span>int</span><span> </span><span>bits</span><span>) {</span></div>
<div><span>    </span><span>float</span><span> span </span><span>=</span><span> x_max </span><span>-</span><span> x_min;</span></div>
<div><span>    </span><span>float</span><span> offset </span><span>=</span><span> x_min;</span></div>
<div><span>    // Perform the conversion</span></div>
<div><span>    </span><span>return</span><span> ((</span><span>float</span><span>)x_int) </span><span>*</span><span> span </span><span>/</span><span> ((</span><span>float</span><span>)((</span><span>1</span><span> </span><span>&lt;&lt;</span><span> bits) </span><span>-</span><span> </span><span>1</span><span>)) </span><span>+</span><span> offset;</span></div>
<div><span>}</span></div>
<br />
<div><span>// =================================================================</span></div>
<div><span>// CAN Communication Functions (Motion Control Mode)</span></div>
<div><span>// CAN通信函数 (运控模式)</span></div>
<div><span>// =================================================================</span></div>
<br />
<div><span>/**</span></div>
<div><span> * </span><span>@brief</span><span> Sends a generic command with an 8-byte payload using a standard CAN frame.</span></div>
<div><span> * </span><span>@param</span><span> </span><span>id</span><span> The motor's CAN ID.</span></div>
<div><span> * </span><span>@param</span><span> </span><span>data</span><span> An 8-byte array containing the command payload.</span></div>
<div><span> */</span></div>
<div><span>void</span><span> </span><span>sendMotionCommand</span><span>(</span><span>uint8_t</span><span> </span><span>id</span><span>, </span><span>const</span><span> </span><span>uint8_t*</span><span> </span><span>data</span><span>) {</span></div>
<div><span>    </span><span>CAN_message_t</span><span> msg;</span></div>
<div><span>    </span><span>msg</span><span>.</span><span>id</span><span> </span><span>=</span><span> id;</span></div>
<div><span>    </span><span>msg</span><span>.</span><span>flags</span><span>.</span><span>extended</span><span> </span><span>=</span><span> </span><span>0</span><span>;</span><span> // Use Standard Frame for Motion Control Mode</span></div>
<div><span>    </span><span>msg</span><span>.</span><span>len</span><span> </span><span>=</span><span> </span><span>8</span><span>;</span></div>
<div><span>    </span><span>memcpy</span><span>(</span><span>msg</span><span>.</span><span>buf</span><span>, data, </span><span>8</span><span>);</span></div>
<div><span>    </span><span>Can0</span><span>.</span><span>write</span><span>(msg);</span></div>
<div><span>}</span></div>
<br />
<div><span>// --- Enter Motor Control Mode / 进入电机控制模式 ---</span></div>
<div><span>void</span><span> </span><span>enterControlMode</span><span>(</span><span>uint8_t</span><span> </span><span>id</span><span>) {</span></div>
<div><span>    </span><span>const</span><span> </span><span>uint8_t</span><span> </span><span>cmd</span><span> </span><span>=</span><span> {</span><span>0xFF</span><span>, </span><span>0xFF</span><span>, </span><span>0xFF</span><span>, </span><span>0xFF</span><span>, </span><span>0xFF</span><span>, </span><span>0xFF</span><span>, </span><span>0xFF</span><span>, </span><span>0xFC</span><span>};</span></div>
<div><span>    </span><span>sendMotionCommand</span><span>(id, cmd);</span></div>
<div><span>    </span><span>Serial</span><span>.</span><span>printf</span><span>(</span><span>"电机ID </span><span>%u</span><span>: 已发送  命令</span><span>\n</span><span>"</span><span>, id);</span></div>
<div><span>}</span></div>
<br />
<div><span>// --- Exit Motor Control Mode / 退出电机控制模式 ---</span></div>
<div><span>void</span><span> </span><span>exitControlMode</span><span>(</span><span>uint8_t</span><span> </span><span>id</span><span>) {</span></div>
<div><span>    </span><span>const</span><span> </span><span>uint8_t</span><span> </span><span>cmd</span><span> </span><span>=</span><span> {</span><span>0xFF</span><span>, </span><span>0xFF</span><span>, </span><span>0xFF</span><span>, </span><span>0xFF</span><span>, </span><span>0xFF</span><span>, </span><span>0xFF</span><span>, </span><span>0xFF</span><span>, </span><span>0xFD</span><span>};</span></div>
<div><span>    </span><span>sendMotionCommand</span><span>(id, cmd);</span></div>
<div><span>     </span><span>Serial</span><span>.</span><span>printf</span><span>(</span><span>"电机ID </span><span>%u</span><span>: 已发送  命令</span><span>\n</span><span>"</span><span>, id);</span></div>
<div><span>}</span></div>
<br />
<div><span>// --- Set Current Position as Zero / 设置当前位置为零点 ---</span></div>
<div><span>void</span><span> </span><span>setOrigin</span><span>(</span><span>uint8_t</span><span> </span><span>id</span><span>) {</span></div>
<div><span>    </span><span>const</span><span> </span><span>uint8_t</span><span> </span><span>cmd</span><span> </span><span>=</span><span> {</span><span>0xFF</span><span>, </span><span>0xFF</span><span>, </span><span>0xFF</span><span>, </span><span>0xFF</span><span>, </span><span>0xFF</span><span>, </span><span>0xFF</span><span>, </span><span>0xFF</span><span>, </span><span>0xFE</span><span>};</span></div>
<div><span>    </span><span>sendMotionCommand</span><span>(id, cmd);</span></div>
<div><span>    </span><span>Serial</span><span>.</span><span>printf</span><span>(</span><span>"电机ID </span><span>%u</span><span>: 已发送  命令</span><span>\n</span><span>"</span><span>, id);</span></div>
<div><span>}</span></div>
<br />
<div><span>/**</span></div>
<div><span> * </span><span>@brief</span><span> Packs motor command data and sends it over CAN.</span></div>
<div><span> * </span><span>@param</span><span> </span><span>id</span><span> Motor CAN ID.</span></div>
<div><span> * </span><span>@param</span><span> </span><span>p_des</span><span> Desired position (radians).</span></div>
<div><span> * </span><span>@param</span><span> </span><span>v_des</span><span> Desired velocity (rad/s).</span></div>
<div><span> * </span><span>@param</span><span> </span><span>kp</span><span> Kp gain.</span></div>
<div><span> * </span><span>@param</span><span> </span><span>kd</span><span> Kd gain.</span></div>
<div><span> * </span><span>@param</span><span> </span><span>t_ff</span><span> Feed-forward torque (N-m).</span></div>
<div><span> */</span></div>
<div><span>void</span><span> </span><span>packAndSendMotionCommand</span><span>(</span><span>uint8_t</span><span> </span><span>id</span><span>, </span><span>float</span><span> </span><span>p_des_rad</span><span>, </span><span>float</span><span> </span><span>v_des_rad</span><span>, </span><span>float</span><span> </span><span>kp</span><span>, </span><span>float</span><span> </span><span>kd</span><span>, </span><span>float</span><span> </span><span>t_ff</span><span>) {</span></div>
<div><span>    </span><span>if</span><span> (emergencyStop) </span><span>return</span><span>;</span></div>
<br />
<div><span>    // Convert floats to unsigned ints</span></div>
<div><span>    </span><span>unsigned</span><span> </span><span>int</span><span> p_int </span><span>=</span><span> </span><span>float_to_uint</span><span>(p_des_rad, P_MIN, P_MAX, </span><span>16</span><span>);</span></div>
<div><span>    </span><span>unsigned</span><span> </span><span>int</span><span> v_int </span><span>=</span><span> </span><span>float_to_uint</span><span>(v_des_rad, V_MIN, V_MAX, </span><span>12</span><span>);</span></div>
<div><span>    </span><span>unsigned</span><span> </span><span>int</span><span> kp_int </span><span>=</span><span> </span><span>float_to_uint</span><span>(kp, KP_MIN, KP_MAX, </span><span>12</span><span>);</span></div>
<div><span>    </span><span>unsigned</span><span> </span><span>int</span><span> kd_int </span><span>=</span><span> </span><span>float_to_uint</span><span>(kd, KD_MIN, KD_MAX, </span><span>12</span><span>);</span></div>
<div><span>    </span><span>unsigned</span><span> </span><span>int</span><span> t_int </span><span>=</span><span> </span><span>float_to_uint</span><span>(t_ff, T_MIN, T_MAX, </span><span>12</span><span>);</span></div>
<br />
<div><span>    // Pack ints into the CAN buffer</span></div>
<div><span>    </span><span>uint8_t</span><span> </span><span>data</span><span>;</span></div>
<div><span>    </span><span>data</span><span> </span><span>=</span><span> p_int </span><span>&gt;&gt;</span><span> </span><span>8</span><span>;</span></div>
<div><span>    </span><span>data</span><span> </span><span>=</span><span> p_int </span><span>&amp;</span><span> </span><span>0xFF</span><span>;</span></div>
<div><span>    </span><span>data</span><span> </span><span>=</span><span> v_int </span><span>&gt;&gt;</span><span> </span><span>4</span><span>;</span></div>
<div><span>    </span><span>data</span><span> </span><span>=</span><span> ((v_int </span><span>&amp;</span><span> </span><span>0xF</span><span>) </span><span>&lt;&lt;</span><span> </span><span>4</span><span>) </span><span>|</span><span> (kp_int </span><span>&gt;&gt;</span><span> </span><span>8</span><span>);</span></div>
<div><span>    </span><span>data</span><span> </span><span>=</span><span> kp_int </span><span>&amp;</span><span> </span><span>0xFF</span><span>;</span></div>
<div><span>    </span><span>data</span><span> </span><span>=</span><span> kd_int </span><span>&gt;&gt;</span><span> </span><span>4</span><span>;</span></div>
<div><span>    </span><span>data</span><span> </span><span>=</span><span> ((kd_int </span><span>&amp;</span><span> </span><span>0xF</span><span>) </span><span>&lt;&lt;</span><span> </span><span>4</span><span>) </span><span>|</span><span> (t_int </span><span>&gt;&gt;</span><span> </span><span>8</span><span>);</span></div>
<div><span>    </span><span>data</span><span> </span><span>=</span><span> t_int </span><span>&amp;</span><span> </span><span>0xFF</span><span>;</span></div>
<br />
<div><span>    </span><span>sendMotionCommand</span><span>(id, data);</span></div>
<div><span>}</span></div>
<br />
<div><span>// --- Unpack motor reply from CAN message / 从CAN报文中解析电机反馈 ---</span></div>
<div><span>void</span><span> </span><span>unpack_reply</span><span>(</span><span>const</span><span> </span><span>CAN_message_t</span><span>&amp;</span><span> </span><span>msg</span><span>) {</span></div>
<div><span>    // According to the docs, the reply ID is 0x00, and the first data byte is the motor's ID.</span></div>
<div><span>    // However, some implementations might use the motor's ID as the reply ID. We check the first byte.</span></div>
<div><span>    </span><span>uint8_t</span><span> motor_id </span><span>=</span><span> </span><span>msg</span><span>.</span><span>buf</span><span>;</span></div>
<br />
<div><span>    // Unpack ints from CAN buffer</span></div>
<div><span>    </span><span>int</span><span> p_int </span><span>=</span><span> (</span><span>msg</span><span>.</span><span>buf</span><span> </span><span>&lt;&lt;</span><span> </span><span>8</span><span>) </span><span>|</span><span> </span><span>msg</span><span>.</span><span>buf</span><span>;</span></div>
<div><span>    </span><span>int</span><span> v_int </span><span>=</span><span> (</span><span>msg</span><span>.</span><span>buf</span><span> </span><span>&lt;&lt;</span><span> </span><span>4</span><span>) </span><span>|</span><span> (</span><span>msg</span><span>.</span><span>buf</span><span> </span><span>&gt;&gt;</span><span> </span><span>4</span><span>);</span></div>
<div><span>    </span><span>int</span><span> i_int </span><span>=</span><span> ((</span><span>msg</span><span>.</span><span>buf</span><span> </span><span>&amp;</span><span> </span><span>0x0F</span><span>) </span><span>&lt;&lt;</span><span> </span><span>8</span><span>) </span><span>|</span><span> </span><span>msg</span><span>.</span><span>buf</span><span>;</span></div>
<div><span>    // int temp = msg.buf; // Temperature</span></div>
<div><span>    // int error = msg.buf; // Error code</span></div>
<br />
<div><span>    // Convert ints to floats</span></div>
<div><span>    </span><span>float</span><span> p </span><span>=</span><span> </span><span>uint_to_float</span><span>(p_int, P_MIN, P_MAX, </span><span>16</span><span>);</span></div>
<div><span>    </span><span>float</span><span> v </span><span>=</span><span> </span><span>uint_to_float</span><span>(v_int, V_MIN, V_MAX, </span><span>12</span><span>);</span></div>
<div><span>    </span><span>float</span><span> i </span><span>=</span><span> </span><span>uint_to_float</span><span>(i_int, T_MIN, T_MAX, </span><span>12</span><span>);</span></div>
<br />
<div><span>    // Update global state variables</span></div>
<div><span>    </span><span>if</span><span> (motor_id </span><span>==</span><span> LEFT_HIP_ID) {</span></div>
<div><span>        leftActualPos_rad </span><span>=</span><span> p;</span></div>
<div><span>        leftActualVel_rad </span><span>=</span><span> v;</span></div>
<div><span>        leftActualTorque </span><span>=</span><span> i;</span></div>
<div><span>    } </span><span>else</span><span> </span><span>if</span><span> (motor_id </span><span>==</span><span> RIGHT_HIP_ID) {</span></div>
<div><span>        rightActualPos_rad </span><span>=</span><span> p;</span></div>
<div><span>        rightActualVel_rad </span><span>=</span><span> v;</span></div>
<div><span>        rightActualTorque </span><span>=</span><span> i;</span></div>
<div><span>    }</span></div>
<div><span>}</span></div>
<br />
<div><span>// --- Read and process motor feedback from CAN bus / 从CAN总线读取并处理电机反馈 ---</span></div>
<div><span>void</span><span> </span><span>readMotorFeedback</span><span>() {</span></div>
<div><span>    </span><span>CAN_message_t</span><span> msg;</span></div>
<div><span>    </span><span>while</span><span> (</span><span>Can0</span><span>.</span><span>read</span><span>(msg)) {</span></div>
<div><span>        // In Motion Control Mode, the reply is a standard frame.</span></div>
<div><span>        </span><span>if</span><span> (</span><span>!</span><span>msg</span><span>.</span><span>flags</span><span>.</span><span>extended</span><span> </span><span>&amp;&amp;</span><span> </span><span>msg</span><span>.</span><span>len</span><span> </span><span>==</span><span> </span><span>8</span><span>) {</span></div>
<div><span>            </span><span>unpack_reply</span><span>(msg);</span></div>
<div><span>        }</span></div>
<div><span>    }</span></div>
<div><span>}</span></div>
<br />
<div><span>// =================================================================</span></div>
<div><span>// High-Level Control Functions / 高级控制函数</span></div>
<div><span>// =================================================================</span></div>
<br />
<div><span>void</span><span> </span><span>stopAllMotors</span><span>() {</span></div>
<div><span>    // Send a command to hold position at 0 with zero gains before exiting</span></div>
<div><span>    </span><span>packAndSendMotionCommand</span><span>(LEFT_HIP_ID, </span><span>0.0</span><span>, </span><span>0.0</span><span>, </span><span>0.0</span><span>, </span><span>0.0</span><span>, </span><span>0.0</span><span>);</span></div>
<div><span>    </span><span>delay</span><span>(</span><span>10</span><span>);</span></div>
<div><span>    </span><span>packAndSendMotionCommand</span><span>(RIGHT_HIP_ID, </span><span>0.0</span><span>, </span><span>0.0</span><span>, </span><span>0.0</span><span>, </span><span>0.0</span><span>, </span><span>0.0</span><span>);</span></div>
<div><span>    </span><span>delay</span><span>(</span><span>10</span><span>);</span></div>
<div><span>    </span></div>
<div><span>    </span><span>exitControlMode</span><span>(LEFT_HIP_ID);</span></div>
<div><span>    </span><span>delay</span><span>(</span><span>10</span><span>);</span></div>
<div><span>    </span><span>exitControlMode</span><span>(RIGHT_HIP_ID);</span></div>
<div><span>    </span><span>Serial</span><span>.</span><span>println</span><span>(</span><span>"已发送停止命令到所有电机"</span><span>);</span></div>
<div><span>}</span></div>
<br />
<div><span>void</span><span> </span><span>disableAllMotors</span><span>() {</span></div>
<div><span>    </span><span>for</span><span> (</span><span>int</span><span> pin : ENABLE_LEFT_PIN) </span><span>digitalWrite</span><span>(pin, LOW);</span></div>
<div><span>    </span><span>for</span><span> (</span><span>int</span><span> pin : ENABLE_RIGHT_PIN) </span><span>digitalWrite</span><span>(pin, LOW);</span></div>
<div><span>    </span><span>Serial</span><span>.</span><span>println</span><span>(</span><span>"所有电机驱动器已禁用"</span><span>);</span></div>
<div><span>}</span></div>
<br /><br />
<div><span>/**</span></div>
<div><span> * </span><span>@brief</span><span> Runs a continuous gait simulation for a specified duration.</span></div>
<div><span> * Motors move in a sinusoidal pattern with a 180-degree phase shift.</span></div>
<div><span> */</span></div>
<div><span>void</span><span> </span><span>runGaitSimulation</span><span>() {</span></div>
<div><span>    // --- Gait Parameters ---</span></div>
<div><span>    </span><span>const</span><span> </span><span>float</span><span> AMPLITUDE_DEG </span><span>=</span><span> </span><span>30.0f</span><span>;</span><span> // Swing amplitude in degrees</span></div>
<div><span>    </span><span>const</span><span> </span><span>float</span><span> PERIOD_S </span><span>=</span><span> </span><span>2.0f</span><span>;</span><span>       // Period of one full cycle in seconds</span></div>
<div><span>    </span><span>const</span><span> </span><span>unsigned</span><span> </span><span>long</span><span> DURATION_MS </span><span>=</span><span> </span><span>120000</span><span>;</span><span> // Total duration of the test (2 minutes)</span></div>
<div><span>    </span><span>const</span><span> </span><span>unsigned</span><span> </span><span>int</span><span> LOOP_DELAY_MS </span><span>=</span><span> </span><span>20</span><span>;</span><span>    // Loop delay, controls update frequency (50Hz)</span></div>
<div><span>    </span><span>const</span><span> </span><span>unsigned</span><span> </span><span>int</span><span> PRINT_INTERVAL_MS </span><span>=</span><span> </span><span>200</span><span>;</span><span> // How often to print status</span></div>
<br />
<div><span>    // --- Convert to radians and milliseconds ---</span></div>
<div><span>    </span><span>const</span><span> </span><span>float</span><span> AMPLITUDE_RAD </span><span>=</span><span> AMPLITUDE_DEG </span><span>*</span><span> DEG_TO_RAD;</span></div>
<div><span>    </span><span>const</span><span> </span><span>float</span><span> PERIOD_MS </span><span>=</span><span> PERIOD_S </span><span>*</span><span> </span><span>1000.0f</span><span>;</span></div>
<br />
<div><span>    </span><span>Serial</span><span>.</span><span>printf</span><span>(</span><span>"</span><span>\n</span><span>开始步态模拟: 幅度 ±</span><span>%.1f</span><span>度, 周期 </span><span>%.1f</span><span>s, 持续时间 </span><span>%lu</span><span>s</span><span>\n</span><span>"</span><span>,</span></div>
<div><span>                  AMPLITUDE_DEG, PERIOD_S, DURATION_MS </span><span>/</span><span> </span><span>1000</span><span>);</span></div>
<div><span>    </span><span>waitForKeyPress</span><span>();</span></div>
<div><span>    </span></div>
<div><span>    </span><span>unsigned</span><span> </span><span>long</span><span> startTime </span><span>=</span><span> </span><span>millis</span><span>();</span></div>
<div><span>    </span><span>unsigned</span><span> </span><span>long</span><span> lastPrintTime </span><span>=</span><span> </span><span>0</span><span>;</span></div>
<br />
<div><span>    </span><span>while</span><span> (</span><span>millis</span><span>() </span><span>-</span><span> startTime </span><span>&lt;</span><span> DURATION_MS) {</span></div>
<div><span>        </span><span>if</span><span> (emergencyStop) </span><span>break</span><span>;</span></div>
<br />
<div><span>        // --- Calculate Target Positions ---</span></div>
<div><span>        </span><span>unsigned</span><span> </span><span>long</span><span> elapsedTime </span><span>=</span><span> </span><span>millis</span><span>() </span><span>-</span><span> startTime;</span></div>
<div><span>        </span><span>float</span><span> angle_rad </span><span>=</span><span> AMPLITUDE_RAD </span><span>*</span><span> </span><span>sin</span><span>((</span><span>float</span><span>)elapsedTime </span><span>*</span><span> </span><span>2.0f</span><span> </span><span>*</span><span> PI </span><span>/</span><span> PERIOD_MS);</span></div>
<div><span>        </span></div>
<div><span>        </span><span>float</span><span> leftTargetRad </span><span>=</span><span> angle_rad;</span></div>
<div><span>        </span><span>float</span><span> rightTargetRad </span><span>=</span><span> </span><span>-</span><span>angle_rad;</span><span> // 180-degree phase shift</span></div>
<br />
<div><span>        // --- Send Commands to Motors ---</span></div>
<div><span>        </span><span>packAndSendMotionCommand</span><span>(LEFT_HIP_ID, leftTargetRad, </span><span>0.0</span><span>, KP, KD, </span><span>0.0</span><span>);</span></div>
<div><span>        </span><span>packAndSendMotionCommand</span><span>(RIGHT_HIP_ID, rightTargetRad, </span><span>0.0</span><span>, KP, KD, </span><span>0.0</span><span>);</span></div>
<br />
<div><span>        // --- Read Feedback ---</span></div>
<div><span>        // This drains the CAN buffer and updates the global position variables</span></div>
<div><span>        </span><span>readMotorFeedback</span><span>();</span></div>
<br />
<div><span>        // --- Print Status Periodically ---</span></div>
<div><span>        </span><span>if</span><span> (</span><span>millis</span><span>() </span><span>-</span><span> lastPrintTime </span><span>&gt;=</span><span> PRINT_INTERVAL_MS) {</span></div>
<div><span>            lastPrintTime </span><span>=</span><span> </span><span>millis</span><span>();</span></div>
<div><span>            </span><span>Serial</span><span>.</span><span>printf</span><span>(</span><span>"左电机 目标: </span><span>%6.2f</span><span>°, 实际: </span><span>%6.2f</span><span>° | 右电机 目标: </span><span>%6.2f</span><span>°, 实际: </span><span>%6.2f</span><span>°</span><span>\n</span><span>"</span><span>,</span></div>
<div><span>                          leftTargetRad </span><span>*</span><span> RAD_TO_DEG, leftActualPos_rad </span><span>*</span><span> RAD_TO_DEG,</span></div>
<div><span>                          rightTargetRad </span><span>*</span><span> RAD_TO_DEG, rightActualPos_rad </span><span>*</span><span> RAD_TO_DEG);</span></div>
<div><span>        }</span></div>
<div><span>        </span></div>
<div><span>        </span><span>delay</span><span>(LOOP_DELAY_MS);</span></div>
<div><span>    }</span></div>
<div><span>    </span></div>
<div><span>    // --- Smoothly return to zero before stopping ---</span></div>
<div><span>    </span><span>if</span><span> (</span><span>!</span><span>emergencyStop) {</span></div>
<div><span>        </span><span>Serial</span><span>.</span><span>println</span><span>(</span><span>"步态模拟结束，正在平滑归零..."</span><span>);</span></div>
<div><span>        </span><span>unsigned</span><span> </span><span>long</span><span> returnStartTime </span><span>=</span><span> </span><span>millis</span><span>();</span></div>
<div><span>        </span><span>float</span><span> startPosLeft </span><span>=</span><span> leftActualPos_rad;</span></div>
<div><span>        </span><span>float</span><span> startPosRight </span><span>=</span><span> rightActualPos_rad;</span></div>
<div><span>        </span></div>
<div><span>        </span><span>while</span><span>(</span><span>millis</span><span>() </span><span>-</span><span> returnStartTime </span><span>&lt;</span><span> </span><span>1000</span><span>) {</span><span> // 1 second to return to zero</span></div>
<div><span>             </span><span>unsigned</span><span> </span><span>long</span><span> elapsedReturnTime </span><span>=</span><span> </span><span>millis</span><span>() </span><span>-</span><span> returnStartTime;</span></div>
<div><span>             </span><span>float</span><span> factor </span><span>=</span><span> </span><span>1.0f</span><span> </span><span>-</span><span> (</span><span>float</span><span>)elapsedReturnTime </span><span>/</span><span> </span><span>1000.0f</span><span>;</span></div>
<div><span>             </span><span>packAndSendMotionCommand</span><span>(LEFT_HIP_ID, startPosLeft </span><span>*</span><span> factor, </span><span>0.0</span><span>, KP, KD, </span><span>0.0</span><span>);</span></div>
<div><span>             </span><span>packAndSendMotionCommand</span><span>(RIGHT_HIP_ID, startPosRight </span><span>*</span><span> factor, </span><span>0.0</span><span>, KP, KD, </span><span>0.0</span><span>);</span></div>
<div><span>             </span><span>delay</span><span>(</span><span>20</span><span>);</span></div>
<div><span>        }</span></div>
<div><span>        </span><span>Serial</span><span>.</span><span>println</span><span>(</span><span>"已回到原点"</span><span>);</span></div>
<div><span>    }</span></div>
<div><span>}</span></div>
<br /><br />
<div><span>// =================================================================</span></div>
<div><span>// Main Setup and Loop / 主程序</span></div>
<div><span>// =================================================================</span></div>
<br />
<div><span>void</span><span> </span><span>setup</span><span>() {</span></div>
<div><span>    </span><span>Serial</span><span>.</span><span>begin</span><span>(</span><span>115200</span><span>);</span></div>
<div><span>    </span><span>while</span><span> (</span><span>!</span><span>Serial) {}</span></div>
<div><span>    </span><span>Serial</span><span>.</span><span>println</span><span>(</span><span>"串口初始化完成"</span><span>);</span></div>
<div><span>    </span><span>waitForKeyPress</span><span>();</span></div>
<br />
<div><span>    // Init E-stop pin</span></div>
<div><span>    </span><span>pinMode</span><span>(MOTOR_STOP_PIN, OUTPUT);</span></div>
<div><span>    </span><span>digitalWrite</span><span>(MOTOR_STOP_PIN, HIGH);</span><span> // Release E-stop</span></div>
<div><span>    </span><span>Serial</span><span>.</span><span>printf</span><span>(</span><span>"急停引脚状态: </span><span>%s</span><span>\n</span><span>"</span><span>, </span><span>digitalRead</span><span>(MOTOR_STOP_PIN) </span><span>?</span><span> </span><span>"释放"</span><span> </span><span>:</span><span> </span><span>"激活"</span><span>);</span></div>
<div><span>    </span><span>waitForKeyPress</span><span>();</span></div>
<br />
<div><span>    // Init enable pins</span></div>
<div><span>    </span><span>for</span><span> (</span><span>int</span><span> pin : ENABLE_LEFT_PIN) { </span><span>pinMode</span><span>(pin, OUTPUT); </span><span>digitalWrite</span><span>(pin, LOW); }</span></div>
<div><span>    </span><span>for</span><span> (</span><span>int</span><span> pin : ENABLE_RIGHT_PIN) { </span><span>pinMode</span><span>(pin, OUTPUT); </span><span>digitalWrite</span><span>(pin, LOW); }</span></div>
<div><span>    </span><span>Serial</span><span>.</span><span>println</span><span>(</span><span>"使能引脚初始化完成"</span><span>);</span></div>
<div><span>    </span><span>waitForKeyPress</span><span>();</span></div>
<br />
<div><span>    // Init CAN bus</span></div>
<div><span>    </span><span>Can0</span><span>.</span><span>begin</span><span>();</span></div>
<div><span>    </span><span>Can0</span><span>.</span><span>setBaudRate</span><span>(</span><span>1000000</span><span>);</span><span> // 1 MHz</span></div>
<div><span>    </span><span>Serial</span><span>.</span><span>println</span><span>(</span><span>"CAN总线初始化完成 (1MHz)"</span><span>);</span></div>
<div><span>    </span><span>waitForKeyPress</span><span>();</span></div>
<br />
<div><span>    // Enable motor power</span></div>
<div><span>    </span><span>for</span><span> (</span><span>int</span><span> pin : ENABLE_LEFT_PIN) </span><span>digitalWrite</span><span>(pin, HIGH);</span></div>
<div><span>    </span><span>for</span><span> (</span><span>int</span><span> pin : ENABLE_RIGHT_PIN) </span><span>digitalWrite</span><span>(pin, HIGH);</span></div>
<div><span>    </span><span>Serial</span><span>.</span><span>println</span><span>(</span><span>"电机电源使能完成"</span><span>);</span></div>
<div><span>    </span><span>delay</span><span>(</span><span>1000</span><span>);</span><span> // Wait for power to stabilize</span></div>
<div><span>    </span><span>waitForKeyPress</span><span>();</span></div>
<br />
<div><span>    // Enter control mode on both motors</span></div>
<div><span>    </span><span>enterControlMode</span><span>(LEFT_HIP_ID);</span></div>
<div><span>    </span><span>delay</span><span>(</span><span>10</span><span>);</span><span> // Small delay between commands</span></div>
<div><span>    </span><span>enterControlMode</span><span>(RIGHT_HIP_ID);</span></div>
<div><span>    </span><span>delay</span><span>(</span><span>10</span><span>);</span></div>
<br />
<div><span>    </span><span>// ** NEW STEP: Prevent initial jump **</span></div>
<div><span>    // Immediately send a command with zero gains and zero torque to prevent the motor</span></div>
<div><span>    // from moving to a default position. This holds the current physical position.</span></div>
<div><span>    </span><span>// **新增步骤：防止初始跳动**</span></div>
<div><span>    // 立即发送一个增益和力矩都为零的命令，防止电机移动到一个默认位置，</span></div>
<div><span>    // 从而让电机在当前物理位置保持不动。</span></div>
<div><span>    </span><span>packAndSendMotionCommand</span><span>(LEFT_HIP_ID, </span><span>0.0</span><span>, </span><span>0.0</span><span>, </span><span>0.0</span><span>, </span><span>0.0</span><span>, </span><span>0.0</span><span>);</span></div>
<div><span>    </span><span>delay</span><span>(</span><span>10</span><span>);</span></div>
<div><span>    </span><span>packAndSendMotionCommand</span><span>(RIGHT_HIP_ID, </span><span>0.0</span><span>, </span><span>0.0</span><span>, </span><span>0.0</span><span>, </span><span>0.0</span><span>, </span><span>0.0</span><span>);</span></div>
<div><span>    </span><span>waitForKeyPress</span><span>();</span></div>
<br />
<div><span>    // Set origin</span></div>
<div><span>    </span><span>Serial</span><span>.</span><span>println</span><span>(</span><span>"请确保髋关节摆动臂垂直地面 (0度位置)"</span><span>);</span></div>
<div><span>    </span><span>waitForKeyPress</span><span>();</span></div>
<div><span>    </span><span>setOrigin</span><span>(LEFT_HIP_ID);</span></div>
<div><span>    </span><span>delay</span><span>(</span><span>10</span><span>);</span></div>
<div><span>    </span><span>setOrigin</span><span>(RIGHT_HIP_ID);</span></div>
<div><span>    </span><span>Serial</span><span>.</span><span>println</span><span>(</span><span>"原点设置完成"</span><span>);</span></div>
<div><span>    </span><span>delay</span><span>(</span><span>1000</span><span>);</span><span> // Wait for origin to be set</span></div>
<div><span>}</span></div>
<br />
<div><span>void</span><span> </span><span>loop</span><span>() {</span></div>
<div><span>    </span><span>static</span><span> </span><span>bool</span><span> testDone </span><span>=</span><span> </span><span>false</span><span>;</span></div>
<div><span>    </span><span>if</span><span> (</span><span>!</span><span>testDone </span><span>&amp;&amp;</span><span> </span><span>!</span><span>emergencyStop) {</span></div>
<div><span>        // Run the new gait simulation</span></div>
<div><span>        </span><span>runGaitSimulation</span><span>();</span></div>
<div><span>        testDone </span><span>=</span><span> </span><span>true</span><span>;</span></div>
<div><span>        </span><span>stopAllMotors</span><span>();</span></div>
<div><span>        </span><span>disableAllMotors</span><span>();</span></div>
<div><span>        </span><span>Serial</span><span>.</span><span>println</span><span>(</span><span>"</span><span>\n</span><span>测试结束"</span><span>);</span></div>
<div><span>    }</span></div>
<div><span>    </span></div>
<div><span>    // Non-blocking emergency stop check</span></div>
<div><span>    </span><span>if</span><span> (</span><span>Serial</span><span>.</span><span>available</span><span>() </span><span>&gt;</span><span> </span><span>0</span><span> </span><span>&amp;&amp;</span><span> </span><span>Serial</span><span>.</span><span>read</span><span>() </span><span>==</span><span> </span><span>'s'</span><span>) {</span></div>
<div><span>        emergencyStop </span><span>=</span><span> </span><span>true</span><span>;</span></div>
<div><span>        </span><span>Serial</span><span>.</span><span>println</span><span>(</span><span>"</span><span>\n</span><span>收到急停命令！"</span><span>);</span></div>
<div><span>        </span><span>stopAllMotors</span><span>();</span></div>
<div><span>        </span><span>disableAllMotors</span><span>();</span></div>
<div><span>    }</span></div>
<div><span>    </span></div>
<div><span>    </span><span>if</span><span> (emergencyStop) {</span></div>
<div><span>        // Keep the program in a safe, halted state</span></div>
<div><span>        </span><span>delay</span><span>(</span><span>100</span><span>);</span></div>
<div><span>    }</span></div>
<div><span>}</span></div>
</div>]]></content:encoded>
						                            <category domain="https://theopenexo.nau.edu/community/firmware/">Microcontroller Firmware (C++)</category>                        <dc:creator>KevinGuo</dc:creator>
                        <guid isPermaLink="true">https://theopenexo.nau.edu/community/firmware/systemcheck/#post-47</guid>
                    </item>
				                    <item>
                        <title>/systemCheck</title>
                        <link>https://theopenexo.nau.edu/community/firmware/systemcheck/#post-24</link>
                        <pubDate>Tue, 09 Sep 2025 00:12:41 +0000</pubDate>
                        <description><![CDATA[OpenExo/issues/15#issue-3386148357


KevinGuoUniAGI




I found almost /systemCheck programs two years ago is not working with including the latest &quot;/src programs&quot;. I will rebuild al...]]></description>
                        <content:encoded><![CDATA[<p><a href="https://github.com/naubiomech/OpenExo/issues/15#issue-3386148357" target="_blank" rel="noopener">OpenExo/issues/15#issue-3386148357</a></p>
<blockquote>
<div class="ActivityHeader-module__narrowViewportWrapper--k4ncm IssueBodyHeader-module__narrowViewportWrapper--cUGQk">
<div class="IssueBodyHeader-module__titleSection--a171Q"><a class="Box-sc-g0xbh4-0 bGcKkD IssueBodyHeaderAuthor-module__authorLoginLink--MsgZJ prc-Link-Link-85e08" href="https://github.com/KevinGuoUniAGI" data-hovercard-url="/users/KevinGuoUniAGI/hovercard" data-testid="issue-body-header-author">KevinGuoUniAGI</a></div>
</div>
<div id="issue-body-viewer" class="IssueBodyViewer-module__IssueBody--Zg6Wy" data-testid="issue-body-viewer">
<div class="markdown-body" data-testid="markdown-body" data-team-hovercards-enabled="true" data-turbolinks="false">
<div class="Box-sc-g0xbh4-0 markdown-body NewMarkdownViewer-module__safe-html-box--dKCgP">
<p dir="auto">I found almost /systemCheck programs two years ago is not working with including the latest "/src programs". I will rebuild all UnitTest case. If our project team had already build the new /systemCheck programs, please update.</p>
</div>
</div>
</div>
</blockquote>]]></content:encoded>
						                            <category domain="https://theopenexo.nau.edu/community/firmware/">Microcontroller Firmware (C++)</category>                        <dc:creator>Shanpu Fang</dc:creator>
                        <guid isPermaLink="true">https://theopenexo.nau.edu/community/firmware/systemcheck/#post-24</guid>
                    </item>
							        </channel>
        </rss>
		