moved docs into docs folder

This commit is contained in:
Oskar Weigl
2018-06-09 20:49:10 -07:00
parent 7fb0f795e0
commit 1294ddff1d
23 changed files with 0 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
docs.odriverobotics.com
+3
View File
@@ -0,0 +1,3 @@
source 'https://rubygems.org'
gem 'github-pages', group: :jekyll_plugins
gem 'jekyll-redirect-from'
+251
View File
@@ -0,0 +1,251 @@
GEM
remote: https://rubygems.org/
specs:
activesupport (4.2.9)
i18n (~> 0.7)
minitest (~> 5.1)
thread_safe (~> 0.3, >= 0.3.4)
tzinfo (~> 1.1)
addressable (2.5.2)
public_suffix (>= 2.0.2, < 4.0)
coffee-script (2.4.1)
coffee-script-source
execjs
coffee-script-source (1.11.1)
colorator (1.1.0)
commonmarker (0.17.9)
ruby-enum (~> 0.5)
concurrent-ruby (1.0.5)
em-websocket (0.5.1)
eventmachine (>= 0.12.9)
http_parser.rb (~> 0.6.0)
ethon (0.11.0)
ffi (>= 1.3.0)
eventmachine (1.2.5)
execjs (2.7.0)
faraday (0.14.0)
multipart-post (>= 1.2, < 3)
ffi (1.9.23)
forwardable-extended (2.6.0)
gemoji (3.0.0)
github-pages (181)
activesupport (= 4.2.9)
github-pages-health-check (= 1.4.0)
jekyll (= 3.7.3)
jekyll-avatar (= 0.5.0)
jekyll-coffeescript (= 1.1.1)
jekyll-commonmark-ghpages (= 0.1.5)
jekyll-default-layout (= 0.1.4)
jekyll-feed (= 0.9.3)
jekyll-gist (= 1.5.0)
jekyll-github-metadata (= 2.9.4)
jekyll-mentions (= 1.3.0)
jekyll-optional-front-matter (= 0.3.0)
jekyll-paginate (= 1.1.0)
jekyll-readme-index (= 0.2.0)
jekyll-redirect-from (= 0.13.0)
jekyll-relative-links (= 0.5.3)
jekyll-remote-theme (= 0.2.3)
jekyll-sass-converter (= 1.5.2)
jekyll-seo-tag (= 2.4.0)
jekyll-sitemap (= 1.2.0)
jekyll-swiss (= 0.4.0)
jekyll-theme-architect (= 0.1.1)
jekyll-theme-cayman (= 0.1.1)
jekyll-theme-dinky (= 0.1.1)
jekyll-theme-hacker (= 0.1.1)
jekyll-theme-leap-day (= 0.1.1)
jekyll-theme-merlot (= 0.1.1)
jekyll-theme-midnight (= 0.1.1)
jekyll-theme-minimal (= 0.1.1)
jekyll-theme-modernist (= 0.1.1)
jekyll-theme-primer (= 0.5.3)
jekyll-theme-slate (= 0.1.1)
jekyll-theme-tactile (= 0.1.1)
jekyll-theme-time-machine (= 0.1.1)
jekyll-titles-from-headings (= 0.5.1)
jemoji (= 0.9.0)
kramdown (= 1.16.2)
liquid (= 4.0.0)
listen (= 3.1.5)
mercenary (~> 0.3)
minima (= 2.4.0)
nokogiri (>= 1.8.1, < 2.0)
rouge (= 2.2.1)
terminal-table (~> 1.4)
github-pages-health-check (1.4.0)
addressable (~> 2.3)
net-dns (~> 0.8)
octokit (~> 4.0)
public_suffix (~> 2.0)
typhoeus (~> 1.3)
html-pipeline (2.7.1)
activesupport (>= 2)
nokogiri (>= 1.4)
http_parser.rb (0.6.0)
i18n (0.9.5)
concurrent-ruby (~> 1.0)
jekyll (3.7.3)
addressable (~> 2.4)
colorator (~> 1.0)
em-websocket (~> 0.5)
i18n (~> 0.7)
jekyll-sass-converter (~> 1.0)
jekyll-watch (~> 2.0)
kramdown (~> 1.14)
liquid (~> 4.0)
mercenary (~> 0.3.3)
pathutil (~> 0.9)
rouge (>= 1.7, < 4)
safe_yaml (~> 1.0)
jekyll-avatar (0.5.0)
jekyll (~> 3.0)
jekyll-coffeescript (1.1.1)
coffee-script (~> 2.2)
coffee-script-source (~> 1.11.1)
jekyll-commonmark (1.2.0)
commonmarker (~> 0.14)
jekyll (>= 3.0, < 4.0)
jekyll-commonmark-ghpages (0.1.5)
commonmarker (~> 0.17.6)
jekyll-commonmark (~> 1)
rouge (~> 2)
jekyll-default-layout (0.1.4)
jekyll (~> 3.0)
jekyll-feed (0.9.3)
jekyll (~> 3.3)
jekyll-gist (1.5.0)
octokit (~> 4.2)
jekyll-github-metadata (2.9.4)
jekyll (~> 3.1)
octokit (~> 4.0, != 4.4.0)
jekyll-mentions (1.3.0)
activesupport (~> 4.0)
html-pipeline (~> 2.3)
jekyll (~> 3.0)
jekyll-optional-front-matter (0.3.0)
jekyll (~> 3.0)
jekyll-paginate (1.1.0)
jekyll-readme-index (0.2.0)
jekyll (~> 3.0)
jekyll-redirect-from (0.13.0)
jekyll (~> 3.3)
jekyll-relative-links (0.5.3)
jekyll (~> 3.3)
jekyll-remote-theme (0.2.3)
jekyll (~> 3.5)
rubyzip (>= 1.2.1, < 3.0)
typhoeus (>= 0.7, < 2.0)
jekyll-sass-converter (1.5.2)
sass (~> 3.4)
jekyll-seo-tag (2.4.0)
jekyll (~> 3.3)
jekyll-sitemap (1.2.0)
jekyll (~> 3.3)
jekyll-swiss (0.4.0)
jekyll-theme-architect (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-cayman (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-dinky (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-hacker (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-leap-day (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-merlot (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-midnight (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-minimal (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-modernist (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-primer (0.5.3)
jekyll (~> 3.5)
jekyll-github-metadata (~> 2.9)
jekyll-seo-tag (~> 2.0)
jekyll-theme-slate (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-tactile (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-time-machine (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-titles-from-headings (0.5.1)
jekyll (~> 3.3)
jekyll-watch (2.0.0)
listen (~> 3.0)
jemoji (0.9.0)
activesupport (~> 4.0, >= 4.2.9)
gemoji (~> 3.0)
html-pipeline (~> 2.2)
jekyll (~> 3.0)
kramdown (1.16.2)
liquid (4.0.0)
listen (3.1.5)
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
ruby_dep (~> 1.2)
mercenary (0.3.6)
mini_portile2 (2.3.0)
minima (2.4.0)
jekyll (~> 3.5)
jekyll-feed (~> 0.9)
jekyll-seo-tag (~> 2.1)
minitest (5.11.3)
multipart-post (2.0.0)
net-dns (0.8.0)
nokogiri (1.8.2)
mini_portile2 (~> 2.3.0)
octokit (4.8.0)
sawyer (~> 0.8.0, >= 0.5.3)
pathutil (0.16.1)
forwardable-extended (~> 2.6)
public_suffix (2.0.5)
rb-fsevent (0.10.3)
rb-inotify (0.9.10)
ffi (>= 0.5.0, < 2)
rouge (2.2.1)
ruby-enum (0.7.2)
i18n
ruby_dep (1.5.0)
rubyzip (1.2.1)
safe_yaml (1.0.4)
sass (3.5.6)
sass-listen (~> 4.0.0)
sass-listen (4.0.0)
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
sawyer (0.8.1)
addressable (>= 2.3.5, < 2.6)
faraday (~> 0.8, < 1.0)
terminal-table (1.8.0)
unicode-display_width (~> 1.1, >= 1.1.1)
thread_safe (0.3.6)
typhoeus (1.3.0)
ethon (>= 0.9.0)
tzinfo (1.2.5)
thread_safe (~> 0.1)
unicode-display_width (1.3.0)
PLATFORMS
ruby
DEPENDENCIES
github-pages
jekyll-redirect-from
BUNDLED WITH
1.16.1
+5
View File
@@ -0,0 +1,5 @@
theme: jekyll-theme-minimal
exclude: [ruby-bundle]
plugins:
- jekyll-redirect-from
google_analytics: UA-93396600-3
+27
View File
@@ -0,0 +1,27 @@
# This data could theoretically be retrieved directly from the md files:
# https://jekyllrb.com/tutorials/navigation/#scenario-8-retrieving-items-based-on-front-matter-properties
sections:
- title: For Users
docs:
- title: Getting Started
url: /
- title: ODrive Tool
url: odrivetool
- title: Parameters & Commands
url: commands
- title: Interfaces
url: interfaces
- title: Encoders
url: encoders
- title: Troubleshooting
url: troubleshooting
- title: For ODrive Developers
docs:
- title: Firmware Developer Guide
url: developer-guide
- title: Configuring Visual Studio Code
url: configuring-vscode
- title: Configuring Eclipse
url: configuring-eclipse
+110
View File
@@ -0,0 +1,110 @@
<!DOCTYPE html>
<!-- source: https://github.com/pages-themes/minimal/blob/master/_layouts/default.html -->
<html lang="{{ site.lang | default: "en-US" }}">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
{% seo %}
<link rel="stylesheet" href="{{ "/assets/css/style.css?v=" | append: site.github.build_revision | relative_url }}">
<!--[if lt IE 9]>
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js"></script>
<![endif]-->
</head>
<body>
<div class="wrapper">
<header>
<div>
<h1><a href="{{ "/" | absolute_url }}">{{ site.title | default: site.github.repository_name }} Documentation</a></h1>
{% if site.logo %}
<img src="{{site.logo | relative_url}}" alt="Logo" />
{% endif %}
<p>{{ site.description | default: site.github.project_tagline }}</p>
<ul id="navbar">
{% for section in site.data.index.sections %}
<li>
<p>{{ section.title }}</p>
<ul>
{% for item in section.docs %}
<li><a href="{{ item.url }}" alt="{{ item.title }}">{{ item.title }}</a></li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
{% if site.github.is_project_page %}
<p class="view"><a href="{{ site.github.repository_url }}">View the Project on GitHub <small>{{ site.github.repository_nwo }}</small></a></p>
{% endif %}
{% if site.github.is_user_page %}
<p class="view"><a href="{{ site.github.owner_url }}">View My GitHub Profile</a></p>
{% endif %}
<p>Help improve these docs: submit edits using the link in the top right.</p>
<p>If you need help, please search or ask the <a href=https://discourse.odriverobotics.com>ODrive Community</a>.</p>
{% if site.show_downloads %}
<ul>
<li><a href="{{ site.github.zip_url }}">Download <strong>ZIP File</strong></a></li>
<li><a href="{{ site.github.tar_url }}">Download <strong>TAR Ball</strong></a></li>
<li><a href="{{ site.github.repository_url }}">View On <strong>GitHub</strong></a></li>
</ul>
{% endif %}
</div>
</header>
<section>
<div class="edit">
{% assign filename = page.url | replace_first: '/', '' | replace: '.html', '.md' %}
{% if filename == '' %}{% assign filename = 'getting-started.md' %}{% endif %}
<!-- edit icon taken from GitHub -->
<svg transform="translate(0,1)" class="octicon octicon-pencil" viewBox="0 0 14 16" version="1.1" width="10" height="12" aria-hidden="true">
<path fill-rule="evenodd" d="M0 12v3h3l8-8-3-3-8 8zm3 2H1v-2h1v1h1v1zm10.3-9.3L12 6 9 3l1.3-1.3a.996.996 0 0 1 1.41 0l1.59 1.59c.39.39.39 1.02 0 1.41z"></path>
</svg>
<a href="https://www.github.com/madcowswe/ODrive/edit/gh-pages/{{ filename }}">edit on GitHub</a>
</div>
{{ content }}
</section>
<!--<footer>
{% if site.github.is_project_page %}
<p>This project is maintained by <a href="{{ site.github.owner_url }}">{{ site.github.owner_name }}</a></p>
{% endif %}
<p><small>Hosted on GitHub Pages &mdash; Theme by <a href="https://github.com/orderedlist">orderedlist</a></small></p>
</footer>-->
</div>
<script src="{{ "/assets/js/scale.fix.js" | relative_url }}"></script>
{% if site.google_analytics %}
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', '{{ site.google_analytics }}', 'auto');
ga('send', 'pageview');
</script>
{% endif %}
<script type="text/javascript">
// Rewrite all links that start with "../" to point to the GitHub repo
window.onload = function(){
var links = document.links;
var i = links.length;
while (i--) {
if (links[i].getAttribute("href").slice(0, 3) == "../") {
links[i].href = "https://github.com/madcowswe/ODrive/blob/master/" + links[i].getAttribute("href").slice(3);
}
if (links[i].getAttribute("href").slice(0, 15) == "getting-started") {
links[i].href = "/" + links[i].getAttribute("href").slice(15);
}
}
}
</script>
</body>
</html>
+80
View File
@@ -0,0 +1,80 @@
# ASCII Protocol
## How to send commands
* **Via USB:**
* **Windows:** Use the Zadig utility to set the ODrive's driver to "usbser". Windows will then make the device available as COM port. You can use [PuTTY](https://www.chiark.greenend.org.uk/~sgtatham/putty/) to manually send commands or open the COM port using your favorite programming language
* **Linux/macOS:** Run `/dev/tty*` to list all serial ports. The ODrive will show up as `/dev/ttyACM0` on Linux and `/dev/tty.usbmodem[...]` on macOS. Once you know the name, you can use `screen /dev/ttyACM0` (with the correct name) to send commands manually or open the device using your favorite programming language. Serial ports on Unix can be opened, written to and read from like a normal file.
* **Via UART:** Connect the ODrive's TX (GPIO1) to your host's RX. Connect your ODrive's RX (GPIO2) to your host's TX. The logic level of the ODrive is 3.3V.
* **Arduino:** You can use the [ODrive Arduino library](https://github.com/madcowswe/ODriveArduino) to talk to the ODrive.
* **Windows/Linux/macOS:** You can use an FTDI USB-UART cable to connect to the ODrive.
## Command format
The ASCII protocol is human-readable and line-oriented, with each line having the following format:
```
command *42 ; comment [new line character]
```
* `*42` stands for a GCode compatible checksum and can be omitted. If and only if a checksum is provided, the device will also include a checksum in the response, if any.
* comments are supported for GCode compatibility
* the command is interpreted once the new-line character is encountered
## Command Reference
#### Motor Position command
```
p motor position velocity_ff current_ff
```
* `p` for position
* `motor` is the motor number, `0` or `1`.
* `position` is the desired position, in encoder counts.
* `velocity_ff` is the velocity feed-forward term, in counts/s (optional).
* `current_ff` is the current feed-forward term, in A (optional).
Example: `p 0 -20000 0 0`
Note that if you don't know what feed-forward is or what it's used for, simply omit it.
#### Motor Velocity command
```
v motor velocity current_ff
```
* `v` for velocity
* `motor` is the motor number, `0` or `1`.
* `velocity` is the desired velocity in counts/s.
* `current_ff` is the current feed-forward term, in A (optional).
Example: `v 0 1000 0`
Note that if you don't know what feed-forward is or what it's used for, simply omit it.
#### Motor Current command
```
c motor current
```
* `c` for current
* `motor` is the motor number, `0` or `1`.
* `current` is the desired current in A.
#### Parameter reading/writing
Not all parameters can be accessed via the ASCII protocol but at least all parameters with float and integer type are supported.
* Reading:
```
r [property]
```
* `property` name of the property, as seen in ODrive Tool
* response: text representation of the requested value
* Example: `r vbus_voltage` => response: `24.087744` <new line>
* Writing:
```
w [property] [value]
```
* `property` name of the property, as seen in ODrive Tool
* `value` text representation of the value to be written
* Example: `w axis0.controller.pos_setpoint -123.456`
+443
View File
@@ -0,0 +1,443 @@
---
---
// Adapted from:
// https://github.com/pages-themes/minimal/blob/master/_sass/jekyll-theme-minimal.scss
//$section-headings-color: #0086b3;
//@import "{{ site.theme }}";
@import "fonts";
@import "rouge-github";
body {
background-color: #fff;
padding:50px;
font: 14px/1.5 "Noto Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
color:#24292e;
font-weight:400;
}
h1, h2, h3, h4, h5, h6 {
color:#222;
margin:0 0 20px;
text-align: left;
}
p, ul, ol, table, pre, dl {
margin:0 0 20px;
}
h1, h2, h3 {
line-height:1.1;
}
h1 {
font-size:28px;
color: #d60000;
}
h2 {
color:#393939;
padding-top: 40px;
}
h3, h4, h5, h6 {
color:#494949;
}
a {
color:#267CB9;
text-decoration:none;
}
// a:hover, a:focus {
// color:#069;
// font-weight: bold;
// }
a small {
font-size:11px;
color:#777;
margin-top:-0.3em;
display:block;
}
a:hover small {
color:#777;
}
.wrapper {
max-width: 970px;
//width:860px;
width: auto;
margin:0 auto;
}
blockquote {
border-left:1px solid #e5e5e5;
margin:0;
padding:0 0 0 20px;
font-style:italic;
}
code, pre {
font-family:Monaco, Bitstream Vera Sans Mono, Lucida Console, Terminal, Consolas, Liberation Mono, DejaVu Sans Mono, Courier New, monospace;
color:#333;
}
pre {
padding:8px 15px;
background: #f8f8f8;
border-radius:5px;
border:1px solid #e5e5e5;
overflow-x: auto;
}
table {
width:100%;
border-collapse:collapse;
display: block;
overflow-x: scroll;
}
th, td {
text-align:left;
padding:5px 10px;
border:1px solid #d4d4d4;
//border-bottom:1px solid #d4d4d4;
}
dt {
color:#444;
font-weight:700;
}
th {
color:#444;
}
img {
max-width:100%;
}
header {
width:270px;
float:left;
position:fixed;
-webkit-font-smoothing:subpixel-antialiased;
overflow-y: auto;
top: 50px;
bottom: 0;
}
section {
float:right;
padding-bottom:50px;
}
small {
font-size:11px;
}
hr {
border:0;
background:#e5e5e5;
height:1px;
margin:0 0 20px;
}
footer {
width:270px;
float:left;
position:fixed;
bottom:50px;
-webkit-font-smoothing:subpixel-antialiased;
}
@media print, screen and (max-width: 960px) {
div.wrapper {
width:auto;
margin:0;
}
header, section, footer {
float:none;
position:static;
width:auto;
}
section {
border:1px solid #e5e5e5;
border-width:1px 0;
padding:20px 0;
margin:0 0 20px;
}
header > div {
margin-right: unset !important;
}
header a small {
display:inline;
}
}
@media print, screen and (max-width: 720px) {
body {
word-wrap:break-word;
}
header {
padding:0;
}
header ul, header p.view {
position:static;
}
pre, code, li {
word-wrap:break-word;
word-break: break-all;
}
}
@media print, screen and (max-width: 480px) {
body {
padding:0px;
}
/* header ul {
width:99%;
}
header li, header ul li + li + li {
width:33%;
}*/
}
@media print {
body {
padding:0.4in;
font-size:12pt;
color:#444;
}
}
/*** Overall layout ***/
section {
/* align-content: center; */
padding-left: 270px;
//width: 500px;
float: none;
}
@media print, screen and (max-width: 960px) {
section {
padding: 20px 0;
}
}
div {
text-align: justify;
}
/*** Links ***/
a {
color: #0366d6;
}
h1 a {
color: unset;
}
// a:hover, a:focus {
// color: #0366d6;
// font-weight: unset;
// }
/*** Navigation bar ***/
header > div {
margin-right: 20px;
}
header ul {
list-style:none;
display: block;
padding: 0;
}
header li {
display: block;
border:none !important;
}
#navbar {
max-width: 250px;
}
header ul p {
margin:0;
padding-left:5px;
display: block;
// color: #d60000;
color: #000;
font-weight: bold;
background-color: #cbcbcb;
}
header ul ul li a {
background: #f8f8f8;
//border:1px solid #e0e0e0;
line-height:1;
font-size:12px;
font-weight: bold;
color:#676767;
display:block;
text-align:left;
padding:12px 0px 5px 5px;
//margin:12px;
height:20px;
}
//// rounded edges (look bad)
//header ul p {
// border-radius:5px 5px 0 0;
//}
//header ul ul li:last-child a {
// border-radius:0 0 5px 5px;
//}
/*** Navbar Hover ***/
header ul a:hover, header ul a:focus {
color: #d60000;
// color:rgb(0, 0, 0);
// background-color: rgba(0, 0, 0, 0.24);
}
// header ul a:active {
// background-color:#f8f8f8;
// }
@media print, screen and (max-width: 960px) {
}
/*** Keyboard symbols ***/
kbd {
display: inline-block;
padding: 3px 5px;
font-size: 11px;
line-height: 10px;
color: #444d56;
vertical-align: middle;
background-color: #fafbfc;
border: solid 1px #c6cbd1;
border-bottom-color: #959da5;
border-radius: 3px;
box-shadow: inset 0 -1px 0 #959da5;
}
p + details {
margin-top: -14px;
}
/*** Expandable details box ***/
details {
//margin-top: -14px;
margin-bottom: 5px;
//background-color: #ffc54727;
//background-color: #f2f8fd;
//border: solid 1px #c6cbd1;
//border-radius: 3px;
border: 1px solid #eee;
border-left-width: 5px;
border-radius: 5px;
border-left-color: #5bc0de;
}
// title of a "details" box
details summary {
padding-left: 8px;
//background-color: #f0f0f0;
cursor: pointer;
//border-bottom: solid 1px #c6cbd1;
//border-radius: 3px;
}
// content of a "details" box
details > div {
padding-top: 5px;
padding-left: 8px;
padding-right: 8px;
padding-bottom: 8px;
//border-radius:5px;
//border:1px solid #e5e5e5;
//border-top: solid 1px #c6cbd1;
}
details > div > p:last-child {
margin-bottom: 0px;
}
/*** Alert box ***/
.alert {
padding: 10px;
margin-bottom: 15px;
color: black;
border: 1px solid #eee;
border-left-width: 5px;
border-radius: 5px;
border-left-color: #f0ad4e;
}
.note {
padding: 10px;
margin-bottom: 15px;
color: black;
border: 1px solid #eee;
border-left-width: 5px;
border-radius: 5px;
border-left-color: #5bc0de;
}
/*** edit link ***/
.edit {
float: right;
font-size: 12px;
}
/*** inline code ***/
:not(pre) > code {
background-color: #ededed;
word-break: break-all;
}
/*** table ***/
table th {
background-color: #cbcbcb;
color: #000;
border: 1px solid rgba(0, 0, 0, 0.219);
}
table tr:nth-child(2n) {
background-color: #f8f8f8;
}
+81
View File
@@ -0,0 +1,81 @@
# Parameters & Commands
We will use the `<odrv>` as a placeholder for any ODrive object. In `odrivetool` this is usually `odrv0`. Furthermore we use `<axis>` as a placeholder for any axis (for example `odrv0.axis0`).
## Per-Axis commands
For the most part, both axes on the ODrive can be controlled independently.
### State Machine
The current state of an axis is indicated by `<axis>.current_state`. The user can request a new state by assigning a new value to `<axis>.requested_state`. The default state after startup is `AXIS_STATE_IDLE`.
1. `AXIS_STATE_IDLE` Disable motor PWM and do nothing.
1. `AXIS_STATE_STARTUP_SEQUENCE` Run the [startup procedure](#startup-procedure).
1. `AXIS_STATE_FULL_CALIBRATION_SEQUENCE` Run motor calibration and then encoder offset calibration (or encoder index search if `<axis>.encoder.use_index` is `True`).
1. `AXIS_STATE_MOTOR_CALIBRATION` Measure phase resistance and phase inductance.
* To store the results set `<axis>.motor.config.pre_calibrated` to `True` and [save the configuration](#saving-the-configuration). After that you don't have to run the motor calibration on the next start up.
* This modifies the variables `<axis>.motor.config.phase_resistance` and `<axis>.motor.config.phase_inductance`.
1. `AXIS_STATE_SENSORLESS_CONTROL` Run sensorless control.
* The motor must be calibrated (`<axis>.motor.is_calibrated`)
* [`<axis>.controller.control_mode`](#control-mode) must be `True`.
1. `AXIS_STATE_ENCODER_INDEX_SEARCH` Turn the motor in one direction until the encoder index is traversed. This state can only be entered if `<axis>.encoder.config.use_index` is `True`.
1. `AXIS_STATE_ENCODER_OFFSET_CALIBRATION` Turn the motor in one direction for a few seconds and then back to measure the offset between the encoder position and the electrical phase.
* Can only be entered if the motor is calibrated (`<axis>.motor.is_calibrated`).
* A successful encoder calibration will make the `<axis>.encoder.is_ready` go to true.
1. `AXIS_STATE_CLOSED_LOOP_CONTROL` Run closed loop control.
* The action depends on the [control mode](#control-mode).
* Can only be entered if the motor is calibrated (`<axis>.motor.is_calibrated`) and the encoder is ready (`<axis>.encoder.is_ready`).
### Startup Procedure
By default the ODrive takes no action at startup and goes to idle immediately.
This behavior can be changed by setting any of the following to `True`. The ODrive will sequence all enabled startup actions in the order shown below.
* `<axis>.config.startup_motor_calibration`
* `<axis>.config.startup_encoder_index_search`
* `<axis>.config.startup_encoder_offset_calibration`
* `<axis>.config.startup_closed_loop_control`
* `<axis>.config.startup_sensorless_control`
See [state machine](#state-machine) for a description of each state.
### Control Mode
The default control mode is position control.
If you want a different mode, you can change `<axis>.controller.config.control_mode`.
Possible values are:
* `CTRL_MODE_POSITION_CONTROL`
* `CTRL_MODE_VELOCITY_CONTROL`
* `CTRL_MODE_CURRENT_CONTROL`
* `CTRL_MODE_VOLTAGE_CONTROL` - this one is not normally used.
### Tuning parameters
The motion control gains are currently manually tuned:
* `<axis>.controller.config.pos_gain = 20.0f` [(counts/s) / counts]
* `<axis>.controller.config.vel_gain = 5.0f / 10000.0f` [A/(counts/s)]
* `<axis>.controller.config.vel_integrator_gain = 10.0f / 10000.0f` [A/((counts/s) * s)]
An upcoming feature will enable automatic tuning. Until then, here is a rough tuning procedure:
* Set the integrator gain to 0
* Make sure you have a stable system. If it is not, decrease all gains until you have one.
* Increase `vel_gain` by around 30% per iteration until the motor exhibits some vibration.
* Back down `vel_gain` to 50% of the vibrating value.
* Increase `pos_gain` by around 30% per iteration until you see some overshoot.
* Back down `pos_gain` until you do not have overshoot anymore.
* The integrator is not easily tuned, nor is it strictly required. Tune at your own discretion.
## General system commands
### Saving the configuration
All variables that are part of a `[...].config` object can be saved to non-volatile memory on the ODrive so they persist after you remove power. The relevant commands are:
* `<odrv>.save_configuration()`: Stores the configuration to persistent memory on the ODrive.
* `<odrv>.erase_configuration()`: Resets the configuration variables to their factory defaults. This only has an effect after a reboot. A side effect of this command is that motor control stops (in case it was running) and the USB communication breaks out temporarily. This is because erasing flash pages hangs the microcontroller for several seconds.
### Diagnostics
* `<odrv>.serial_number`: A number that uniquely identifies your device. When printed in upper case hexadecimal (`hex(<odrv>.serial_number).upper()`), this is identical to the serial number indicated by the USB descriptor.
* `<odrv>.fw_version_major`, `<odrv>.fw_version_minor`, `<odrv>.fw_version_revision`: The firmware version that is currently running.
* `<odrv>.hw_version_major`, `<odrv>.hw_version_minor`, `<odrv>.hw_version_revision`: The hardware version of your ODrive.
+35
View File
@@ -0,0 +1,35 @@
# Setting up Eclipse development environment
## Install
* Install [Eclipse IDE for C/C++ Developers](http://www.eclipse.org/downloads/packages/eclipse-ide-cc-developers/neon3)
* Install the [OpenOCD Eclipse plugin](http://gnuarmeclipse.github.io/plugins/install/)
## Import project
* File -> Import -> C/C++ -> Existing Code as Makefile Project
* Browse for existing code location, find the OdriveFirmware root.
* In the Toolchain options, select `Cross GCC`
* Hit Finish
* Build the project (press ctrl-B)
![Toolchain options](screenshots/CodeAsMakefile.png "Toolchain options")
## Load the launch configuration
* File -> Import -> Run/Debug -> Launch Configurations -> Next
* Highlight (don't tick) the OdriveFirmare folder in the left column
* Tick OdriveFirmware.launch in the right column
* Hit Finish
![Launch Configurations](screenshots/ImportLaunch.png "Launch Configurations")
## Launch!
* Make sure the programmer is connected to the board as per [Flashing the firmware](#flashing-the-firmware).
* Press the down-arrow of the debug symbol in the toolbar, and hit Debug Configurations
* You can also hit Run -> Debug Configurations
* Highlight the debug configuration you imported, called OdriveFirmware. If you do not see the imported launch configuration rename your project to `ODriveFirmware` or edit the launch configuration to match your project name by unfiltering unavailable projects:
![Launch Configuration Filters](screenshots/LaunchConfigFilter.png "Launch Configuration Filters")
* Hit Debug
* Eclipse should flash the board for you and the program should start halted on the first instruction in `Main`
* Set beakpoints, step, hit Resume, etc.
* Make some cool features! ;D
+54
View File
@@ -0,0 +1,54 @@
# Configuring Visual Studio Code
VSCode is the recommended IDE for working with the ODrive codebase. It is a light-weight text editor with Git integration and GDB debugging functionality.
Before doing the VSCode setup, make sure you've installed all of your [prerequisites](README.md#installing-prerequisites)
## Setup Procedure
1. Clone the ODrive repository
1. [Download VSCode](https://code.visualstudio.com/download)
1. Open VSCode
1. Install extensions. This can be done directly from VSCode (Ctrl+Shift+X)
* Required extensions:
* C/C++
* Recommended Extensions:
* Cortex-Debug
* vscode-icons
* Code Outline
* Include Autocomplete
* Path Autocomplete
* Auto Comment Blocks
1. Restart VSCode
1. Open the VSCode Workspace file, which is located in the root of the ODrive repository. It is called `ODrive_Workspace.code-workspace`. The first time you open it, VSCode will install some dependencies. If it fails, you may need to [change your proxy settings](https://code.visualstudio.com/docs/getstarted/settings).
You should now be ready to compile and test the ODrive project.
## Building the Firmware
* Tasks -> Run Build Task
A terminal window will open with your native shell. VSCode is configured to run the command `make -j4` in this terminal.
## Flashing the Firmware
* Tasks -> Run Task -> flash
A terminal window will open with your native shell. VSCode is configured to run the command `make flash` in this terminal.
If the flashing worked, you can connect to the board using the [odrivetool](../docs/getting-started#start-odrivetool).
## Debugging
An extension called Cortex-Debug has recently been released which is designed specifically for debugging ARM Cortex projects. You can read more on Cortex-Debug here: https://github.com/Marus/cortex-debug
Note: If developing on Windows, you should have `arm-none-eabi-gdb` and `openOCD` on your PATH.
* Make sure you have the Firmware folder as your active folder
* Flash the board with the newest code (starting debug session doesn't do this)
* Debug -> Start Debugging (or press F5)
* The processor will reset and halt.
* Set your breakpoints. Note: you can only set breakpoints when the processor is halted, if you set them during run mode, they won't get applied.
* Run (F5)
* Stepping over/in/out, restarting, and changing breakpoints can be done by first pressing the "pause" (F6) button at the top the screen.
* When done debugging, simply stop (Shift+F5) the debugger. It will kill your openOCD process too.
## Cleaning the Build
This sometimes needs to be done if you change branches.
* Open a terminal (View -> Integrated Terminal) and enter `make clean`
+240
View File
@@ -0,0 +1,240 @@
# ODrive Firmware Developer Guide
This guide is intended for developers who wish to modify the firmware of the ODrive.
As such it assumes that you know things like how to use Git, what a compiler is, etc. If that sounds scary, turn around now.
The official releases are maintained on the `master` branch. However since you are a developer, you are encouraged to use the `devel` branch, as it contains the latest features.
The project is under active development, so make sure to check the [Changelog](CHANGELOG.md) to keep track of updates.
### Table of contents
<!-- MarkdownTOC depth=2 autolink=true bracket=round -->
- [Prerequisites](#prerequisites)
- [Configuring the build](#configuring-the-build)
- [Building and flashing the Firmware](#building-and-flashing-the-firmware)
- [Debugging](#debugging)
- [Testing](#testing)
- [Setting up an IDE](#setting-up-an-ide)
- [STM32CubeMX](#stm32cubemx)
- [Troubleshooting](#troubleshooting)
- [Documentation](#documentation)
- [Releases](#releases)
- [Notes for Contributors](#notes-for-contributors)
<!-- /MarkdownTOC -->
<br><br>
## Prerequisites
The recommended tools for ODrive development are:
* **make**: Used to invoke tup
* **Tup**: The build system used to invoke the compile commands
* **ARM GNU Compiler**: For cross-comiling code
* **ARM GDB**: For debugging the code and stepping through on the device
* **OpenOCD**: For flashing the ODrive with the STLink/v2 programmer
* **Python**: For running the Python tools
See below for specific installation instructions for your OS.
Depending on what you're gonna do, you may not need all of the components.
Once you have everything, you can verify the correct installation by running:
```bash
$ arm-none-eabi-gcc --version
$ arm-none-eabi-gdb --version
$ openocd --version # should be 0.10.0 or later
$ tup --version # should be 0.7.5 or later
$ python --version # should be 3.7 or later
```
#### Linux (Ubuntu)
```bash
sudo apt-get install gcc-arm-none-eabi
sudo apt-get install gdb-arm-none-eabi
sudo apt-get install openocd
sudo add-apt-repository ppa:jonathonf/tup && sudo apt-get update && sudo apt-get install tup
```
#### Arch Linux
```bash
sudo pacman -S arm-none-eabi-gcc arm-none-eabi-binutils
sudo pacman -S arm-none-eabi-gdb
sudo pacman -S tup
```
* [OpenOCD AUR package](https://aur.archlinux.org/packages/openocd/)
#### Mac
First install [Homebrew](https://brew.sh/). Then you can run these commands in Terminal:
```bash
brew cask install gcc-arm-embedded
brew cask install osxfuse && brew install tup
brew install openocd
```
#### Windows
__Note__: make sure these programs are not only installed but also added to your `PATH`.
Some instructions in this document may assume that you're using a bash command prompt, such as the Windows 10 built-in bash or [Git](https://git-scm.com/download/win) bash.
* [ARM compiler](https://developer.arm.com/open-source/gnu-toolchain/gnu-rm/downloads)
* [Tup](http://gittup.org/tup/index.html)
* [Make for Windows](http://gnuwin32.sourceforge.net/packages/make.htm)
* [OpenOCD](http://gnuarmeclipse.github.io/openocd/install/). Also follow the instructions on the ST-LINK/V2 drivers.
<br>
## Configuring the build
To customize the compile time parameters, copy or rename the file `Firmware/tup.config.default` to `Firmware/tup.config` and edit the parameters in that file:
__CONFIG_BOARD_VERSION__: The board version you're using. Can be `v3.1`, `v3.2`, `v3.3`, `v3.4-24V`, `v3.4-48V`, `v3.5-24V`, `v3.5-48V`, etc. Check for a label on the upper side of the ODrive to find out which version you have. Some ODrive versions don't specify the voltage: in that case you can read the value of the main capacitors: 120uF are 48V ODrives, 470uF are 24V ODrives.
__CONFIG_USB_PROTOCOL__: Defines which protocol the ODrive should use on the USB interface.
* `native`: The native ODrive protocol. Use this if you want to use the python tools in this repo.
* `native-stream`: Like the native ODrive protocol, but the ODrive will treat the USB connection exactly as if it was a UART connection. __Use this if you're on macOS__. This is necessary because macOS doesn't grant our python tools sufficient low-level access to treat the device as the USB device that it is.
* `ascii`: The ASCII protocol. This allows sending simple commands like position setpoints directly from the terminal to the ODrive without the use of intermediate utilities.
* `none`: Disable USB. The device will still show up when plugged in but it will ignore any commands.
__CONFIG_UART_PROTOCOL__: Defines which protocol the ODrive should use on the UART interface (GPIO1 and GPIO2). Note that UART is only supported on ODrive v3.3 and higher.
* `native`: The native ODrive protocol. Use this if you're connecting the ODrive to a PC using UART and want to use the python tools to control and setup the ODrive.
* `ascii`: The ASCII protocol. Use this option if you control the ODrive with an Arduino. The ODrive Arduino library is not yet updated to the native protocol.
* `none`: Disable UART.
You can also modify the compile-time defaults for all `.config` parameters. You will find them if you search for `AxisConfig`, `MotorConfig`, etc.
<br><br>
## Building and flashing the Firmware
1. Run `make` in the `Firmware` directory.
2. Connect the ODrive via USB and power it up.
3. Flash the firmware using [`odrivetool dfu`](odrivetool#device-firmware-update).
### Flashing using an STLink/v2 programmer
* Connect `GND`, `SWD`, and `SWC` on connector J2 to the programmer. Note: Always plug in `GND` first!
* You need to power the board by only **ONE** of the following: VCC(3.3v), 5V, or the main power connection (the DC bus). The USB port (J1) does not power the board.
* Run `make flash` in the `Firmware` directory.
If the flashing worked, you can connect to the board using the [odrivetool](getting-started#start-odrivetool).
<br><br>
## Testing
The script `tools/run_tests.py` runs a sequence of automated tests for several firmware features as well as high power burn-in tests. Some tests only need one ODrive and one motor/encoder pair while other tests need a back-to-back test rig such as [this one](https://cad.onshape.com/documents/026bda35ad5dff4d73c1d37f/w/ae302174f402737e1fdb3783/e/5ca143a6e5e24daf1fe8e434). In any case, to run the tests you need to provide a YAML file that lists the parameters of your test setup. An example can be found at [`tools/test-rig-parallel.yaml`](tools/test-rig-parallel.yaml`). The programmer serial number can be found by running `Firmware/find_programmer.sh` (make sure it has the latest formware from STM).
<div class="alert" markdown="span">The test script commands the ODrive to high currents and high motor speeds so if your ODrive is connected to anything other than a stirdy test-rig (or free spinning motors), it will probably break your machine.</div>
Example usage: `./run_tests.py --test-rig-yaml ../tools/test-rig-parallel.yaml`
<br><br>
## Debugging
* Run `make gdb`. This will reset and halt at program start. Now you can set breakpoints and run the program. If you know how to use gdb, you are good to go.
<br><br>
## Setting up an IDE
For working with the ODrive code you don't need an IDE, but the open-source IDE VSCode is recommended. It is also possible to use Eclipse. If you'd like to go that route, please see the respective configuration document:
* [Configuring VSCode](configuring-vscode.md)
* [Configuring Eclipse](configuring-eclipse.md)
<br><br>
## STM32CubeMX
This project uses the STM32CubeMX tool to generate startup code and to ease the configuration of the peripherals. You can download it from [here](http://www2.st.com/content/st_com/en/products/development-tools/software-development-tools/stm32-software-development-tools/stm32-configurators-and-code-generators/stm32cubemx.html?icmp=stm32cubemx_pron_pr-stm32cubef2_apr2014&sc=stm32cube-pr2). All CubeMX related files are in `Firmware/Board/v3`.
You will likely want the pinout for this process. It is available [here](https://docs.google.com/spreadsheets/d/1QXDCs1IRtUyG__M_9WruWOheywb-GhOwFtfPcHuN2Fg/edit#gid=404444347).
### Generate code
* Run stm32cubeMX and load the `Firmware/Board/v3/Odrive.ioc` project file.
* Press `Project -> Generate code`
* You may need to let it download some drivers and such.
* After generating/updating the code, some minor patches need to be applied. To do this, run:
`git apply Firmware/Board/v3/*.patch`
* Run `git config --local core.autocrlf input`. This will tell git that all files should be checked in with LF endings (CubeMX generates CRLF endings).
* `git status` will still claim that many files are modified but the actual diff (using `git diff`) is empty (apart from all the line ending warnings).
### Generating patchfiles
If you made changes to CubeMX generated files outside of the `USER CODE BEGIN`...`USER CODE END` sections and contribute them back, please add a patch file so that the next person who runs CubeMX doesn't run into problems.
CubeMX will reset everything outside these sections to the original state; we will capturing into a patch file the changes required to undo this resetting.
* Make sure your current desired state is committed.
* Make a new temporary branch: `git checkout -b cubemx_temp`
* Run the CubeMX code generation as described in the previous section, including applying previous patches.
* The diff will now _not_ be empty since CubeMX reset your changes.
* Stage this state and commit it with a message like "CubeMX reset my changes".
* Run `git revert HEAD` to undo the resetting action CubeMX's regeneration had. This is the commit which you will export, so write a meaningful commit message.
* Run `git format-patch HEAD~1` to export the commit as patch file.
* Check out your previous branch and then force-delete the temporary branch: `git branch -D cubemx_temp`
* Move the patch file to `Firmware/Board/v3/` and add it in a new commit.
<br><br>
## Troubleshooting
### `LIBUSB_ERROR_IO` when flashing with the STLink/v2
**Problem:** when I try to flash the ODrive with the STLink using `make flash` I get this error:
```
Open On-Chip Debugger 0.10.0
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
Info : auto-selecting first available session transport "hla_swd". To override use 'transport select <transport>'.
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
adapter speed: 2000 kHz
adapter_nsrst_delay: 100
none separate
Info : Unable to match requested speed 2000 kHz, using 1800 kHz
Info : Unable to match requested speed 2000 kHz, using 1800 kHz
Info : clock speed 1800 kHz
Error: libusb_open() failed with LIBUSB_ERROR_IO
Error: open failed
in procedure 'init'
in procedure 'ocd_bouncer'
```
**Solution:**
This happens from time to time.
1. Unplug the STLink and all ODrives from your computer
2. Power off the ODrive you're trying to flash
3. Plug in the STLink into your computer
4. Power on the ODrive
5. Run `make flash` again
## Documentation
All *.md files in the `docs/` directory of the master branch are served up by GitHub Pages on [this domain](https://docs.odriverobotics.com).
* Theme: [minimal](https://github.com/pages-themes/minimal) by [orderedlist](https://github.com/orderedlist)
* HTML layout: `docs/_layouts/default.html`
* CSS style: `docs/assets/css/styles.scss`
* Site index: `docs/_data/index.yaml`
To run the docs server locally:
```bash
cd docs
gem install bundler
bundle install --path ruby-bundle
bundle exec jekyll serve --host=0.0.0.0
```
## Releases
We use GitHub Releases to provide firmware releases.
1. Cut off the changelog to reflect the new release
2. Merge the release candidate into master.
3. Push a (lightweight) tag to the master branch. Follow the existing naming convention.
4. Push the python tools to PyPI.
5. Edit the release on GitHub to add a title and description (copy&paste from changelog).
## Other code maintenance notes
The cortex M4F processor has hardware single precision float unit. However double precision operations are not accelerated, and hence should be avoided. The following regex is helpful for cleaning out double constants:
find: `([-+]?[0-9]+\.[0-9]+(?:[eE][-+]?[0-9]+)?)([^f0-9e])`
replace: `\1f\2`
<br><br>
## Notes for Contributors
In general the project uses the [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html), except that the default indendtation is 4 spaces, and that the 80 character limit is not very strictly enforced, merely encouraged.
+49
View File
@@ -0,0 +1,49 @@
# Encoders
## Known and Supported Encoders
Contributions to this table highly encouraged!
Encoder|Manufacturer|Output|Index|Max CPR|Max RPM|Voltage|Supported|Price|Link|Datasheet
:--|:--|:--|:-:|:-:|:-:|:-:|:--|:--|:--|:--
AMT102|CUI|Quadrature|Yes|8192|-|5V|Yes|-|-|-
AMT203|CUI|Quadrature + Absolute|Yes|4096|-|5V|Quadrature Only|-|-|-
AS5047P|AMS|Quadrature + Absolute|Yes|4096|28k|3.3V or 5V|Quadrature Only|-|-|-
E6B2-CWZ6C|Omron|Quadrature|Yes|8000|-|5-24V|Yes|-|-|[Datasheet](http://www.ia.omron.com/products/family/487/)
J733|-|Quadrature|No|2400|-|5-24V|Yes|-|-|-
## Encoder Calibration
All encoder types that are currently supported require the ODrive to do some sort of encoder calibration at every startup before you can run the motor control. Take this into account when designing your application.
### Encoder without index signal
During encoder offset calibration the rotor must be allowed to rotate without any biased load during startup. That means mass and weak friction loads are fine, but gravity or spring loads are not okay.
In the `odrivetool`, type `<axis>.encoder.requested_state = AXIS_STATE_ENCODER_OFFSET_CALIBRATION` <kbd>Enter</kbd>.
To verify everything went well, check the following variables:
* `<axis>.error` should be 0.
* `<axis>.encoder.config.offset` - This should print a number, like -326 or 1364.
* `<axis>.motor.config.direction` - This should print 1 or -1.
### Encoder with index signal
If you have an encoder with an index (Z) signal, you may avoid having to do the offset calibration on every startup, and instead use the index signal to re-sync the encoder to a stored calibration.
Below are the steps to do the one-time calibration and configuration. Note that you can follow these steps with one motor at a time, or all motors together, as you wish.
* Since you will only do this once, it is recommended that you mechanically disengage the motor from anything other than the encoder, so it can spin freely.
* Set `<axis>.encoder.config.use_index` to `True`.
* Run `<axis>.requested_state = AXIS_STATE_ENCODER_INDEX_SEARCH`. This will make the motor turn in one direction until it finds the encoder index.
* Follow the calibration instructions for an [encoder without index signal](#encoder-without-index-signal).
* Set `<axis>.encoder.config.pre_calibrated` to `True` to confirm that the offset is valid with respect to the index pulse.
* If you would like to search for the index at startup, set `<axis>.config.startup_encoder_index_search` to `True`.
* If you'd rather do it manually, just run `<axis>.requested_state = AXIS_STATE_ENCODER_INDEX_SEARCH` on every bootup.
* If you are looking to start your machine as quickly as possible on bootup, also set `<axis>.motor.config.pre_calibrated` to `True` to save the current motor calibration and avoid doing it again on bootup.
* Save the configuration by typing `<odrv>.save_configuration()` <kbd>Enter</kbd>.
That's it, now on every reboot the motor will turn in one direction until it finds the encoder index.
* If you wish to scan for the index pulse in the other direction (if for example your axis usually starts close to a hard-stop), you can set a negative value in `<axis>.encoder.config.idx_search_speed`.
* If your motor has problems reaching the index location due to the mechanical load, you can increase `<axis>.motor.config.calibration_current`.
+191
View File
@@ -0,0 +1,191 @@
---
redirect_from:
- /getting-started
permalink: /
---
# Getting Started
### Table of contents
<!-- MarkdownTOC depth=2 autolink=true bracket=round -->
- [Hardware Requirements](#hardware-requirements)
- [Wiring up the ODrive](#wiring-up-the-odrive)
- [Downloading and Installing Tools](#downloading-and-installing-tools)
- [Start `odrivetool`](#start-odrivetool)
- [Configure M0](#configure-m0)
- [Position control of M0](#position-control-of-m0)
- [What's next?](#whats-next)
<!-- /MarkdownTOC -->
## Hardware Requirements
You will need:
* One or two [brushless motors](https://docs.google.com/spreadsheets/d/12vzz7XVEK6YNIOqH0jAz51F5VUpc-lJEs3mmkWP1H4Y). It is fine, even recommended, to start testing with just a single motor and encoder.
* One or two [quadrature incremental encoder(s)](encoders)
* A power resistor. A good starting point would be the 50W resistor included with your ODrive.
<details><summary markdown="span">Do I really need a power resistor? What values to choose?</summary><div markdown="block">
If you don't have a brake resistor, the ODrive will pump excess power back into the power supply during deceleration to achieve the desired deceleration torque. If your power supply doesn't eat that power (which it won't if it's not a battery), the bus voltage will inevitebly rise. If you're unlucky this will break the power supply.
At some point, the ODrive's overvoltage protection will trip, after which both motors will be allowed to spin freely. Depending on your machine, this may or may not be a problem.
The power resistor values you need depends on your motor setup, and peak/average deceleration power.
To be on the safe side, think about what speed and current limits you want to set for the motor.
When braking at max speed and with maximum motor current, the power that is dissipated in the power resistor can be calulated as: `P_brake = V_emf * I_motor` where `V_emf = motor_rpm / motor_kv`.
</div></details>
* A power supply (12V-24V for the 24V board variant, 12V-48V for the 48V board variant). A battery is also fine.
## Wiring up the ODrive
<div class="alert">
Make sure you have a good mechanical connection between the encoder and the motor, slip can cause disasterous oscillations or runaway.
</div>
All non-power I/O is 3.3V output and 5V tolerant on input, on ODrive v3.3 and newer.
1. Wire up the motor phases into the 3-phase screw terminals, and the power resistor to the AUX terminal. Wire up the power source to the DC terminal, make sure to pay attention to the polarity. Do not apply power just yet.
2. Wire up the encoder(s) to J4. The A,B phases are required, and the Z (index pulse) is optional. The A,B and Z lines have 3.3k pull up resistors, for use with open-drain encoder outputs. For single ended push-pull signals with weak drive current (\<4mA), you may want to desolder the pull-ups.
![Image of ODrive all hooked up](https://docs.google.com/drawings/d/e/2PACX-1vTCD0P40Cd-wvD7Fl8UYEaxp3_UL81oI4qUVqrrCJPi6tkJeSs2rsffIXQRpdu6rNZs6-2mRKKYtILG/pub?w=1716&h=1281)
## Downloading and Installing Tools
Most instructions in this guide refer to a utility called `odrivetool`, so you should install that first.
### Windows
1. Install Python 3. We recommend the Anaconda distribution because it packs a lot of useful scientific tools, however you can also install the standalone python.
* __Anaconda__: Download the installer from [here](https://www.anaconda.com/download/#windows). Execute the downloaded file and follow the instructions.
* __Standalone Python__: Download the installer from [here](https://www.python.org/downloads/). Execute the downloaded file and follow the instructions.
* If you have Python 2 installed alongside Python 3, replace `pip` by `C:\Users\YOUR_USERNAME\AppData\Local\Programs\Python\Python36-32\Scripts\pip`. If you have trouble with this step then refer to [this walkthrough](https://www.youtube.com/watch?v=jnpC_Ib_lbc).
2. Launch the command prompt.
* __Anaconda__: In the start menu, type `Anaconda Prompt` <kbd>Enter</kbd>
* __Standalone Python__: In the start menu, type `cmd` <kbd>Enter</kbd>
3. Install dependencies by typing `pip install pywin32==222` <kbd>Enter</kbd>
3. Install the ODrive tools by typing `pip install odrive` <kbd>Enter</kbd>
4. Plug in a USB cable into the microUSB connector on ODrive, and connect it to your PC.
5. Use the [Zadig](http://zadig.akeo.ie/) utility to set ODrive driver to libusb-win32.
* Check 'List All Devices' from the options menu, and select 'ODrive 3.x Native Interface (Interface 2)'. With that selected in the device list choose 'libusb-win32' from the target driver list and then press the large 'install driver' button.
### Linux/macOS
1. [Install Python 3](https://www.python.org/downloads/).
2. Install the ODrive tools by opening a terminal and typing `pip install odrive` <kbd>Enter</kbd>
3. __Linux__: set up USB permissions
```bash
echo 'SUBSYSTEM=="usb", ATTR{idVendor}=="1209", ATTR{idProduct}=="0d[0-9][0-9]", MODE="0666"' | sudo tee /etc/udev/rules.d/50-odrive.rules
sudo udevadm control --reload-rules
sudo udevadm trigger # until you reboot you may need to do this everytime you reset the ODrive
```
## Start `odrivetool`
<div class="note" markdown="span">__ODrive v3.4 and earlier:__ Your board does not come preflashed with any firmware. Follow the instructions [here](odrivetool#device-firmware-update) before you continue.</div>
To launch the main interactive ODrive tool, type `odrivetool` <kbd>Enter</kbd>. Connect your ODrive and wait for the tool to find it. Now you can for instance type `odrv0.vbus_voltage` <kbd>Enter</kbd> to inpect the boards main supply voltage.
It should look something like this:
```text
ODrive control utility v0.4.0
Please connect your ODrive.
Type help() for help.
Connected to ODrive 306A396A3235 as odrv0
In [1]: odrv0.vbus_voltage
Out[1]: 11.97055721282959
```
The tool you're looking at is a fully capable Python command prompt, so you can type any valid python code.
You can read more about the odrivetool [here](odrivetool.md).
## Configure M0
<div class="alert">Read this section carefully, else you risk breaking something.</div>
1. Set the limits:
<details><summary markdown="span">Wait, how do I set these?</summary><div markdown="block">
In the previous step we started `odrivetool`. In there, you can assign variables directly by name.
For instance, to set the current limit of M0 to 10A you would type: `odrv0.axis0.motor.config.current_lim = 10` <kbd>Enter</kbd>
</div></details>
* The current limit: `odrv0.axis0.motor.config.current_lim` [A]. The default current limit, for safety reasons, is set to 10A. This is quite weak, and good for making sure the drive is stable. Once you have tuned the drive, you can increase this to 75A to get some performance. Note that above 75A, you must change the current amplifier gains.
* Note: The motor current and the current drawn from the power supply is not the same in general. You should not look at the power supply current to see what is going on with the motor current.
<details><summary markdown="span">Ok so tell me how it actually works then...</summary><div markdown="block">
The current in the motor is only connected to the current in the power supply _sometimes_ and other times it just cycles out of one phase and back in the other. This is what the modulation magnitude is (sometimes people call this duty cycle, but that's a bit confusing because we use SVM not straight PWM). When the modulation magnitude is 0, the average voltage seen across the motor phases is 0, and the motor current is never connected to the power supply. When the magnitude is 100%, it is always connected, and at 50% it's connected half the time, and cycled in just the motor half the time.
The largest effect on modulation magnitude is speed. There are other smaller factors, but in general: if the motor is still it's not unreasonable to have 50A in the motor from 5A on the power supply. When the motor is spinning close to top speed, the power supply current and the motor current will be somewhat close to each other.
</div></details>
* The velocity limit: `odrv0.axis0.motor.config.vel_limit` [counts/s]. The motor will be limited to this speed; again the default value is quite slow.
* You can change `odrv0.axis0.motor.config.calibration_current` [A] to the largest value you feel comfortable leaving running through the motor continously when the motor is stationary.
2. Set other hardware parameters:
* `odrv0.config.brake_resistance` [Ohm]: This is the resistance of the brake resistor. If you are not using it, you may set it to `0`.
* `odrv0.axis0.motor.config.pole_pairs`: This is the number of **magnet poles** in the rotor, **divided by two**. You can simply count the number of permanent magnets in the rotor, if you can see them. _Note: this is not the same as the number of coils in the stator._
* `odrv0.axis0.motor.config.motor_type`: This is the type of motor being used. Currently two types of motors are supported: High-current motors (`MOTOR_TYPE_HIGH_CURRENT`) and Gimbal motors (`MOTOR_TYPE_GIMBAL`).
<details><summary markdown="span">Which `motor_type` to choose?</summary><div markdown="block">
If you're using a regular hobby brushless motor like [this](https://hobbyking.com/en_us/turnigy-aerodrive-sk3-5065-236kv-brushless-outrunner-motor.html) one, you should set `motor_mode` to `MOTOR_TYPE_HIGH_CURRENT`. For low-current gimbal motors like [this](https://hobbyking.com/en_us/turnigy-hd-5208-brushless-gimbal-motor-bldc.html) one, you should choose `MOTOR_TYPE_GIMBAL`. Do not use `MOTOR_TYPE_GIMBAL` on a motor that is not a gimbal motor, as it may overheat the motor or the ODrive.
**Further detail:**
If 100's of mA of current noise is "small" for you, you can choose `MOTOR_TYPE_HIGH_CURRENT`.
If 100's of mA of current noise is "large" for you, and you do not intend to spin the motor very fast (omega * L << R), and the motor is fairly large resistance (1 ohm or larger), you can chose `MOTOR_TYPE_GIMBAL`.
If 100's of mA current noise is "large" for you, _and_ you intend to spin the motor fast, then you need to replace the shunt resistors on the ODrive.
</div></details>
* `odrv0.axis0.encoder.config.cpr`: Encoder Count Per Revolution (CPR). This is 4x the Pulse Per Revolution (PPR) value. Usually this is indicated in the datasheet of your encoder.
3. Save configuration. You can save all `.config` parameters to persistent memory such that the ODrive remembers them between power cycles.
* `odrv0.save_configuration()` <kbd>Enter</kbd>
## Position control of M0
Let's get motor 0 up and running. The procedure for motor 1 is exactly the same, so feel free to replace read "axis1" wherever it says "axis0".
1. Type `odrv0.axis0.requested_state = AXIS_STATE_FULL_CALIBRATION_SEQUENCE` <kbd>Enter</kbd>. After about 2 seconds should hear a beep. Then the motor will turn slowly in one direction for a few seconds, then back in the other direction.
<details><summary markdown="span">What's the point of this?</summary><div markdown="block">
This procedure first measures your motor's electrical properties (namely phase resistance and phase inductance) and then the offset between the motor's electrical phase and the encoder position.
</div></details>
The startup procedure is demonstrated [here](https://www.youtube.com/watch?v=VCX1bA2xnuY).
**Note**: the rotor must be allowed to rotate without any biased load during startup. That means mass and weak friction loads are fine, but gravity or spring loads are not okay. Also note that in the video, the motors spin after initalisation, but in the current software the default behaviour is not like that.
<details><summary markdown="span">My motor doesn't beep or doesn't turn</summary><div markdown="block">
Make sure the motor wires are connected firmly. Check the value of `odrv0.axis0.error` and then refer to the [error code documentation](troubleshooting.md#error-codes) for details.
Once you have understood the error and fixed its cause, you may clear the error state (`odrv0.axis0.error = 0` <kbd>Enter</kbd>) and retry. You may also need to clear the error state of other subcomponents (e.g. `odrv0.axis0.motor.error`).
</div></details>
<!--1. Type `odrv0.axis0.motor.config.pre_calibrated = True` <kbd>Enter</kbd> and then `odrv0.save_configuration()` <kbd>Enter</kbd>. This will save all the configuration and calibration you just did so the next time you start the device it's already ready to go. Except for one thing: you need to run the encoder offset calibration after every power cycle. -->
2. Type `odrv0.axis0.requested_state = AXIS_STATE_CLOSED_CONTROL_LOOP` <kbd>Enter</kbd>. From now on the ODrive will try to hold the motor's position. If you try to turn it by hand, it will fight you gently. That is unless you bump up `odrv0.axis0.motor.config.current_lim`, in which case it will fight you more fiercely.
## What's next?
You can now:
* See what other [commands and parameters](commands.md) are available, including setting tuning parameters for better performance.
* Control the ODrive from your own program or hook it up to an existing system through one of it's [interfaces](interfaces).
* See how you can improve the behavior during the startup procedure, like [bypassing encoder calibration](encoders.md#encoder-with-index-signal).
If you have any issues or any questions please get in touch. The [ODrive Community](https://discourse.odriverobotics.com/) warmly welcomes you.
+154
View File
@@ -0,0 +1,154 @@
# Interfaces
<div class="alert"> While developing custom ODrive control code it is recommend that your motors are free to spin continuously and are not connected to a drivetrain with limited travel. </div>
The ODrive can be controlled over various ports and protocols. If you're comfortable with embedded systems development, you can also run custom code directly on the ODrive. For that refer to the [developer documentation](developer-guide.md).
### Table of contents
<!-- MarkdownTOC depth=2 autolink=true bracket=round -->
- [Pinout](#pinout)
- [Native Protocol](#native-protocol)
- [ASCII Protocol](#ascii-protocol) (and Arduino)
- [Step/direction](#stepdirection)
- [RC PWM input](#rc-pwm-input) (coming soon)
- [Ports](#ports)
- [USB](#usb)
- [UART](#uart)
<!-- /MarkdownTOC -->
## Pinout
| GPIO | primary | step/dir | other |
|-----------|-----------|---------------|-------------------------|
| GPIO1 | UART TX | Axis0 Step | Analog input, PWM input |
| GPIO2 | UART RX | Axis0 Dir | Analog input, PWM input |
| GPIO3 | | Axis1 Step (+)| Analog input, PWM input |
| GPIO4 | | Axis1 Dir (+) | Analog input, PWM input |
| GPIO5 | | | Analog input (*) |
| GPIO6 (*) | | | |
| GPIO7 (*) | | Axis1 Step (*)| |
| GPIO8 (*) | | Axis1 Dir (*) | |
(+) on ODrive v3.4 and earlier <br>
(*) ODrive v3.5 and later
ODrive v3.3 and onward have 5V tolerant GPIO pins.
## Native Protocol
This protocol is what the ODrive Tool uses to talk to the ODrive. If you have a choice, this is the recommended protocol for all applications. The native protocol runs on USB and can also be configured to run on UART.
#### Python
The ODrive Tool you installed as part of the [Getting Started guide](getting-started#downloading-and-installing-tools) comes with a library that you can use to easily control the ODrive from Python.
Assuming you already installed the odrive library (`pip install odrive`), the simplest program to control the ODrive is this:
```python
import odrive
odrv0 = odrive.find_any()
print(str(odrv0.vbus_voltage))
```
For a more comprehensive example, see [tools/demo.py](../tools/demo.py).
#### Other languages
We don't have an official library for you just yet. Check the community, there might be someone working on it. If you want to write a library yourself, refer to the [native protocol specification](protocol). You are of course welcome to contribute it back.
## ASCII protocol
This is a simpler alternative to the native protocol if you don't need all its bells and whistles. Before you use this, be sure that you're ok with its limitations. The ASCII protocol is enabled by default on UART and can also be enabled on USB alongside with the native protocol.
For more details, see the [ASCII protocol specification](ascii-protocol.md).
### Arduino
There is an Arduino library that gives some expamples on how to use the ASCII protocol to communicate with the ODrive. TODO LINK HERE.
## Step/direction
This is the simplest possible way of controlling the ODrive. It is also the most primitive and fragile one. So don't use it unless you must interoperate with other hardware that you don't control.
Pinout:
* Step/dir signals: see [Pinout](#pinout) above.
* GND: you must connect the grounds of the devices together. Use any GND pin on J3 of the ODrive.
To enable step/dir mode for the GPIO, set `<axis>.config.enable_step_dir` to true for each axis that you wish to use this on.
Axis 0 step/dir pins conflicts with UART, and the UART takes priority. So to be able to use step/dir on Axis 0, you must also set `odrv0.config.enable_uart = False`.
To apply these settings you must reboot, and to keep them on reboot you must save configuration:
* `odrv0.save_configuration()`
* `odrv0.reboot()`
There is also a config variable called `<axis>.config.counts_per_step`, which specifies how many encoder counts a "step" corresponds to. It can be any floating point value.
The maximum step rate is pending tests, but it should handle at least 50kHz. If you want to test it, please be aware that the failure mode on too high step rates is expected to be that the motors shuts down and coasts.
Please be aware that there is no enable line right now, and the step/direction interface is enabled by default, and remains active as long as the ODrive is in position control mode. To get the ODrive to go into position control mode at bootup, see how to configure the [startup procedure](commands.md#startup-procedure).
<!--
## RC PWM input
You can control the ODrive directly from an hobby RC receiver.
Up to 4 channels (GPIOs 1, 2, 3 and 4) can be used simultaneously if the respective pins are not assigned to other functions. Any of the numerical parameters that are writable from the ODrive Tool can be hooked up to a PWM input.
As an example, we'll configure GPIO4 to control the angle of axis 0. We want the axis to move within a range of -1500 to 1500 encoder counts.
1. Make sure you're able control the axis 0 angle by writing to `odrv0.axis0.controller.pos_setpoint`. If you need help with this follow the [getting started guide](getting-started.md).
2. It is recommended that you configure the ODrive such that axis 0 automatically goes operational after a reboot. You may have to set `odrv0.axis0.config.startup_encoder_offset_calibration` and `odrv0.axis0.config.startup_closed_loop_control` to `True`. The exact procedure may vary depending on what type of encoder you're using.
3. In ODrive Tool, configure the PWM input mapping
```
In [1]: odrv0.config.gpio4_pwm_mapping.min = -1500
In [2]: odrv0.config.gpio4_pwm_mapping.max = 1500
In [3]: odrv0.config.gpio4_pwm_mapping.endpoint = odrv0.axis0.controller._remote_attributes['pos_setpoint']
```
Note: you can disable the input by setting `odrv0.config.gpio4_pwm_mapping.endpoint = None`
4. Save the configuration and reboot
```
In [4]: odrv0.save_configuration()
In [5]: odrv0.reboot()
```
5. Connect the RC receiver ground to the ODrive's GND and one of the RC receiver signals to GPIO4. You may try to power the receiver from the ODrive's 5V supply if it doesn't draw too much power. Power up the the RC transmitter. You should now be able to control axis 0 from one of the RC sticks.
-->
## Ports
Note: when you use an existing library you don't have to deal with the specifics described in this section.
### USB
This section assumes that you are familiar with the general USB architecture, in particular with terms like "configuration", "interface" and "endpoint".
On USB the ODrive provides a single configuration which is a composite device consisting of a CDC device (virtual COM port) and a vendor specific device.
<details><summary markdown="span">What is a composite device?</summary><div markdown="block">
A composite device is a device where interfaces are grouped by interface association descriptors. For such devices, the host OS loads an intermediate driver, so that each of the interface groups can be treated like a separate device and have its own host-side driver attached.
</div></details>
On the ODrive, the following interface groups are present:
* Interface Association: Communication Device Class (CDC)
* Interface 0:
* Endpoint `0x82`: CDC commands
* Interface 1:
* Endpoint `0x01`: CDC data OUT
* Endpoint `0x81`: CDC data IN
* Interface Association: Vendor Specific Device Class
* Interface 2:
* Endpoint `0x03`: data OUT
* Endpoint `0x83`: data IN
The endpoint pairs `0x01, 0x81` and `0x03, 0x83` behave exactly identical, only their descriptors (interface class, ...) are different.
If you plan to access the USB endpoints directly it is recommended that you use interface 2. The other interfaces (the ones associated with the CDC device) are usually claimed by the CDC driver of the host OS, so their endpoints cannot be used without first detaching the CDC driver.
### UART
Baud rate: 115200
Pinout:
* GPIO 1: Tx (connect to Rx of other device)
* GPIO 2: Rx (connect to Tx of other device)
* GND: you must connect the grounds of the devices together. Use any GND pin on J3 of the ODrive.
+133
View File
@@ -0,0 +1,133 @@
# ODrive Tool
The ODrive Tool is the accompanying PC program for the ODrive. It's main purpose is to provide an interactive shell to control the device manually, as well as some supporting functions like firmware update.
## Installation
Refer to the [Getting Started guide](getting-started#downloading-and-installing-tools).
Type `odrivetool --help` to see what features are available.
## Multiple ODrives
By default, `odrivetool` will connect to any ODrive it finds. If this is not what you want, you can select a specific ODrive.
To find the serial number of your ODrive, run `odrivetool`, connect exactly one ODrive and power it up. You should see this:
```
Connected to ODrive 306A396A3235 as odrv0
In [1]:
```
`306A396A3235` is the serial number of this particular ODrive. If you want ODrive Tool to ignore all other devices you would close it and then run `odrivetool --serial-number 306A396A3235`.
<details><summary markdown="span">My ODrive is stuck in DFU mode, can I still find the serial number?</summary><div markdown="block">
Yes, the serial number is part of the USB descriptors.
In Linux you can find it by running:
```
$ (sudo lsusb -d 1209:0d32 -v; sudo lsusb -d 0483:df11 -v) 2>/dev/null | grep iSerial
iSerial 3 385F324D3037
iSerial 3 306A396A3235
```
Here, two ODrives are connected.
</div></details>
## Configuration Backup
You can use ODrive Tool to back up and restore device configurations or transfer the configuration of one ODrive to another one.
* To save the configuration to a file on the PC, run `odrivetool backup-config my_config.json`.
* To restore the configuration form such a file, run `odrivetool restore-config my_config.json`.
## Device Firmware Update
<div class="note" markdown="span">__ODrive v3.4 or earlier__: DFU is not supported on these devices. You need to [flash with the external programmer](#flashing-with-an-stlink) instead.</div>
To update the ODrive to the newest firmware release, simply open up a terminal and run the following command:
```
~ $ odrivetool dfu
ODrive control utility v0.3.7.dev
Waiting for ODrive...
Found ODrive 308039673235 (v3.5-24V) with firmware v0.3.7-dev
Checking online for newest firmware... found v0.3.7
Downloading firmware...
Putting device 308039673235 into DFU mode...
Erasing... done
Flashing... done
Verifying... done
```
Note that this command will connect to GitHub servers to retrieve the latest firmware.
If you have a non-default configuration saved on the device, ODrive Tool will try to carry over the configuration across the firmware update. If any of the settings are removed or renamed, you will get warning messages.
<details><summary markdown="span">How to flash a custom firmware</summary><div markdown="block">
If you want to flash a specific firmware file instead of automatically downloading one, you can run `odrivetool dfu [path/to/firmware/file.hex]`.
You can download one of the officially released firmware files from [here](https://github.com/madcowswe/ODrive/releases). You will need one of the __.hex__ files (not the __.elf__ file). Make sure you select the file that matches your board version.
To compile firmware from source, refer to the [developer guide](developer-guide).
</div></details>
### Troubleshooting
* __Windows__: During the update, a new device called "STM32 BOOTLOADER" will appear. Open the [Zadig utility](http://zadig.akeo.ie/) and set the driver for "STM32 BOOTLOADER" to libusb-win32. After that the firmware update will continue.
* On some machines you will need to unplug and plug back in the USB cable to make the PC understand that we switched from regular mode to bootloader mode.
* If the DFU script can't find the device, try forcing it into DFU mode.
<details><summary markdown="span">How to force DFU mode (ODrive v3.5)</summary><div markdown="block">
Flick the DIP switch that "DFU, RUN" to "DFU" and power cycle the board. If that alone doesn't work, also connect the After you're done, put the switch back into the "RUN" position and power cycle the board again.
</div></details>
<details><summary markdown="span">How to force DFU mode (ODrive v3.1, v3.2)</summary><div markdown="block">
Connect the pin "BOOT0" to "3.3V" and power cycle the board. If that alone doesn't work, also connect the pin "GPIO1" to "GND". After you're done, remove the wires and power cycle the board again.
</div></details>
## Flashing with an STLink
This procedure is only necessary for ODrive v3.4 or earlier. You will need an STLink/v2 or compatible programmer. You should have received one with your ODrive.
1. Install OpenOCD
* **Windows:** [instructions](http://gnuarmeclipse.github.io/openocd/install/) (also follow the instructions on the ST-LINK/V2 drivers)
* **Linux:** `sudo apt-get install openocd`
* **macOS:** `brew install openocd`
2. Download the latest firmware release form [here](https://github.com/madcowswe/ODrive/releases). You will need the __.elf__ file. Make sure you select the file that matches your board version.
3. Wire up the ODrive and STLink/v2 programmer as shown in this picture:<br>
![stlink-wiring](stlink-wiring-cropped.jpg)
Power up the ODrive.
4. Open up a terminal and navigate to the directory where the firmware is.
5. Run the following command (replace `ODriveFirmware_v3.4-24V.elf` with the name of your firmware file):
```
~/Downloads $ openocd -f interface/stlink-v2.cfg -f target/stm32f4x.cfg -c init -c reset\ halt -c flash\ write_image\ erase\ ODriveFirmware_v3.4-24V.elf -c reset\ run -c exit
Open On-Chip Debugger 0.10.0
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
Info : auto-selecting first available session transport "hla_swd". To override use 'transport select <transport>'.
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
adapter speed: 2000 kHz
adapter_nsrst_delay: 100
none separate
Info : Unable to match requested speed 2000 kHz, using 1800 kHz
Info : Unable to match requested speed 2000 kHz, using 1800 kHz
Info : clock speed 1800 kHz
Info : STLINK v2 JTAG v17 API v2 SWIM v4 VID 0x0483 PID 0x3748
Info : using stlink api v2
Info : Target voltage: 3.236027
Info : stm32f4x.cpu: hardware has 6 breakpoints, 4 watchpoints
adapter speed: 2000 kHz
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x08009224 msp: 0x20020000
auto erase enabled
Info : device id = 0x10076413
Info : flash size = 1024kbytes
target halted due to breakpoint, current mode: Thread
xPSR: 0x61000000 pc: 0x20000046 msp: 0x20020000
Warn : no flash bank found for address 10000000
wrote 262144 bytes from file ODriveFirmware_v3.4-24V.elf in 10.194110s (25.113 KiB/s)
adapter speed: 2000 kHz
```
If something doesn't work, make sure `openocd` is in your `PATH` variable, check that the wires are connected properly and try with elevated privileges.
+68
View File
@@ -0,0 +1,68 @@
# ODrive Communication Protocol #
Communicating with an ODrive consists of a series of endpoint operations.
An endpoint can theoretically be any kind data serialized in any way.
There is a default seralization implementation for POD types; for custom types
you must (de)seralize yourself. In the future we may provide a default seralizer
for stucts.
The available endpoints can be enumerated by reading the JSON from endpoint 0
and can theoretically be different for each communication interface (they are not in practice).
Each endpoint operation can send bytes to one endpoint (referenced by it's ID)
and at the same time receive bytes from the same endpoint. The semantics of
these payloads are specific to each endpoint's type, the name of which is
indicated in the JSON.
For instance an int32 endpoint's input and output is a 4 byte little endian
representation. In general the convention for combined read/write requests is
_exchange_, i.e. the returned value is the old value. Custom endpoint handlers
may be non-compliant.
There is a packet based version and a stream based variant of the protocol. Each
variant is employed as appropriate. For instance USB runs the packet based variant
by default while UART runs the stream based variant.
## Packet format ##
We will call the ODrive "server" and the PC "client". A request is a message
from the PC to the ODrive and a response is a message from the ODrive to the
PC.
Each request-response transaction corresponds to a single endpoint operation.
__Request__
- __Bytes 0, 1__ Sequence number, MSB = 0
- Currently the server does not care about ordering and does not filter resent messages.
- __Bytes 2, 3__ Endpoint ID
- The IDs of all endpoints can be obtained from the JSON definition. The JSON definition can be obtained by reading from endpoint 0.
If (and only if) the MSB is set to 1 the client expects a response for this request.
- __Bytes 4, 5__ Expected response size
- The number of bytes that should be returned to the client. If the client doesn't need any response data, it can set this value to 0. The operation will still be acknowledged if the
MSB in EndpointID is set.
- __Bytes 6 to N-3__ Payload
- The length of the payload is determined by the total packet size. The format of the payload depends on the endpoint type. The endpoint type can be obtained from the JSON definition.
- __Bytes N-2, N-1__
- For endpoint 0: Protocol version (currently 1). A server shall ignore packets with other values.
- For all other endpoints: The CRC16 calculated over the JSON definition. The CRC16 init value is the protocol version (currently 1). A server shall ignore packets that set this field incorrectly. See protocol.hpp for CRC details.
__Response__
- __Bytes 0, 1__ Sequence number, MSB = 1
- The sequence number of the request to which this is the response.
- __Bytes 2, 3__ Payload
- The length of the payload tends to be equal to the number of expected bytes as indicated
in the request. The server must not expect the client to accept more bytes than it requested.
## Stream format ##
The stream based format is just a wrapper for the packet format.
- __Byte 0__ Sync byte `0xAA`
- __Bytes 1, 2__ Packet length
- Currently both parties shall only emit and accept values of 0 through 127.
- __Bytes 3__ CRC8 of bytes 0 through 2
- See protocol.hpp for CRC details.
- __Bytes 4 to N-3__ Packet
- __Bytes N-2, N-1__ CRC16
- See protocol.hpp for CRC details.
Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 997 KiB

+1
View File
@@ -0,0 +1 @@
test
+91
View File
@@ -0,0 +1,91 @@
# Troubleshooting
## Error codes
If your ODrive is not working as expected, run `odrivetool` and type `hex(<axis>.error)` <kbd>Enter</kbd> where `<axis>` is the axis that isn't working. This will display a [hexadecimal](https://en.wikipedia.org/wiki/Hexadecimal) representation of the error code. Each bit represents one error flag.
<details><summary markdown="span">Example</summary><div markdown="block">
Say you got this error output:
```python
In [1]: hex(odrv0.axis0.error)
Out[1]: '0x6'
```
Written in binary, the number `0x6` corresponds to `110`, that means bits 1 and 2 are set (counting starts at 0).
Looking at the reference below, this means that both `ERROR_DC_BUS_UNDER_VOLTAGE` and `ERROR_DC_BUS_OVER_VOLTAGE` occurred.
</div></details>
There is a slight chance that the values here are out of sync with the actual firmware. To be completely sure, check the linked definition in the source code.
### Axis error flags
Defined [here](../Firmware/MotorControl/axis.hpp)
0. `ERROR_INVALID_STATE`
1. `ERROR_DC_BUS_UNDER_VOLTAGE`
2. `ERROR_DC_BUS_OVER_VOLTAGE`
3. `ERROR_CURRENT_MEASUREMENT_TIMEOUT`
4. `ERROR_BRAKE_RESISTOR_DISARMED`
5. `ERROR_MOTOR_DISARMED`
6. `ERROR_MOTOR_FAILED` (check `.motor.error` for more details)
7. `ERROR_SENSORLESS_ESTIMATOR_FAILED` (check `.sensorless_estimator.error` for more details)
8. `ERROR_ENCODER_FAILED` (check `.encoder.error` for more details)
9. `ERROR_CONTROLLER_FAILED`
10. `ERROR_POS_CTRL_DURING_SENSORLESS`
### Motor error flags
Defined [here](../Firmware/MotorControl/motor.hpp)
0. `ERROR_PHASE_RESISTANCE_OUT_OF_RANGE`
1. `ERROR_PHASE_INDUCTANCE_OUT_OF_RANGE`
2. `ERROR_ADC_FAILED`
3. [`ERROR_DRV_FAULT`](#drv-fault)
4. `ERROR_CONTROL_DEADLINE_MISSED`
5. `ERROR_NOT_IMPLEMENTED_MOTOR_TYPE`
6. `ERROR_BRAKE_CURRENT_OUT_OF_RANGE`
7. `ERROR_NUMERICAL`
### Encoder error flags
Defined [here](../Firmware/MotorControl/encoder.hpp)
0. `ERROR_NUMERICAL`
1. `ERROR_CPR_OUT_OF_RANGE`
2. `ERROR_RESPONSE`
### Sensorless estimator error flags
Defined [here](../Firmware/MotorControl/sensorless_estimator.hpp)
0. `ERROR_NUMERICAL`
## DRV fault
The ODrive v3.4 is known to have a hardware issue whereby the motors would stop operating
when applying high currents to M0. The reported error of both motors in this case
is `ERROR_DRV_FAULT`.
The conjecture is that the high switching current creates large ripples in the
power supply of the DRV8301 gate driver chips, thus tripping its undervoltage
fault detection.
* Limit the M0 current to 40A. The lowest current at which the DRV fault was observed is 45A on one test motor and 50A on another test motor.
* Refer to [this post](https://discourse.odriverobotics.com/t/drv-fault-on-odrive-v3-4/558) for instructions for a hardware fix
## USB Connectivity Issues
* Try turning it off and on again (the ODrive, the script, the PC)
* Make sure you're using the latest firmware and python tools release
* **Linux**: Type `lsusb` to list all USB devices. Verify that your ODrive is listed.
* **Linux**: Make sure you [set up your udev rules](getting-started#downloading-and-installing-tools) correctly.
* **Windows**: Right-click on the start menu and open "Device Manager". Verify that your ODrive is listed.
* **Windows**: Use the [Zadig utility](http://zadig.akeo.ie/) to verify the driver is set to `libusb-win32`.
* Ensure that no other ODrive program is running
* Run `odrivetools` with the `--verbose` option.
* Run `PYUSB_DEBUG=debug odrivetools` to get even more log output.
* If you're a developer you can use Wireshark to capture USB traffic.