News

ROS 2021: intimidating and unfriendly

ROS on Windows is a needlessly difficult process. Read on for tips and thoughts.

For added fun, make it a drinking game! Every time something seems stupid, if it makes you lol, if you breath extra hard through your nose, or shake your head, take a sip. Maybe bring the whole bottle because you will be refilling and quite drunk by the end.

Installing ROS

The first place to go is https://www.ros.org/install/ . I chose “Get ROS Foxy Fitzroy on Ubuntu Linux, macOS, or Windows 10”, which leads to https://docs.ros.org/en/foxy/Installation.html . Already I’m having my doubts – where any other program would just download a windows installed and do the job, this thing wants you to start reading install documentation. I chose Binary Packages > Windows, because maybe it’s just one more click away. Nope!

The installer that should do all of that doesn’t exist? FINE, I’ll do it myself.

So how bad is installing Chocolatey?

Take a moment to find the install button. Notice how they seem to care more about why you should install than getting you installed. This is because they need you to really want it so that you will fight through the next steps, a masterpiece of understated comedy.

I came here to install, not subscribe to your newsletter.

The average person has already said “nope” and quit. But I’m in it for the lulz, I guess, so let’s see. I guess I’m an individual, so let’s pop open that administrative shell… https://www.howtogeek.com/194041/how-to-open-the-command-prompt-as-administrator-in-windows-8.1/ … and disable security on my Windows to get this thing in, always a good idea…

5. If you don’t see any errors, you are ready to use Chocolatey! Type choco or choco -? now, or see Getting Started for usage instructions.

What does “no errors” look like?

So friendly! Now keep that powershell open because the next steps (remember, the next steps? We were installing ROS) will want it.

Install Python and Visual C++ Redistributables

Actually, this was copy + paste + wait and not terrible. Not great, but not painful. Leave that powershell open for later.

Installing OpenSSL

The instructions are clear, here. The page I’m sent to looks like Geocities 1995. It looks like the emails Rachel in marketing sends, reply all, marked important. Super trustworthy.

Why would anyone need a screenshot of the installer? Who does that?

I’ve highlighted the link you’re looking for. Finally! A real installer! Wow! I bet when that was built they felt ahead of their time. Anyways, now it’s back to the ROS install steps. Which one are we on now, 203? 206?

But wait, there’s more!

Oh, apparently OpenSSL doesn’t install everything it should because reasons. They say I have to add something to the PATH variable, which is the Windows version of a vestigial organ, like a hindbrain or an appendix.

Leave this window open because we’ll be back!

Install Visual Studio 2019

Well…. no. I already have Visual Studio Code, which has a ROS extension.

Why do they call it visual studio? There’s no Auditory Studio. there’s no Tactile Studio. It doesn’t let me edit pictures, I can’t even embed meme gifs as method names. What marketing team came up with this? Was it the least garbage name still available?

Install additional DDS implementations (optional)

Since I have no idea what this means, I’m skipping it. A single sentence explanation would have been nice.

Install OpenCV and dependencies

Apparently OpenCV isn’t strictly necessary, and I’ll err on the side of caution and put it in anyways.

Next they want me to install cmake. So I’m using one installer to install another installer because nobody wants to play nice with a Windows installer. It’s starting to feel like robotics is hard because somebody wants it to be hard.

What’s next?

Please download these packages from this GitHub repository.

– asio.1.12.1.nupkg

– bullet.2.89.0.nupkg

– cunit.2.1.3.nupkg

– eigen-3.3.4.nupkg

– tinyxml-usestl.2.6.2.nupkg

– tinyxml2.6.0.0.nupkg

– log4cxx.0.10.0.nupkg

The page at the link has eight packages and you only need seven. Also the “download source” will not help you get all of them in a single click, because that would be against the ethos. That being the ethos set by people fired from the department of motor vehicles for being too frustrating.

Python problems

The instructions say to use powershell and paste this:

python -m pip install -U catkin_pkg cryptography empy ifcfg lark-parser lxml netifaces numpy opencv-python pyparsing pyyaml setuptools rosdistro

python cannot be found because it is not in the PATH variable. Rather than touch PATH again, I changed the start of the line to say c:\phython38\python -m… which worked better. And by better I mean a screaming red page of angry text.

Oh, so all of that was caused because something called pip was out of date? Why wasn’t it upgraded to the latest when I installed? I have to do it manually? Fine. c:\python38\python -m pip install –upgrade pip. After that I was able to run the previous command again and get a full install. Note that there were warnings in white text mixed into the other no-problem white text. If you don’t see the warnings do you have to worry about them?

Back to dependencies

“You agreed to having a pineapple put up your butt, but now I need you to confirm you also want lube.”

I hope you still have that PATH editor open, coz you still need it here! The instructions say I need to add C:\Program Files (x86)\GraphvizX.XX\bin and the powershell chocolatey said it installed to C:\Program Files\Graphviz\bin. I did the second one.

Downloading ROS 2

Remember ROS? We were installing ROS. Apparently we’ve made it through the desert and reached mount Sinai.

It is normal that the previous command, if nothing else went wrong, outputs “The system cannot find the path specified.” exactly once.

Well, about that.

So… the thing that was optional wasn’t actually? Hm. Well, the next step says to run demos and they don’t work. The step after that says Troubleshooting so I’ll just click o-oooooh wait a second, what is this?

This whole time it could have been

  • install chocolately
  • make chocolately install ROS.
  • C:\dev\ros2_foxy\local_setup.bat

?

Nope.

Although the earlier steps failed and the alternative ROS 2 build instructions worked, the demos fail to run. They say run these commands in two separate powershells:

  • ros2 run demo_nodes_cpp talker
  • ros2 run demo_nodes_py listener

Do they work? No. “ros2” does not exist on the system. “ros2.*” cannot be found anywhere in the c:\opt folder.

News

Cycloidal gearbox, 3D printed

I’m putting together a 2 stage 20:1 Cycloid Gearbox that’s designed in Fusion360 and 3D printed on a Prusa Mini. The motor is a NEMA17 with about 30Ncm torque. As I go I’m explaining some design features and areas to improve. I hope this is fun and educational for you. If you find this useful, please caress that like button and show it to a friend.

Here is the Thingiverse file.

I try to make these gearboxes because I have yet to find someone that can make the gearbox I want. If you are that someone, ping me.

Mad love for Levi Janssen’s video about cycloid design in Fusion360. I don’t remember who first suggested to me to use the mounting screws in this way, but to you I say thank you! Very interesting!

News

Make a DND Discord Dice Roller in Java (with regular expressions!)

Discord is a popular online service for gaming that really took off during the 2020’s pandemic. My friends and I play Dungeons and Dragons and we wanted a way to roll dice online without giving all our data to some wierdo company. So, being that I’m good with code, I wrote my own. Here’s my steps so that you can learn from what I did. We’re also going to use a really powerful tool called regular expressions to make reading dice requests super easy.

On the shoulders of giants

Writing my own code from scratch and figuring things out is fun and educational, but I’m getting old and I don’t have a lot of time left. To save time I used Java, available libraries, and a slightly out of date tutorial by Oliy Barrett. I recommend you read this for the basics.

Hello, World!

When using someone else’s library there’s always a bit of setup and teardown. Here’s the essentials.

token.txt is the private token for this bot. Never share it. If you check it into Github then Discord will send you a friendly email telling you the token has been rejected forever and you have to make a new one. use your .gitignore to make sure that never happens.

public class DiscordDND extends ListenerAdapter {
    static final String MY_ENTITY_ID = "***";
    static final String MY_ENTITY_NAME = "Dice Roller";
    static public final String ROLL_COMMAND = "~r";

    public static void main( String[] args ) throws LoginException {
        String token = readAllBytesFromFile(DiscordDND.class.getResource("token.txt"));
        JDA jda = JDABuilder.createDefault(token).build();
        jda.addEventListener(new DiscordDND());
    }

    private static String readAllBytesFromFile(URL filePath) {
        String content = "";
        try {
            System.out.println("Token search: "+filePath.toURI());
            content = new String ( Files.readAllBytes( Paths.get( filePath.toURI() ) ) );
        }  catch (Exception e) {
            e.printStackTrace();
	}
        return content;
    }

    @Override
    public void onMessageReceived(MessageReceivedEvent event) {
    	if(event.getAuthor().isBot()) return;

    	String message = event.getMessage().getContentDisplay();
    	
    	if(!message.startsWith(ROLL_COMMAND)) return;
    	// remove the prefix and continue
    	message = message.substring(ROLL_COMMAND.length());

        System.out.println("I heard: "+message);

        // handle the roll here
    }
}

When the app runs it loads the token, connects to Discord with the token, and gets ready to listen to things being said in the discord servers to which it has been invited. How do you invite a bot to a server? Uh… I don’t remember.

Understanding a roll request

The regular expression syntax for a dnd dice roll

The normal format for writing out a dice roll in DND is [number of dice]d(number of sides)[k(number to keep)][+/-(modifier)] with no spaces. I found a regular expression online that pretty closely matches this pattern and modified it.

  • Anything in a () is a group.
  • ? means “zero or one of the previous element” (in this case, the previous group).
  • [\+\-] means “any characters inside the [] braces”. Combined with the ? it means “at most one + or – symbol.” Because of text formatting rules in regular expressions the + and - have to escaped by putting a backslash \ in front of them.
  • \d means digit. \d+ means 1 or more digits.

So putting it all together it says:

  • The first group has a positive or negative whole number. The group is optional.
  • The second group starts with the letter ‘d’, then a positive or negative whole number. The group is required.
  • The third group starts with the letter ‘k’ and then a positive or negative whole number. The group is optional.
  • The fourth group MUST start with +/- and then a whole number. The group is optional.

Hey! Why negative dice and negative sides? It’s not that I want negative dice. But sometimes users type silly things. I thought it would be fun to catch those and deal with them in equally funny ways.

Why keep negative dice? In DND sometimes the player rolls with advantage to keep the highest dice and sometimes they roll with disadvantage to keep the lowest dice. Negative numbers mean keep the low dice.

To use regular expressions in Java (or OpenJDK) I’m using the Pattern and Matcher classes.

// remove all whitespace and the roll command from the start
String saneMessage = sanitizeMessage(event.message);

Pattern p = Pattern.compile("([\\+\\-]?\\d+)?(d[\\+\\-]?\\d+)(k[\\+\\-]?\\d+)?([\\+\\-]\\d+)?");
Matcher m = p.matcher(saneMessage);
id(m.find()) {
	int numDice=1, numSides=20, numKeep, modifier=0;
	if(m.group(1) !=null && !m.group(1).isEmpty()) numDice  = Integer.parseInt(m.group(1));
	if(m.group(2) !=null && !m.group(2).isEmpty()) numSides = Integer.parseInt(m.group(2).substring(1));
	if(m.group(3) !=null && !m.group(3).isEmpty()) numKeep  = Integer.parseInt(m.group(3).substring(1));
	else numKeep=numDice;
	if(m.group(4) !=null && !m.group(4).isEmpty()) modifier = Integer.parseInt(m.group(4));

	roll(event,numDice,numSides,numKeep,modifier);
	return;
}

In Java Strings the \ symbol is special so I have to escape them again – the double backslash is not a mistake.

Matcher returns the original expression in m.group(0). If Matcher does not find a group it returns null for that group index. That means I can reliably expect group 4 is always the modifier and so on.

Rolling and keeping

int [] rolls = rollDice(numDice,numSides);
if(numKeep!=numDice) {
	if(numKeep>0) keepSomeHighRolls(rolls,numKeep);
	else keepSomeLowRolls(rolls,-numKeep);
}
event.reply(event.actorName + ": "+renderResults(rolls,modifier));

The catch here is that I don’t want to sort the rolls into high > low because it would look wrong to the user. Organic rolls do not happen that way! I’m stuck searching for the worst roll, numRolls – numKeep times.

for(int k = numKeep;k<rolls.length;++k) {
	int worst = 0;
	for(int i=0;i<rolls.length;++i) {
		if(rolls[i]>0 && rolls[worst]>rolls[i]) worst = i;
	}
	rolls[worst] *= -1;  // mark it as rejected but keep the value.
}

Final thoughts

As a final bit of flair I add a meme pic for natural 20 and natural 1 rolls. If a natural 1 is not kept I put in Neo’s famous bullet dodge from The Matrix. What would be a good meme for a not-kept-natural-20?

All the code for this project can be found at https://github.com/i-make-robots/DiscordDND/. It’s got way more stuff!